Changeset 3743505
- Timestamp:
- 10/13/2026 10:00:51 AM (13 days ago)
- Location:
- optimazz
- Files:
-
- 6 added
- 58 edited
- 1 copied
-
tags/1.1.0 (copied) (copied from optimazz/trunk )
-
tags/1.1.0/build/index-rtl.css (modified) (1 diff)
-
tags/1.1.0/build/index.asset.php (modified) (1 diff)
-
tags/1.1.0/build/index.css (modified) (1 diff)
-
tags/1.1.0/build/index.js (modified) (1 diff)
-
tags/1.1.0/changelog.txt (modified) (1 diff)
-
tags/1.1.0/includes/Admin/Admin.php (modified) (6 diffs)
-
tags/1.1.0/includes/Admin/AdminBar.php (modified) (2 diffs)
-
tags/1.1.0/includes/Admin/CacheConflictNotice.php (modified) (2 diffs)
-
tags/1.1.0/includes/Admin/NetworkRestController.php (added)
-
tags/1.1.0/includes/Admin/RestController.php (modified) (4 diffs)
-
tags/1.1.0/includes/Core/Lifecycle.php (modified) (3 diffs)
-
tags/1.1.0/includes/Core/Network.php (added)
-
tags/1.1.0/includes/Core/Paths.php (modified) (2 diffs)
-
tags/1.1.0/includes/Core/Plugin.php (modified) (3 diffs)
-
tags/1.1.0/includes/Core/Recommendations.php (modified) (2 diffs)
-
tags/1.1.0/includes/Core/Settings.php (modified) (4 diffs)
-
tags/1.1.0/includes/Modules/Cache/BrowserCache.php (modified) (1 diff)
-
tags/1.1.0/includes/Modules/Cache/Dropin.php (modified) (5 diffs)
-
tags/1.1.0/includes/Modules/Cache/Manager.php (modified) (3 diffs)
-
tags/1.1.0/includes/Modules/Cache/Preloader.php (modified) (8 diffs)
-
tags/1.1.0/includes/Modules/Cache/Storage/DiskStorage.php (modified) (4 diffs)
-
tags/1.1.0/includes/Modules/Cache/engine.php (modified) (15 diffs)
-
tags/1.1.0/includes/Modules/Fonts/FontsModule.php (modified) (7 diffs)
-
tags/1.1.0/includes/Modules/Js/JsModule.php (modified) (1 diff)
-
tags/1.1.0/languages/optimazz.pot (modified) (18 diffs)
-
tags/1.1.0/optimazz.php (modified) (2 diffs)
-
tags/1.1.0/optimazz.pot (modified) (18 diffs)
-
tags/1.1.0/package.json (added)
-
tags/1.1.0/readme.txt (modified) (4 diffs)
-
tags/1.1.0/src/index.js (modified) (16 diffs)
-
tags/1.1.0/src/index.scss (modified) (3 diffs)
-
tags/1.1.0/uninstall.php (modified) (2 diffs)
-
trunk/build/index-rtl.css (modified) (1 diff)
-
trunk/build/index.asset.php (modified) (1 diff)
-
trunk/build/index.css (modified) (1 diff)
-
trunk/build/index.js (modified) (1 diff)
-
trunk/changelog.txt (modified) (1 diff)
-
trunk/includes/Admin/Admin.php (modified) (6 diffs)
-
trunk/includes/Admin/AdminBar.php (modified) (2 diffs)
-
trunk/includes/Admin/CacheConflictNotice.php (modified) (2 diffs)
-
trunk/includes/Admin/NetworkRestController.php (added)
-
trunk/includes/Admin/RestController.php (modified) (4 diffs)
-
trunk/includes/Core/Lifecycle.php (modified) (3 diffs)
-
trunk/includes/Core/Network.php (added)
-
trunk/includes/Core/Paths.php (modified) (2 diffs)
-
trunk/includes/Core/Plugin.php (modified) (3 diffs)
-
trunk/includes/Core/Recommendations.php (modified) (2 diffs)
-
trunk/includes/Core/Settings.php (modified) (4 diffs)
-
trunk/includes/Modules/Cache/BrowserCache.php (modified) (1 diff)
-
trunk/includes/Modules/Cache/Dropin.php (modified) (5 diffs)
-
trunk/includes/Modules/Cache/Manager.php (modified) (3 diffs)
-
trunk/includes/Modules/Cache/Preloader.php (modified) (8 diffs)
-
trunk/includes/Modules/Cache/Storage/DiskStorage.php (modified) (4 diffs)
-
trunk/includes/Modules/Cache/engine.php (modified) (15 diffs)
-
trunk/includes/Modules/Fonts/FontsModule.php (modified) (7 diffs)
-
trunk/includes/Modules/Js/JsModule.php (modified) (1 diff)
-
trunk/languages/optimazz.pot (modified) (18 diffs)
-
trunk/optimazz.php (modified) (2 diffs)
-
trunk/optimazz.pot (modified) (18 diffs)
-
trunk/package.json (added)
-
trunk/readme.txt (modified) (4 diffs)
-
trunk/src/index.js (modified) (16 diffs)
-
trunk/src/index.scss (modified) (3 diffs)
-
trunk/uninstall.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
optimazz/tags/1.1.0/build/index-rtl.css
r3723128 r3743505 1 .oz-shell{align-items:flex-start;background:#fff;border:1px solid #e7ecef;border-radius:16px;color:#0f2027;display:flex;font-size:14px;margin:10px 0 130px 20px;min-height:640px;overflow:hidden}.oz-shell *{box-sizing:border-box}.oz-shell .dashicons{font-family:dashicons}.oz-sidebar{align-self:flex-start;background:#fff;display:flex;flex:0 0 264px;flex-direction:column;padding:18px 14px}.oz-sidebar__brand{align-items:center;border-bottom:1px solid #e7ecef;display:flex;gap:12px;margin-bottom:10px;padding:10px 10px 18px}.oz-sidebar__brand strong{display:block;font-size:18px;font-weight:750;letter-spacing:-.01em}.oz-sidebar__brand span{color:#6b7a86;font-size:11px}.oz-sidebar__brand-fallback{align-items:center;display:flex;gap:12px}.oz-sidebar__foot{border-top:1px solid #e7ecef;color:#6b7a86;font-size:11px;margin-top:12px;padding:12px 12px 2px}.oz-logo{background:linear-gradient(-135deg,#1dd1a1,#10ac81);border-radius:12px;color:#fff;display:grid;font-size:26px;height:44px;place-items:center;width:44px}.oz-logo-img{display:block;height:30px;max-width:100%;width:auto}.oz-navitem{align-items:center;background:transparent;border:0;border-right:3px solid transparent;border-radius:10px 0 0 10px;color:#0f2027;cursor:pointer;display:flex;gap:12px;padding:11px 12px;text-align:right;transition:all .14s ease;width:100%}.oz-navitem>.oz-ico{color:#6b7a86;flex:0 0 20px;height:20px;transition:color .14s ease,transform .14s ease;width:20px}.oz-navitem:hover>.oz-ico{color:#0f2027}.oz-navitem.is-active>.oz-ico{color:#0c8f6b}.oz-navitem__text{flex:1;min-width:0}.oz-navitem__text strong{display:block;font-size:13.5px;font-weight:650;line-height:1.2}.oz-navitem__text small{color:#6b7a86;font-size:11px}.oz-navitem__dot{background:#1dd1a1;border-radius:50%;height:8px;width:8px}.oz-navitem:hover{background:#f4f7f8}.oz-navitem.is-active{background:rgba(29,209,161,.12);border-right-color:#1dd1a1}.oz-navitem.is-active .oz-navitem__text strong{color:#0c8f6b}.oz-content{align-self:stretch;background:#f4f7f8;border-right:1px solid #e7ecef;display:flex;flex:1;flex-direction:column;min-width:0}.oz-topbar{align-items:center;background:#fff;border-bottom:1px solid #e7ecef;display:flex;gap:14px;padding:20px 28px}.oz-topbar__icon{background:rgba(29,209,161,.14);border-radius:12px;color:#0c8f6b;display:grid;font-size:22px;height:44px;place-items:center;width:44px}.oz-topbar h1{font-size:20px;font-weight:700;margin:0;padding:0}.oz-topbar p{color:#6b7a86;font-size:12.5px;margin:0}.oz-main{display:flex;flex-direction:column;gap:16px;padding:24px 28px}.oz-welcome{align-items:center;background:linear-gradient(-125deg,rgba(29,209,161,.9),rgba(16,172,129,.92));border-radius:16px;color:#06382c;display:flex;gap:20px;justify-content:space-between;overflow:hidden;padding:22px 26px}.oz-welcome h2{color:#06382c;font-size:18px;font-weight:700;margin:0 0 6px}.oz-welcome p{color:#0c5b46;font-size:13px;margin:0;max-width:620px}.oz-welcome__icon{flex:0 0 auto;font-size:64px;height:64px;opacity:.5;width:64px}.oz-score{align-items:center;background:linear-gradient(-125deg,#1dd1a1,#10ac81 62%,#0c8f6b);border-radius:16px;box-shadow:0 16px 34px -18px rgba(12,143,107,.65);color:#06382c;display:flex;gap:30px;overflow:hidden;padding:28px 32px;position:relative}.oz-score:before{background:radial-gradient(circle,hsla(0,0%,100%,.3),transparent 62%);height:440px;left:-8%;top:-55%;width:440px}.oz-score:after,.oz-score:before{border-radius:50%;content:"";pointer-events:none;position:absolute}.oz-score:after{background:radial-gradient(circle,rgba(0,0,0,.08),transparent 60%);bottom:-70%;height:320px;right:8%;width:320px}.oz-score>*{position:relative;z-index:1}.oz-score__gauge{filter:drop-shadow(0 8px 16px rgba(3,40,30,.28));flex:0 0 auto;line-height:0;position:relative}.oz-score__grade{background:#fff;border-radius:50%;box-shadow:0 4px 10px rgba(3,40,30,.28);color:#0c8f6b;display:grid;font-size:14px;font-weight:800;height:30px;place-items:center;position:absolute;left:2px;top:8px;width:30px}.oz-score__body{flex:1;min-width:0}.oz-score__body h2{color:#06382c;font-size:21px;font-weight:750;letter-spacing:-.02em;margin:0 0 4px}.oz-score__body p{color:rgba(6,56,44,.82);font-size:13px;font-weight:500;margin:0 0 15px}.oz-score__chips{display:flex;flex-wrap:wrap;gap:8px}.oz-chip{align-items:center;background:hsla(0,0%,100%,.18);border:1px solid hsla(0,0%,100%,.28);border-radius:999px;color:rgba(6,56,44,.72);display:inline-flex;font-size:11.5px;font-weight:650;gap:6px;padding:5px 9px 5px 12px;transition:transform .15s ease,box-shadow .15s ease}.oz-chip .dashicons{font-size:15px;height:15px;opacity:.65;width:15px}.oz-chip.is-on{background:#fff;border-color:#fff;box-shadow:0 3px 8px -2px rgba(3,40,30,.22);color:#0c8f6b}.oz-chip.is-on .dashicons{color:#1dd1a1;opacity:1}.oz-chip.is-on:hover{box-shadow:0 6px 14px -4px rgba(3,40,30,.3);transform:translateY(-1px)}.oz-convert{align-items:center;background:#f4f7f8;border-radius:12px;display:flex;gap:16px;justify-content:space-between;margin-top:16px;padding:16px}.oz-convert__info{color:#6b7a86;display:flex;flex-direction:column;font-size:12.5px;gap:3px}.oz-convert__info strong{color:#0f2027;font-size:13px}.oz-progress{background:#dfe6e8;border-radius:999px;height:6px;margin-top:4px;overflow:hidden;width:200px}.oz-progress div{background:linear-gradient(-90deg,#1dd1a1,#10ac81);height:100%;transition:width .3s ease}.oz-kpis{display:grid;gap:14px;grid-template-columns:repeat(3,1fr)}.oz-kpi {background:#fff;border:1px solid #e7ecef;border-radius:14px;display:flex;flex-direction:column;padding:16px 18px}.oz-kpi>.dashicons{color:#10ac81;font-size:20px;margin-bottom:8px}.oz-kpi strong{font-size:24px;font-weight:750;min-height:28px}.oz-kpi span{color:#6b7a86;font-size:12px}.oz-quick{display:grid;gap:16px;grid-template-columns:1fr 1fr}.oz-quick__actions{display:flex;flex-wrap:wrap;gap:10px}.oz-cachelist{border-top:1px solid #e7ecef;margin:18px 0 0}.oz-cachelist>li{align-items:center;border-bottom:1px solid #f1f4f5;color:#6b7a86;display:flex;font-size:13px;gap:10px;padding:11px 2px}.oz-cachelist>li:last-child{border-bottom:0}.oz-cachelist>li .dashicons{color:#10ac81;font-size:17px}.oz-cachelist>li strong{color:#0f2027;font-weight:650;margin-right:auto}.oz-cachelist>li .oz-row-label{display:flex;flex-direction:column;line-height:1.3}.oz-cachelist>li .oz-row-label small{color:#6b7a86;font-size:11.5px;opacity:.85}.oz-status{margin:0}.oz-status li{align-items:center;border-bottom:1px solid #e7ecef;display:flex;font-size:13.5px;font-weight:600;gap:10px;padding:9px 6px}.oz-status li:last-child{border-bottom:0}.oz-status li>.oz-ico{color:#6b7a86;flex:0 0 18px;height:18px;width:18px}.oz-status__name{background:transparent;border:0;color:#0f2027;cursor:pointer;flex:1;font:inherit;font-weight:600;padding:4px 0;text-align:right}.oz-status__name:hover{color:#0c8f6b}.oz-status .components-toggle-control{margin-right:auto}.oz-card,.oz-panel{background:#fff;border:1px solid #e7ecef;border-radius:16px;overflow:visible}.oz-card .oz-card__head{border-radius:16px 16px 0 0}.oz-panel__head{padding:18px 22px 0}.oz-panel__head h2{font-size:16px;font-weight:650;margin:0}.oz-panel__head p{color:#6b7a86;font-size:12.5px;margin:4px 0 0}.oz-panel__body{padding:8px 22px 20px}.oz-panel__body .components-base-control{border-bottom:1px solid #f1f4f5;padding:14px 0}.oz-panel__body .components-base-control:last-child{border-bottom:0}.oz-card{transition:box-shadow .18s ease,border-color .18s ease}.oz-card.is-enabled{border-color:rgba(29,209,161,.5);box-shadow:0 0 0 1px rgba(29,209,161,.22)}.oz-card__head{align-items:flex-start;display:flex;gap:15px;padding:20px 22px}.oz-card__icon{background:#f4f7f8;border-radius:12px;color:#6b7a86;display:grid;flex:0 0 46px;font-size:22px;height:46px;place-items:center;width:46px}.oz-card__title{flex:1;min-width:0}.oz-card__title h2{align-items:center;display:flex;font-size:16px;font-weight:650;gap:10px;margin:0 0 3px}.oz-card__title p{color:#6b7a86;font-size:12.5px;margin:0}.oz-card__body{border-top:1px solid #e7ecef;padding:4px 22px 16px}.oz-card__body .components-base-control{border-bottom:1px solid #f1f4f5;padding:14px 0}.oz-card__body .components-base-control:last-child{border-bottom:0}.is-enabled .oz-card__icon{background:linear-gradient(-135deg,#1dd1a1,#10ac81);color:#fff}.oz-inline-actions{display:flex;gap:10px;padding-top:16px}.oz-dbgroup{border-top:1px solid #eef0f2;padding:18px 0}.oz-dbgroup:first-of-type{border-top:0;padding-top:4px}.oz-dbgroup__title{color:#0e1116;font-size:14px;font-weight:700;margin:0 0 4px}.oz-dbgroup__desc{color:#6b7280;font-size:13px;line-height:1.5;margin:0 0 14px}.oz-dbgroup .components-toggle-control{margin-bottom:14px}.oz-dbgroup .components-toggle-control:last-child{margin-bottom:0}.oz-dbwarn{align-items:flex-start;background:#fff5f5;border:1px solid #fcd9d9;border-radius:10px;color:#b42318;display:flex;font-size:13px;font-weight:600;gap:8px;line-height:1.5;margin:4px 0;padding:12px 14px}.oz-dbwarn .dashicons{color:#e05242;flex:0 0 auto}.oz-cachewarn{align-items:center;background:#fff8ec;border:1px solid #f4dcae;border-radius:12px;color:#7a4b00;display:flex;gap:13px;margin:0 0 18px;padding:14px 16px}.oz-cachewarn .dashicons{color:#e0900f;flex:0 0 auto;font-size:22px;height:22px;width:22px}.oz-cachewarn__text{flex:1 1 auto;min-width:0}.oz-cachewarn__text strong{color:#6b4200;display:block;font-size:14px;font-weight:700}.oz-cachewarn__text p{color:#8a6410;font-size:12.5px;line-height:1.5;margin:3px 0 0}.oz-cachewarn .oz-btn-primary{flex:0 0 auto}@media(max-width:640px){.oz-cachewarn{flex-wrap:wrap}.oz-cachewarn .oz-btn-primary{justify-content:center;width:100%}}.oz-subhead{color:#8a96a0;font-size:11px;font-weight:700;letter-spacing:.08em;margin:18px 0 2px;padding-top:14px;text-transform:uppercase}.oz-health,.oz-subhead{border-top:1px solid #e7ecf0}.oz-health{margin-top:18px;padding-top:16px}.oz-health__head{align-items:center;display:flex;gap:12px;justify-content:space-between}.oz-health__head strong{font-size:14px}.oz-health__hint{color:#6b7280;font-size:12px;line-height:1.5;margin:4px 0 10px}.oz-health__list{display:flex;flex-direction:column;gap:6px;margin:0}.oz-health__row{align-items:center;display:flex;font-size:12.5px;gap:8px;line-height:1.4;margin:0}.oz-health__row .dashicons{flex:0 0 auto;font-size:17px;height:17px;width:17px}.oz-health__row.is-ok{color:#216e4e}.oz-health__row.is-ok .dashicons{color:#1dd1a1}.oz-health__row.is-warn{color:#8a5a00}.oz-health__row.is-warn .dashicons{color:#e0900f}.oz-health__row.is-bad{color:#b42318}.oz-health__row.is-bad .dashicons{color:#e05242}.oz-health__err{color:#b42318;font-size:12.5px}.oz-health__fix{align-items:center;display:flex;flex-wrap:wrap;gap:12px;margin-top:14px}.oz-health__fix span{color:#6b7280;flex:1 1 160px;font-size:12px}.oz-health__conflict{display:flex;flex-direction:column;gap:8px;margin-top:12px}.oz-health__conflict-row{align-items:center;background:#fff5f5;border:1px solid #fcd9d9;border-radius:9px;display:flex;flex-wrap:wrap;gap:10px;padding:10px 12px}.oz-health__conflict-row .dashicons{color:#e05242;flex:0 0 auto}.oz-health__conflict-row span:not(.dashicons){color:#8a2f22;flex:1 1 200px;font-size:12.5px}.oz-health__conflict-row .oz-btn-danger.components-button{flex:0 0 auto;height:32px}.oz-health__manual{background:#fff8ec;border:1px solid #f4dcae;border-radius:9px;margin-top:12px;padding:10px 12px}.oz-health__manual p{color:#7a4b00;font-size:12px;margin:0 0 6px}.oz-health__manual code{background:#2b2f36;border-radius:6px;color:#e7f0ec;display:inline-block;font-size:12.5px;padding:6px 10px}.oz-btn-ghost.components-button{border:1px solid #d5dbe0;border-radius:8px;color:#384049;font-size:12.5px;height:30px}.oz-btn-ghost.components-button:hover:not(:disabled){border-color:#1dd1a1;color:#0b7a5c}.oz-pill{align-items:center;border-radius:999px;display:inline-flex;font-size:11px;font-weight:700;gap:5px;padding:3px 9px}.oz-pill i{background:currentColor;border-radius:50%;height:6px;width:6px}.oz-pill.is-on{background:rgba(29,209,161,.16);color:#0c8f6b}.oz-pill.is-off{background:#eef1f2;color:#6b7a86}.oz-shell .components-range-control__thumb,.oz-shell .components-range-control__track{background-color:#1dd1a1!important}.oz-fieldgroup{background:rgba(29,209,161,.035);border:1px solid #e7ecef;border-radius:12px;margin-top:18px;padding:16px 16px 4px}.oz-fieldgroup__title{color:#0e1116;font-size:13px;font-weight:700;margin:0 0 4px}.oz-fieldgroup__desc{color:#6b7280;font-size:12.5px;line-height:1.5;margin:0 0 14px}.oz-fieldgroup .components-base-control{margin-bottom:14px}.oz-shell .components-form-token-field__input-container{background:#fff;border:1px solid #e7ecef;border-radius:10px;min-height:44px;padding:5px 8px;transition:border-color .15s ease,box-shadow .15s ease}.oz-shell .components-form-token-field__input-container.is-active,.oz-shell .components-form-token-field__input-container:focus-within{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)}.oz-shell .components-form-token-field__token{border-radius:8px;font-size:12.5px;margin:3px;overflow:hidden}.oz-shell .components-form-token-field__token-text{background:rgba(29,209,161,.14);border-radius:0 8px 8px 0;color:#0c8f6b;font-weight:650;line-height:1.7;padding:2px 11px 2px 4px}.oz-shell .components-form-token-field__remove-token{background:rgba(29,209,161,.14);border-radius:8px 0 0 8px;color:#0c8f6b;min-width:24px;padding:0 7px;transition:background .15s ease,color .15s ease}.oz-shell .components-form-token-field__remove-token svg{fill:currentColor}.oz-shell .components-form-token-field__remove-token:hover{background:#10ac81;color:#fff}.oz-shell .components-form-token-field__input{font-size:13.5px;margin:4px;min-height:28px}.oz-shell .components-input-control__backdrop{display:none!important}.oz-shell .components-input-control__input,.oz-shell .components-select-control__input,.oz-shell .components-text-control__input{background:#fff!important;border:1px solid #e7ecef!important;border-radius:10px!important;box-shadow:none!important;color:#0f2027;font-size:13.5px;min-height:42px;padding:0 14px}.oz-shell .components-input-control__input:hover,.oz-shell .components-select-control__input:hover,.oz-shell .components-text-control__input:hover{border-color:#bcc6cc!important}.oz-shell .components-input-control__input:focus,.oz-shell .components-select-control__input:focus,.oz-shell .components-text-control__input:focus{border-color:#1dd1a1!important;box-shadow:0 0 0 3px rgba(29,209,161,.16)!important;outline:none!important}.oz-shell select.components-select-control__input{cursor:pointer}.oz-pillgroup .components-base-control__label{display:block;font-weight:600;margin-bottom:8px}.oz-pillgroup__row{background:#f0f2f4;border-radius:12px;display:inline-flex;flex-wrap:wrap;gap:6px;padding:4px}.oz-pillgroup .components-base-control__help{display:block;margin-top:8px}.oz-pillbtn{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;border-radius:9px;color:#4b5563;cursor:pointer;font-size:13px;font-weight:600;min-width:44px;padding:7px 16px;transition:background .12s ease,color .12s ease,box-shadow .12s ease}.oz-pillbtn:hover{color:#0e1116}.oz-pillbtn.is-on{background:#fff;box-shadow:0 1px 3px rgba(4,56,44,.14);color:#0c8f6b}.oz-pillbtn:focus-visible{outline:2px solid #1dd1a1;outline-offset:1px}.oz-select{position:relative}.oz-select .components-base-control__label{display:block;margin-bottom:8px}.oz-select__btn{align-items:center;background:#fff;border:1px solid #e7ecef;border-radius:10px;color:#0f2027;cursor:pointer;display:flex;font-size:13.5px;height:42px;justify-content:space-between;padding:0 14px;transition:border-color .15s ease,box-shadow .15s ease;width:100%}.oz-select__btn .dashicons{color:#0c8f6b;transition:transform .18s ease}.oz-select__btn:hover{border-color:#bcc6cc}.oz-select__btn:focus,.oz-select__btn:focus-visible{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)!important;outline:none!important}.oz-select__btn[aria-expanded=true]{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)}.oz-select__btn[aria-expanded=true] .dashicons{transform:rotate(-180deg)}.oz-select__menu{background:#fff;border:1px solid #e7ecef;border-radius:12px;box-shadow:0 14px 34px rgba(15,32,39,.18);right:0;list-style:none;margin:0;padding:6px;position:absolute;left:0;top:calc(100% + 6px);z-index:30}.oz-select__item{align-items:center;border-radius:8px;color:#0f2027;cursor:pointer;display:flex;font-size:13.5px;justify-content:space-between;padding:9px 12px}.oz-select__item .dashicons{color:#10ac81;font-size:18px}.oz-select__item.is-active,.oz-select__item:hover{background:rgba(29,209,161,.12);color:#0c8f6b}.oz-select__item.is-selected{color:#0c8f6b;font-weight:650}.oz-shell .oz-btn-danger.components-button,.oz-shell .oz-btn-ghost.components-button,.oz-shell .oz-btn-primary.components-button{border-radius:10px;font-weight:650;height:38px;transition:transform .16s cubic-bezier(.2,.7,.3,1),box-shadow .18s ease,background .16s ease,border-color .16s ease,color .16s ease}.oz-shell .oz-btn-danger.components-button .dashicons,.oz-shell .oz-btn-danger.components-button svg,.oz-shell .oz-btn-ghost.components-button .dashicons,.oz-shell .oz-btn-ghost.components-button svg,.oz-shell .oz-btn-primary.components-button .dashicons,.oz-shell .oz-btn-primary.components-button svg{transition:transform .22s cubic-bezier(.2,.7,.3,1)}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled),.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled),.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){transform:translateY(-1px)}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) svg{transform:scale(1.14)}.oz-shell .oz-btn-danger.components-button:active:not(:disabled),.oz-shell .oz-btn-ghost.components-button:active:not(:disabled),.oz-shell .oz-btn-primary.components-button:active:not(:disabled){transform:translateY(0) scale(.975);transition-duration:.05s}.oz-shell .oz-btn-danger.components-button:focus-visible,.oz-shell .oz-btn-ghost.components-button:focus-visible,.oz-shell .oz-btn-primary.components-button:focus-visible{outline:2px solid #1dd1a1;outline-offset:2px}.oz-shell .oz-btn-danger.components-button:disabled,.oz-shell .oz-btn-ghost.components-button:disabled,.oz-shell .oz-btn-primary.components-button:disabled{opacity:.5;transform:none}.oz-shell .oz-btn-primary.components-button{background:linear-gradient(-135deg,#1dd1a1,#10ac81);border:0;box-shadow:0 1px 2px rgba(20,60,48,.12);color:#fff;padding:0 18px}.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){background:linear-gradient(-135deg,#10ac81,#0c8f6b);box-shadow:0 8px 20px -7px rgba(29,209,161,.65);color:#fff}.oz-shell .oz-btn-primary.components-button.is-busy{animation:oz-btn-sheen 1.4s linear infinite;background-size:200% 100%}.oz-shell .oz-btn-ghost.components-button{background:#fff;border:1px solid rgba(29,209,161,.5);box-shadow:none;color:#0c8f6b}.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled){background:rgba(29,209,161,.08);border-color:#1dd1a1;box-shadow:0 6px 16px -8px rgba(29,209,161,.5);color:#0c8f6b}.oz-shell .oz-btn-danger.components-button{background:#fff;border:1px solid rgba(214,54,56,.5);color:#d63638}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled){background:rgba(214,54,56,.08);border-color:#d63638;box-shadow:0 6px 16px -8px rgba(214,54,56,.5);color:#b32d2e}.oz-shell .oz-btn--x.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn--x.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg{transform:scale(1.1) rotate(-90deg)}@media(prefers-reduced-motion:reduce){.oz-shell .oz-btn--x.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn--x.components-button:hover:not(:disabled) svg{transform:none}}@keyframes oz-btn-sheen{0%{background-image:linear-gradient(-135deg,#1dd1a1,#10ac81,#1dd1a1);background-position:-100% 0}to{background-image:linear-gradient(-135deg,#1dd1a1,#10ac81,#1dd1a1);background-position:300% 0}}@media(prefers-reduced-motion:reduce){.oz-shell .oz-btn-danger.components-button,.oz-shell .oz-btn-ghost.components-button,.oz-shell .oz-btn-primary.components-button{transition:background .16s ease,color .16s ease,border-color .16s ease}.oz-shell .oz-btn-danger.components-button:active:not(:disabled),.oz-shell .oz-btn-danger.components-button:hover:not(:disabled),.oz-shell .oz-btn-ghost.components-button:active:not(:disabled),.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled),.oz-shell .oz-btn-primary.components-button:active:not(:disabled),.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){transform:none}.oz-shell .oz-btn-danger.components-button .dashicons,.oz-shell .oz-btn-danger.components-button svg,.oz-shell .oz-btn-ghost.components-button .dashicons,.oz-shell .oz-btn-ghost.components-button svg,.oz-shell .oz-btn-primary.components-button .dashicons,.oz-shell .oz-btn-primary.components-button svg{transition:none}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) svg{transform:none}.oz-shell .oz-btn-danger.components-button.is-busy,.oz-shell .oz-btn-ghost.components-button.is-busy,.oz-shell .oz-btn-primary.components-button.is-busy{animation:none}}.oz-shell .components-button.is-primary:focus:not(:disabled),.oz-shell .components-button:focus,.oz-shell .components-button:focus:not(:disabled){box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle,.oz-shell .components-form-toggle__input,.oz-shell .components-form-toggle__input:active,.oz-shell .components-form-toggle__input:focus,.oz-shell .components-form-toggle__input:focus-visible{border:none!important;box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle__track{background-color:#cdd6db!important;border:1px solid transparent!important;box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle.is-checked .components-form-toggle__track{background-color:#1dd1a1!important}.oz-shell .components-form-toggle__input:focus+.components-form-toggle__track,.oz-shell .components-form-toggle__input:focus-visible+.components-form-toggle__track{box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle__thumb{background-color:#fff!important;border:none!important;box-shadow:0 1px 2px rgba(15,32,39,.25)!important}.oz-savebar{align-items:center;background:#fff;border-top:1px solid #e7ecef;bottom:0;box-shadow:0 -6px 18px rgba(15,32,39,.06);display:flex;gap:16px;justify-content:flex-end;right:160px;padding:13px 32px;position:fixed;left:0;z-index:9}.oz-savebar__hint{color:#6b7a86;font-size:13px}.oz-savebar.is-dirty .oz-savebar__hint{color:#0c8f6b;font-weight:650}.folded .oz-savebar{right:36px}.oz-toast{bottom:72px;z-index:99}.oz-job,.oz-toast{position:fixed;left:24px}.oz-job{background:#fff;border:1px solid #e7ecef;border-radius:16px;bottom:84px;box-shadow:0 18px 50px rgba(15,32,39,.22);color:#0f2027;font-size:13px;max-width:calc(100vw - 48px);overflow:hidden;width:420px;z-index:100}.oz-job__head{align-items:center;display:flex;gap:12px;padding:16px 16px 12px}.oz-job__head .components-spinner{margin:0}.oz-job__icon-status{font-size:22px;height:22px;width:22px}.oz-job__icon-status--done{color:#10ac81}.oz-job__icon-status--stopped{color:#6b7a86}.oz-job__icon-status--error{color:#d63638}.oz-job__title{flex:1;min-width:0}.oz-job__title strong{display:block;font-size:15px;font-weight:700;line-height:1.2}.oz-job__title span{color:#6b7a86;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.oz-job__pct{color:#0f2027;font-size:13px;font-variant-numeric:tabular-nums;font-weight:700}.oz-job__icon{background:transparent;border:0;border-radius:6px;color:#6b7a86;cursor:pointer;display:inline-flex;padding:4px}.oz-job__icon:hover{background:#f4f7f8;color:#0f2027}.oz-job__icon .dashicons{font-size:18px;height:18px;width:18px}.oz-job__body{padding:0 16px 16px}.oz-job__row{align-items:baseline;display:flex;gap:12px;justify-content:space-between;margin-bottom:8px}.oz-job__row span{line-height:1.4}.oz-job__row strong{flex:0 0 auto;font-variant-numeric:tabular-nums;font-weight:700}.oz-job__bar{background:#e7ecef;border-radius:999px;height:7px;overflow:hidden}.oz-job__bar div{background:linear-gradient(-90deg,#1dd1a1,#10ac81);border-radius:999px;height:100%;transition:width .35s ease}.oz-job__bar.is-indeterminate div{animation:oz-indeterminate 1.1s ease-in-out infinite;border-radius:999px;width:40%!important}.oz-job.is-min .oz-job__bar{margin:2px 16px 16px}.oz-job.is-min .oz-job__head{padding-bottom:10px}.oz-job.is-done .oz-job__bar div{background:linear-gradient(-90deg,#10ac81,#0c8f6b)}.oz-job.is-stopped .oz-job__bar div{background:#c3ccd2}.oz-job.is-error .oz-job__bar div{background:linear-gradient(-90deg,#f16b6b,#d63638)}.oz-job__step{color:#6b7a86;font-size:12px;margin-top:8px}.oz-job__stop{align-items:center;background:transparent;border:0;color:#d63638;cursor:pointer;display:inline-flex;font-size:13px;font-weight:650;gap:6px;margin-top:14px;padding:4px 0}.oz-job__stop .dashicons{font-size:18px}.oz-job__stop:hover{color:#b32d2e}.oz-job__log{background:#11202b;border-radius:10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.7;margin-top:14px;max-height:150px;overflow-y:auto;padding:10px 12px}.oz-logline{color:#cdd8df;display:flex;gap:8px}.oz-logline__tag{color:#5fd0ad;flex:0 0 auto;font-weight:700;min-width:42px}.oz-logline--warn .oz-logline__tag{color:#f0b429}.oz-logline--error .oz-logline__tag{color:#ff6b6b}.oz-logline--done .oz-logline__tag{color:#1dd1a1}.oz-logline__text{word-break:break-word}@media(max-width:1100px){.oz-kpis,.oz-quick{grid-template-columns:1fr}}@media(max-width:1024px){.oz-score{align-items:flex-start;flex-direction:column;text-align:right}.oz-convert{align-items:stretch;flex-direction:column;gap:12px}.oz-convert .oz-btn-ghost.components-button,.oz-convert .oz-btn-primary.components-button{justify-content:center;width:100%}}.oz-mobile-toggle,.oz-nav-overlay{display:none!important}@media(max-width:1024px){.oz-shell{flex-direction:column;margin:8px 8px 110px}.oz-content{border-right:0;min-width:0;width:100%}.oz-sidebar{border-bottom:1px solid #e7ecef;border-left:0;flex:none;max-height:none;overflow:visible;padding:14px 12px;position:static;width:100%}.oz-sidebar__brand{margin-bottom:10px;padding-bottom:12px}.oz-sidebar nav{display:flex;flex-wrap:nowrap;gap:6px;overflow-x:auto;-webkit-overflow-scrolling:touch;padding-bottom:4px}.oz-navitem{border-bottom:3px solid transparent;border-right:0;border-radius:10px;flex:0 0 auto;padding:8px 12px;width:auto}.oz-navitem.is-active{border-bottom-color:#1dd1a1;border-right-color:transparent}.oz-navitem .oz-navitem__text strong{white-space:nowrap}.oz-navitem .oz-navitem__dot,.oz-navitem small,.oz-sidebar__foot{display:none}.oz-topbar{padding:16px 18px}.oz-topbar h1{font-size:18px}.oz-main{padding:16px 18px}.oz-savebar{right:0;padding:12px 18px}.oz-card__body{padding-right:18px;padding-left:18px}.oz-job{right:8px;left:8px;width:auto}}@media(max-width:560px){.oz-tiles{grid-template-columns:1fr}.oz-hero,.oz-score{padding:18px}.oz-card__head{gap:12px;padding:16px}.oz-card__icon{flex-basis:40px;height:40px;width:40px}.oz-savebar__hint{display:none}}@keyframes oz-indeterminate{0%{transform:translateX(100%)}to{transform:translateX(-350%)}}.oz-muted{color:#6b7a86;font-size:13px}.oz-reqs{list-style:none;margin:0;padding:0}.oz-req{align-items:center;border-bottom:1px solid #e7ecef;border-radius:8px;display:grid;font-size:13.5px;gap:6px 10px;grid-template-columns:20px 1fr auto;padding:10px 6px}.oz-req:last-child{border-bottom:0}.oz-req__dot{font-size:18px;height:18px;width:18px}.oz-req__label{font-weight:650}.oz-req__value{color:#6b7a86;font-weight:600;justify-self:end}.oz-req__note{color:#6b7a86;font-size:12px;font-weight:400;grid-column:2/-1;line-height:1.45;margin-top:2px}.oz-req--ok>.oz-req__dot{color:#0c8f6b}.oz-req--warn>.oz-req__dot{color:#e0900f}.oz-req--error>.oz-req__dot{color:#e05242}.oz-req--info>.oz-req__dot,.oz-req--info>.oz-req__value{color:#6b7a86}.oz-req--warn{background:rgba(224,144,15,.06)}.oz-req--error{background:rgba(224,82,66,.06)}.oz-notice{align-items:flex-start;background:rgba(224,82,66,.08);border:1px solid rgba(224,82,66,.28);border-radius:10px;color:#7a2018;display:flex;font-size:13px;gap:10px;line-height:1.5;margin:0 0 14px;padding:12px 14px}.oz-notice .dashicons{color:#e05242;flex:0 0 20px;margin-top:1px}.oz-notice--warn{background:rgba(224,144,15,.09);border-color:rgba(224,144,15,.3);color:#7a5406}.oz-notice--warn .dashicons{color:#e0900f}.oz-recs{list-style:none;margin:0;padding:0}.oz-rec{align-items:center;border-bottom:1px solid #e7ecef;border-radius:8px;display:flex;gap:12px;padding:12px 8px}.oz-rec:last-child{border-bottom:0}.oz-rec__dot{flex:0 0 20px;font-size:20px;height:20px;margin-top:1px;width:20px}.oz-rec__text{flex:1;min-width:0}.oz-rec__text strong{display:block;font-size:13.5px;font-weight:650}.oz-rec__text small{color:#6b7a86;display:block;font-size:12px;line-height:1.45;margin-top:2px}.oz-rec__btn.components-button{flex:0 0 auto}.oz-rec--high>.oz-rec__dot{color:#e05242}.oz-rec--medium>.oz-rec__dot{color:#e0900f}.oz-rec--low>.oz-rec__dot{color:#6b7a86}.oz-rec--high{background:rgba(224,82,66,.05)}.oz-rec--medium{background:rgba(224,144,15,.05)}.oz-recs-clear{align-items:center;color:#6b7a86;display:flex;font-size:13px;gap:8px}.oz-recs-clear .dashicons{color:#0c8f6b}.oz-select--compact{border-bottom:0!important;flex:none;margin:0 auto 0 0!important;padding:0!important}.oz-select--compact .oz-select__btn{background:#f7faf9;border-radius:9px;font-size:13px;font-weight:650;gap:10px;height:30px;min-width:132px;padding:0 12px 0 10px;width:auto}.oz-select--compact .oz-select__btn:disabled{cursor:progress;opacity:.6}.oz-select--compact .oz-select__btn .dashicons{font-size:16px;height:16px;width:16px}.oz-select--compact .oz-select__menu{right:auto;min-width:190px;left:0}.oz-select--compact .oz-select__item{font-size:13px;padding:8px 10px}1 .oz-shell{align-items:flex-start;background:#fff;border:1px solid #e7ecef;border-radius:16px;color:#0f2027;display:flex;font-size:14px;margin:10px 0 130px 20px;min-height:640px;overflow:hidden}.oz-shell *{box-sizing:border-box}.oz-shell .dashicons{font-family:dashicons}.oz-sidebar{align-self:flex-start;background:#fff;display:flex;flex:0 0 264px;flex-direction:column;padding:18px 14px}.oz-sidebar__brand{align-items:center;border-bottom:1px solid #e7ecef;display:flex;gap:12px;margin-bottom:10px;padding:10px 10px 18px}.oz-sidebar__brand strong{display:block;font-size:18px;font-weight:750;letter-spacing:-.01em}.oz-sidebar__brand span{color:#6b7a86;font-size:11px}.oz-sidebar__brand-fallback{align-items:center;display:flex;gap:12px}.oz-sidebar__foot{border-top:1px solid #e7ecef;color:#6b7a86;font-size:11px;margin-top:12px;padding:12px 12px 2px}.oz-logo{background:linear-gradient(-135deg,#1dd1a1,#10ac81);border-radius:12px;color:#fff;display:grid;font-size:26px;height:44px;place-items:center;width:44px}.oz-logo-img{display:block;height:30px;max-width:100%;width:auto}.oz-navitem{align-items:center;background:transparent;border:0;border-right:3px solid transparent;border-radius:10px 0 0 10px;color:#0f2027;cursor:pointer;display:flex;gap:12px;padding:11px 12px;text-align:right;transition:all .14s ease;width:100%}.oz-navitem>.oz-ico{color:#6b7a86;flex:0 0 20px;height:20px;transition:color .14s ease,transform .14s ease;width:20px}.oz-navitem:hover>.oz-ico{color:#0f2027}.oz-navitem.is-active>.oz-ico{color:#0c8f6b}.oz-navitem__text{flex:1;min-width:0}.oz-navitem__text strong{display:block;font-size:13.5px;font-weight:650;line-height:1.2}.oz-navitem__text small{color:#6b7a86;font-size:11px}.oz-navitem__dot{background:#1dd1a1;border-radius:50%;height:8px;width:8px}.oz-navitem:hover{background:#f4f7f8}.oz-navitem.is-active{background:rgba(29,209,161,.12);border-right-color:#1dd1a1}.oz-navitem.is-active .oz-navitem__text strong{color:#0c8f6b}.oz-content{align-self:stretch;background:#f4f7f8;border-right:1px solid #e7ecef;display:flex;flex:1;flex-direction:column;min-width:0}.oz-topbar{align-items:center;background:#fff;border-bottom:1px solid #e7ecef;display:flex;gap:14px;padding:20px 28px}.oz-topbar__icon{background:rgba(29,209,161,.14);border-radius:12px;color:#0c8f6b;display:grid;font-size:22px;height:44px;place-items:center;width:44px}.oz-topbar h1{font-size:20px;font-weight:700;margin:0;padding:0}.oz-topbar p{color:#6b7a86;font-size:12.5px;margin:0}.oz-main{display:flex;flex-direction:column;gap:16px;padding:24px 28px}.oz-welcome{align-items:center;background:linear-gradient(-125deg,rgba(29,209,161,.9),rgba(16,172,129,.92));border-radius:16px;color:#06382c;display:flex;gap:20px;justify-content:space-between;overflow:hidden;padding:22px 26px}.oz-welcome h2{color:#06382c;font-size:18px;font-weight:700;margin:0 0 6px}.oz-welcome p{color:#0c5b46;font-size:13px;margin:0;max-width:620px}.oz-welcome__icon{flex:0 0 auto;font-size:64px;height:64px;opacity:.5;width:64px}.oz-score{align-items:center;background:linear-gradient(-125deg,#1dd1a1,#10ac81 62%,#0c8f6b);border-radius:16px;box-shadow:0 16px 34px -18px rgba(12,143,107,.65);color:#06382c;display:flex;gap:30px;overflow:hidden;padding:28px 32px;position:relative}.oz-score:before{background:radial-gradient(circle,hsla(0,0%,100%,.3),transparent 62%);height:440px;left:-8%;top:-55%;width:440px}.oz-score:after,.oz-score:before{border-radius:50%;content:"";pointer-events:none;position:absolute}.oz-score:after{background:radial-gradient(circle,rgba(0,0,0,.08),transparent 60%);bottom:-70%;height:320px;right:8%;width:320px}.oz-score>*{position:relative;z-index:1}.oz-score__gauge{filter:drop-shadow(0 8px 16px rgba(3,40,30,.28));flex:0 0 auto;line-height:0;position:relative}.oz-score__grade{background:#fff;border-radius:50%;box-shadow:0 4px 10px rgba(3,40,30,.28);color:#0c8f6b;display:grid;font-size:14px;font-weight:800;height:30px;place-items:center;position:absolute;left:2px;top:8px;width:30px}.oz-score__body{flex:1;min-width:0}.oz-score__body h2{color:#06382c;font-size:21px;font-weight:750;letter-spacing:-.02em;margin:0 0 4px}.oz-score__body p{color:rgba(6,56,44,.82);font-size:13px;font-weight:500;margin:0 0 15px}.oz-score__chips{display:flex;flex-wrap:wrap;gap:8px}.oz-chip{align-items:center;background:hsla(0,0%,100%,.18);border:1px solid hsla(0,0%,100%,.28);border-radius:999px;color:rgba(6,56,44,.72);display:inline-flex;font-size:11.5px;font-weight:650;gap:6px;padding:5px 9px 5px 12px;transition:transform .15s ease,box-shadow .15s ease}.oz-chip .dashicons{font-size:15px;height:15px;opacity:.65;width:15px}.oz-chip.is-on{background:#fff;border-color:#fff;box-shadow:0 3px 8px -2px rgba(3,40,30,.22);color:#0c8f6b}.oz-chip.is-on .dashicons{color:#1dd1a1;opacity:1}.oz-chip.is-on:hover{box-shadow:0 6px 14px -4px rgba(3,40,30,.3);transform:translateY(-1px)}.oz-convert{align-items:center;background:#f4f7f8;border-radius:12px;display:flex;gap:16px;justify-content:space-between;margin-top:16px;padding:16px}.oz-convert__info{color:#6b7a86;display:flex;flex-direction:column;font-size:12.5px;gap:3px}.oz-convert__info strong{color:#0f2027;font-size:13px}.oz-progress{background:#dfe6e8;border-radius:999px;height:6px;margin-top:4px;overflow:hidden;width:200px}.oz-progress div{background:linear-gradient(-90deg,#1dd1a1,#10ac81);height:100%;transition:width .3s ease}.oz-kpis{display:grid;gap:14px;grid-template-columns:repeat(3,1fr)}.oz-kpis--single{grid-template-columns:1fr}.oz-kpi{background:#fff;border:1px solid #e7ecef;border-radius:14px;display:flex;flex-direction:column;padding:16px 18px}.oz-kpi>.dashicons{color:#10ac81;font-size:20px;margin-bottom:8px}.oz-kpi strong{font-size:24px;font-weight:750;min-height:28px}.oz-kpi span{color:#6b7a86;font-size:12px}.oz-quick{display:grid;gap:16px;grid-template-columns:1fr 1fr}.oz-quick__actions{display:flex;flex-wrap:wrap;gap:10px}.oz-cachelist{border-top:1px solid #e7ecef;margin:18px 0 0}.oz-cachelist>li{align-items:center;border-bottom:1px solid #f1f4f5;color:#6b7a86;display:flex;font-size:13px;gap:10px;padding:11px 2px}.oz-cachelist>li:last-child{border-bottom:0}.oz-cachelist>li .dashicons{color:#10ac81;font-size:17px}.oz-cachelist>li strong{color:#0f2027;font-weight:650;margin-right:auto}.oz-cachelist>li .oz-row-label{display:flex;flex-direction:column;line-height:1.3}.oz-cachelist>li .oz-row-label small{color:#6b7a86;font-size:11.5px;opacity:.85}.oz-status{margin:0}.oz-status li{align-items:center;border-bottom:1px solid #e7ecef;display:flex;font-size:13.5px;font-weight:600;gap:10px;padding:9px 6px}.oz-status li:last-child{border-bottom:0}.oz-status li>.oz-ico{color:#6b7a86;flex:0 0 18px;height:18px;width:18px}.oz-status__name{background:transparent;border:0;color:#0f2027;cursor:pointer;flex:1;font:inherit;font-weight:600;padding:4px 0;text-align:right}.oz-status__name:hover{color:#0c8f6b}.oz-status .components-toggle-control{margin-right:auto}.oz-card,.oz-panel{background:#fff;border:1px solid #e7ecef;border-radius:16px;overflow:visible}.oz-card .oz-card__head{border-radius:16px 16px 0 0}.oz-panel__head{padding:18px 22px 0}.oz-panel__head h2{font-size:16px;font-weight:650;margin:0}.oz-panel__head p{color:#6b7a86;font-size:12.5px;margin:4px 0 0}.oz-panel__body{padding:8px 22px 20px}.oz-panel__body .components-base-control{border-bottom:1px solid #f1f4f5;padding:14px 0}.oz-panel__body .components-base-control:last-child{border-bottom:0}.oz-card{transition:box-shadow .18s ease,border-color .18s ease}.oz-card.is-enabled{border-color:rgba(29,209,161,.5);box-shadow:0 0 0 1px rgba(29,209,161,.22)}.oz-card__head{align-items:flex-start;display:flex;gap:15px;padding:20px 22px}.oz-card__icon{background:#f4f7f8;border-radius:12px;color:#6b7a86;display:grid;flex:0 0 46px;font-size:22px;height:46px;place-items:center;width:46px}.oz-card__title{flex:1;min-width:0}.oz-card__title h2{align-items:center;display:flex;font-size:16px;font-weight:650;gap:10px;margin:0 0 3px}.oz-card__title p{color:#6b7a86;font-size:12.5px;margin:0}.oz-card__body{border-top:1px solid #e7ecef;padding:4px 22px 16px}.oz-card__body .components-base-control{border-bottom:1px solid #f1f4f5;padding:14px 0}.oz-card__body .components-base-control:last-child{border-bottom:0}.is-enabled .oz-card__icon{background:linear-gradient(-135deg,#1dd1a1,#10ac81);color:#fff}.oz-inline-actions{display:flex;gap:10px;padding-top:16px}.oz-dbgroup{border-top:1px solid #eef0f2;padding:18px 0}.oz-dbgroup:first-of-type{border-top:0;padding-top:4px}.oz-dbgroup__title{color:#0e1116;font-size:14px;font-weight:700;margin:0 0 4px}.oz-dbgroup__desc{color:#6b7280;font-size:13px;line-height:1.5;margin:0 0 14px}.oz-dbgroup .components-toggle-control{margin-bottom:14px}.oz-dbgroup .components-toggle-control:last-child{margin-bottom:0}.oz-dbwarn{align-items:flex-start;background:#fff5f5;border:1px solid #fcd9d9;border-radius:10px;color:#b42318;display:flex;font-size:13px;font-weight:600;gap:8px;line-height:1.5;margin:4px 0;padding:12px 14px}.oz-dbwarn .dashicons{color:#e05242;flex:0 0 auto}.oz-cachewarn{align-items:center;background:#fff8ec;border:1px solid #f4dcae;border-radius:12px;color:#7a4b00;display:flex;gap:13px;margin:0 0 18px;padding:14px 16px}.oz-cachewarn .dashicons{color:#e0900f;flex:0 0 auto;font-size:22px;height:22px;width:22px}.oz-cachewarn__text{flex:1 1 auto;min-width:0}.oz-cachewarn__text strong{color:#6b4200;display:block;font-size:14px;font-weight:700}.oz-cachewarn__text p{color:#8a6410;font-size:12.5px;line-height:1.5;margin:3px 0 0}.oz-cachewarn .oz-btn-primary{flex:0 0 auto}@media(max-width:640px){.oz-cachewarn{flex-wrap:wrap}.oz-cachewarn .oz-btn-primary{justify-content:center;width:100%}}.oz-subhead{color:#8a96a0;font-size:11px;font-weight:700;letter-spacing:.08em;margin:18px 0 2px;padding-top:14px;text-transform:uppercase}.oz-health,.oz-subhead{border-top:1px solid #e7ecf0}.oz-health{margin-top:18px;padding-top:16px}.oz-health__head{align-items:center;display:flex;gap:12px;justify-content:space-between}.oz-health__head strong{font-size:14px}.oz-health__hint{color:#6b7280;font-size:12px;line-height:1.5;margin:4px 0 10px}.oz-health__list{display:flex;flex-direction:column;gap:6px;margin:0}.oz-health__row{align-items:center;display:flex;font-size:12.5px;gap:8px;line-height:1.4;margin:0}.oz-health__row .dashicons{flex:0 0 auto;font-size:17px;height:17px;width:17px}.oz-health__row.is-ok{color:#216e4e}.oz-health__row.is-ok .dashicons{color:#1dd1a1}.oz-health__row.is-warn{color:#8a5a00}.oz-health__row.is-warn .dashicons{color:#e0900f}.oz-health__row.is-bad{color:#b42318}.oz-health__row.is-bad .dashicons{color:#e05242}.oz-health__err{color:#b42318;font-size:12.5px}.oz-health__fix{align-items:center;display:flex;flex-wrap:wrap;gap:12px;margin-top:14px}.oz-health__fix span{color:#6b7280;flex:1 1 160px;font-size:12px}.oz-health__conflict{display:flex;flex-direction:column;gap:8px;margin-top:12px}.oz-health__conflict-row{align-items:center;background:#fff5f5;border:1px solid #fcd9d9;border-radius:9px;display:flex;flex-wrap:wrap;gap:10px;padding:10px 12px}.oz-health__conflict-row .dashicons{color:#e05242;flex:0 0 auto}.oz-health__conflict-row span:not(.dashicons){color:#8a2f22;flex:1 1 200px;font-size:12.5px}.oz-health__conflict-row .oz-btn-danger.components-button{flex:0 0 auto;height:32px}.oz-health__manual{background:#fff8ec;border:1px solid #f4dcae;border-radius:9px;margin-top:12px;padding:10px 12px}.oz-health__manual p{color:#7a4b00;font-size:12px;margin:0 0 6px}.oz-health__manual code{background:#2b2f36;border-radius:6px;color:#e7f0ec;display:inline-block;font-size:12.5px;padding:6px 10px}.oz-btn-ghost.components-button{border:1px solid #d5dbe0;border-radius:8px;color:#384049;font-size:12.5px;height:30px}.oz-btn-ghost.components-button:hover:not(:disabled){border-color:#1dd1a1;color:#0b7a5c}.oz-pill{align-items:center;border-radius:999px;display:inline-flex;font-size:11px;font-weight:700;gap:5px;padding:3px 9px}.oz-pill i{background:currentColor;border-radius:50%;height:6px;width:6px}.oz-pill.is-on{background:rgba(29,209,161,.16);color:#0c8f6b}.oz-pill.is-off{background:#eef1f2;color:#6b7a86}.oz-shell .components-range-control__thumb,.oz-shell .components-range-control__track{background-color:#1dd1a1!important}.oz-fieldgroup{background:rgba(29,209,161,.035);border:1px solid #e7ecef;border-radius:12px;margin-top:18px;padding:16px 16px 4px}.oz-fieldgroup__title{color:#0e1116;font-size:13px;font-weight:700;margin:0 0 4px}.oz-fieldgroup__desc{color:#6b7280;font-size:12.5px;line-height:1.5;margin:0 0 14px}.oz-fieldgroup .components-base-control{margin-bottom:14px}.oz-shell .components-form-token-field__input-container{background:#fff;border:1px solid #e7ecef;border-radius:10px;min-height:44px;padding:5px 8px;transition:border-color .15s ease,box-shadow .15s ease}.oz-shell .components-form-token-field__input-container.is-active,.oz-shell .components-form-token-field__input-container:focus-within{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)}.oz-shell .components-form-token-field__token{border-radius:8px;font-size:12.5px;margin:3px;overflow:hidden}.oz-shell .components-form-token-field__token-text{background:rgba(29,209,161,.14);border-radius:0 8px 8px 0;color:#0c8f6b;font-weight:650;line-height:1.7;padding:2px 11px 2px 4px}.oz-shell .components-form-token-field__remove-token{background:rgba(29,209,161,.14);border-radius:8px 0 0 8px;color:#0c8f6b;min-width:24px;padding:0 7px;transition:background .15s ease,color .15s ease}.oz-shell .components-form-token-field__remove-token svg{fill:currentColor}.oz-shell .components-form-token-field__remove-token:hover{background:#10ac81;color:#fff}.oz-shell .components-form-token-field__input{font-size:13.5px;margin:4px;min-height:28px}.oz-shell .components-input-control__backdrop{display:none!important}.oz-shell .components-input-control__input,.oz-shell .components-select-control__input,.oz-shell .components-text-control__input{background:#fff!important;border:1px solid #e7ecef!important;border-radius:10px!important;box-shadow:none!important;color:#0f2027;font-size:13.5px;min-height:42px;padding:0 14px}.oz-shell .components-input-control__input:hover,.oz-shell .components-select-control__input:hover,.oz-shell .components-text-control__input:hover{border-color:#bcc6cc!important}.oz-shell .components-input-control__input:focus,.oz-shell .components-select-control__input:focus,.oz-shell .components-text-control__input:focus{border-color:#1dd1a1!important;box-shadow:0 0 0 3px rgba(29,209,161,.16)!important;outline:none!important}.oz-shell select.components-select-control__input{cursor:pointer}.oz-pillgroup .components-base-control__label{display:block;font-weight:600;margin-bottom:8px}.oz-pillgroup__row{background:#f0f2f4;border-radius:12px;display:inline-flex;flex-wrap:wrap;gap:6px;padding:4px}.oz-pillgroup .components-base-control__help{display:block;margin-top:8px}.oz-pillbtn{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;border-radius:9px;color:#4b5563;cursor:pointer;font-size:13px;font-weight:600;min-width:44px;padding:7px 16px;transition:background .12s ease,color .12s ease,box-shadow .12s ease}.oz-pillbtn:hover{color:#0e1116}.oz-pillbtn.is-on{background:#fff;box-shadow:0 1px 3px rgba(4,56,44,.14);color:#0c8f6b}.oz-pillbtn:focus-visible{outline:2px solid #1dd1a1;outline-offset:1px}.oz-select{position:relative}.oz-select .components-base-control__label{display:block;margin-bottom:8px}.oz-select__btn{align-items:center;background:#fff;border:1px solid #e7ecef;border-radius:10px;color:#0f2027;cursor:pointer;display:flex;font-size:13.5px;height:42px;justify-content:space-between;padding:0 14px;transition:border-color .15s ease,box-shadow .15s ease;width:100%}.oz-select__btn .dashicons{color:#0c8f6b;transition:transform .18s ease}.oz-select__btn:hover{border-color:#bcc6cc}.oz-select__btn:focus,.oz-select__btn:focus-visible{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)!important;outline:none!important}.oz-select__btn[aria-expanded=true]{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)}.oz-select__btn[aria-expanded=true] .dashicons{transform:rotate(-180deg)}.oz-select__menu{background:#fff;border:1px solid #e7ecef;border-radius:12px;box-shadow:0 14px 34px rgba(15,32,39,.18);right:0;list-style:none;margin:0;padding:6px;position:absolute;left:0;top:calc(100% + 6px);z-index:30}.oz-select__item{align-items:center;border-radius:8px;color:#0f2027;cursor:pointer;display:flex;font-size:13.5px;justify-content:space-between;padding:9px 12px}.oz-select__item .dashicons{color:#10ac81;font-size:18px}.oz-select__item.is-active,.oz-select__item:hover{background:rgba(29,209,161,.12);color:#0c8f6b}.oz-select__item.is-selected{color:#0c8f6b;font-weight:650}.oz-shell .oz-btn-danger.components-button,.oz-shell .oz-btn-ghost.components-button,.oz-shell .oz-btn-primary.components-button{border-radius:10px;font-weight:650;height:38px;transition:transform .16s cubic-bezier(.2,.7,.3,1),box-shadow .18s ease,background .16s ease,border-color .16s ease,color .16s ease}.oz-shell .oz-btn-danger.components-button .dashicons,.oz-shell .oz-btn-danger.components-button svg,.oz-shell .oz-btn-ghost.components-button .dashicons,.oz-shell .oz-btn-ghost.components-button svg,.oz-shell .oz-btn-primary.components-button .dashicons,.oz-shell .oz-btn-primary.components-button svg{transition:transform .22s cubic-bezier(.2,.7,.3,1)}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled),.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled),.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){transform:translateY(-1px)}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) svg{transform:scale(1.14)}.oz-shell .oz-btn-danger.components-button:active:not(:disabled),.oz-shell .oz-btn-ghost.components-button:active:not(:disabled),.oz-shell .oz-btn-primary.components-button:active:not(:disabled){transform:translateY(0) scale(.975);transition-duration:.05s}.oz-shell .oz-btn-danger.components-button:focus-visible,.oz-shell .oz-btn-ghost.components-button:focus-visible,.oz-shell .oz-btn-primary.components-button:focus-visible{outline:2px solid #1dd1a1;outline-offset:2px}.oz-shell .oz-btn-danger.components-button:disabled,.oz-shell .oz-btn-ghost.components-button:disabled,.oz-shell .oz-btn-primary.components-button:disabled{opacity:.5;transform:none}.oz-shell .oz-btn-primary.components-button{background:linear-gradient(-135deg,#1dd1a1,#10ac81);border:0;box-shadow:0 1px 2px rgba(20,60,48,.12);color:#fff;padding:0 18px}.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){background:linear-gradient(-135deg,#10ac81,#0c8f6b);box-shadow:0 8px 20px -7px rgba(29,209,161,.65);color:#fff}.oz-shell .oz-btn-primary.components-button.is-busy{animation:oz-btn-sheen 1.4s linear infinite;background-size:200% 100%}.oz-shell .oz-btn-ghost.components-button{background:#fff;border:1px solid rgba(29,209,161,.5);box-shadow:none;color:#0c8f6b}.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled){background:rgba(29,209,161,.08);border-color:#1dd1a1;box-shadow:0 6px 16px -8px rgba(29,209,161,.5);color:#0c8f6b}.oz-shell .oz-btn-danger.components-button{background:#fff;border:1px solid rgba(214,54,56,.5);color:#d63638}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled){background:rgba(214,54,56,.08);border-color:#d63638;box-shadow:0 6px 16px -8px rgba(214,54,56,.5);color:#b32d2e}.oz-shell .oz-btn--x.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn--x.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg{transform:scale(1.1) rotate(-90deg)}@media(prefers-reduced-motion:reduce){.oz-shell .oz-btn--x.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn--x.components-button:hover:not(:disabled) svg{transform:none}}@keyframes oz-btn-sheen{0%{background-image:linear-gradient(-135deg,#1dd1a1,#10ac81,#1dd1a1);background-position:-100% 0}to{background-image:linear-gradient(-135deg,#1dd1a1,#10ac81,#1dd1a1);background-position:300% 0}}@media(prefers-reduced-motion:reduce){.oz-shell .oz-btn-danger.components-button,.oz-shell .oz-btn-ghost.components-button,.oz-shell .oz-btn-primary.components-button{transition:background .16s ease,color .16s ease,border-color .16s ease}.oz-shell .oz-btn-danger.components-button:active:not(:disabled),.oz-shell .oz-btn-danger.components-button:hover:not(:disabled),.oz-shell .oz-btn-ghost.components-button:active:not(:disabled),.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled),.oz-shell .oz-btn-primary.components-button:active:not(:disabled),.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){transform:none}.oz-shell .oz-btn-danger.components-button .dashicons,.oz-shell .oz-btn-danger.components-button svg,.oz-shell .oz-btn-ghost.components-button .dashicons,.oz-shell .oz-btn-ghost.components-button svg,.oz-shell .oz-btn-primary.components-button .dashicons,.oz-shell .oz-btn-primary.components-button svg{transition:none}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) svg{transform:none}.oz-shell .oz-btn-danger.components-button.is-busy,.oz-shell .oz-btn-ghost.components-button.is-busy,.oz-shell .oz-btn-primary.components-button.is-busy{animation:none}}.oz-shell .components-button.is-primary:focus:not(:disabled),.oz-shell .components-button:focus,.oz-shell .components-button:focus:not(:disabled){box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle,.oz-shell .components-form-toggle__input,.oz-shell .components-form-toggle__input:active,.oz-shell .components-form-toggle__input:focus,.oz-shell .components-form-toggle__input:focus-visible{border:none!important;box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle__track{background-color:#cdd6db!important;border:1px solid transparent!important;box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle.is-checked .components-form-toggle__track{background-color:#1dd1a1!important}.oz-shell .components-form-toggle__input:focus+.components-form-toggle__track,.oz-shell .components-form-toggle__input:focus-visible+.components-form-toggle__track{box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle__thumb{background-color:#fff!important;border:none!important;box-shadow:0 1px 2px rgba(15,32,39,.25)!important}.oz-savebar{align-items:center;background:#fff;border-top:1px solid #e7ecef;bottom:0;box-shadow:0 -6px 18px rgba(15,32,39,.06);display:flex;gap:16px;justify-content:flex-end;right:160px;padding:13px 32px;position:fixed;left:0;z-index:9}.oz-savebar__hint{color:#6b7a86;font-size:13px}.oz-savebar.is-dirty .oz-savebar__hint{color:#0c8f6b;font-weight:650}.folded .oz-savebar{right:36px}.oz-toast{bottom:72px;z-index:99}.oz-job,.oz-toast{position:fixed;left:24px}.oz-job{background:#fff;border:1px solid #e7ecef;border-radius:16px;bottom:84px;box-shadow:0 18px 50px rgba(15,32,39,.22);color:#0f2027;font-size:13px;max-width:calc(100vw - 48px);overflow:hidden;width:420px;z-index:100}.oz-job__head{align-items:center;display:flex;gap:12px;padding:16px 16px 12px}.oz-job__head .components-spinner{margin:0}.oz-job__icon-status{font-size:22px;height:22px;width:22px}.oz-job__icon-status--done{color:#10ac81}.oz-job__icon-status--stopped{color:#6b7a86}.oz-job__icon-status--error{color:#d63638}.oz-job__title{flex:1;min-width:0}.oz-job__title strong{display:block;font-size:15px;font-weight:700;line-height:1.2}.oz-job__title span{color:#6b7a86;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.oz-job__pct{color:#0f2027;font-size:13px;font-variant-numeric:tabular-nums;font-weight:700}.oz-job__icon{background:transparent;border:0;border-radius:6px;color:#6b7a86;cursor:pointer;display:inline-flex;padding:4px}.oz-job__icon:hover{background:#f4f7f8;color:#0f2027}.oz-job__icon .dashicons{font-size:18px;height:18px;width:18px}.oz-job__body{padding:0 16px 16px}.oz-job__row{align-items:baseline;display:flex;gap:12px;justify-content:space-between;margin-bottom:8px}.oz-job__row span{line-height:1.4}.oz-job__row strong{flex:0 0 auto;font-variant-numeric:tabular-nums;font-weight:700}.oz-job__bar{background:#e7ecef;border-radius:999px;height:7px;overflow:hidden}.oz-job__bar div{background:linear-gradient(-90deg,#1dd1a1,#10ac81);border-radius:999px;height:100%;transition:width .35s ease}.oz-job__bar.is-indeterminate div{animation:oz-indeterminate 1.1s ease-in-out infinite;border-radius:999px;width:40%!important}.oz-job.is-min .oz-job__bar{margin:2px 16px 16px}.oz-job.is-min .oz-job__head{padding-bottom:10px}.oz-job.is-done .oz-job__bar div{background:linear-gradient(-90deg,#10ac81,#0c8f6b)}.oz-job.is-stopped .oz-job__bar div{background:#c3ccd2}.oz-job.is-error .oz-job__bar div{background:linear-gradient(-90deg,#f16b6b,#d63638)}.oz-job__step{color:#6b7a86;font-size:12px;margin-top:8px}.oz-job__stop{align-items:center;background:transparent;border:0;color:#d63638;cursor:pointer;display:inline-flex;font-size:13px;font-weight:650;gap:6px;margin-top:14px;padding:4px 0}.oz-job__stop .dashicons{font-size:18px}.oz-job__stop:hover{color:#b32d2e}.oz-job__log{background:#11202b;border-radius:10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.7;margin-top:14px;max-height:150px;overflow-y:auto;padding:10px 12px}.oz-logline{color:#cdd8df;display:flex;gap:8px}.oz-logline__tag{color:#5fd0ad;flex:0 0 auto;font-weight:700;min-width:42px}.oz-logline--warn .oz-logline__tag{color:#f0b429}.oz-logline--error .oz-logline__tag{color:#ff6b6b}.oz-logline--done .oz-logline__tag{color:#1dd1a1}.oz-logline__text{word-break:break-word}@media(max-width:1100px){.oz-kpis,.oz-quick{grid-template-columns:1fr}}@media(max-width:1024px){.oz-score{align-items:flex-start;flex-direction:column;text-align:right}.oz-convert{align-items:stretch;flex-direction:column;gap:12px}.oz-convert .oz-btn-ghost.components-button,.oz-convert .oz-btn-primary.components-button{justify-content:center;width:100%}}.oz-mobile-toggle,.oz-nav-overlay{display:none!important}@media(max-width:1024px){.oz-shell{flex-direction:column;margin:8px 8px 110px}.oz-content{border-right:0;min-width:0;width:100%}.oz-sidebar{border-bottom:1px solid #e7ecef;border-left:0;flex:none;max-height:none;overflow:visible;padding:14px 12px;position:static;width:100%}.oz-sidebar__brand{margin-bottom:10px;padding-bottom:12px}.oz-sidebar nav{display:flex;flex-wrap:nowrap;gap:6px;overflow-x:auto;-webkit-overflow-scrolling:touch;padding-bottom:4px}.oz-navitem{border-bottom:3px solid transparent;border-right:0;border-radius:10px;flex:0 0 auto;padding:8px 12px;width:auto}.oz-navitem.is-active{border-bottom-color:#1dd1a1;border-right-color:transparent}.oz-navitem .oz-navitem__text strong{white-space:nowrap}.oz-navitem .oz-navitem__dot,.oz-navitem small,.oz-sidebar__foot{display:none}.oz-topbar{padding:16px 18px}.oz-topbar h1{font-size:18px}.oz-main{padding:16px 18px}.oz-savebar{right:0;padding:12px 18px}.oz-card__body{padding-right:18px;padding-left:18px}.oz-job{right:8px;left:8px;width:auto}}@media(max-width:560px){.oz-tiles{grid-template-columns:1fr}.oz-hero,.oz-score{padding:18px}.oz-card__head{gap:12px;padding:16px}.oz-card__icon{flex-basis:40px;height:40px;width:40px}.oz-savebar__hint{display:none}}@keyframes oz-indeterminate{0%{transform:translateX(100%)}to{transform:translateX(-350%)}}.oz-muted{color:#6b7a86;font-size:13px}.oz-reqs{list-style:none;margin:0;padding:0}.oz-req{align-items:center;border-bottom:1px solid #e7ecef;border-radius:8px;display:grid;font-size:13.5px;gap:6px 10px;grid-template-columns:20px 1fr auto;padding:10px 6px}.oz-req:last-child{border-bottom:0}.oz-req__dot{font-size:18px;height:18px;width:18px}.oz-req__label{font-weight:650}.oz-req__value{color:#6b7a86;font-weight:600;justify-self:end}.oz-req__note{color:#6b7a86;font-size:12px;font-weight:400;grid-column:2/-1;line-height:1.45;margin-top:2px}.oz-req--ok>.oz-req__dot{color:#0c8f6b}.oz-req--warn>.oz-req__dot{color:#e0900f}.oz-req--error>.oz-req__dot{color:#e05242}.oz-req--info>.oz-req__dot,.oz-req--info>.oz-req__value{color:#6b7a86}.oz-req--warn{background:rgba(224,144,15,.06)}.oz-req--error{background:rgba(224,82,66,.06)}.oz-notice{align-items:flex-start;background:rgba(224,82,66,.08);border:1px solid rgba(224,82,66,.28);border-radius:10px;color:#7a2018;display:flex;font-size:13px;gap:10px;line-height:1.5;margin:0 0 14px;padding:12px 14px}.oz-notice .dashicons{color:#e05242;flex:0 0 20px;margin-top:1px}.oz-notice--warn{background:rgba(224,144,15,.09);border-color:rgba(224,144,15,.3);color:#7a5406}.oz-notice--warn .dashicons{color:#e0900f}.oz-notice--ok{background:#eaf8f3;border-color:#b8e6d6;color:#0f5e48}.oz-notice--ok .dashicons{color:#0e9f7e}.oz-recs{list-style:none;margin:0;padding:0}.oz-rec{align-items:center;border-bottom:1px solid #e7ecef;border-radius:8px;display:flex;gap:12px;padding:12px 8px}.oz-rec:last-child{border-bottom:0}.oz-rec__dot{flex:0 0 20px;font-size:20px;height:20px;margin-top:1px;width:20px}.oz-rec__text{flex:1;min-width:0}.oz-rec__text strong{display:block;font-size:13.5px;font-weight:650}.oz-rec__text small{color:#6b7a86;display:block;font-size:12px;line-height:1.45;margin-top:2px}.oz-rec__btn.components-button{flex:0 0 auto}.oz-rec--high>.oz-rec__dot{color:#e05242}.oz-rec--medium>.oz-rec__dot{color:#e0900f}.oz-rec--low>.oz-rec__dot{color:#6b7a86}.oz-rec--high{background:rgba(224,82,66,.05)}.oz-rec--medium{background:rgba(224,144,15,.05)}.oz-recs-clear{align-items:center;color:#6b7a86;display:flex;font-size:13px;gap:8px}.oz-recs-clear .dashicons{color:#0c8f6b}.oz-select--compact{border-bottom:0!important;flex:none;margin:0 auto 0 0!important;padding:0!important}.oz-select--compact .oz-select__btn{background:#f7faf9;border-radius:9px;font-size:13px;font-weight:650;gap:10px;height:30px;min-width:132px;padding:0 12px 0 10px;width:auto}.oz-select--compact .oz-select__btn:disabled{cursor:progress;opacity:.6}.oz-select--compact .oz-select__btn .dashicons{font-size:16px;height:16px;width:16px}.oz-select--compact .oz-select__menu{right:auto;min-width:190px;left:0}.oz-select--compact .oz-select__item{font-size:13px;padding:8px 10px}.oz-cachewarn--ok{background:#eaf8f3;border-color:#b8e6d6;color:#0f5e48}.oz-cachewarn__text p+p{margin-top:6px}.oz-cachewarn__link{color:inherit;display:inline-block;font-weight:600;margin-top:8px;text-decoration:underline}.oz-network__sites{align-items:start;margin-top:16px}.oz-network__sites .oz-panel__head h2,.oz-network__sites .oz-panel__head p{overflow-wrap:anywhere}.oz-cachelist__link{color:#1dd1a1;font-weight:600;margin-right:auto;text-decoration:none}.oz-cachelist__link:focus,.oz-cachelist__link:hover{text-decoration:underline} -
optimazz/tags/1.1.0/build/index.asset.php
r3723128 r3743505 1 <?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => ' e40d7f0648a583c6302d');1 <?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => '5a501c583130cba224b3'); -
optimazz/tags/1.1.0/build/index.css
r3723128 r3743505 1 .oz-shell{align-items:flex-start;background:#fff;border:1px solid #e7ecef;border-radius:16px;color:#0f2027;display:flex;font-size:14px;margin:10px 20px 130px 0;min-height:640px;overflow:hidden}.oz-shell *{box-sizing:border-box}.oz-shell .dashicons{font-family:dashicons}.oz-sidebar{align-self:flex-start;background:#fff;display:flex;flex:0 0 264px;flex-direction:column;padding:18px 14px}.oz-sidebar__brand{align-items:center;border-bottom:1px solid #e7ecef;display:flex;gap:12px;margin-bottom:10px;padding:10px 10px 18px}.oz-sidebar__brand strong{display:block;font-size:18px;font-weight:750;letter-spacing:-.01em}.oz-sidebar__brand span{color:#6b7a86;font-size:11px}.oz-sidebar__brand-fallback{align-items:center;display:flex;gap:12px}.oz-sidebar__foot{border-top:1px solid #e7ecef;color:#6b7a86;font-size:11px;margin-top:12px;padding:12px 12px 2px}.oz-logo{background:linear-gradient(135deg,#1dd1a1,#10ac81);border-radius:12px;color:#fff;display:grid;font-size:26px;height:44px;place-items:center;width:44px}.oz-logo-img{display:block;height:30px;max-width:100%;width:auto}.oz-navitem{align-items:center;background:transparent;border:0;border-left:3px solid transparent;border-radius:0 10px 10px 0;color:#0f2027;cursor:pointer;display:flex;gap:12px;padding:11px 12px;text-align:left;transition:all .14s ease;width:100%}.oz-navitem>.oz-ico{color:#6b7a86;flex:0 0 20px;height:20px;transition:color .14s ease,transform .14s ease;width:20px}.oz-navitem:hover>.oz-ico{color:#0f2027}.oz-navitem.is-active>.oz-ico{color:#0c8f6b}.oz-navitem__text{flex:1;min-width:0}.oz-navitem__text strong{display:block;font-size:13.5px;font-weight:650;line-height:1.2}.oz-navitem__text small{color:#6b7a86;font-size:11px}.oz-navitem__dot{background:#1dd1a1;border-radius:50%;height:8px;width:8px}.oz-navitem:hover{background:#f4f7f8}.oz-navitem.is-active{background:rgba(29,209,161,.12);border-left-color:#1dd1a1}.oz-navitem.is-active .oz-navitem__text strong{color:#0c8f6b}.oz-content{align-self:stretch;background:#f4f7f8;border-left:1px solid #e7ecef;display:flex;flex:1;flex-direction:column;min-width:0}.oz-topbar{align-items:center;background:#fff;border-bottom:1px solid #e7ecef;display:flex;gap:14px;padding:20px 28px}.oz-topbar__icon{background:rgba(29,209,161,.14);border-radius:12px;color:#0c8f6b;display:grid;font-size:22px;height:44px;place-items:center;width:44px}.oz-topbar h1{font-size:20px;font-weight:700;margin:0;padding:0}.oz-topbar p{color:#6b7a86;font-size:12.5px;margin:0}.oz-main{display:flex;flex-direction:column;gap:16px;padding:24px 28px}.oz-welcome{align-items:center;background:linear-gradient(125deg,rgba(29,209,161,.9),rgba(16,172,129,.92));border-radius:16px;color:#06382c;display:flex;gap:20px;justify-content:space-between;overflow:hidden;padding:22px 26px}.oz-welcome h2{color:#06382c;font-size:18px;font-weight:700;margin:0 0 6px}.oz-welcome p{color:#0c5b46;font-size:13px;margin:0;max-width:620px}.oz-welcome__icon{flex:0 0 auto;font-size:64px;height:64px;opacity:.5;width:64px}.oz-score{align-items:center;background:linear-gradient(125deg,#1dd1a1,#10ac81 62%,#0c8f6b);border-radius:16px;box-shadow:0 16px 34px -18px rgba(12,143,107,.65);color:#06382c;display:flex;gap:30px;overflow:hidden;padding:28px 32px;position:relative}.oz-score:before{background:radial-gradient(circle,hsla(0,0%,100%,.3),transparent 62%);height:440px;right:-8%;top:-55%;width:440px}.oz-score:after,.oz-score:before{border-radius:50%;content:"";pointer-events:none;position:absolute}.oz-score:after{background:radial-gradient(circle,rgba(0,0,0,.08),transparent 60%);bottom:-70%;height:320px;left:8%;width:320px}.oz-score>*{position:relative;z-index:1}.oz-score__gauge{filter:drop-shadow(0 8px 16px rgba(3,40,30,.28));flex:0 0 auto;line-height:0;position:relative}.oz-score__grade{background:#fff;border-radius:50%;box-shadow:0 4px 10px rgba(3,40,30,.28);color:#0c8f6b;display:grid;font-size:14px;font-weight:800;height:30px;place-items:center;position:absolute;right:2px;top:8px;width:30px}.oz-score__body{flex:1;min-width:0}.oz-score__body h2{color:#06382c;font-size:21px;font-weight:750;letter-spacing:-.02em;margin:0 0 4px}.oz-score__body p{color:rgba(6,56,44,.82);font-size:13px;font-weight:500;margin:0 0 15px}.oz-score__chips{display:flex;flex-wrap:wrap;gap:8px}.oz-chip{align-items:center;background:hsla(0,0%,100%,.18);border:1px solid hsla(0,0%,100%,.28);border-radius:999px;color:rgba(6,56,44,.72);display:inline-flex;font-size:11.5px;font-weight:650;gap:6px;padding:5px 12px 5px 9px;transition:transform .15s ease,box-shadow .15s ease}.oz-chip .dashicons{font-size:15px;height:15px;opacity:.65;width:15px}.oz-chip.is-on{background:#fff;border-color:#fff;box-shadow:0 3px 8px -2px rgba(3,40,30,.22);color:#0c8f6b}.oz-chip.is-on .dashicons{color:#1dd1a1;opacity:1}.oz-chip.is-on:hover{box-shadow:0 6px 14px -4px rgba(3,40,30,.3);transform:translateY(-1px)}.oz-convert{align-items:center;background:#f4f7f8;border-radius:12px;display:flex;gap:16px;justify-content:space-between;margin-top:16px;padding:16px}.oz-convert__info{color:#6b7a86;display:flex;flex-direction:column;font-size:12.5px;gap:3px}.oz-convert__info strong{color:#0f2027;font-size:13px}.oz-progress{background:#dfe6e8;border-radius:999px;height:6px;margin-top:4px;overflow:hidden;width:200px}.oz-progress div{background:linear-gradient(90deg,#1dd1a1,#10ac81);height:100%;transition:width .3s ease}.oz-kpis{display:grid;gap:14px;grid-template-columns:repeat(3,1fr)}.oz-kpi {background:#fff;border:1px solid #e7ecef;border-radius:14px;display:flex;flex-direction:column;padding:16px 18px}.oz-kpi>.dashicons{color:#10ac81;font-size:20px;margin-bottom:8px}.oz-kpi strong{font-size:24px;font-weight:750;min-height:28px}.oz-kpi span{color:#6b7a86;font-size:12px}.oz-quick{display:grid;gap:16px;grid-template-columns:1fr 1fr}.oz-quick__actions{display:flex;flex-wrap:wrap;gap:10px}.oz-cachelist{border-top:1px solid #e7ecef;margin:18px 0 0}.oz-cachelist>li{align-items:center;border-bottom:1px solid #f1f4f5;color:#6b7a86;display:flex;font-size:13px;gap:10px;padding:11px 2px}.oz-cachelist>li:last-child{border-bottom:0}.oz-cachelist>li .dashicons{color:#10ac81;font-size:17px}.oz-cachelist>li strong{color:#0f2027;font-weight:650;margin-left:auto}.oz-cachelist>li .oz-row-label{display:flex;flex-direction:column;line-height:1.3}.oz-cachelist>li .oz-row-label small{color:#6b7a86;font-size:11.5px;opacity:.85}.oz-status{margin:0}.oz-status li{align-items:center;border-bottom:1px solid #e7ecef;display:flex;font-size:13.5px;font-weight:600;gap:10px;padding:9px 6px}.oz-status li:last-child{border-bottom:0}.oz-status li>.oz-ico{color:#6b7a86;flex:0 0 18px;height:18px;width:18px}.oz-status__name{background:transparent;border:0;color:#0f2027;cursor:pointer;flex:1;font:inherit;font-weight:600;padding:4px 0;text-align:left}.oz-status__name:hover{color:#0c8f6b}.oz-status .components-toggle-control{margin-left:auto}.oz-card,.oz-panel{background:#fff;border:1px solid #e7ecef;border-radius:16px;overflow:visible}.oz-card .oz-card__head{border-radius:16px 16px 0 0}.oz-panel__head{padding:18px 22px 0}.oz-panel__head h2{font-size:16px;font-weight:650;margin:0}.oz-panel__head p{color:#6b7a86;font-size:12.5px;margin:4px 0 0}.oz-panel__body{padding:8px 22px 20px}.oz-panel__body .components-base-control{border-bottom:1px solid #f1f4f5;padding:14px 0}.oz-panel__body .components-base-control:last-child{border-bottom:0}.oz-card{transition:box-shadow .18s ease,border-color .18s ease}.oz-card.is-enabled{border-color:rgba(29,209,161,.5);box-shadow:0 0 0 1px rgba(29,209,161,.22)}.oz-card__head{align-items:flex-start;display:flex;gap:15px;padding:20px 22px}.oz-card__icon{background:#f4f7f8;border-radius:12px;color:#6b7a86;display:grid;flex:0 0 46px;font-size:22px;height:46px;place-items:center;width:46px}.oz-card__title{flex:1;min-width:0}.oz-card__title h2{align-items:center;display:flex;font-size:16px;font-weight:650;gap:10px;margin:0 0 3px}.oz-card__title p{color:#6b7a86;font-size:12.5px;margin:0}.oz-card__body{border-top:1px solid #e7ecef;padding:4px 22px 16px}.oz-card__body .components-base-control{border-bottom:1px solid #f1f4f5;padding:14px 0}.oz-card__body .components-base-control:last-child{border-bottom:0}.is-enabled .oz-card__icon{background:linear-gradient(135deg,#1dd1a1,#10ac81);color:#fff}.oz-inline-actions{display:flex;gap:10px;padding-top:16px}.oz-dbgroup{border-top:1px solid #eef0f2;padding:18px 0}.oz-dbgroup:first-of-type{border-top:0;padding-top:4px}.oz-dbgroup__title{color:#0e1116;font-size:14px;font-weight:700;margin:0 0 4px}.oz-dbgroup__desc{color:#6b7280;font-size:13px;line-height:1.5;margin:0 0 14px}.oz-dbgroup .components-toggle-control{margin-bottom:14px}.oz-dbgroup .components-toggle-control:last-child{margin-bottom:0}.oz-dbwarn{align-items:flex-start;background:#fff5f5;border:1px solid #fcd9d9;border-radius:10px;color:#b42318;display:flex;font-size:13px;font-weight:600;gap:8px;line-height:1.5;margin:4px 0;padding:12px 14px}.oz-dbwarn .dashicons{color:#e05242;flex:0 0 auto}.oz-cachewarn{align-items:center;background:#fff8ec;border:1px solid #f4dcae;border-radius:12px;color:#7a4b00;display:flex;gap:13px;margin:0 0 18px;padding:14px 16px}.oz-cachewarn .dashicons{color:#e0900f;flex:0 0 auto;font-size:22px;height:22px;width:22px}.oz-cachewarn__text{flex:1 1 auto;min-width:0}.oz-cachewarn__text strong{color:#6b4200;display:block;font-size:14px;font-weight:700}.oz-cachewarn__text p{color:#8a6410;font-size:12.5px;line-height:1.5;margin:3px 0 0}.oz-cachewarn .oz-btn-primary{flex:0 0 auto}@media(max-width:640px){.oz-cachewarn{flex-wrap:wrap}.oz-cachewarn .oz-btn-primary{justify-content:center;width:100%}}.oz-subhead{color:#8a96a0;font-size:11px;font-weight:700;letter-spacing:.08em;margin:18px 0 2px;padding-top:14px;text-transform:uppercase}.oz-health,.oz-subhead{border-top:1px solid #e7ecf0}.oz-health{margin-top:18px;padding-top:16px}.oz-health__head{align-items:center;display:flex;gap:12px;justify-content:space-between}.oz-health__head strong{font-size:14px}.oz-health__hint{color:#6b7280;font-size:12px;line-height:1.5;margin:4px 0 10px}.oz-health__list{display:flex;flex-direction:column;gap:6px;margin:0}.oz-health__row{align-items:center;display:flex;font-size:12.5px;gap:8px;line-height:1.4;margin:0}.oz-health__row .dashicons{flex:0 0 auto;font-size:17px;height:17px;width:17px}.oz-health__row.is-ok{color:#216e4e}.oz-health__row.is-ok .dashicons{color:#1dd1a1}.oz-health__row.is-warn{color:#8a5a00}.oz-health__row.is-warn .dashicons{color:#e0900f}.oz-health__row.is-bad{color:#b42318}.oz-health__row.is-bad .dashicons{color:#e05242}.oz-health__err{color:#b42318;font-size:12.5px}.oz-health__fix{align-items:center;display:flex;flex-wrap:wrap;gap:12px;margin-top:14px}.oz-health__fix span{color:#6b7280;flex:1 1 160px;font-size:12px}.oz-health__conflict{display:flex;flex-direction:column;gap:8px;margin-top:12px}.oz-health__conflict-row{align-items:center;background:#fff5f5;border:1px solid #fcd9d9;border-radius:9px;display:flex;flex-wrap:wrap;gap:10px;padding:10px 12px}.oz-health__conflict-row .dashicons{color:#e05242;flex:0 0 auto}.oz-health__conflict-row span:not(.dashicons){color:#8a2f22;flex:1 1 200px;font-size:12.5px}.oz-health__conflict-row .oz-btn-danger.components-button{flex:0 0 auto;height:32px}.oz-health__manual{background:#fff8ec;border:1px solid #f4dcae;border-radius:9px;margin-top:12px;padding:10px 12px}.oz-health__manual p{color:#7a4b00;font-size:12px;margin:0 0 6px}.oz-health__manual code{background:#2b2f36;border-radius:6px;color:#e7f0ec;display:inline-block;font-size:12.5px;padding:6px 10px}.oz-btn-ghost.components-button{border:1px solid #d5dbe0;border-radius:8px;color:#384049;font-size:12.5px;height:30px}.oz-btn-ghost.components-button:hover:not(:disabled){border-color:#1dd1a1;color:#0b7a5c}.oz-pill{align-items:center;border-radius:999px;display:inline-flex;font-size:11px;font-weight:700;gap:5px;padding:3px 9px}.oz-pill i{background:currentColor;border-radius:50%;height:6px;width:6px}.oz-pill.is-on{background:rgba(29,209,161,.16);color:#0c8f6b}.oz-pill.is-off{background:#eef1f2;color:#6b7a86}.oz-shell .components-range-control__thumb,.oz-shell .components-range-control__track{background-color:#1dd1a1!important}.oz-fieldgroup{background:rgba(29,209,161,.035);border:1px solid #e7ecef;border-radius:12px;margin-top:18px;padding:16px 16px 4px}.oz-fieldgroup__title{color:#0e1116;font-size:13px;font-weight:700;margin:0 0 4px}.oz-fieldgroup__desc{color:#6b7280;font-size:12.5px;line-height:1.5;margin:0 0 14px}.oz-fieldgroup .components-base-control{margin-bottom:14px}.oz-shell .components-form-token-field__input-container{background:#fff;border:1px solid #e7ecef;border-radius:10px;min-height:44px;padding:5px 8px;transition:border-color .15s ease,box-shadow .15s ease}.oz-shell .components-form-token-field__input-container.is-active,.oz-shell .components-form-token-field__input-container:focus-within{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)}.oz-shell .components-form-token-field__token{border-radius:8px;font-size:12.5px;margin:3px;overflow:hidden}.oz-shell .components-form-token-field__token-text{background:rgba(29,209,161,.14);border-radius:8px 0 0 8px;color:#0c8f6b;font-weight:650;line-height:1.7;padding:2px 4px 2px 11px}.oz-shell .components-form-token-field__remove-token{background:rgba(29,209,161,.14);border-radius:0 8px 8px 0;color:#0c8f6b;min-width:24px;padding:0 7px;transition:background .15s ease,color .15s ease}.oz-shell .components-form-token-field__remove-token svg{fill:currentColor}.oz-shell .components-form-token-field__remove-token:hover{background:#10ac81;color:#fff}.oz-shell .components-form-token-field__input{font-size:13.5px;margin:4px;min-height:28px}.oz-shell .components-input-control__backdrop{display:none!important}.oz-shell .components-input-control__input,.oz-shell .components-select-control__input,.oz-shell .components-text-control__input{background:#fff!important;border:1px solid #e7ecef!important;border-radius:10px!important;box-shadow:none!important;color:#0f2027;font-size:13.5px;min-height:42px;padding:0 14px}.oz-shell .components-input-control__input:hover,.oz-shell .components-select-control__input:hover,.oz-shell .components-text-control__input:hover{border-color:#bcc6cc!important}.oz-shell .components-input-control__input:focus,.oz-shell .components-select-control__input:focus,.oz-shell .components-text-control__input:focus{border-color:#1dd1a1!important;box-shadow:0 0 0 3px rgba(29,209,161,.16)!important;outline:none!important}.oz-shell select.components-select-control__input{cursor:pointer}.oz-pillgroup .components-base-control__label{display:block;font-weight:600;margin-bottom:8px}.oz-pillgroup__row{background:#f0f2f4;border-radius:12px;display:inline-flex;flex-wrap:wrap;gap:6px;padding:4px}.oz-pillgroup .components-base-control__help{display:block;margin-top:8px}.oz-pillbtn{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;border-radius:9px;color:#4b5563;cursor:pointer;font-size:13px;font-weight:600;min-width:44px;padding:7px 16px;transition:background .12s ease,color .12s ease,box-shadow .12s ease}.oz-pillbtn:hover{color:#0e1116}.oz-pillbtn.is-on{background:#fff;box-shadow:0 1px 3px rgba(4,56,44,.14);color:#0c8f6b}.oz-pillbtn:focus-visible{outline:2px solid #1dd1a1;outline-offset:1px}.oz-select{position:relative}.oz-select .components-base-control__label{display:block;margin-bottom:8px}.oz-select__btn{align-items:center;background:#fff;border:1px solid #e7ecef;border-radius:10px;color:#0f2027;cursor:pointer;display:flex;font-size:13.5px;height:42px;justify-content:space-between;padding:0 14px;transition:border-color .15s ease,box-shadow .15s ease;width:100%}.oz-select__btn .dashicons{color:#0c8f6b;transition:transform .18s ease}.oz-select__btn:hover{border-color:#bcc6cc}.oz-select__btn:focus,.oz-select__btn:focus-visible{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)!important;outline:none!important}.oz-select__btn[aria-expanded=true]{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)}.oz-select__btn[aria-expanded=true] .dashicons{transform:rotate(180deg)}.oz-select__menu{background:#fff;border:1px solid #e7ecef;border-radius:12px;box-shadow:0 14px 34px rgba(15,32,39,.18);left:0;list-style:none;margin:0;padding:6px;position:absolute;right:0;top:calc(100% + 6px);z-index:30}.oz-select__item{align-items:center;border-radius:8px;color:#0f2027;cursor:pointer;display:flex;font-size:13.5px;justify-content:space-between;padding:9px 12px}.oz-select__item .dashicons{color:#10ac81;font-size:18px}.oz-select__item.is-active,.oz-select__item:hover{background:rgba(29,209,161,.12);color:#0c8f6b}.oz-select__item.is-selected{color:#0c8f6b;font-weight:650}.oz-shell .oz-btn-danger.components-button,.oz-shell .oz-btn-ghost.components-button,.oz-shell .oz-btn-primary.components-button{border-radius:10px;font-weight:650;height:38px;transition:transform .16s cubic-bezier(.2,.7,.3,1),box-shadow .18s ease,background .16s ease,border-color .16s ease,color .16s ease}.oz-shell .oz-btn-danger.components-button .dashicons,.oz-shell .oz-btn-danger.components-button svg,.oz-shell .oz-btn-ghost.components-button .dashicons,.oz-shell .oz-btn-ghost.components-button svg,.oz-shell .oz-btn-primary.components-button .dashicons,.oz-shell .oz-btn-primary.components-button svg{transition:transform .22s cubic-bezier(.2,.7,.3,1)}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled),.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled),.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){transform:translateY(-1px)}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) svg{transform:scale(1.14)}.oz-shell .oz-btn-danger.components-button:active:not(:disabled),.oz-shell .oz-btn-ghost.components-button:active:not(:disabled),.oz-shell .oz-btn-primary.components-button:active:not(:disabled){transform:translateY(0) scale(.975);transition-duration:.05s}.oz-shell .oz-btn-danger.components-button:focus-visible,.oz-shell .oz-btn-ghost.components-button:focus-visible,.oz-shell .oz-btn-primary.components-button:focus-visible{outline:2px solid #1dd1a1;outline-offset:2px}.oz-shell .oz-btn-danger.components-button:disabled,.oz-shell .oz-btn-ghost.components-button:disabled,.oz-shell .oz-btn-primary.components-button:disabled{opacity:.5;transform:none}.oz-shell .oz-btn-primary.components-button{background:linear-gradient(135deg,#1dd1a1,#10ac81);border:0;box-shadow:0 1px 2px rgba(20,60,48,.12);color:#fff;padding:0 18px}.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){background:linear-gradient(135deg,#10ac81,#0c8f6b);box-shadow:0 8px 20px -7px rgba(29,209,161,.65);color:#fff}.oz-shell .oz-btn-primary.components-button.is-busy{animation:oz-btn-sheen 1.4s linear infinite;background-size:200% 100%}.oz-shell .oz-btn-ghost.components-button{background:#fff;border:1px solid rgba(29,209,161,.5);box-shadow:none;color:#0c8f6b}.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled){background:rgba(29,209,161,.08);border-color:#1dd1a1;box-shadow:0 6px 16px -8px rgba(29,209,161,.5);color:#0c8f6b}.oz-shell .oz-btn-danger.components-button{background:#fff;border:1px solid rgba(214,54,56,.5);color:#d63638}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled){background:rgba(214,54,56,.08);border-color:#d63638;box-shadow:0 6px 16px -8px rgba(214,54,56,.5);color:#b32d2e}.oz-shell .oz-btn--x.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn--x.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg{transform:scale(1.1) rotate(90deg)}@media(prefers-reduced-motion:reduce){.oz-shell .oz-btn--x.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn--x.components-button:hover:not(:disabled) svg{transform:none}}@keyframes oz-btn-sheen{0%{background-image:linear-gradient(135deg,#1dd1a1,#10ac81,#1dd1a1);background-position:200% 0}to{background-image:linear-gradient(135deg,#1dd1a1,#10ac81,#1dd1a1);background-position:-200% 0}}@media(prefers-reduced-motion:reduce){.oz-shell .oz-btn-danger.components-button,.oz-shell .oz-btn-ghost.components-button,.oz-shell .oz-btn-primary.components-button{transition:background .16s ease,color .16s ease,border-color .16s ease}.oz-shell .oz-btn-danger.components-button:active:not(:disabled),.oz-shell .oz-btn-danger.components-button:hover:not(:disabled),.oz-shell .oz-btn-ghost.components-button:active:not(:disabled),.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled),.oz-shell .oz-btn-primary.components-button:active:not(:disabled),.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){transform:none}.oz-shell .oz-btn-danger.components-button .dashicons,.oz-shell .oz-btn-danger.components-button svg,.oz-shell .oz-btn-ghost.components-button .dashicons,.oz-shell .oz-btn-ghost.components-button svg,.oz-shell .oz-btn-primary.components-button .dashicons,.oz-shell .oz-btn-primary.components-button svg{transition:none}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) svg{transform:none}.oz-shell .oz-btn-danger.components-button.is-busy,.oz-shell .oz-btn-ghost.components-button.is-busy,.oz-shell .oz-btn-primary.components-button.is-busy{animation:none}}.oz-shell .components-button.is-primary:focus:not(:disabled),.oz-shell .components-button:focus,.oz-shell .components-button:focus:not(:disabled){box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle,.oz-shell .components-form-toggle__input,.oz-shell .components-form-toggle__input:active,.oz-shell .components-form-toggle__input:focus,.oz-shell .components-form-toggle__input:focus-visible{border:none!important;box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle__track{background-color:#cdd6db!important;border:1px solid transparent!important;box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle.is-checked .components-form-toggle__track{background-color:#1dd1a1!important}.oz-shell .components-form-toggle__input:focus+.components-form-toggle__track,.oz-shell .components-form-toggle__input:focus-visible+.components-form-toggle__track{box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle__thumb{background-color:#fff!important;border:none!important;box-shadow:0 1px 2px rgba(15,32,39,.25)!important}.oz-savebar{align-items:center;background:#fff;border-top:1px solid #e7ecef;bottom:0;box-shadow:0 -6px 18px rgba(15,32,39,.06);display:flex;gap:16px;justify-content:flex-end;left:160px;padding:13px 32px;position:fixed;right:0;z-index:9}.oz-savebar__hint{color:#6b7a86;font-size:13px}.oz-savebar.is-dirty .oz-savebar__hint{color:#0c8f6b;font-weight:650}.folded .oz-savebar{left:36px}.oz-toast{bottom:72px;z-index:99}.oz-job,.oz-toast{position:fixed;right:24px}.oz-job{background:#fff;border:1px solid #e7ecef;border-radius:16px;bottom:84px;box-shadow:0 18px 50px rgba(15,32,39,.22);color:#0f2027;font-size:13px;max-width:calc(100vw - 48px);overflow:hidden;width:420px;z-index:100}.oz-job__head{align-items:center;display:flex;gap:12px;padding:16px 16px 12px}.oz-job__head .components-spinner{margin:0}.oz-job__icon-status{font-size:22px;height:22px;width:22px}.oz-job__icon-status--done{color:#10ac81}.oz-job__icon-status--stopped{color:#6b7a86}.oz-job__icon-status--error{color:#d63638}.oz-job__title{flex:1;min-width:0}.oz-job__title strong{display:block;font-size:15px;font-weight:700;line-height:1.2}.oz-job__title span{color:#6b7a86;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.oz-job__pct{color:#0f2027;font-size:13px;font-variant-numeric:tabular-nums;font-weight:700}.oz-job__icon{background:transparent;border:0;border-radius:6px;color:#6b7a86;cursor:pointer;display:inline-flex;padding:4px}.oz-job__icon:hover{background:#f4f7f8;color:#0f2027}.oz-job__icon .dashicons{font-size:18px;height:18px;width:18px}.oz-job__body{padding:0 16px 16px}.oz-job__row{align-items:baseline;display:flex;gap:12px;justify-content:space-between;margin-bottom:8px}.oz-job__row span{line-height:1.4}.oz-job__row strong{flex:0 0 auto;font-variant-numeric:tabular-nums;font-weight:700}.oz-job__bar{background:#e7ecef;border-radius:999px;height:7px;overflow:hidden}.oz-job__bar div{background:linear-gradient(90deg,#1dd1a1,#10ac81);border-radius:999px;height:100%;transition:width .35s ease}.oz-job__bar.is-indeterminate div{animation:oz-indeterminate 1.1s ease-in-out infinite;border-radius:999px;width:40%!important}.oz-job.is-min .oz-job__bar{margin:2px 16px 16px}.oz-job.is-min .oz-job__head{padding-bottom:10px}.oz-job.is-done .oz-job__bar div{background:linear-gradient(90deg,#10ac81,#0c8f6b)}.oz-job.is-stopped .oz-job__bar div{background:#c3ccd2}.oz-job.is-error .oz-job__bar div{background:linear-gradient(90deg,#f16b6b,#d63638)}.oz-job__step{color:#6b7a86;font-size:12px;margin-top:8px}.oz-job__stop{align-items:center;background:transparent;border:0;color:#d63638;cursor:pointer;display:inline-flex;font-size:13px;font-weight:650;gap:6px;margin-top:14px;padding:4px 0}.oz-job__stop .dashicons{font-size:18px}.oz-job__stop:hover{color:#b32d2e}.oz-job__log{background:#11202b;border-radius:10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.7;margin-top:14px;max-height:150px;overflow-y:auto;padding:10px 12px}.oz-logline{color:#cdd8df;display:flex;gap:8px}.oz-logline__tag{color:#5fd0ad;flex:0 0 auto;font-weight:700;min-width:42px}.oz-logline--warn .oz-logline__tag{color:#f0b429}.oz-logline--error .oz-logline__tag{color:#ff6b6b}.oz-logline--done .oz-logline__tag{color:#1dd1a1}.oz-logline__text{word-break:break-word}@media(max-width:1100px){.oz-kpis,.oz-quick{grid-template-columns:1fr}}@media(max-width:1024px){.oz-score{align-items:flex-start;flex-direction:column;text-align:left}.oz-convert{align-items:stretch;flex-direction:column;gap:12px}.oz-convert .oz-btn-ghost.components-button,.oz-convert .oz-btn-primary.components-button{justify-content:center;width:100%}}.oz-mobile-toggle,.oz-nav-overlay{display:none!important}@media(max-width:1024px){.oz-shell{flex-direction:column;margin:8px 8px 110px}.oz-content{border-left:0;min-width:0;width:100%}.oz-sidebar{border-bottom:1px solid #e7ecef;border-right:0;flex:none;max-height:none;overflow:visible;padding:14px 12px;position:static;width:100%}.oz-sidebar__brand{margin-bottom:10px;padding-bottom:12px}.oz-sidebar nav{display:flex;flex-wrap:nowrap;gap:6px;overflow-x:auto;-webkit-overflow-scrolling:touch;padding-bottom:4px}.oz-navitem{border-bottom:3px solid transparent;border-left:0;border-radius:10px;flex:0 0 auto;padding:8px 12px;width:auto}.oz-navitem.is-active{border-bottom-color:#1dd1a1;border-left-color:transparent}.oz-navitem .oz-navitem__text strong{white-space:nowrap}.oz-navitem .oz-navitem__dot,.oz-navitem small,.oz-sidebar__foot{display:none}.oz-topbar{padding:16px 18px}.oz-topbar h1{font-size:18px}.oz-main{padding:16px 18px}.oz-savebar{left:0;padding:12px 18px}.oz-card__body{padding-left:18px;padding-right:18px}.oz-job{left:8px;right:8px;width:auto}}@media(max-width:560px){.oz-tiles{grid-template-columns:1fr}.oz-hero,.oz-score{padding:18px}.oz-card__head{gap:12px;padding:16px}.oz-card__icon{flex-basis:40px;height:40px;width:40px}.oz-savebar__hint{display:none}}@keyframes oz-indeterminate{0%{transform:translateX(-100%)}to{transform:translateX(350%)}}.oz-muted{color:#6b7a86;font-size:13px}.oz-reqs{list-style:none;margin:0;padding:0}.oz-req{align-items:center;border-bottom:1px solid #e7ecef;border-radius:8px;display:grid;font-size:13.5px;gap:6px 10px;grid-template-columns:20px 1fr auto;padding:10px 6px}.oz-req:last-child{border-bottom:0}.oz-req__dot{font-size:18px;height:18px;width:18px}.oz-req__label{font-weight:650}.oz-req__value{color:#6b7a86;font-weight:600;justify-self:end}.oz-req__note{color:#6b7a86;font-size:12px;font-weight:400;grid-column:2/-1;line-height:1.45;margin-top:2px}.oz-req--ok>.oz-req__dot{color:#0c8f6b}.oz-req--warn>.oz-req__dot{color:#e0900f}.oz-req--error>.oz-req__dot{color:#e05242}.oz-req--info>.oz-req__dot,.oz-req--info>.oz-req__value{color:#6b7a86}.oz-req--warn{background:rgba(224,144,15,.06)}.oz-req--error{background:rgba(224,82,66,.06)}.oz-notice{align-items:flex-start;background:rgba(224,82,66,.08);border:1px solid rgba(224,82,66,.28);border-radius:10px;color:#7a2018;display:flex;font-size:13px;gap:10px;line-height:1.5;margin:0 0 14px;padding:12px 14px}.oz-notice .dashicons{color:#e05242;flex:0 0 20px;margin-top:1px}.oz-notice--warn{background:rgba(224,144,15,.09);border-color:rgba(224,144,15,.3);color:#7a5406}.oz-notice--warn .dashicons{color:#e0900f}.oz-recs{list-style:none;margin:0;padding:0}.oz-rec{align-items:center;border-bottom:1px solid #e7ecef;border-radius:8px;display:flex;gap:12px;padding:12px 8px}.oz-rec:last-child{border-bottom:0}.oz-rec__dot{flex:0 0 20px;font-size:20px;height:20px;margin-top:1px;width:20px}.oz-rec__text{flex:1;min-width:0}.oz-rec__text strong{display:block;font-size:13.5px;font-weight:650}.oz-rec__text small{color:#6b7a86;display:block;font-size:12px;line-height:1.45;margin-top:2px}.oz-rec__btn.components-button{flex:0 0 auto}.oz-rec--high>.oz-rec__dot{color:#e05242}.oz-rec--medium>.oz-rec__dot{color:#e0900f}.oz-rec--low>.oz-rec__dot{color:#6b7a86}.oz-rec--high{background:rgba(224,82,66,.05)}.oz-rec--medium{background:rgba(224,144,15,.05)}.oz-recs-clear{align-items:center;color:#6b7a86;display:flex;font-size:13px;gap:8px}.oz-recs-clear .dashicons{color:#0c8f6b}.oz-select--compact{border-bottom:0!important;flex:none;margin:0 0 0 auto!important;padding:0!important}.oz-select--compact .oz-select__btn{background:#f7faf9;border-radius:9px;font-size:13px;font-weight:650;gap:10px;height:30px;min-width:132px;padding:0 10px 0 12px;width:auto}.oz-select--compact .oz-select__btn:disabled{cursor:progress;opacity:.6}.oz-select--compact .oz-select__btn .dashicons{font-size:16px;height:16px;width:16px}.oz-select--compact .oz-select__menu{left:auto;min-width:190px;right:0}.oz-select--compact .oz-select__item{font-size:13px;padding:8px 10px}1 .oz-shell{align-items:flex-start;background:#fff;border:1px solid #e7ecef;border-radius:16px;color:#0f2027;display:flex;font-size:14px;margin:10px 20px 130px 0;min-height:640px;overflow:hidden}.oz-shell *{box-sizing:border-box}.oz-shell .dashicons{font-family:dashicons}.oz-sidebar{align-self:flex-start;background:#fff;display:flex;flex:0 0 264px;flex-direction:column;padding:18px 14px}.oz-sidebar__brand{align-items:center;border-bottom:1px solid #e7ecef;display:flex;gap:12px;margin-bottom:10px;padding:10px 10px 18px}.oz-sidebar__brand strong{display:block;font-size:18px;font-weight:750;letter-spacing:-.01em}.oz-sidebar__brand span{color:#6b7a86;font-size:11px}.oz-sidebar__brand-fallback{align-items:center;display:flex;gap:12px}.oz-sidebar__foot{border-top:1px solid #e7ecef;color:#6b7a86;font-size:11px;margin-top:12px;padding:12px 12px 2px}.oz-logo{background:linear-gradient(135deg,#1dd1a1,#10ac81);border-radius:12px;color:#fff;display:grid;font-size:26px;height:44px;place-items:center;width:44px}.oz-logo-img{display:block;height:30px;max-width:100%;width:auto}.oz-navitem{align-items:center;background:transparent;border:0;border-left:3px solid transparent;border-radius:0 10px 10px 0;color:#0f2027;cursor:pointer;display:flex;gap:12px;padding:11px 12px;text-align:left;transition:all .14s ease;width:100%}.oz-navitem>.oz-ico{color:#6b7a86;flex:0 0 20px;height:20px;transition:color .14s ease,transform .14s ease;width:20px}.oz-navitem:hover>.oz-ico{color:#0f2027}.oz-navitem.is-active>.oz-ico{color:#0c8f6b}.oz-navitem__text{flex:1;min-width:0}.oz-navitem__text strong{display:block;font-size:13.5px;font-weight:650;line-height:1.2}.oz-navitem__text small{color:#6b7a86;font-size:11px}.oz-navitem__dot{background:#1dd1a1;border-radius:50%;height:8px;width:8px}.oz-navitem:hover{background:#f4f7f8}.oz-navitem.is-active{background:rgba(29,209,161,.12);border-left-color:#1dd1a1}.oz-navitem.is-active .oz-navitem__text strong{color:#0c8f6b}.oz-content{align-self:stretch;background:#f4f7f8;border-left:1px solid #e7ecef;display:flex;flex:1;flex-direction:column;min-width:0}.oz-topbar{align-items:center;background:#fff;border-bottom:1px solid #e7ecef;display:flex;gap:14px;padding:20px 28px}.oz-topbar__icon{background:rgba(29,209,161,.14);border-radius:12px;color:#0c8f6b;display:grid;font-size:22px;height:44px;place-items:center;width:44px}.oz-topbar h1{font-size:20px;font-weight:700;margin:0;padding:0}.oz-topbar p{color:#6b7a86;font-size:12.5px;margin:0}.oz-main{display:flex;flex-direction:column;gap:16px;padding:24px 28px}.oz-welcome{align-items:center;background:linear-gradient(125deg,rgba(29,209,161,.9),rgba(16,172,129,.92));border-radius:16px;color:#06382c;display:flex;gap:20px;justify-content:space-between;overflow:hidden;padding:22px 26px}.oz-welcome h2{color:#06382c;font-size:18px;font-weight:700;margin:0 0 6px}.oz-welcome p{color:#0c5b46;font-size:13px;margin:0;max-width:620px}.oz-welcome__icon{flex:0 0 auto;font-size:64px;height:64px;opacity:.5;width:64px}.oz-score{align-items:center;background:linear-gradient(125deg,#1dd1a1,#10ac81 62%,#0c8f6b);border-radius:16px;box-shadow:0 16px 34px -18px rgba(12,143,107,.65);color:#06382c;display:flex;gap:30px;overflow:hidden;padding:28px 32px;position:relative}.oz-score:before{background:radial-gradient(circle,hsla(0,0%,100%,.3),transparent 62%);height:440px;right:-8%;top:-55%;width:440px}.oz-score:after,.oz-score:before{border-radius:50%;content:"";pointer-events:none;position:absolute}.oz-score:after{background:radial-gradient(circle,rgba(0,0,0,.08),transparent 60%);bottom:-70%;height:320px;left:8%;width:320px}.oz-score>*{position:relative;z-index:1}.oz-score__gauge{filter:drop-shadow(0 8px 16px rgba(3,40,30,.28));flex:0 0 auto;line-height:0;position:relative}.oz-score__grade{background:#fff;border-radius:50%;box-shadow:0 4px 10px rgba(3,40,30,.28);color:#0c8f6b;display:grid;font-size:14px;font-weight:800;height:30px;place-items:center;position:absolute;right:2px;top:8px;width:30px}.oz-score__body{flex:1;min-width:0}.oz-score__body h2{color:#06382c;font-size:21px;font-weight:750;letter-spacing:-.02em;margin:0 0 4px}.oz-score__body p{color:rgba(6,56,44,.82);font-size:13px;font-weight:500;margin:0 0 15px}.oz-score__chips{display:flex;flex-wrap:wrap;gap:8px}.oz-chip{align-items:center;background:hsla(0,0%,100%,.18);border:1px solid hsla(0,0%,100%,.28);border-radius:999px;color:rgba(6,56,44,.72);display:inline-flex;font-size:11.5px;font-weight:650;gap:6px;padding:5px 12px 5px 9px;transition:transform .15s ease,box-shadow .15s ease}.oz-chip .dashicons{font-size:15px;height:15px;opacity:.65;width:15px}.oz-chip.is-on{background:#fff;border-color:#fff;box-shadow:0 3px 8px -2px rgba(3,40,30,.22);color:#0c8f6b}.oz-chip.is-on .dashicons{color:#1dd1a1;opacity:1}.oz-chip.is-on:hover{box-shadow:0 6px 14px -4px rgba(3,40,30,.3);transform:translateY(-1px)}.oz-convert{align-items:center;background:#f4f7f8;border-radius:12px;display:flex;gap:16px;justify-content:space-between;margin-top:16px;padding:16px}.oz-convert__info{color:#6b7a86;display:flex;flex-direction:column;font-size:12.5px;gap:3px}.oz-convert__info strong{color:#0f2027;font-size:13px}.oz-progress{background:#dfe6e8;border-radius:999px;height:6px;margin-top:4px;overflow:hidden;width:200px}.oz-progress div{background:linear-gradient(90deg,#1dd1a1,#10ac81);height:100%;transition:width .3s ease}.oz-kpis{display:grid;gap:14px;grid-template-columns:repeat(3,1fr)}.oz-kpis--single{grid-template-columns:1fr}.oz-kpi{background:#fff;border:1px solid #e7ecef;border-radius:14px;display:flex;flex-direction:column;padding:16px 18px}.oz-kpi>.dashicons{color:#10ac81;font-size:20px;margin-bottom:8px}.oz-kpi strong{font-size:24px;font-weight:750;min-height:28px}.oz-kpi span{color:#6b7a86;font-size:12px}.oz-quick{display:grid;gap:16px;grid-template-columns:1fr 1fr}.oz-quick__actions{display:flex;flex-wrap:wrap;gap:10px}.oz-cachelist{border-top:1px solid #e7ecef;margin:18px 0 0}.oz-cachelist>li{align-items:center;border-bottom:1px solid #f1f4f5;color:#6b7a86;display:flex;font-size:13px;gap:10px;padding:11px 2px}.oz-cachelist>li:last-child{border-bottom:0}.oz-cachelist>li .dashicons{color:#10ac81;font-size:17px}.oz-cachelist>li strong{color:#0f2027;font-weight:650;margin-left:auto}.oz-cachelist>li .oz-row-label{display:flex;flex-direction:column;line-height:1.3}.oz-cachelist>li .oz-row-label small{color:#6b7a86;font-size:11.5px;opacity:.85}.oz-status{margin:0}.oz-status li{align-items:center;border-bottom:1px solid #e7ecef;display:flex;font-size:13.5px;font-weight:600;gap:10px;padding:9px 6px}.oz-status li:last-child{border-bottom:0}.oz-status li>.oz-ico{color:#6b7a86;flex:0 0 18px;height:18px;width:18px}.oz-status__name{background:transparent;border:0;color:#0f2027;cursor:pointer;flex:1;font:inherit;font-weight:600;padding:4px 0;text-align:left}.oz-status__name:hover{color:#0c8f6b}.oz-status .components-toggle-control{margin-left:auto}.oz-card,.oz-panel{background:#fff;border:1px solid #e7ecef;border-radius:16px;overflow:visible}.oz-card .oz-card__head{border-radius:16px 16px 0 0}.oz-panel__head{padding:18px 22px 0}.oz-panel__head h2{font-size:16px;font-weight:650;margin:0}.oz-panel__head p{color:#6b7a86;font-size:12.5px;margin:4px 0 0}.oz-panel__body{padding:8px 22px 20px}.oz-panel__body .components-base-control{border-bottom:1px solid #f1f4f5;padding:14px 0}.oz-panel__body .components-base-control:last-child{border-bottom:0}.oz-card{transition:box-shadow .18s ease,border-color .18s ease}.oz-card.is-enabled{border-color:rgba(29,209,161,.5);box-shadow:0 0 0 1px rgba(29,209,161,.22)}.oz-card__head{align-items:flex-start;display:flex;gap:15px;padding:20px 22px}.oz-card__icon{background:#f4f7f8;border-radius:12px;color:#6b7a86;display:grid;flex:0 0 46px;font-size:22px;height:46px;place-items:center;width:46px}.oz-card__title{flex:1;min-width:0}.oz-card__title h2{align-items:center;display:flex;font-size:16px;font-weight:650;gap:10px;margin:0 0 3px}.oz-card__title p{color:#6b7a86;font-size:12.5px;margin:0}.oz-card__body{border-top:1px solid #e7ecef;padding:4px 22px 16px}.oz-card__body .components-base-control{border-bottom:1px solid #f1f4f5;padding:14px 0}.oz-card__body .components-base-control:last-child{border-bottom:0}.is-enabled .oz-card__icon{background:linear-gradient(135deg,#1dd1a1,#10ac81);color:#fff}.oz-inline-actions{display:flex;gap:10px;padding-top:16px}.oz-dbgroup{border-top:1px solid #eef0f2;padding:18px 0}.oz-dbgroup:first-of-type{border-top:0;padding-top:4px}.oz-dbgroup__title{color:#0e1116;font-size:14px;font-weight:700;margin:0 0 4px}.oz-dbgroup__desc{color:#6b7280;font-size:13px;line-height:1.5;margin:0 0 14px}.oz-dbgroup .components-toggle-control{margin-bottom:14px}.oz-dbgroup .components-toggle-control:last-child{margin-bottom:0}.oz-dbwarn{align-items:flex-start;background:#fff5f5;border:1px solid #fcd9d9;border-radius:10px;color:#b42318;display:flex;font-size:13px;font-weight:600;gap:8px;line-height:1.5;margin:4px 0;padding:12px 14px}.oz-dbwarn .dashicons{color:#e05242;flex:0 0 auto}.oz-cachewarn{align-items:center;background:#fff8ec;border:1px solid #f4dcae;border-radius:12px;color:#7a4b00;display:flex;gap:13px;margin:0 0 18px;padding:14px 16px}.oz-cachewarn .dashicons{color:#e0900f;flex:0 0 auto;font-size:22px;height:22px;width:22px}.oz-cachewarn__text{flex:1 1 auto;min-width:0}.oz-cachewarn__text strong{color:#6b4200;display:block;font-size:14px;font-weight:700}.oz-cachewarn__text p{color:#8a6410;font-size:12.5px;line-height:1.5;margin:3px 0 0}.oz-cachewarn .oz-btn-primary{flex:0 0 auto}@media(max-width:640px){.oz-cachewarn{flex-wrap:wrap}.oz-cachewarn .oz-btn-primary{justify-content:center;width:100%}}.oz-subhead{color:#8a96a0;font-size:11px;font-weight:700;letter-spacing:.08em;margin:18px 0 2px;padding-top:14px;text-transform:uppercase}.oz-health,.oz-subhead{border-top:1px solid #e7ecf0}.oz-health{margin-top:18px;padding-top:16px}.oz-health__head{align-items:center;display:flex;gap:12px;justify-content:space-between}.oz-health__head strong{font-size:14px}.oz-health__hint{color:#6b7280;font-size:12px;line-height:1.5;margin:4px 0 10px}.oz-health__list{display:flex;flex-direction:column;gap:6px;margin:0}.oz-health__row{align-items:center;display:flex;font-size:12.5px;gap:8px;line-height:1.4;margin:0}.oz-health__row .dashicons{flex:0 0 auto;font-size:17px;height:17px;width:17px}.oz-health__row.is-ok{color:#216e4e}.oz-health__row.is-ok .dashicons{color:#1dd1a1}.oz-health__row.is-warn{color:#8a5a00}.oz-health__row.is-warn .dashicons{color:#e0900f}.oz-health__row.is-bad{color:#b42318}.oz-health__row.is-bad .dashicons{color:#e05242}.oz-health__err{color:#b42318;font-size:12.5px}.oz-health__fix{align-items:center;display:flex;flex-wrap:wrap;gap:12px;margin-top:14px}.oz-health__fix span{color:#6b7280;flex:1 1 160px;font-size:12px}.oz-health__conflict{display:flex;flex-direction:column;gap:8px;margin-top:12px}.oz-health__conflict-row{align-items:center;background:#fff5f5;border:1px solid #fcd9d9;border-radius:9px;display:flex;flex-wrap:wrap;gap:10px;padding:10px 12px}.oz-health__conflict-row .dashicons{color:#e05242;flex:0 0 auto}.oz-health__conflict-row span:not(.dashicons){color:#8a2f22;flex:1 1 200px;font-size:12.5px}.oz-health__conflict-row .oz-btn-danger.components-button{flex:0 0 auto;height:32px}.oz-health__manual{background:#fff8ec;border:1px solid #f4dcae;border-radius:9px;margin-top:12px;padding:10px 12px}.oz-health__manual p{color:#7a4b00;font-size:12px;margin:0 0 6px}.oz-health__manual code{background:#2b2f36;border-radius:6px;color:#e7f0ec;display:inline-block;font-size:12.5px;padding:6px 10px}.oz-btn-ghost.components-button{border:1px solid #d5dbe0;border-radius:8px;color:#384049;font-size:12.5px;height:30px}.oz-btn-ghost.components-button:hover:not(:disabled){border-color:#1dd1a1;color:#0b7a5c}.oz-pill{align-items:center;border-radius:999px;display:inline-flex;font-size:11px;font-weight:700;gap:5px;padding:3px 9px}.oz-pill i{background:currentColor;border-radius:50%;height:6px;width:6px}.oz-pill.is-on{background:rgba(29,209,161,.16);color:#0c8f6b}.oz-pill.is-off{background:#eef1f2;color:#6b7a86}.oz-shell .components-range-control__thumb,.oz-shell .components-range-control__track{background-color:#1dd1a1!important}.oz-fieldgroup{background:rgba(29,209,161,.035);border:1px solid #e7ecef;border-radius:12px;margin-top:18px;padding:16px 16px 4px}.oz-fieldgroup__title{color:#0e1116;font-size:13px;font-weight:700;margin:0 0 4px}.oz-fieldgroup__desc{color:#6b7280;font-size:12.5px;line-height:1.5;margin:0 0 14px}.oz-fieldgroup .components-base-control{margin-bottom:14px}.oz-shell .components-form-token-field__input-container{background:#fff;border:1px solid #e7ecef;border-radius:10px;min-height:44px;padding:5px 8px;transition:border-color .15s ease,box-shadow .15s ease}.oz-shell .components-form-token-field__input-container.is-active,.oz-shell .components-form-token-field__input-container:focus-within{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)}.oz-shell .components-form-token-field__token{border-radius:8px;font-size:12.5px;margin:3px;overflow:hidden}.oz-shell .components-form-token-field__token-text{background:rgba(29,209,161,.14);border-radius:8px 0 0 8px;color:#0c8f6b;font-weight:650;line-height:1.7;padding:2px 4px 2px 11px}.oz-shell .components-form-token-field__remove-token{background:rgba(29,209,161,.14);border-radius:0 8px 8px 0;color:#0c8f6b;min-width:24px;padding:0 7px;transition:background .15s ease,color .15s ease}.oz-shell .components-form-token-field__remove-token svg{fill:currentColor}.oz-shell .components-form-token-field__remove-token:hover{background:#10ac81;color:#fff}.oz-shell .components-form-token-field__input{font-size:13.5px;margin:4px;min-height:28px}.oz-shell .components-input-control__backdrop{display:none!important}.oz-shell .components-input-control__input,.oz-shell .components-select-control__input,.oz-shell .components-text-control__input{background:#fff!important;border:1px solid #e7ecef!important;border-radius:10px!important;box-shadow:none!important;color:#0f2027;font-size:13.5px;min-height:42px;padding:0 14px}.oz-shell .components-input-control__input:hover,.oz-shell .components-select-control__input:hover,.oz-shell .components-text-control__input:hover{border-color:#bcc6cc!important}.oz-shell .components-input-control__input:focus,.oz-shell .components-select-control__input:focus,.oz-shell .components-text-control__input:focus{border-color:#1dd1a1!important;box-shadow:0 0 0 3px rgba(29,209,161,.16)!important;outline:none!important}.oz-shell select.components-select-control__input{cursor:pointer}.oz-pillgroup .components-base-control__label{display:block;font-weight:600;margin-bottom:8px}.oz-pillgroup__row{background:#f0f2f4;border-radius:12px;display:inline-flex;flex-wrap:wrap;gap:6px;padding:4px}.oz-pillgroup .components-base-control__help{display:block;margin-top:8px}.oz-pillbtn{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;border-radius:9px;color:#4b5563;cursor:pointer;font-size:13px;font-weight:600;min-width:44px;padding:7px 16px;transition:background .12s ease,color .12s ease,box-shadow .12s ease}.oz-pillbtn:hover{color:#0e1116}.oz-pillbtn.is-on{background:#fff;box-shadow:0 1px 3px rgba(4,56,44,.14);color:#0c8f6b}.oz-pillbtn:focus-visible{outline:2px solid #1dd1a1;outline-offset:1px}.oz-select{position:relative}.oz-select .components-base-control__label{display:block;margin-bottom:8px}.oz-select__btn{align-items:center;background:#fff;border:1px solid #e7ecef;border-radius:10px;color:#0f2027;cursor:pointer;display:flex;font-size:13.5px;height:42px;justify-content:space-between;padding:0 14px;transition:border-color .15s ease,box-shadow .15s ease;width:100%}.oz-select__btn .dashicons{color:#0c8f6b;transition:transform .18s ease}.oz-select__btn:hover{border-color:#bcc6cc}.oz-select__btn:focus,.oz-select__btn:focus-visible{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)!important;outline:none!important}.oz-select__btn[aria-expanded=true]{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)}.oz-select__btn[aria-expanded=true] .dashicons{transform:rotate(180deg)}.oz-select__menu{background:#fff;border:1px solid #e7ecef;border-radius:12px;box-shadow:0 14px 34px rgba(15,32,39,.18);left:0;list-style:none;margin:0;padding:6px;position:absolute;right:0;top:calc(100% + 6px);z-index:30}.oz-select__item{align-items:center;border-radius:8px;color:#0f2027;cursor:pointer;display:flex;font-size:13.5px;justify-content:space-between;padding:9px 12px}.oz-select__item .dashicons{color:#10ac81;font-size:18px}.oz-select__item.is-active,.oz-select__item:hover{background:rgba(29,209,161,.12);color:#0c8f6b}.oz-select__item.is-selected{color:#0c8f6b;font-weight:650}.oz-shell .oz-btn-danger.components-button,.oz-shell .oz-btn-ghost.components-button,.oz-shell .oz-btn-primary.components-button{border-radius:10px;font-weight:650;height:38px;transition:transform .16s cubic-bezier(.2,.7,.3,1),box-shadow .18s ease,background .16s ease,border-color .16s ease,color .16s ease}.oz-shell .oz-btn-danger.components-button .dashicons,.oz-shell .oz-btn-danger.components-button svg,.oz-shell .oz-btn-ghost.components-button .dashicons,.oz-shell .oz-btn-ghost.components-button svg,.oz-shell .oz-btn-primary.components-button .dashicons,.oz-shell .oz-btn-primary.components-button svg{transition:transform .22s cubic-bezier(.2,.7,.3,1)}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled),.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled),.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){transform:translateY(-1px)}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) svg{transform:scale(1.14)}.oz-shell .oz-btn-danger.components-button:active:not(:disabled),.oz-shell .oz-btn-ghost.components-button:active:not(:disabled),.oz-shell .oz-btn-primary.components-button:active:not(:disabled){transform:translateY(0) scale(.975);transition-duration:.05s}.oz-shell .oz-btn-danger.components-button:focus-visible,.oz-shell .oz-btn-ghost.components-button:focus-visible,.oz-shell .oz-btn-primary.components-button:focus-visible{outline:2px solid #1dd1a1;outline-offset:2px}.oz-shell .oz-btn-danger.components-button:disabled,.oz-shell .oz-btn-ghost.components-button:disabled,.oz-shell .oz-btn-primary.components-button:disabled{opacity:.5;transform:none}.oz-shell .oz-btn-primary.components-button{background:linear-gradient(135deg,#1dd1a1,#10ac81);border:0;box-shadow:0 1px 2px rgba(20,60,48,.12);color:#fff;padding:0 18px}.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){background:linear-gradient(135deg,#10ac81,#0c8f6b);box-shadow:0 8px 20px -7px rgba(29,209,161,.65);color:#fff}.oz-shell .oz-btn-primary.components-button.is-busy{animation:oz-btn-sheen 1.4s linear infinite;background-size:200% 100%}.oz-shell .oz-btn-ghost.components-button{background:#fff;border:1px solid rgba(29,209,161,.5);box-shadow:none;color:#0c8f6b}.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled){background:rgba(29,209,161,.08);border-color:#1dd1a1;box-shadow:0 6px 16px -8px rgba(29,209,161,.5);color:#0c8f6b}.oz-shell .oz-btn-danger.components-button{background:#fff;border:1px solid rgba(214,54,56,.5);color:#d63638}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled){background:rgba(214,54,56,.08);border-color:#d63638;box-shadow:0 6px 16px -8px rgba(214,54,56,.5);color:#b32d2e}.oz-shell .oz-btn--x.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn--x.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg{transform:scale(1.1) rotate(90deg)}@media(prefers-reduced-motion:reduce){.oz-shell .oz-btn--x.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn--x.components-button:hover:not(:disabled) svg{transform:none}}@keyframes oz-btn-sheen{0%{background-image:linear-gradient(135deg,#1dd1a1,#10ac81,#1dd1a1);background-position:200% 0}to{background-image:linear-gradient(135deg,#1dd1a1,#10ac81,#1dd1a1);background-position:-200% 0}}@media(prefers-reduced-motion:reduce){.oz-shell .oz-btn-danger.components-button,.oz-shell .oz-btn-ghost.components-button,.oz-shell .oz-btn-primary.components-button{transition:background .16s ease,color .16s ease,border-color .16s ease}.oz-shell .oz-btn-danger.components-button:active:not(:disabled),.oz-shell .oz-btn-danger.components-button:hover:not(:disabled),.oz-shell .oz-btn-ghost.components-button:active:not(:disabled),.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled),.oz-shell .oz-btn-primary.components-button:active:not(:disabled),.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){transform:none}.oz-shell .oz-btn-danger.components-button .dashicons,.oz-shell .oz-btn-danger.components-button svg,.oz-shell .oz-btn-ghost.components-button .dashicons,.oz-shell .oz-btn-ghost.components-button svg,.oz-shell .oz-btn-primary.components-button .dashicons,.oz-shell .oz-btn-primary.components-button svg{transition:none}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) svg{transform:none}.oz-shell .oz-btn-danger.components-button.is-busy,.oz-shell .oz-btn-ghost.components-button.is-busy,.oz-shell .oz-btn-primary.components-button.is-busy{animation:none}}.oz-shell .components-button.is-primary:focus:not(:disabled),.oz-shell .components-button:focus,.oz-shell .components-button:focus:not(:disabled){box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle,.oz-shell .components-form-toggle__input,.oz-shell .components-form-toggle__input:active,.oz-shell .components-form-toggle__input:focus,.oz-shell .components-form-toggle__input:focus-visible{border:none!important;box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle__track{background-color:#cdd6db!important;border:1px solid transparent!important;box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle.is-checked .components-form-toggle__track{background-color:#1dd1a1!important}.oz-shell .components-form-toggle__input:focus+.components-form-toggle__track,.oz-shell .components-form-toggle__input:focus-visible+.components-form-toggle__track{box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle__thumb{background-color:#fff!important;border:none!important;box-shadow:0 1px 2px rgba(15,32,39,.25)!important}.oz-savebar{align-items:center;background:#fff;border-top:1px solid #e7ecef;bottom:0;box-shadow:0 -6px 18px rgba(15,32,39,.06);display:flex;gap:16px;justify-content:flex-end;left:160px;padding:13px 32px;position:fixed;right:0;z-index:9}.oz-savebar__hint{color:#6b7a86;font-size:13px}.oz-savebar.is-dirty .oz-savebar__hint{color:#0c8f6b;font-weight:650}.folded .oz-savebar{left:36px}.oz-toast{bottom:72px;z-index:99}.oz-job,.oz-toast{position:fixed;right:24px}.oz-job{background:#fff;border:1px solid #e7ecef;border-radius:16px;bottom:84px;box-shadow:0 18px 50px rgba(15,32,39,.22);color:#0f2027;font-size:13px;max-width:calc(100vw - 48px);overflow:hidden;width:420px;z-index:100}.oz-job__head{align-items:center;display:flex;gap:12px;padding:16px 16px 12px}.oz-job__head .components-spinner{margin:0}.oz-job__icon-status{font-size:22px;height:22px;width:22px}.oz-job__icon-status--done{color:#10ac81}.oz-job__icon-status--stopped{color:#6b7a86}.oz-job__icon-status--error{color:#d63638}.oz-job__title{flex:1;min-width:0}.oz-job__title strong{display:block;font-size:15px;font-weight:700;line-height:1.2}.oz-job__title span{color:#6b7a86;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.oz-job__pct{color:#0f2027;font-size:13px;font-variant-numeric:tabular-nums;font-weight:700}.oz-job__icon{background:transparent;border:0;border-radius:6px;color:#6b7a86;cursor:pointer;display:inline-flex;padding:4px}.oz-job__icon:hover{background:#f4f7f8;color:#0f2027}.oz-job__icon .dashicons{font-size:18px;height:18px;width:18px}.oz-job__body{padding:0 16px 16px}.oz-job__row{align-items:baseline;display:flex;gap:12px;justify-content:space-between;margin-bottom:8px}.oz-job__row span{line-height:1.4}.oz-job__row strong{flex:0 0 auto;font-variant-numeric:tabular-nums;font-weight:700}.oz-job__bar{background:#e7ecef;border-radius:999px;height:7px;overflow:hidden}.oz-job__bar div{background:linear-gradient(90deg,#1dd1a1,#10ac81);border-radius:999px;height:100%;transition:width .35s ease}.oz-job__bar.is-indeterminate div{animation:oz-indeterminate 1.1s ease-in-out infinite;border-radius:999px;width:40%!important}.oz-job.is-min .oz-job__bar{margin:2px 16px 16px}.oz-job.is-min .oz-job__head{padding-bottom:10px}.oz-job.is-done .oz-job__bar div{background:linear-gradient(90deg,#10ac81,#0c8f6b)}.oz-job.is-stopped .oz-job__bar div{background:#c3ccd2}.oz-job.is-error .oz-job__bar div{background:linear-gradient(90deg,#f16b6b,#d63638)}.oz-job__step{color:#6b7a86;font-size:12px;margin-top:8px}.oz-job__stop{align-items:center;background:transparent;border:0;color:#d63638;cursor:pointer;display:inline-flex;font-size:13px;font-weight:650;gap:6px;margin-top:14px;padding:4px 0}.oz-job__stop .dashicons{font-size:18px}.oz-job__stop:hover{color:#b32d2e}.oz-job__log{background:#11202b;border-radius:10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.7;margin-top:14px;max-height:150px;overflow-y:auto;padding:10px 12px}.oz-logline{color:#cdd8df;display:flex;gap:8px}.oz-logline__tag{color:#5fd0ad;flex:0 0 auto;font-weight:700;min-width:42px}.oz-logline--warn .oz-logline__tag{color:#f0b429}.oz-logline--error .oz-logline__tag{color:#ff6b6b}.oz-logline--done .oz-logline__tag{color:#1dd1a1}.oz-logline__text{word-break:break-word}@media(max-width:1100px){.oz-kpis,.oz-quick{grid-template-columns:1fr}}@media(max-width:1024px){.oz-score{align-items:flex-start;flex-direction:column;text-align:left}.oz-convert{align-items:stretch;flex-direction:column;gap:12px}.oz-convert .oz-btn-ghost.components-button,.oz-convert .oz-btn-primary.components-button{justify-content:center;width:100%}}.oz-mobile-toggle,.oz-nav-overlay{display:none!important}@media(max-width:1024px){.oz-shell{flex-direction:column;margin:8px 8px 110px}.oz-content{border-left:0;min-width:0;width:100%}.oz-sidebar{border-bottom:1px solid #e7ecef;border-right:0;flex:none;max-height:none;overflow:visible;padding:14px 12px;position:static;width:100%}.oz-sidebar__brand{margin-bottom:10px;padding-bottom:12px}.oz-sidebar nav{display:flex;flex-wrap:nowrap;gap:6px;overflow-x:auto;-webkit-overflow-scrolling:touch;padding-bottom:4px}.oz-navitem{border-bottom:3px solid transparent;border-left:0;border-radius:10px;flex:0 0 auto;padding:8px 12px;width:auto}.oz-navitem.is-active{border-bottom-color:#1dd1a1;border-left-color:transparent}.oz-navitem .oz-navitem__text strong{white-space:nowrap}.oz-navitem .oz-navitem__dot,.oz-navitem small,.oz-sidebar__foot{display:none}.oz-topbar{padding:16px 18px}.oz-topbar h1{font-size:18px}.oz-main{padding:16px 18px}.oz-savebar{left:0;padding:12px 18px}.oz-card__body{padding-left:18px;padding-right:18px}.oz-job{left:8px;right:8px;width:auto}}@media(max-width:560px){.oz-tiles{grid-template-columns:1fr}.oz-hero,.oz-score{padding:18px}.oz-card__head{gap:12px;padding:16px}.oz-card__icon{flex-basis:40px;height:40px;width:40px}.oz-savebar__hint{display:none}}@keyframes oz-indeterminate{0%{transform:translateX(-100%)}to{transform:translateX(350%)}}.oz-muted{color:#6b7a86;font-size:13px}.oz-reqs{list-style:none;margin:0;padding:0}.oz-req{align-items:center;border-bottom:1px solid #e7ecef;border-radius:8px;display:grid;font-size:13.5px;gap:6px 10px;grid-template-columns:20px 1fr auto;padding:10px 6px}.oz-req:last-child{border-bottom:0}.oz-req__dot{font-size:18px;height:18px;width:18px}.oz-req__label{font-weight:650}.oz-req__value{color:#6b7a86;font-weight:600;justify-self:end}.oz-req__note{color:#6b7a86;font-size:12px;font-weight:400;grid-column:2/-1;line-height:1.45;margin-top:2px}.oz-req--ok>.oz-req__dot{color:#0c8f6b}.oz-req--warn>.oz-req__dot{color:#e0900f}.oz-req--error>.oz-req__dot{color:#e05242}.oz-req--info>.oz-req__dot,.oz-req--info>.oz-req__value{color:#6b7a86}.oz-req--warn{background:rgba(224,144,15,.06)}.oz-req--error{background:rgba(224,82,66,.06)}.oz-notice{align-items:flex-start;background:rgba(224,82,66,.08);border:1px solid rgba(224,82,66,.28);border-radius:10px;color:#7a2018;display:flex;font-size:13px;gap:10px;line-height:1.5;margin:0 0 14px;padding:12px 14px}.oz-notice .dashicons{color:#e05242;flex:0 0 20px;margin-top:1px}.oz-notice--warn{background:rgba(224,144,15,.09);border-color:rgba(224,144,15,.3);color:#7a5406}.oz-notice--warn .dashicons{color:#e0900f}.oz-notice--ok{background:#eaf8f3;border-color:#b8e6d6;color:#0f5e48}.oz-notice--ok .dashicons{color:#0e9f7e}.oz-recs{list-style:none;margin:0;padding:0}.oz-rec{align-items:center;border-bottom:1px solid #e7ecef;border-radius:8px;display:flex;gap:12px;padding:12px 8px}.oz-rec:last-child{border-bottom:0}.oz-rec__dot{flex:0 0 20px;font-size:20px;height:20px;margin-top:1px;width:20px}.oz-rec__text{flex:1;min-width:0}.oz-rec__text strong{display:block;font-size:13.5px;font-weight:650}.oz-rec__text small{color:#6b7a86;display:block;font-size:12px;line-height:1.45;margin-top:2px}.oz-rec__btn.components-button{flex:0 0 auto}.oz-rec--high>.oz-rec__dot{color:#e05242}.oz-rec--medium>.oz-rec__dot{color:#e0900f}.oz-rec--low>.oz-rec__dot{color:#6b7a86}.oz-rec--high{background:rgba(224,82,66,.05)}.oz-rec--medium{background:rgba(224,144,15,.05)}.oz-recs-clear{align-items:center;color:#6b7a86;display:flex;font-size:13px;gap:8px}.oz-recs-clear .dashicons{color:#0c8f6b}.oz-select--compact{border-bottom:0!important;flex:none;margin:0 0 0 auto!important;padding:0!important}.oz-select--compact .oz-select__btn{background:#f7faf9;border-radius:9px;font-size:13px;font-weight:650;gap:10px;height:30px;min-width:132px;padding:0 10px 0 12px;width:auto}.oz-select--compact .oz-select__btn:disabled{cursor:progress;opacity:.6}.oz-select--compact .oz-select__btn .dashicons{font-size:16px;height:16px;width:16px}.oz-select--compact .oz-select__menu{left:auto;min-width:190px;right:0}.oz-select--compact .oz-select__item{font-size:13px;padding:8px 10px}.oz-cachewarn--ok{background:#eaf8f3;border-color:#b8e6d6;color:#0f5e48}.oz-cachewarn__text p+p{margin-top:6px}.oz-cachewarn__link{color:inherit;display:inline-block;font-weight:600;margin-top:8px;text-decoration:underline}.oz-network__sites{align-items:start;margin-top:16px}.oz-network__sites .oz-panel__head h2,.oz-network__sites .oz-panel__head p{overflow-wrap:anywhere}.oz-cachelist__link{color:#1dd1a1;font-weight:600;margin-left:auto;text-decoration:none}.oz-cachelist__link:focus,.oz-cachelist__link:hover{text-decoration:underline} -
optimazz/tags/1.1.0/build/index.js
r3723128 r3743505 1 (()=>{"use strict";const e={n:a=>{const t=a&&a.__esModule?()=>a.default:()=>a;return e.d(t,{a:t}),t},d:(a,t)=>{for(var o in t)e.o(t,o)&&!e.o(a,o)&&Object.defineProperty(a,o,{enumerable:!0,get:t[o]})},o:(e,a)=>Object.hasOwn(e,a)},a=window.React,t=window.wp.element,o=window.wp.components,n=window.wp.apiFetch;var l=e.n(n);const i=window.wp.i18n,s=window.wp.hooks,{restUrl:r,nonce:c,settings:m}=window.optimazzData||{};l().use(l().createNonceMiddleware(c));const d=[{key:"dashboard",title:(0,i.__)("Dashboard","optimazz"),sub:(0,i.__)("Overview & quick actions","optimazz"),icon:"admin-home"},{key:"cache",title:(0,i.__)("Page Cache","optimazz"),sub:(0,i.__)("Static HTML caching","optimazz"),icon:"database",mod:"cache"},{key:"files",title:(0,i.__)("File Optimization","optimazz"),sub:(0,i.__)("Optimize CSS & JS","optimazz"),icon:"editor-code"},{key:"media",title:(0,i.__)("Media","optimazz"),sub:(0,i.__)("LazyLoad, dimensions, WebP","optimazz"),icon:"format-image",mod:"media"},{key:"fonts",title:(0,i.__)("Fonts","optimazz"),sub:(0,i.__)("Preload, swap, preconnect","optimazz"),icon:"editor-textcolor",mod:"fonts"},{key:"preload",title:(0,i.__)("Preload","optimazz"),sub:(0,i.__)("Generate cache files","optimazz"),icon:"update"},{key:"advanced",title:(0,i.__)("Advanced Rules","optimazz"),sub:(0,i.__)("Fine-tune cache rules","optimazz"),icon:"list-view"},{key:"database",title:(0,i.__)("Database","optimazz"),sub:(0,i.__)("Optimize, reduce bloat","optimazz"),icon:"database-view",mod:"database"},{key:"heartbeat",title:(0,i.__)("Heartbeat","optimazz"),sub:(0,i.__)("Control Heartbeat API","optimazz"),icon:"heart",mod:"heartbeat"},{key:"tweaks",title:(0,i.__)("Tweaks","optimazz"),sub:(0,i.__)("Remove WordPress bloat","optimazz"),icon:"admin-generic",mod:"tweaks"},{key:"cdn",title:(0,i.__)("CDN","optimazz"),sub:(0,i.__)("Rewrite asset URLs","optimazz"),icon:"cloud",mod:"cdn"},{key:"tools",title:(0,i.__)("Tools","optimazz"),sub:(0,i.__)("Import, export","optimazz"),icon:"admin-tools"}],p=(0,s.applyFilters)("optimazz.adminSections",[]),_=[...d,...p],z=["cache","js","css","media","fonts","database","heartbeat","tweaks","cdn"],h=[{title:(0,i.__)("Content leftovers","optimazz"),desc:(0,i.__)("Old post data that piles up over time. Removing it is permanent, so keep anything you still need.","optimazz"),items:[{key:"revisions",label:(0,i.__)("Post revisions","optimazz"),help:(0,i.__)("Old copies of posts saved automatically. Deleting them keeps only your current content.","optimazz")},{key:"auto_drafts",label:(0,i.__)("Auto drafts","optimazz"),help:(0,i.__)("Empty drafts WordPress creates while you write. Safe to remove.","optimazz")},{key:"trashed_posts",label:(0,i.__)("Trashed posts","optimazz"),help:(0,i.__)("Posts in the trash that have not been permanently deleted yet.","optimazz")}]},{title:(0,i.__)("Comment clutter","optimazz"),desc:(0,i.__)("Spam and trashed comments you are never going to approve. Clearing them keeps the comments table light.","optimazz"),items:[{key:"spam_comments",label:(0,i.__)("Spam comments","optimazz"),help:(0,i.__)("Comments marked as spam. Removing them frees up the comments table.","optimazz")},{key:"trashed_comments",label:(0,i.__)("Trashed comments","optimazz"),help:(0,i.__)("Comments in the trash awaiting permanent deletion.","optimazz")}]},{title:(0,i.__)("Temporary data","optimazz"),desc:(0,i.__)("Short-lived cached values. They are safe to clear and are rebuilt automatically whenever a plugin needs them.","optimazz"),items:[{key:"expired_transients",label:(0,i.__)("Expired transients","optimazz"),help:(0,i.__)("Cached values that have already passed their expiry date.","optimazz")}]},{title:(0,i.__)("Table maintenance","optimazz"),desc:(0,i.__)("Defragment your tables to reclaim wasted space and trim database overhead.","optimazz"),items:[{key:"optimize_tables",label:(0,i.__)("Optimize database tables","optimazz"),help:(0,i.__)("Runs OPTIMIZE on your tables to defragment them and free wasted space.","optimazz")}]}],u=[{label:(0,i.__)("Do not limit","optimazz"),value:"default"},{label:(0,i.__)("Reduce activity","optimazz"),value:"reduce"},{label:(0,i.__)("Disable","optimazz"),value:"disable"}];function g({on:e}){return(0,a.createElement)("span",{className:"oz-pill "+(e?"is-on":"is-off")},(0,a.createElement)("i",null),e?(0,i.__)("Active","optimazz"):(0,i.__)("Off","optimazz"))}function b({icon:e,title:t,description:n,enabled:l,onToggle:i,children:s}){return(0,a.createElement)("section",{className:"oz-card "+(l?"is-enabled":"")},(0,a.createElement)("header",{className:"oz-card__head"},(0,a.createElement)("span",{className:`oz-card__icon dashicons dashicons-${e}`}),(0,a.createElement)("div",{className:"oz-card__title"},(0,a.createElement)("h2",null,t," ",(0,a.createElement)(g,{on:l})),(0,a.createElement)("p",null,n)),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,checked:l,onChange:i})),l&&(0,a.createElement)("div",{className:"oz-card__body"},s))}function f({job:e,onStop:n,onClose:l,onToggleMin:s}){const r=(0,t.useRef)(null);if((0,t.useEffect)(()=>{r.current&&(r.current.scrollTop=r.current.scrollHeight)},[e?.logs?.length]),!e)return null;const c=e.status||(e.running?"running":"done"),m={stopped:(0,i.__)("Cancelled","optimazz"),error:(0,i.__)("Failed","optimazz")},d="running"===c||"done"===c?e.title:`${e.title} — ${m[c]}`;return(0,a.createElement)("div",{className:`oz-job is-${c} ${e.minimized?"is-min":""}`},(0,a.createElement)("div",{className:"oz-job__head"},"running"===c?(0,a.createElement)(o.Spinner,null):(0,a.createElement)("span",{className:`oz-job__icon-status oz-job__icon-status--${c} dashicons dashicons-${{done:"yes-alt",stopped:"dismiss",error:"warning"}[c]||"yes-alt"}`}),(0,a.createElement)("div",{className:"oz-job__title"},(0,a.createElement)("strong",null,d),(0,a.createElement)("span",null,e.minimized?`${e.pct}%${e.step?` · ${e.step}`:""}`:e.subtitle)),(0,a.createElement)("button",{className:"oz-job__icon","aria-label":e.minimized?(0,i.__)("Expand","optimazz"):(0,i.__)("Minimize","optimazz"),onClick:s},(0,a.createElement)("span",{className:"dashicons dashicons-"+(e.minimized?"editor-expand":"minus")})),(0,a.createElement)("button",{className:"oz-job__icon","aria-label":(0,i.__)("Close","optimazz"),onClick:l},(0,a.createElement)("span",{className:"dashicons dashicons-no-alt"}))),e.minimized?(0,a.createElement)("div",{className:"oz-job__bar "+(e.indeterminate&&e.running?"is-indeterminate":"")},(0,a.createElement)("div",{style:{width:`${e.pct}%`}})):(0,a.createElement)("div",{className:"oz-job__body"},(0,a.createElement)("div",{className:"oz-job__row"},(0,a.createElement)("span",null,e.label),(0,a.createElement)("strong",null,e.pct,"%")),(0,a.createElement)("div",{className:"oz-job__bar "+(e.indeterminate&&e.running?"is-indeterminate":"")},(0,a.createElement)("div",{style:{width:`${e.pct}%`}})),e.step&&(0,a.createElement)("div",{className:"oz-job__step"},(0,i.__)("Step","optimazz")," ",e.step),e.running&&(0,a.createElement)("button",{className:"oz-job__stop",onClick:n},(0,a.createElement)("span",{className:"dashicons dashicons-controls-pause"}),(0,i.__)("Stop","optimazz")),(0,a.createElement)("div",{className:"oz-job__log",ref:r},e.logs.map((e,t)=>{const o=e.level||"INFO";return(0,a.createElement)("div",{key:t,className:`oz-logline oz-logline--${o.toLowerCase()}`},(0,a.createElement)("span",{className:"oz-logline__tag"},o),(0,a.createElement)("span",{className:"oz-logline__text"},e.text))}))))}let E=0;function v({label:e,value:o,options:n,onChange:l,compact:i=!1,disabled:s=!1}){const[r,c]=(0,t.useState)(!1),m=Math.max(0,n.findIndex(e=>e.value===o)),[d,p]=(0,t.useState)(m),_=(0,t.useRef)(null),z=(0,t.useRef)("oz-sel-"+ ++E).current;(0,t.useEffect)(()=>{const e=e=>{_.current&&!_.current.contains(e.target)&&c(!1)};return document.addEventListener("mousedown",e),()=>document.removeEventListener("mousedown",e)},[]);const h=n.find(e=>e.value===o)||n[0],u=e=>{l(n[e].value),c(!1)};return(0,a.createElement)("div",{className:"components-base-control oz-select"+(i?" oz-select--compact":""),ref:_},(0,a.createElement)("span",{className:i?"screen-reader-text":"components-base-control__label",id:`${z}-label`},e),(0,a.createElement)("button",{type:"button",className:"oz-select__btn",disabled:s,role:"combobox","aria-haspopup":"listbox","aria-expanded":r,"aria-labelledby":`${z}-label`,"aria-activedescendant":r?`${z}-opt-${d}`:void 0,onClick:()=>c(e=>!e),onKeyDown:e=>{r?"Escape"===e.key||"Tab"===e.key?c(!1):"ArrowDown"===e.key?(e.preventDefault(),p(e=>Math.min(n.length-1,e+1))):"ArrowUp"===e.key?(e.preventDefault(),p(e=>Math.max(0,e-1))):"Home"===e.key?(e.preventDefault(),p(0)):"End"===e.key?(e.preventDefault(),p(n.length-1)):"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),u(d)):["ArrowDown","ArrowUp","Enter"," "].includes(e.key)&&(e.preventDefault(),p(m),c(!0))}},(0,a.createElement)("span",null,h?.label),(0,a.createElement)("span",{className:"dashicons dashicons-arrow-down-alt2"})),r&&(0,a.createElement)("ul",{className:"oz-select__menu",role:"listbox","aria-labelledby":`${z}-label`},n.map((e,t)=>(0,a.createElement)("li",{key:e.value,id:`${z}-opt-${t}`,role:"option","aria-selected":e.value===o,className:`oz-select__item ${e.value===o?"is-selected":""} ${t===d?"is-active":""}`,onMouseEnter:()=>p(t),onClick:()=>u(t)},(0,a.createElement)("span",null,e.label),e.value===o&&(0,a.createElement)("span",{className:"dashicons dashicons-yes"})))))}const y={dashboard:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"M3 9.6 12 3l9 6.6"}),(0,a.createElement)("path",{d:"M5 9.1V20h5v-5h4v5h5V9.1"})),cache:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"M12 3 2.5 8 12 13l9.5-5L12 3Z"}),(0,a.createElement)("path",{d:"M2.5 12 12 17l9.5-5"})),files:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"m9 8-4 4 4 4"}),(0,a.createElement)("path",{d:"m15 8 4 4-4 4"})),media:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("rect",{x:"3",y:"4",width:"18",height:"16",rx:"2"}),(0,a.createElement)("circle",{cx:"8.5",cy:"9.5",r:"1.5"}),(0,a.createElement)("path",{d:"m21 16-5-5-6 6"})),fonts:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"M5 6V4h14v2"}),(0,a.createElement)("path",{d:"M12 4v16"}),(0,a.createElement)("path",{d:"M9 20h6"})),preload:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"M21 12a9 9 0 1 1-2.6-6.4"}),(0,a.createElement)("path",{d:"M21 4v5h-5"})),advanced:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"M4 8h9"}),(0,a.createElement)("circle",{cx:"16",cy:"8",r:"2.1"}),(0,a.createElement)("path",{d:"M19 8h1"}),(0,a.createElement)("path",{d:"M4 16h1"}),(0,a.createElement)("circle",{cx:"8",cy:"16",r:"2.1"}),(0,a.createElement)("path",{d:"M11 16h9"})),database:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("ellipse",{cx:"12",cy:"5",rx:"8",ry:"3"}),(0,a.createElement)("path",{d:"M4 5v14c0 1.6 3.6 3 8 3s8-1.4 8-3V5"}),(0,a.createElement)("path",{d:"M4 12c0 1.6 3.6 3 8 3s8-1.4 8-3"})),heartbeat:(0,a.createElement)("path",{d:"M3 12h4l2 6 4-14 2 8h6"}),tweaks:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"m12 4 1.5 4.5L18 10l-4.5 1.5L12 16l-1.5-4.5L6 10l4.5-1.5Z"}),(0,a.createElement)("path",{d:"M18.5 15v3"}),(0,a.createElement)("path",{d:"M17 16.5h3"})),cdn:(0,a.createElement)("path",{d:"M17 18H7a4 4 0 0 1-.3-8A6 6 0 0 1 18.3 9 3.5 3.5 0 0 1 17 18Z"}),tools:(0,a.createElement)("path",{d:"M14.5 5.5a4 4 0 0 0-5 5L4 16v4h4l5.5-5.5a4 4 0 0 0 5-5l-2.6 2.6-2-.4-.4-2 2.5-2.6Z"})};function w({name:e,paths:t}){return(0,a.createElement)("svg",{className:"oz-ico",viewBox:"0 0 24 24",width:"20",height:"20",fill:"none",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",focusable:"false"},t||y[e]||y.dashboard)}function k({ok:e,warn:t,label:o}){const n=e?"ok":t?"warn":"bad",l=e?"yes-alt":t?"warning":"dismiss";return(0,a.createElement)("li",{className:`oz-health__row is-${n}`},(0,a.createElement)("span",{className:`dashicons dashicons-${l}`}),(0,a.createElement)("span",null,o))}function C({label:e,help:t,value:o,options:n,onChange:l}){return(0,a.createElement)("div",{className:"components-base-control oz-pillgroup"},e&&(0,a.createElement)("span",{className:"components-base-control__label"},e),(0,a.createElement)("div",{className:"oz-pillgroup__row",role:"radiogroup","aria-label":e,onKeyDown:e=>{const a=n.findIndex(e=>e.value===o);"ArrowRight"===e.key||"ArrowDown"===e.key?(e.preventDefault(),l(n[Math.min(n.length-1,a+1)].value)):"ArrowLeft"!==e.key&&"ArrowUp"!==e.key||(e.preventDefault(),l(n[Math.max(0,a-1)].value))}},n.map(e=>{const t=e.value===o;return(0,a.createElement)("button",{key:String(e.value),type:"button",role:"radio","aria-checked":t,tabIndex:t?0:-1,className:"oz-pillbtn "+(t?"is-on":""),onClick:()=>l(e.value)},e.label)})),t&&(0,a.createElement)("span",{className:"components-base-control__help"},t))}function N({title:e,description:t,children:o}){return(0,a.createElement)("section",{className:"oz-panel"},(0,a.createElement)("header",{className:"oz-panel__head"},(0,a.createElement)("h2",null,e),t&&(0,a.createElement)("p",null,t)),(0,a.createElement)("div",{className:"oz-panel__body"},o))}const x=document.getElementById("optimazz-app");x&&(0,t.createRoot)(x).render((0,a.createElement)(function(){var e,n,s,c,g,E,y,x;const[S,M]=(0,t.useState)(m||{}),[T,$]=(0,t.useState)(m||{}),[B,R]=(0,t.useState)(null),[P,D]=(0,t.useState)(null),[O,F]=(0,t.useState)(!1),[H,L]=(0,t.useState)("dashboard"),[A,j]=(0,t.useState)(!1),[W,I]=(0,t.useState)(null),[q,U]=(0,t.useState)(!(!window.optimazzData||!window.optimazzData.hasBackup)),J=(0,t.useRef)(null);(0,t.useEffect)(()=>{const e=e=>{const a=e&&e.detail&&e.detail.key;a&&L(a)};return window.addEventListener("optimazz:navigate",e),()=>window.removeEventListener("optimazz:navigate",e)},[]);const G=JSON.stringify(S)!==JSON.stringify(T),V=(0,t.useCallback)((e,a,t)=>M(o=>({...o,[e]:{...o[e],[a]:t}})),[]),K=(e,a)=>V(e,a,!S[e]?.[a]),Y=(0,t.useCallback)(async()=>{try{R(await l()({url:`${r}/cache/stats`}))}catch(e){}},[]),[Z,Q]=(0,t.useState)(null),X=(0,t.useCallback)(async()=>{try{Q(await l()({url:`${r}/system?_=${Date.now()}`}))}catch(e){Q({error:!0})}},[]);(0,t.useEffect)(()=>{"tools"!==H||Z||X()},[H,Z,X]);const[ee,ae]=(0,t.useState)(null),te=(0,t.useCallback)(async()=>{try{ae(await l()({url:`${r}/recommendations?_=${Date.now()}`}))}catch(e){ae({error:!0})}},[]);(0,t.useEffect)(()=>{"dashboard"!==H||ee||te()},[H,ee,te]);const oe=(0,t.useCallback)(async()=>{F(!0);try{D(await l()({url:`${r}/cache/health`}))}catch(e){D({error:!0})}F(!1)},[]),ne=(0,t.useCallback)(async(e,a)=>{if(window.confirm(`${(0,i.__)("Deactivate","optimazz")} ${a}? ${(0,i.__)("Optimazz will keep handling caching.","optimazz")}`)){F(!0);try{const t=await l()({url:`${r}/cache/deactivate-conflict`,method:"POST",data:{plugin:e}});D(t),ie(`${a} ${(0,i.__)("deactivated — Optimazz is now the only cache.","optimazz")}`,"success")}catch(e){ie((0,i.__)("Could not deactivate the plugin.","optimazz"),"error")}F(!1)}},[]),le=(0,t.useCallback)(async()=>{if(window.confirm((0,i.__)("Make Optimazz the caching plugin? This replaces the current advanced-cache.php (from another caching plugin) and turns on WP_CACHE. Deactivate the other caching plugin afterwards to avoid conflicts.","optimazz"))){F(!0);try{const e=await l()({url:`${r}/cache/takeover`,method:"POST"});D(e),ie("hit"===e.live?(0,i.__)("Optimazz is now serving pages from cache.","optimazz"):e.dropin_ours?(0,i.__)("Drop-in installed. Preload the cache, then check again.","optimazz"):(0,i.__)("Could not fully take over — see the cache health details.","optimazz"),e.dropin_ours?"success":"error")}catch(e){ie((0,i.__)("Take-over failed. Check file permissions.","optimazz"),"error")}F(!1)}},[]);(0,t.useEffect)(()=>{Y()},[Y]);const ie=(e,a="success")=>{I({text:e,status:a}),window.clearTimeout(ie._t),ie._t=window.setTimeout(()=>I(null),3e3)};(0,t.useEffect)(()=>{try{const e=new URLSearchParams(window.location.search),a=e.get("optimazz_notice"),t={purged:(0,i.__)("Cache purged.","optimazz"),preloaded:(0,i.__)("Cache preload started in the background.","optimazz"),cleared:(0,i.__)("Cache cleared; preload started in the background.","optimazz")};if(a&&t[a]){ie(t[a]),e.delete("optimazz_notice");const o=e.toString();window.history.replaceState({},"",window.location.pathname+(o?`?${o}`:""))}window.optimazzData?.wpconfigUnwritable&&ie((0,i.__)("wp-config.php is not writable — add WP_CACHE manually to enable page caching.","optimazz"),"error")}catch(e){}},[]);const se=async e=>{j(!0);try{const a=await l()({url:`${r}/settings`,method:"POST",data:e});$(a.settings),M(a.settings),U(!!a.hasBackup),ae(null),ie((0,i.__)("Settings saved.","optimazz")),Y()}catch(e){ie(e.message||(0,i.__)("Save failed.","optimazz"),"error")}j(!1)},re=async()=>{await l()({url:`${r}/cache/purge`,method:"POST"}),ie((0,i.__)("Cache purged.","optimazz")),Y()},ce=()=>ve("preload"),[me,de]=(0,t.useState)(null),[pe,_e]=(0,t.useState)(null),[ze,he]=(0,t.useState)(null),ue=(0,t.useRef)(!1),ge=(0,t.useCallback)(async()=>{try{_e(await l()({url:`${r}/media/stats`}))}catch(e){}},[]);(0,t.useEffect)(()=>{"media"!==H||pe||ge()},[H,pe,ge]);const be={convert:{title:(0,i.__)("Converting images to WebP/AVIF","optimazz"),subtitle:(0,i.__)("Next-gen format generation","optimazz"),endpoint:`${r}/media/convert`,refresh:ge,summarize:e=>`${(0,i.__)("Generated","optimazz")} ${e.generated||0} ${(0,i.__)("files in this batch","optimazz")}${e.skipped?` · ${e.skipped} ${(0,i.__)("skipped","optimazz")}`:""}`},compress:{title:(0,i.__)("Compressing images","optimazz"),subtitle:(0,i.__)("Recompress, resize & strip metadata","optimazz"),endpoint:`${r}/media/optimize`,refresh:ge,summarize:e=>{const a=Math.max(0,(e.before||0)-(e.after||0)),t=e.skipped?` · ${e.skipped} ${(0,i.__)("skipped","optimazz")}`:"";return`${(0,i.__)("Processed","optimazz")} ${e.processed||0} — ${we(a)} ${(0,i.__)("saved","optimazz")}${t}`}},preload:{title:(0,i.__)("Preloading cache","optimazz"),subtitle:(0,i.__)("Warming pages","optimazz"),endpoint:`${r}/cache/preload`,refresh:Y,onBatch:Y,summarize:(e,a)=>`${(0,i.__)("Warmed","optimazz")} ${Math.min(e.next_offset||0,e.total||0)} / ${e.total||0} ${(0,i.__)("pages","optimazz")} · ${a.cached} ${(0,i.__)("cached","optimazz")}${a.missed?` · ${a.missed} ${(0,i.__)("not cached","optimazz")}`:""}`}},fe=e=>!e||e<1?"":e>=3600?`~${Math.round(e/3600)}h`:e>=60?`~${Math.round(e/60)}m`:`~${Math.round(e)}s`,Ee=()=>l()({url:`${r}/media/job`,method:"DELETE"}).catch(()=>{}),ve=async(e,{startOffset:a=0,reconnect:t=!1}={})=>{const o=be[e];if(!o||ze?.running)return;ue.current=!1;const n=t?(0,i.__)("Resuming…","optimazz"):`${o.title}…`;he({title:o.title,subtitle:o.subtitle,pct:0,label:n,step:"",logs:[{level:"INFO",text:n}],running:!0,status:"running",minimized:!1});let s=a,r=0,c=0;const m={cached:0,missed:0,diagnosis:""},d=Date.now();try{for(;;){var p;if(ue.current){await Ee(),he(e=>e&&{...e,running:!1,status:"stopped",logs:[...e.logs,{level:"WARN",text:(0,i.__)("Stopped by user.","optimazz")}]});break}const a=s;let t;try{t=await l()({url:o.endpoint,method:"POST",data:{offset:s}})}catch(e){if(c++,c>20)throw e;if(s=a+1,he(e=>e&&{...e,logs:[...e.logs.slice(-60),{level:"WARN",text:(0,i.__)("A batch failed (likely one slow/large image); skipped it and continued.","optimazz")}]}),r&&s>=r){he(e=>e&&{...e,running:!1,status:"done",pct:100,logs:[...e.logs,{level:"DONE",text:(0,i.__)("Completed (some items skipped).","optimazz")}]});break}await new Promise(e=>setTimeout(e,600));continue}if(c=0,s=null!==(p=t.next_offset)&&void 0!==p?p:s+(t.processed||0),r=t.total||r,!t.done&&s<=a){await Ee(),he(e=>e&&{...e,running:!1,status:"error",logs:[...e.logs,{level:"ERROR",text:(0,i.__)("Job stalled — no progress.","optimazz")}]});break}m.cached+=t.cached||0,m.missed+=t.missed||0,m.diagnosis=t.diagnosis||m.diagnosis;const n=r?Math.min(100,Math.round(s/r*100)):100,_=o.summarize(t,m),z=(Date.now()-d)/1e3,h=s>0?s/Math.max(z,.001):0,u=h>0&&r&&s<r?(r-s)/h:0,g=u?` · ${fe(u)} ${(0,i.__)("left","optimazz")}`:"";if(he(e=>e&&{...e,pct:n,label:_,step:r?`${Math.min(s,r)} / ${r}${g}`:"",logs:[...e.logs.slice(-60),...t.breakdown?[{level:"INFO",text:`${(0,i.sprintf)(/* translators: %d: number of pages to warm. */ /* translators: %d: number of pages to warm. */ 2 (0,i.__)("Found %d pages to warm:","optimazz"),t.total||0)} ${t.breakdown}.`}]:[],{level:"INFO",text:_}]}),o.onBatch&&o.onBatch(),t.unsupported){await Ee(),he(e=>e&&{...e,running:!1,status:"error",logs:[...e.logs,{level:"ERROR",text:t.message||(0,i.__)("Image processing is unavailable on this server (no GD or ImageMagick). See Tools → Server Requirements.","optimazz")}]});break}if(t.blocked){await Ee(),he(e=>e&&{...e,running:!1,status:"error",logs:[...e.logs,{level:"ERROR",text:(0,i.__)('Warming stopped: the server returned 403/429, so a firewall or security plugin is blocking the preload requests. Cached what we could; allow-list the "Optimazz-CacheWarmer" user-agent in your firewall or security plugin (e.g. All-In-One Security, Wordfence, Cloudflare), or slow warming with the optimazz_preload_delay_ms filter.',"optimazz")}]});break}if("preload"===e&&!t.done&&0===m.cached&&m.missed>=3){await Ee(),he(e=>e&&{...e,running:!1,status:"error",label:(0,i.__)("Pages are not being cached","optimazz"),logs:[...e.logs,{level:"ERROR",text:`${(0,i.__)("Stopped: pages are loading but none are being saved to the cache.","optimazz")} ${m.diagnosis}`}]});break}if(t.done){const e=[{level:"DONE",text:(0,i.__)("Completed.","optimazz")}];m.missed&&e.unshift({level:"WARN",text:`${(0,i.sprintf)(/* translators: %d: number of pages. */ /* translators: %d: number of pages. */ 3 (0,i._n)("%d page was not cached.","%d pages were not cached.",m.missed,"optimazz"),m.missed)} ${m.diagnosis}`}),he(a=>a&&{...a,running:!1,status:"done",pct:100,logs:[...a.logs,...e]});break}}}catch(e){he(a=>a&&{...a,running:!1,status:"error",logs:[...a.logs,{level:"ERROR",text:e.message||"Error"}]})}(o.refresh||ge)()};(0,t.useEffect)(()=>{let e=!1;return(async()=>{try{const a=await l()({url:`${r}/media/job`});!e&&a&&a.running&&be[a.type]&&ve(a.type,{startOffset:a.offset||0,reconnect:!0})}catch(e){}})(),()=>{e=!0}},[]);const ye=(0,t.useCallback)(async()=>{try{de(await l()({url:`${r}/database/counts`}))}catch(e){}},[]);(0,t.useEffect)(()=>{"database"!==H||me||ye()},[H,me,ye]);const we=e=>{if(!e)return"0 KB";const a=Math.floor(Math.log(e)/Math.log(1024));return`${(e/Math.pow(1024,a)).toFixed(a?1:0)} ${["B","KB","MB","GB"][a]}`},ke=z.filter(e=>S[e]?.enabled).length,Ce=S.cache||{},Ne=S.js||{},xe=S.css||{},Se=S.media||{},Me=S.fonts||{},Te=S.database||{},$e=S.heartbeat||{},Be=S.tweaks||{},Re=S.cdn||{},Pe=_.find(e=>e.key===H),De=[{on:!!Ce.enabled,w:30,label:(0,i.__)("Page caching","optimazz")},{on:!(!Ne.enabled||!Ne.delay),w:18,label:(0,i.__)("Delay JavaScript","optimazz")},{on:!(!Ne.enabled||!Ne.defer),w:8,label:(0,i.__)("Defer JavaScript","optimazz")},{on:!(!xe.enabled||!xe.minify),w:10,label:(0,i.__)("Minify CSS","optimazz")},{on:!(!Se.enabled||!Se.lazy_load),w:12,label:(0,i.__)("Lazy-load images","optimazz")},{on:!(!Se.enabled||!Se.webp&&!Se.webp_generate),w:8,label:(0,i.__)("Next-gen images","optimazz")},{on:!(!Me.enabled||!Me.display_swap),w:6,label:(0,i.__)("Font optimization","optimazz")},{on:!!Ce.preload,w:5,label:(0,i.__)("Cache preloading","optimazz")},{on:!!$e.enabled,w:3,label:(0,i.__)("Heartbeat control","optimazz")}],Oe=De.reduce((e,a)=>e+(a.on?a.w:0),0),Fe=De.filter(e=>!e.on).sort((e,a)=>a.w-e.w)[0],He=Oe>=90?"A":Oe>=75?"B":Oe>=55?"C":Oe>=35?"D":"E",Le=2*Math.PI*52,Ae=(Ne.enabled||xe.enabled||Se.enabled)&&!Ce.enabled,je=(0,a.createElement)(a.Fragment,null,Ae&&(0,a.createElement)("div",{className:"oz-cachewarn",role:"status"},(0,a.createElement)("span",{className:"dashicons dashicons-info-outline"}),(0,a.createElement)("div",{className:"oz-cachewarn__text"},(0,a.createElement)("strong",null,(0,i.__)("Turn on Page Cache for the real speed gain","optimazz")),(0,a.createElement)("p",null,(0,i.__)("File and media optimizations are on, but Page Cache is off — so the server rebuilds every page on every request. Without caching this can make a site feel slower, not faster, especially on mobile. Enable Page Cache to serve ready-made HTML.","optimazz"))),(0,a.createElement)(o.Button,{className:"oz-btn-primary",onClick:()=>L("cache")},(0,i.__)("Enable Page Cache","optimazz"))),(0,a.createElement)("div",{className:"oz-score"},(0,a.createElement)("div",{className:"oz-score__gauge"},(0,a.createElement)("svg",{viewBox:"0 0 120 120",width:"132",height:"132"},(0,a.createElement)("circle",{cx:"60",cy:"60",r:"52",fill:"none",stroke:"rgba(255,255,255,.25)",strokeWidth:"10"}),(0,a.createElement)("circle",{cx:"60",cy:"60",r:"52",fill:"none",stroke:"#fff",strokeWidth:"10",strokeLinecap:"round",strokeDasharray:Le,strokeDashoffset:Le-Le*Oe/100,transform:"rotate(-90 60 60)",style:{transition:"stroke-dashoffset .6s ease"}}),(0,a.createElement)("text",{x:"60",y:"56",textAnchor:"middle",fontSize:"30",fontWeight:"800",fill:"#fff"},Oe),(0,a.createElement)("text",{x:"60",y:"78",textAnchor:"middle",fontSize:"12",fill:"rgba(255,255,255,.85)"},(0,i.__)("SCORE","optimazz"))),(0,a.createElement)("span",{className:"oz-score__grade"},He)),(0,a.createElement)("div",{className:"oz-score__body"},(0,a.createElement)("h2",null,(0,i.__)("Performance score","optimazz")),(0,a.createElement)("p",null,Fe?`${(0,i.__)("Biggest remaining win:","optimazz")} ${Fe.label} (+${Fe.w})`:(0,i.__)("Every optimization is enabled. Nicely done!","optimazz")),(0,a.createElement)("div",{className:"oz-score__chips"},De.map(e=>(0,a.createElement)("span",{key:e.label,className:"oz-chip "+(e.on?"is-on":"")},(0,a.createElement)("span",{className:"dashicons dashicons-"+(e.on?"yes-alt":"marker")}),e.label))))),(0,a.createElement)("div",{className:"oz-kpis"},(0,a.createElement)("div",{className:"oz-kpi"},(0,a.createElement)("span",{className:"dashicons dashicons-database"}),(0,a.createElement)("strong",null,B?null!==(e=B.pages)&&void 0!==e?e:B.files:(0,a.createElement)(o.Spinner,null)),(0,a.createElement)("span",null,(0,i.__)("Cached pages","optimazz"))),(0,a.createElement)("div",{className:"oz-kpi"},(0,a.createElement)("span",{className:"dashicons dashicons-chart-area"}),(0,a.createElement)("strong",null,B?we(B.bytes):(0,a.createElement)(o.Spinner,null)),(0,a.createElement)("span",null,(0,i.__)("Cache size","optimazz"))),(0,a.createElement)("div",{className:"oz-kpi"},(0,a.createElement)("span",{className:"dashicons dashicons-yes-alt"}),(0,a.createElement)("strong",null,ke,"/",z.length),(0,a.createElement)("span",null,(0,i.__)("Modules active","optimazz")))),ee&&!ee.error&&(ee.items||[]).length>0&&(0,a.createElement)(N,{title:(0,i.__)("Recommendations","optimazz"),description:(0,i.__)("What to fix next to speed up your site — one click each.","optimazz")},(0,a.createElement)("ul",{className:"oz-recs"},(ee.items||[]).map(e=>(0,a.createElement)("li",{key:e.id,className:`oz-rec oz-rec--${e.severity}`},(0,a.createElement)("span",{className:"oz-rec__dot dashicons "+("high"===e.severity?"dashicons-warning":"medium"===e.severity?"dashicons-info":"dashicons-marker")}),(0,a.createElement)("span",{className:"oz-rec__text"},(0,a.createElement)("strong",null,e.title),(0,a.createElement)("small",null,e.desc)),e.fix?(0,a.createElement)(o.Button,{className:"oz-btn-primary oz-rec__btn",isBusy:A,onClick:()=>(async e=>{if(!e||!e.module||!e.key)return;const a={...S,[e.module]:{...S[e.module],[e.key]:e.value}};await se(a),ae(null)})(e.fix)},(0,i.__)("Fix it","optimazz")):e.link?(0,a.createElement)(o.Button,{className:"oz-btn-ghost oz-rec__btn",onClick:()=>L(e.link)},(0,i.__)("Open","optimazz")):null)))),ee&&!ee.error&&0===(ee.items||[]).length&&(0,a.createElement)(N,{title:(0,i.__)("Recommendations","optimazz")},(0,a.createElement)("p",{className:"oz-recs-clear"},(0,a.createElement)("span",{className:"dashicons dashicons-yes-alt"}),(0,i.__)("Nice — everything we recommend is already on. Your site is well optimized.","optimazz"))),(0,a.createElement)("div",{className:"oz-quick"},(0,a.createElement)(N,{title:(0,i.__)("Cache Files","optimazz"),description:(0,i.__)("Clear and preload all cache files.","optimazz")},(0,a.createElement)("div",{className:"oz-quick__actions"},(0,a.createElement)(o.Button,{className:"oz-btn-primary",icon:"trash",isBusy:!!ze?.running,onClick:async()=>{await re(),ce()}},(0,i.__)("Clear & Preload","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"update",onClick:ce},(0,i.__)("Preload","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost oz-btn--x",icon:"dismiss",onClick:re},(0,i.__)("Purge cache","optimazz"))),(0,a.createElement)("ul",{className:"oz-cachelist"},(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-media-text"}),(0,i.__)("Cached pages","optimazz"),(0,a.createElement)("strong",null,B?null!==(n=B.pages)&&void 0!==n?n:B.files:"—")),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-database"}),(0,i.__)("Page cache size","optimazz"),(0,a.createElement)("strong",null,B?we(B.bytes):"—")),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-admin-appearance"}),(0,i.__)("Optimized CSS","optimazz"),(0,a.createElement)("strong",null,B?`${B.css_files} (${we(B.css_bytes)})`:"—")),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-clock"}),(0,i.__)("Cache lifespan","optimazz"),(0,a.createElement)(v,{compact:!0,disabled:A,label:(0,i.__)("Cache lifespan","optimazz"),value:Number(null!==(s=Ce.lifespan_hours)&&void 0!==s?s:0),options:(We=[{value:0,label:(0,i.__)("Never expire","optimazz")},{value:12,label:(0,i.__)("12 hours","optimazz")},{value:24,label:(0,i.__)("1 day","optimazz")},{value:168,label:(0,i.__)("1 week","optimazz")},{value:720,label:(0,i.__)("1 month","optimazz")}],Ie=Number(null!==(c=Ce.lifespan_hours)&&void 0!==c?c:0),qe=e=>(0,i.sprintf)(/* translators: %d: number of hours. */ /* translators: %d: number of hours. */ 4 (0,i._n)("%d hour","%d hours",e,"optimazz"),e),We.some(e=>e.value===Ie)?We:[...We,{value:Ie,label:qe(Ie)}].sort((e,a)=>e.value-a.value)),onChange:e=>se({...S,cache:{...S.cache,lifespan_hours:e}})})),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-update"}),(0,a.createElement)("span",{className:"oz-row-label"},(0,i.__)("Auto-preload","optimazz"),(0,a.createElement)("small",null,B?B.preload&&B.next_preload?B.next_preload_overdue?(0,i.__)("next run: due now","optimazz"):`${(0,i.__)("next run in","optimazz")} ${B.next_preload}`:(0,i.__)("off","optimazz"):"")),(0,a.createElement)(v,{compact:!0,disabled:A,label:(0,i.__)("Auto-preload frequency","optimazz"),value:Ce.preload?Ce.preload_interval||"twicedaily":"off",options:[{value:"off",label:(0,i.__)("Off","optimazz")},{value:"hourly",label:(0,i.__)("Every hour","optimazz")},{value:"twicedaily",label:(0,i.__)("Twice a day","optimazz")},{value:"daily",label:(0,i.__)("Once a day","optimazz")}],onChange:e=>{se({...S,cache:{...S.cache,preload:"off"!==e,preload_interval:"off"===e?Ce.preload_interval||"twicedaily":e}})}})),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-backup"}),(0,i.__)("Last auto-preload","optimazz"),(0,a.createElement)("strong",null,B?B.last_preload?(0,i.sprintf)(/* translators: 1: time ago, e.g. "2 hours", 2: number of pages */ /* translators: 1: time ago, e.g. "2 hours", 2: number of pages */ 5 (0,i.__)("%1$s ago · %2$d pages","optimazz"),B.last_preload.ago,B.last_preload.warmed)+(B.last_preload.blocked?` · ${(0,i.__)("blocked by firewall","optimazz")}`:""):(0,i.__)("not yet","optimazz"):"—")))),(0,a.createElement)(N,{title:(0,i.__)("Module Status","optimazz"),description:(0,i.__)("Enable or disable any module right here.","optimazz")},(0,a.createElement)("ul",{className:"oz-status"},d.filter(e=>e.mod).map(e=>(0,a.createElement)("li",{key:e.key},(0,a.createElement)(w,{name:e.key}),(0,a.createElement)("button",{className:"oz-status__name",onClick:()=>L(e.key)},e.title),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,checked:!!S[e.mod]?.enabled,onChange:()=>K(e.mod,"enabled")}))),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-editor-code"}),(0,a.createElement)("button",{className:"oz-status__name",onClick:()=>L("files")},(0,i.__)("File Optimization","optimazz")),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,checked:!(!Ne.enabled&&!xe.enabled),onChange:()=>{const e=!(Ne.enabled||xe.enabled);V("js","enabled",e),V("css","enabled",e)}}))))));var We,Ie,qe;const Ue=(0,a.createElement)(b,{icon:"database",title:(0,i.__)("Page Cache","optimazz"),description:(0,i.__)("Serve static HTML instead of running PHP on every request.","optimazz"),enabled:!!Ce.enabled,onToggle:()=>K("cache","enabled")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Cache mobile devices separately","optimazz"),checked:!!Ce.cache_mobile,onChange:()=>K("cache","cache_mobile")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Cache logged-in users","optimazz"),help:(0,i.__)("Leave off unless you know what you are doing.","optimazz"),checked:!!Ce.cache_logged_in,onChange:()=>K("cache","cache_logged_in")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Browser cache headers","optimazz"),help:(0,i.__)("Add Expires / Cache-Control rules so repeat visits load CSS, JS, images and fonts from the browser cache. Apache and LiteSpeed only (written to .htaccess); ignored on Nginx.","optimazz"),checked:!!Ce.browser_cache,onChange:()=>K("cache","browser_cache")}),(0,a.createElement)(C,{label:(0,i.__)("Cache lifetime","optimazz"),help:(0,i.__)("How long a cached page is kept before it is refreshed. Pages are always purged immediately when their content changes.","optimazz"),value:null!==(g=Ce.lifespan_hours)&&void 0!==g?g:0,options:[{value:0,label:(0,i.__)("Never","optimazz")},{value:12,label:(0,i.__)("12 hours","optimazz")},{value:24,label:(0,i.__)("1 day","optimazz")},{value:168,label:(0,i.__)("1 week","optimazz")},{value:720,label:(0,i.__)("1 month","optimazz")}],onChange:e=>V("cache","lifespan_hours",e)}),(0,a.createElement)("div",{className:"oz-health"},(0,a.createElement)("div",{className:"oz-health__head"},(0,a.createElement)("strong",null,(0,i.__)("Cache health","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost",isBusy:O,onClick:oe},(0,i.__)("Check now","optimazz"))),(0,a.createElement)("p",{className:"oz-health__hint"},(0,i.__)("Confirms caching is actually serving pages. If it is not, the plugin rebuilds every page on each request — which is slower, not faster.","optimazz")),P&&!P.error&&(0,a.createElement)("ul",{className:"oz-health__list"},(0,a.createElement)(k,{ok:"hit"===P.live,warn:"miss"===P.live,label:"hit"===P.live?(0,i.__)("Pages ARE being served from cache (HIT)","optimazz"):"miss"===P.live?(0,i.__)("Pages are NOT being served from cache (MISS) — see below","optimazz"):(0,i.__)("Live check unavailable","optimazz")}),(0,a.createElement)(k,{ok:P.dropin_installed&&P.dropin_ours,warn:P.conflict,label:P.conflict?P.conflict_name?`${(0,i.__)("advanced-cache.php belongs to","optimazz")} ${P.conflict_name} — ${(0,i.__)("take it over below","optimazz")}`:(0,i.__)("advanced-cache.php belongs to ANOTHER plugin — take it over below","optimazz"):P.dropin_ours?(0,i.__)("advanced-cache.php drop-in installed","optimazz"):(0,i.__)("advanced-cache.php drop-in is missing","optimazz")}),(0,a.createElement)(k,{ok:P.wp_cache,label:P.wp_cache?(0,i.__)("WP_CACHE is enabled in wp-config.php","optimazz"):(0,i.__)("WP_CACHE is not enabled in wp-config.php","optimazz")}),!P.wpconfig_writable&&(0,a.createElement)(k,{warn:!0,label:(0,i.__)("wp-config.php is not writable — add the WP_CACHE line manually (below)","optimazz")}),(0,a.createElement)(k,{ok:P.warmed>0,label:`${P.warmed} ${(0,i.__)("pages warmed in cache","optimazz")}`})),P&&!P.error&&(P.conflict||!P.wp_cache&&P.wpconfig_writable||!P.dropin_ours)&&P.enabled&&(0,a.createElement)("div",{className:"oz-health__fix"},(0,a.createElement)(o.Button,{className:"oz-btn-primary",isBusy:O,onClick:le},(0,i.__)("Take over caching","optimazz")),(0,a.createElement)("span",null,(0,i.__)("Make Optimazz the caching plugin — installs its drop-in and enables WP_CACHE.","optimazz"))),P&&!P.error&&Array.isArray(P.active_conflicts)&&P.active_conflicts.length>0&&(0,a.createElement)("div",{className:"oz-health__conflict"},P.active_conflicts.map(e=>(0,a.createElement)("div",{className:"oz-health__conflict-row",key:e.file},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,`${e.name} ${(0,i.__)("is still active and may reclaim caching. Deactivate it so Optimazz keeps serving the cache.","optimazz")}`),(0,a.createElement)(o.Button,{className:"oz-btn-danger",isBusy:O,onClick:()=>ne(e.file,e.name)},`${(0,i.__)("Deactivate","optimazz")} ${e.name}`)))),P&&!P.error&&!P.wp_cache&&!P.wpconfig_writable&&(0,a.createElement)("div",{className:"oz-health__manual"},(0,a.createElement)("p",null,(0,i.__)("wp-config.php is not writable. Add this line just after the opening <?php tag:","optimazz")),(0,a.createElement)("code",null,"define( 'WP_CACHE', true );")),P&&P.error&&(0,a.createElement)("p",{className:"oz-health__err"},(0,i.__)("Could not run the check. Try reloading the page.","optimazz")))),Je=(0,a.createElement)(a.Fragment,null,(0,a.createElement)(b,{icon:"editor-code",title:(0,i.__)("JavaScript","optimazz"),description:(0,i.__)("Defer and delay scripts to cut Total Blocking Time.","optimazz"),enabled:!!Ne.enabled,onToggle:()=>K("js","enabled")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Minify JavaScript","optimazz"),help:(0,i.__)("Strip comments and whitespace from inline scripts. Line breaks are kept for safety; test your site after enabling.","optimazz"),checked:!!Ne.minify,onChange:()=>K("js","minify")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Defer JavaScript","optimazz"),help:(0,i.__)("Load scripts without blocking page render.","optimazz"),checked:!!Ne.defer,onChange:()=>K("js","defer")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Delay JS until interaction","optimazz"),help:(0,i.__)("Run scripts only after the first user interaction.","optimazz"),checked:!!Ne.delay,onChange:()=>K("js","delay")}),Ne.delay&&(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Delay all scripts","optimazz"),help:(0,i.__)("Delay every script until interaction. jQuery, your theme, and Elementor are always kept safe. Still, on sites where content is rendered by JavaScript this can delay the visible page — test before keeping it on.","optimazz"),checked:!!Ne.delay_all,onChange:()=>K("js","delay_all")}),Ne.delay&&Ne.delay_all&&(0,a.createElement)("div",{className:"oz-dbwarn"},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,(0,i.__)('Delaying all scripts can slow the first paint on JavaScript-rendered sites (Elementor, sliders). If your PageSpeed FCP/LCP get worse, switch to "Delay selected" and delay only tracking scripts.',"optimazz"))),Ne.delay&&!Ne.delay_all&&(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Delay these scripts","optimazz"),help:(0,i.__)("Only scripts matching these keywords are delayed. Match by filename, handle or URL — e.g. gtag, analytics, facebook.","optimazz"),placeholder:(0,i.__)("e.g. gtag, analytics","optimazz"),value:Ne.delay_list||[],onChange:e=>V("js","delay_list",e)}),(Ne.defer||Ne.delay)&&(0,a.createElement)("div",{className:"oz-fieldgroup"},(0,a.createElement)("h4",{className:"oz-fieldgroup__title"},(0,i.__)("Exclude specific scripts","optimazz")),(0,a.createElement)("p",{className:"oz-fieldgroup__desc"},(0,i.__)("If a script breaks, exclude it from just one optimization instead of both. Match by filename, script handle, or part of the URL.","optimazz")),Ne.defer&&(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never defer","optimazz"),help:(0,i.__)("Keep loading these render-blocking, but still allow delay. Use for scripts that must run during page load.","optimazz"),placeholder:(0,i.__)("e.g. jquery.min.js","optimazz"),value:Ne.no_defer||[],onChange:e=>V("js","no_defer",e)}),Ne.delay&&(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never delay","optimazz"),help:(0,i.__)("Run these before any interaction, but still allow defer. Use for sliders, above-the-fold, or consent scripts.","optimazz"),placeholder:(0,i.__)("e.g. slider, swiper","optimazz"),value:Ne.no_delay||[],onChange:e=>V("js","no_delay",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never optimize","optimazz"),help:(0,i.__)("Skip both defer and delay — leave these scripts exactly as they are.","optimazz"),placeholder:(0,i.__)("e.g. recaptcha","optimazz"),value:Ne.exclusions||[],onChange:e=>V("js","exclusions",e)}))),(0,a.createElement)(b,{icon:"admin-appearance",title:(0,i.__)("CSS","optimazz"),description:(0,i.__)("Minify, combine, and remove render-blocking stylesheets.","optimazz"),enabled:!!xe.enabled,onToggle:()=>K("css","enabled")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Minify CSS","optimazz"),checked:!!xe.minify,onChange:()=>K("css","minify")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Combine CSS files","optimazz"),help:(0,i.__)("Merge stylesheets to reduce requests.","optimazz"),checked:!!xe.combine,onChange:()=>K("css","combine")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Load CSS asynchronously","optimazz"),help:(0,i.__)("Removes render-blocking CSS. Paste your above-the-fold (critical) CSS below, or the page may briefly flash unstyled.","optimazz"),checked:!!xe.async_load,onChange:()=>K("css","async_load")}),xe.async_load&&(0,a.createElement)(a.Fragment,null,!(xe.critical_css||"").trim()&&(0,a.createElement)("div",{className:"oz-dbwarn"},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,(0,i.__)("No critical CSS provided. Async loading is on, so visitors may see a flash of unstyled content until you paste critical CSS below.","optimazz"))),(0,a.createElement)(o.TextareaControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Critical CSS (above-the-fold)","optimazz"),help:(0,i.__)("Paste the above-the-fold CSS to inline. Generate it with a free tool such as a critical-CSS generator. Leave empty to disable inlining.","optimazz"),value:xe.critical_css||"",onChange:e=>V("css","critical_css",e),rows:6})),(0,a.createElement)("div",{className:"oz-subhead"},(0,i.__)("Remove unused CSS","optimazz")),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Remove block-editor CSS on the front end","optimazz"),help:(0,i.__)("Dequeues WordPress block-library and global styles. Turn on only if your front end does NOT render Gutenberg blocks (e.g. a page-builder or classic theme). Big byte saving when it applies.","optimazz"),checked:!!xe.remove_block_css,onChange:()=>K("css","remove_block_css")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Load WooCommerce CSS only on shop pages","optimazz"),help:(0,i.__)("Dequeues WooCommerce styles on non-shop pages (kept on cart, checkout, account, product and shop). Turn off if you show Woo products or widgets on other pages.","optimazz"),checked:!!xe.remove_woo_css,onChange:()=>K("css","remove_woo_css")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Load Easy Digital Downloads CSS only on EDD pages","optimazz"),help:(0,i.__)("Dequeues EDD styles on non-EDD pages (kept on checkout, purchase-confirmation, purchase history and download archives). Turn off if you show download or purchase links elsewhere.","optimazz"),checked:!!xe.remove_edd_css,onChange:()=>K("css","remove_edd_css")}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Excluded CSS files (keywords)","optimazz"),value:xe.exclusions||[],onChange:e=>V("css","exclusions",e)}))),Ge=(0,a.createElement)(b,{icon:"format-image",title:(0,i.__)("Image Optimization","optimazz"),description:(0,i.__)("Lazy-load, prevent layout shift, serve next-gen formats, and lighten media.","optimazz"),enabled:!!Se.enabled,onToggle:()=>K("media","enabled")},pe&&!pe.can_process&&(0,a.createElement)("div",{className:"oz-notice oz-notice--error"},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,(0,i.__)('This server has no GD or ImageMagick extension, so image compression and WebP/AVIF cannot run — every image will be skipped. Ask your host to enable "php-gd" (or "php-imagick"). See Tools → Server Requirements.',"optimazz"))),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Lazy-load images","optimazz"),help:(0,i.__)("Load off-screen images only when scrolled into view.","optimazz"),checked:!!Se.lazy_load,onChange:()=>K("media","lazy_load")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Lazy-load iframes","optimazz"),checked:!!Se.lazy_iframes,onChange:()=>K("media","lazy_iframes")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Lazy-load CSS background images","optimazz"),help:(0,i.__)("Defer inline background-image elements (test with page builders).","optimazz"),checked:!!Se.lazy_bg,onChange:()=>K("media","lazy_bg")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Replace YouTube embeds with a preview","optimazz"),help:(0,i.__)("Load the video iframe only when the visitor clicks play.","optimazz"),checked:!!Se.youtube_preview,onChange:()=>K("media","youtube_preview")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Add missing image dimensions","optimazz"),help:(0,i.__)("Set width/height to prevent layout shift (CLS).","optimazz"),checked:!!Se.add_dimensions,onChange:()=>K("media","add_dimensions")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Preload the LCP image","optimazz"),help:(0,i.__)("Preload the first/hero image with high priority.","optimazz"),checked:!!Se.preload_lcp,onChange:()=>K("media","preload_lcp")}),Se.lazy_load&&(0,a.createElement)(C,{label:(0,i.__)("Keep first images eager (LCP)","optimazz"),help:(0,i.__)("Do not lazy-load the first N images, to protect LCP.","optimazz"),value:null!==(E=Se.skip_first)&&void 0!==E?E:1,onChange:e=>V("media","skip_first",e),options:[0,1,2,3,4,5].map(e=>({label:String(e),value:e}))}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Serve WebP via <picture>","optimazz"),help:(0,i.__)("Deliver WebP to supporting browsers, original as fallback.","optimazz"),checked:!!Se.webp,onChange:()=>K("media","webp")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Generate WebP/AVIF on upload","optimazz"),help:pe&&!pe.can_webp?(0,i.__)("WebP generation is not available on this server.","optimazz"):(0,i.__)("Auto-create next-gen copies for newly uploaded images.","optimazz"),checked:!!Se.webp_generate,onChange:()=>K("media","webp_generate")}),pe?.can_avif&&(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Also generate/serve AVIF","optimazz"),help:(0,i.__)("Smaller than WebP; served first when supported.","optimazz"),checked:!!Se.avif,onChange:()=>K("media","avif")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Compress & resize on upload","optimazz"),help:(0,i.__)("Recompress, cap dimensions, and strip metadata from new uploads.","optimazz"),checked:!!Se.compress,onChange:()=>K("media","compress")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Convert opaque PNG to JPEG","optimazz"),help:(0,i.__)("Re-save non-transparent PNGs as much smaller JPEGs during bulk optimization.","optimazz"),checked:!!Se.png_to_jpg,onChange:()=>K("media","png_to_jpg")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Keep a backup of originals","optimazz"),help:(0,i.__)("Copy originals to uploads/optimazz-backup before optimizing.","optimazz"),checked:!!Se.backup_originals,onChange:()=>K("media","backup_originals")}),(0,a.createElement)(o.RangeControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Image quality","optimazz"),value:null!==(y=Se.quality)&&void 0!==y?y:82,onChange:e=>V("media","quality",e),min:40,max:100,help:(0,i.__)("Used for compression and WebP/AVIF generation.","optimazz")}),(0,a.createElement)(o.RangeControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Max image width (px)","optimazz"),value:null!==(x=Se.max_width)&&void 0!==x?x:2560,onChange:e=>V("media","max_width",e),min:800,max:4e3,step:80}),(0,a.createElement)("div",{className:"oz-convert"},(0,a.createElement)("div",{className:"oz-convert__info"},(0,a.createElement)("strong",null,(0,i.__)("WebP coverage","optimazz")),pe?(0,a.createElement)("span",null,pe.converted," / ",pe.images," ",(0,i.__)("images converted","optimazz")):(0,a.createElement)("span",null,(0,i.__)("Loading…","optimazz"))),(0,a.createElement)(o.Button,{className:"oz-btn-primary",icon:"images-alt2",isBusy:ze?.running,disabled:pe&&!pe.can_webp||ze?.running,onClick:()=>ve("convert")},(0,i.__)("Convert to WebP","optimazz"))),(0,a.createElement)("div",{className:"oz-convert"},(0,a.createElement)("div",{className:"oz-convert__info"},(0,a.createElement)("strong",null,(0,i.__)("Bulk compression","optimazz")),pe?(0,a.createElement)("span",null,pe.optimized," ",(0,i.__)("optimized","optimazz")," · ",we(pe.saved)," ",(0,i.__)("saved","optimazz")):(0,a.createElement)("span",null,(0,i.__)("Loading…","optimazz"))),(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"filter",isBusy:ze?.running,disabled:ze?.running,onClick:()=>ve("compress")},(0,i.__)("Compress existing images","optimazz")))),Ve=(0,a.createElement)(N,{title:(0,i.__)("Preload Cache","optimazz"),description:(0,i.__)("Warm the cache automatically so visitors never hit a cold page.","optimazz")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Activate preloading","optimazz"),help:(0,i.__)("Warms the cache on a schedule and right after every full purge (for example after changing a setting). Warming follows the sitemap — home, pages, posts, custom post types, then the busiest category and tag archives — most-important first, for both mobile and desktop.","optimazz"),checked:!!Ce.preload,onChange:()=>K("cache","preload")}),(0,a.createElement)(o.SelectControl,{__nextHasNoMarginBottom:!0,__next40pxDefaultSize:!0,label:(0,i.__)("Automatic preload","optimazz"),help:(0,i.__)("How often to re-warm the whole site. Choose “Every hour” if your content changes often or you use a short cache lifespan.","optimazz"),value:Ce.preload_interval||"twicedaily",disabled:!Ce.preload,options:[{label:(0,i.__)("Every hour","optimazz"),value:"hourly"},{label:(0,i.__)("Twice a day","optimazz"),value:"twicedaily"},{label:(0,i.__)("Once a day","optimazz"),value:"daily"}],onChange:e=>V("cache","preload_interval",e)}),B&&B.wp_cron_disabled&&(0,a.createElement)("div",{className:"oz-notice oz-notice--warn",style:{marginTop:12}},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,(0,i.__)("WP-Cron is disabled on this site (DISABLE_WP_CRON). Scheduled preloads only run if a server cron job calls wp-cron.php — ask your host to set one up (e.g. every 5 minutes).","optimazz"))),(0,a.createElement)("div",{className:"oz-inline-actions"},(0,a.createElement)(o.Button,{className:"oz-btn-primary",icon:"update",isBusy:!!ze?.running,onClick:ce},(0,i.__)("Preload now","optimazz")))),Ke=(0,a.createElement)(N,{title:(0,i.__)("Advanced Rules","optimazz"),description:(0,i.__)("Fine-tune which requests are cached.","optimazz")},(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never cache these URLs (contains)","optimazz"),value:Ce.excluded_uris||[],onChange:e=>V("cache","excluded_uris",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never cache when these cookies are set","optimazz"),value:Ce.excluded_cookies||[],onChange:e=>V("cache","excluded_cookies",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never cache for these user agents","optimazz"),value:Ce.excluded_user_agents||[],onChange:e=>V("cache","excluded_user_agents",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Ignore these query arguments","optimazz"),value:Ce.ignored_query_args||[],onChange:e=>V("cache","ignored_query_args",e)})),Ye=!(!S.tools||!S.tools.safe_mode),Ze=(0,a.createElement)(a.Fragment,null,(0,a.createElement)(N,{title:(0,i.__)("Safe Mode","optimazz"),description:(0,i.__)("If a theme or page builder looks broken after optimizing, turn this on. It pauses the riskiest optimizations — combine CSS, async CSS, remove block CSS and JavaScript delay — while keeping caching, minify, defer and lazy-load. Your saved settings are not changed; they simply pause until you turn Safe Mode off.","optimazz")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Pause risky optimizations (Safe Mode)","optimazz"),checked:Ye,onChange:e=>{const a={...S,tools:{...S.tools||{},safe_mode:e}};M(a),se(a)}}),Ye&&(0,a.createElement)("div",{className:"oz-notice oz-notice--warn",style:{marginTop:12}},(0,a.createElement)("span",{className:"dashicons dashicons-shield"}),(0,a.createElement)("span",null,(0,i.__)("Safe Mode is ON — combine/async CSS, remove-block-CSS and JavaScript delay are paused site-wide. Turn it off to resume full optimization.","optimazz"))),(0,a.createElement)("div",{className:"oz-inline-actions",style:{marginTop:14}},(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"undo",disabled:!q,onClick:async()=>{try{const e=await l()({url:`${r}/settings/revert`,method:"POST"});if("nothing"===e.message)return U(!1),void ie((0,i.__)("Nothing to undo.","optimazz"),"error");$(e.settings),M(e.settings),U(!!e.hasBackup),ae(null),ie((0,i.__)("Reverted to the previous settings.","optimazz")),Y()}catch(e){ie(e.message||(0,i.__)("Undo failed.","optimazz"),"error")}}},(0,i.__)("Undo last change","optimazz")),(0,a.createElement)("span",{className:"oz-muted",style:{fontSize:12}},q?(0,i.__)("Restores the settings from just before your last save, reset or Safe Mode toggle.","optimazz"):(0,i.__)("Nothing to undo yet — this becomes available after your next change.","optimazz")))),(0,a.createElement)(N,{title:(0,i.__)("Server Requirements","optimazz"),description:(0,i.__)("What your host supports. Warnings here explain why a feature (like WebP/AVIF) may be unavailable or slow.","optimazz")},!Z&&(0,a.createElement)("p",{className:"oz-muted"},(0,i.__)("Checking your server…","optimazz")),Z&&Z.error&&(0,a.createElement)("p",{className:"oz-muted"},(0,i.__)("Could not read the server report.","optimazz")),Z&&!Z.error&&(0,a.createElement)("ul",{className:"oz-reqs"},(Z.checks||[]).map((e,t)=>(0,a.createElement)("li",{key:t,className:`oz-req oz-req--${e.status}`},(0,a.createElement)("span",{className:"oz-req__dot dashicons "+("ok"===e.status?"dashicons-yes-alt":"warn"===e.status?"dashicons-warning":"info"===e.status?"dashicons-minus":"dashicons-dismiss")}),(0,a.createElement)("span",{className:"oz-req__label"},e.label),(0,a.createElement)("span",{className:"oz-req__value"},e.value),e.note&&(0,a.createElement)("span",{className:"oz-req__note"},e.note)))),(0,a.createElement)("div",{className:"oz-inline-actions",style:{marginTop:14}},(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"update",isBusy:!Z,onClick:()=>{Q(null),X()}},(0,i.__)("Re-check","optimazz")))),(0,a.createElement)(N,{title:(0,i.__)("Import / Export Settings","optimazz"),description:(0,i.__)("Back up your configuration or copy it to another site.","optimazz")},(0,a.createElement)("div",{className:"oz-inline-actions"},(0,a.createElement)(o.Button,{className:"oz-btn-primary",icon:"download",onClick:()=>{const e=new window.Blob([JSON.stringify(S,null,2)],{type:"application/json"}),a=document.createElement("a");a.href=URL.createObjectURL(e),a.download="optimazz-settings.json",a.click(),URL.revokeObjectURL(a.href)}},(0,i.__)("Export settings","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"upload",onClick:()=>J.current?.click()},(0,i.__)("Import settings","optimazz")),(0,a.createElement)("input",{ref:J,type:"file",accept:"application/json",style:{display:"none"},onChange:e=>{const a=e.target.files?.[0];if(!a)return;const t=new window.FileReader;t.onload=()=>{try{const e=JSON.parse(t.result);se(e)}catch(e){ie((0,i.__)("Invalid settings file.","optimazz"),"error")}},t.readAsText(a),e.target.value=""}}))),(0,a.createElement)(N,{title:(0,i.__)("Reset","optimazz"),description:(0,i.__)("Restore every module to its default configuration. This cannot be undone.","optimazz")},(0,a.createElement)("div",{className:"oz-inline-actions"},(0,a.createElement)(o.Button,{className:"oz-btn-danger",icon:"image-rotate",onClick:async()=>{if(window.confirm((0,i.__)('Reset all Optimazz settings to defaults? You can undo this with "Undo last change".',"optimazz")))try{const e=await l()({url:`${r}/settings/reset`,method:"POST"});$(e.settings),M(e.settings),U(!!e.hasBackup),ie((0,i.__)("Settings reset to defaults.","optimazz")),Y()}catch(e){ie(e.message||(0,i.__)("Reset failed.","optimazz"),"error")}}},(0,i.__)("Reset all settings to defaults","optimazz"))))),Qe=(0,a.createElement)(b,{icon:"editor-textcolor",title:(0,i.__)("Fonts","optimazz"),description:(0,i.__)("Swap, preconnect, and preload fonts to remove render-blocking requests.","optimazz"),enabled:!!Me.enabled,onToggle:()=>K("fonts","enabled")},(0,a.createElement)(o.SelectControl,{__nextHasNoMarginBottom:!0,__next40pxDefaultSize:!0,label:(0,i.__)("Font loading (font-display)","optimazz"),help:(0,i.__)("Optional = no layout shift (best for CLS): the web font is skipped on the first uncached load and used once cached. Swap = text stays visible but the web font swaps in and can shift layout (higher CLS). Block = brief invisible text, then the web font.","optimazz"),value:Me.font_display||(Me.display_swap,"swap"),options:[{label:(0,i.__)("Optional — no layout shift (recommended for CLS)","optimazz"),value:"optional"},{label:(0,i.__)("Swap — always show text (may shift layout)","optimazz"),value:"swap"},{label:(0,i.__)("Block — brief hidden text, then web font","optimazz"),value:"block"}],onChange:e=>V("fonts","font_display",e)}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Combine Google Fonts into one request","optimazz"),help:(0,i.__)("Page builders often load each Google Font family separately. This merges them into a single request.","optimazz"),checked:!!Me.combine_google,onChange:e=>V("fonts","combine_google",e)}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Load only the font weights your pages use","optimazz"),help:(0,i.__)("Drops unused weights (e.g. 100, 200, 900) and italics your pages never use — often cutting font CSS by more than half. Weights 400 and 700 are always kept. Turn off if some text looks thinner or bolder than intended.","optimazz"),checked:!!Me.trim_weights,disabled:!Me.combine_google,onChange:e=>V("fonts","trim_weights",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Preload these font files (URLs)","optimazz"),value:Me.preload||[],onChange:e=>V("fonts","preload",e)})),Xe=e=>{var a;if(!me||void 0===me[e.key])return e.help;const t=null!==(a=me[e.key])&&void 0!==a?a:0; 6 /* translators: %d: number of items found in the database. */return`${(0,i.sprintf)((0,i._n)("%d item in your database.","%d items in your database.",t,"optimazz"),t)} ${e.help}`},ea=(0,a.createElement)(N,{title:(0,i.__)("Database Optimization","optimazz"),description:(0,i.__)("Reduce bloat and reclaim space. Once a cleanup runs it cannot be undone, so back up your database first.","optimazz")},h.map(e=>(0,a.createElement)("div",{key:e.title,className:"oz-dbgroup"},(0,a.createElement)("h3",{className:"oz-dbgroup__title"},e.title),(0,a.createElement)("p",{className:"oz-dbgroup__desc"},e.desc),e.items.map(e=>{var t;return(0,a.createElement)(o.ToggleControl,{key:e.key,__nextHasNoMarginBottom:!0,label:me?`${e.label} (${null!==(t=me[e.key])&&void 0!==t?t:0})`:e.label,help:Xe(e),checked:!!Te[e.key],onChange:()=>K("database",e.key)})}))),(0,a.createElement)("div",{className:"oz-dbgroup"},(0,a.createElement)("h3",{className:"oz-dbgroup__title"},(0,i.__)("Set it and forget it","optimazz")),(0,a.createElement)("p",{className:"oz-dbgroup__desc"},(0,i.__)("Let Optimazz repeat the cleanups you picked above on a schedule, so your database stays lean without you lifting a finger.","optimazz")),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Schedule automatic cleanup","optimazz"),help:(0,i.__)("Uses WP-Cron to repeat the cleanups you enabled above.","optimazz"),checked:!!Te.enabled,onChange:()=>K("database","enabled")}),Te.enabled&&(0,a.createElement)(C,{label:(0,i.__)("Cleanup frequency","optimazz"),value:Te.schedule||"off",onChange:e=>V("database","schedule",e),options:[{label:(0,i.__)("Off","optimazz"),value:"off"},{label:(0,i.__)("Daily","optimazz"),value:"daily"},{label:(0,i.__)("Weekly","optimazz"),value:"weekly"},{label:(0,i.__)("Monthly","optimazz"),value:"monthly"}]})),(0,a.createElement)("div",{className:"oz-dbwarn"},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,(0,i.__)("Back up your database before you run a cleanup — a database optimization cannot be undone.","optimazz"))),(0,a.createElement)("div",{className:"oz-inline-actions"},(0,a.createElement)(o.Button,{className:"oz-btn-primary",icon:"trash",isBusy:!!ze?.running,onClick:async()=>{if(ze?.running)return;const e=(0,i.__)("Cleaning and optimizing database…","optimazz");he({title:(0,i.__)("Optimizing database","optimazz"),subtitle:(0,i.__)("Cleanup & table optimization","optimazz"),pct:100,indeterminate:!0,label:e,step:"",logs:[{level:"INFO",text:e}],running:!0,status:"running",minimized:!1});try{const e=await l()({url:`${r}/database/optimize`,method:"POST",data:S.database||{}});de(e.counts);const a=Object.values(e.removed||{}).reduce((e,a)=>e+a,0),t=`${(0,i.__)("Cleaned","optimazz")} ${a} ${(0,i.__)("rows,","optimazz")} ${e.tables} ${(0,i.__)("tables optimized.","optimazz")}`;he(e=>e&&{...e,running:!1,status:"done",indeterminate:!1,pct:100,label:t,logs:[...e.logs,{level:"INFO",text:t},{level:"DONE",text:(0,i.__)("Completed.","optimazz")}]}),ie(t)}catch(e){he(a=>a&&{...a,running:!1,status:"error",indeterminate:!1,logs:[...a.logs,{level:"ERROR",text:e.message||"Error"}]}),ie(e.message||(0,i.__)("Optimize failed.","optimazz"),"error")}}},(0,i.__)("Optimize now","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"update",onClick:ye},(0,i.__)("Refresh counts","optimazz")))),aa={dashboard:je,cache:Ue,files:Je,media:Ge,fonts:Qe,preload:Ve,advanced:Ke,database:ea,heartbeat:(0,a.createElement)(N,{title:(0,i.__)("Heartbeat Control","optimazz"),description:(0,i.__)("Reduce or disable the WordPress Heartbeat API to lower server load.","optimazz")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Enable Heartbeat control","optimazz"),checked:!!$e.enabled,onChange:()=>K("heartbeat","enabled")}),$e.enabled&&(0,a.createElement)(a.Fragment,null,(0,a.createElement)(C,{label:(0,i.__)("Behavior in admin dashboard","optimazz"),value:$e.backend||"default",options:u,onChange:e=>V("heartbeat","backend",e)}),(0,a.createElement)(C,{label:(0,i.__)("Behavior in post editor","optimazz"),value:$e.editor||"default",options:u,onChange:e=>V("heartbeat","editor",e)}),(0,a.createElement)(C,{label:(0,i.__)("Behavior on front-end","optimazz"),value:$e.frontend||"default",options:u,onChange:e=>V("heartbeat","frontend",e)}))),tweaks:(0,a.createElement)(b,{icon:"admin-generic",title:(0,i.__)("Tweaks","optimazz"),description:(0,i.__)("Strip unnecessary WordPress front-end weight.","optimazz"),enabled:!!Be.enabled,onToggle:()=>K("tweaks","enabled")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Disable emojis","optimazz"),help:(0,i.__)("Remove the emoji detection script and styles.","optimazz"),checked:!!Be.disable_emojis,onChange:()=>K("tweaks","disable_emojis")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Disable embeds (oEmbed)","optimazz"),checked:!!Be.disable_embeds,onChange:()=>K("tweaks","disable_embeds")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Disable dashicons on front-end","optimazz"),help:(0,i.__)("Only for anonymous visitors.","optimazz"),checked:!!Be.disable_dashicons,onChange:()=>K("tweaks","disable_dashicons")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Remove query strings from assets","optimazz"),help:(0,i.__)("Strip ?ver= for better proxy caching.","optimazz"),checked:!!Be.remove_query_strings,onChange:()=>K("tweaks","remove_query_strings")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Disable XML-RPC","optimazz"),checked:!!Be.disable_xmlrpc,onChange:()=>K("tweaks","disable_xmlrpc")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Disable self-pingbacks","optimazz"),checked:!!Be.disable_pingback,onChange:()=>K("tweaks","disable_pingback")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Remove head meta (generator, RSD, shortlink)","optimazz"),checked:!!Be.remove_meta,onChange:()=>K("tweaks","remove_meta")}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("DNS-prefetch domains","optimazz"),value:Be.dns_prefetch||[],onChange:e=>V("tweaks","dns_prefetch",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Preconnect origins","optimazz"),value:Be.preconnect||[],onChange:e=>V("tweaks","preconnect",e)})),cdn:(0,a.createElement)(b,{icon:"cloud",title:(0,i.__)("Content Delivery Network","optimazz"),description:(0,i.__)("Rewrite static asset URLs to your CDN hostname.","optimazz"),enabled:!!Re.enabled,onToggle:()=>K("cdn","enabled")},(0,a.createElement)(o.TextControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("CDN URL (CNAME)","optimazz"),placeholder:"https://cdn.example.com",value:Re.url||"",onChange:e=>V("cdn","url",e),help:(0,i.__)("CSS, JS, image, and font URLs will be served from here.","optimazz")}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Exclude paths containing","optimazz"),value:Re.exclusions||[],onChange:e=>V("cdn","exclusions",e)})),tools:Ze};return p.forEach(e=>{aa[e.key]="function"==typeof e.render?e.render():e.render}),(0,a.createElement)("div",{className:"oz-shell"},(0,a.createElement)("aside",{className:"oz-sidebar"},(0,a.createElement)("div",{className:"oz-sidebar__brand"},window.optimazzData?.logoUrl?(0,a.createElement)("img",{className:"oz-logo-img",src:window.optimazzData.logoUrl,alt:"Optimazz"}):(0,a.createElement)("div",{className:"oz-sidebar__brand-fallback"},(0,a.createElement)("span",{className:"oz-logo dashicons dashicons-performance"}),(0,a.createElement)("strong",null,"Optimazz"))),(0,a.createElement)("nav",null,_.map(e=>(0,a.createElement)("button",{key:e.key,className:"oz-navitem "+(H===e.key?"is-active":""),onClick:()=>L(e.key)},(0,a.createElement)(w,{name:e.key,paths:e.iconSvg}),(0,a.createElement)("span",{className:"oz-navitem__text"},(0,a.createElement)("strong",null,e.title),(0,a.createElement)("small",null,e.sub)),e.mod&&S[e.mod]?.enabled&&(0,a.createElement)("i",{className:"oz-navitem__dot"})))),(0,a.createElement)("div",{className:"oz-sidebar__foot"},"v",window.optimazzData?.version)),(0,a.createElement)("div",{className:"oz-content"},(0,a.createElement)("header",{className:"oz-topbar"},(0,a.createElement)("span",{className:`oz-topbar__icon dashicons dashicons-${Pe.icon}`}),(0,a.createElement)("div",null,(0,a.createElement)("h1",null,Pe.title),(0,a.createElement)("p",null,Pe.sub))),(0,a.createElement)("main",{className:"oz-main"},aa[H])),(0,a.createElement)("div",{className:"oz-savebar "+(G?"is-dirty":"")},(0,a.createElement)("span",{className:"oz-savebar__hint"},G?(0,i.__)("You have unsaved changes","optimazz"):(0,i.__)("All changes saved","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-primary",isBusy:A,disabled:!G,onClick:()=>se(S)},(0,i.__)("Save changes","optimazz"))),W&&(0,a.createElement)("div",{className:"oz-toast"},(0,a.createElement)(o.Snackbar,{status:W.status,onRemove:()=>I(null)},W.text)),(0,a.createElement)(f,{job:ze,onStop:()=>{ue.current=!0},onClose:()=>{ue.current=!0,he(null)},onToggleMin:()=>he(e=>e&&{...e,minimized:!e.minimized})}))},null))})(); 1 (()=>{"use strict";const e={n:a=>{const t=a&&a.__esModule?()=>a.default:()=>a;return e.d(t,{a:t}),t},d:(a,t)=>{for(var o in t)e.o(t,o)&&!e.o(a,o)&&Object.defineProperty(a,o,{enumerable:!0,get:t[o]})},o:(e,a)=>Object.hasOwn(e,a)},a=window.React,t=window.wp.element,o=window.wp.components,n=window.wp.apiFetch;var l=e.n(n);const i=window.wp.i18n,s=window.wp.hooks,{restUrl:r,nonce:c,settings:m}=window.optimazzData||{};l().use(l().createNonceMiddleware(c));const d=[{key:"dashboard",title:(0,i.__)("Dashboard","optimazz"),sub:(0,i.__)("Overview & quick actions","optimazz"),icon:"admin-home"},{key:"cache",title:(0,i.__)("Page Cache","optimazz"),sub:(0,i.__)("Static HTML caching","optimazz"),icon:"database",mod:"cache"},{key:"files",title:(0,i.__)("File Optimization","optimazz"),sub:(0,i.__)("Optimize CSS & JS","optimazz"),icon:"editor-code"},{key:"media",title:(0,i.__)("Media","optimazz"),sub:(0,i.__)("LazyLoad, dimensions, WebP","optimazz"),icon:"format-image",mod:"media"},{key:"fonts",title:(0,i.__)("Fonts","optimazz"),sub:(0,i.__)("Preload, swap, preconnect","optimazz"),icon:"editor-textcolor",mod:"fonts"},{key:"preload",title:(0,i.__)("Preload","optimazz"),sub:(0,i.__)("Generate cache files","optimazz"),icon:"update"},{key:"advanced",title:(0,i.__)("Advanced Rules","optimazz"),sub:(0,i.__)("Fine-tune cache rules","optimazz"),icon:"list-view"},{key:"database",title:(0,i.__)("Database","optimazz"),sub:(0,i.__)("Optimize, reduce bloat","optimazz"),icon:"database-view",mod:"database"},{key:"heartbeat",title:(0,i.__)("Heartbeat","optimazz"),sub:(0,i.__)("Control Heartbeat API","optimazz"),icon:"heart",mod:"heartbeat"},{key:"tweaks",title:(0,i.__)("Tweaks","optimazz"),sub:(0,i.__)("Remove WordPress bloat","optimazz"),icon:"admin-generic",mod:"tweaks"},{key:"cdn",title:(0,i.__)("CDN","optimazz"),sub:(0,i.__)("Rewrite asset URLs","optimazz"),icon:"cloud",mod:"cdn"},{key:"tools",title:(0,i.__)("Tools","optimazz"),sub:(0,i.__)("Import, export","optimazz"),icon:"admin-tools"}],p=(0,s.applyFilters)("optimazz.adminSections",[]),_=[...d,...p],z=e=>![!1,"","0",0,void 0,null].includes(e),h=![!1,"","0",0].includes(window.optimazzData?.serverFeatures),u=window.optimazzData?.networkCache||null,g=!h&&!!u&&z(u.enabled),b=h||g,f=z(window.optimazzData?.isSuperAdmin),E=window.optimazzData?.networkAdminUrl||"",v=e=>{if(!e)return"0 KB";const a=Math.floor(Math.log(e)/Math.log(1024));return`${(e/Math.pow(1024,a)).toFixed(a?1:0)} ${["B","KB","MB","GB"][a]}`},y=e=>({hourly:(0,i.__)("every hour","optimazz"),twicedaily:(0,i.__)("twice a day","optimazz"),daily:(0,i.__)("once a day","optimazz")}[e]||(0,i.__)("twice a day","optimazz"));function w(){return f&&E?(0,a.createElement)("a",{className:"oz-cachewarn__link",href:E},(0,i.__)("Open Network Admin → Optimazz","optimazz")):null}function k(){return(0,a.createElement)("div",{className:"oz-cachewarn",role:"status"},(0,a.createElement)("span",{className:"dashicons dashicons-info-outline"}),(0,a.createElement)("div",{className:"oz-cachewarn__text"},(0,a.createElement)("strong",null,(0,i.__)("Page caching is off for this network","optimazz")),(0,a.createElement)("p",null,f?(0,i.__)("Turn it on for every site in Network Admin → Optimazz. Each site gets its own cache, and keeps its own exclusions here under Advanced Rules.","optimazz"):(0,i.__)("Your network administrator can turn it on for every site in Network Admin → Optimazz. All other optimizations — CSS, JavaScript, images, fonts and more — work normally on this site.","optimazz")),(0,a.createElement)(w,null)))}function C(){const e=u||{};return(0,a.createElement)("div",{className:"oz-cachewarn oz-cachewarn--ok",role:"status"},(0,a.createElement)("span",{className:"dashicons dashicons-yes-alt"}),(0,a.createElement)("div",{className:"oz-cachewarn__text"},(0,a.createElement)("strong",null,(0,i.__)("Page caching is on — managed for the whole network","optimazz")),(0,a.createElement)("p",null,(0,i.sprintf)(/* translators: 1: yes/no, 2: cache lifetime, 3: preload frequency. */ /* translators: 1: yes/no, 2: cache lifetime, 3: preload frequency. */ 2 (0,i.__)("Separate mobile cache: %1$s · Cache lifetime: %2$s · Automatic preload: %3$s.","optimazz"),z(e.cache_mobile)?(0,i.__)("yes","optimazz"):(0,i.__)("no","optimazz"),(t=e.lifespan_hours,null!==(o={0:(0,i.__)("never expires","optimazz"),12:(0,i.__)("12 hours","optimazz"),24:(0,i.__)("1 day","optimazz"),168:(0,i.__)("1 week","optimazz"),720:(0,i.__)("1 month","optimazz")}[Number(t)])&&void 0!==o?o:(0,i.sprintf)((0,i._n)("%d hour","%d hours",Number(t),"optimazz"),Number(t))),z(e.preload)?y(e.preload_interval):(0,i.__)("off","optimazz"))),(0,a.createElement)("p",null,(0,i.__)("This site has its own cache. Purge or preload it from the Dashboard or the Preload screen, and set its own exclusions under Advanced Rules.","optimazz")),(0,a.createElement)(w,null)));var t,o}const N=["cache","js","css","media","fonts","database","heartbeat","tweaks","cdn"],x=[{title:(0,i.__)("Content leftovers","optimazz"),desc:(0,i.__)("Old post data that piles up over time. Removing it is permanent, so keep anything you still need.","optimazz"),items:[{key:"revisions",label:(0,i.__)("Post revisions","optimazz"),help:(0,i.__)("Old copies of posts saved automatically. Deleting them keeps only your current content.","optimazz")},{key:"auto_drafts",label:(0,i.__)("Auto drafts","optimazz"),help:(0,i.__)("Empty drafts WordPress creates while you write. Safe to remove.","optimazz")},{key:"trashed_posts",label:(0,i.__)("Trashed posts","optimazz"),help:(0,i.__)("Posts in the trash that have not been permanently deleted yet.","optimazz")}]},{title:(0,i.__)("Comment clutter","optimazz"),desc:(0,i.__)("Spam and trashed comments you are never going to approve. Clearing them keeps the comments table light.","optimazz"),items:[{key:"spam_comments",label:(0,i.__)("Spam comments","optimazz"),help:(0,i.__)("Comments marked as spam. Removing them frees up the comments table.","optimazz")},{key:"trashed_comments",label:(0,i.__)("Trashed comments","optimazz"),help:(0,i.__)("Comments in the trash awaiting permanent deletion.","optimazz")}]},{title:(0,i.__)("Temporary data","optimazz"),desc:(0,i.__)("Short-lived cached values. They are safe to clear and are rebuilt automatically whenever a plugin needs them.","optimazz"),items:[{key:"expired_transients",label:(0,i.__)("Expired transients","optimazz"),help:(0,i.__)("Cached values that have already passed their expiry date.","optimazz")}]},{title:(0,i.__)("Table maintenance","optimazz"),desc:(0,i.__)("Defragment your tables to reclaim wasted space and trim database overhead.","optimazz"),items:[{key:"optimize_tables",label:(0,i.__)("Optimize database tables","optimazz"),help:(0,i.__)("Runs OPTIMIZE on your tables to defragment them and free wasted space.","optimazz")}]}],S=[{label:(0,i.__)("Do not limit","optimazz"),value:"default"},{label:(0,i.__)("Reduce activity","optimazz"),value:"reduce"},{label:(0,i.__)("Disable","optimazz"),value:"disable"}];function $({on:e}){return(0,a.createElement)("span",{className:"oz-pill "+(e?"is-on":"is-off")},(0,a.createElement)("i",null),e?(0,i.__)("Active","optimazz"):(0,i.__)("Off","optimazz"))}function T({icon:e,title:t,description:n,enabled:l,onToggle:i,children:s}){return(0,a.createElement)("section",{className:"oz-card "+(l?"is-enabled":"")},(0,a.createElement)("header",{className:"oz-card__head"},(0,a.createElement)("span",{className:`oz-card__icon dashicons dashicons-${e}`}),(0,a.createElement)("div",{className:"oz-card__title"},(0,a.createElement)("h2",null,t," ",(0,a.createElement)($,{on:l})),(0,a.createElement)("p",null,n)),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,checked:l,onChange:i})),l&&(0,a.createElement)("div",{className:"oz-card__body"},s))}function M({job:e,onStop:n,onClose:l,onToggleMin:s}){const r=(0,t.useRef)(null);if((0,t.useEffect)(()=>{r.current&&(r.current.scrollTop=r.current.scrollHeight)},[e?.logs?.length]),!e)return null;const c=e.status||(e.running?"running":"done"),m={stopped:(0,i.__)("Cancelled","optimazz"),error:(0,i.__)("Failed","optimazz")},d="running"===c||"done"===c?e.title:`${e.title} — ${m[c]}`;return(0,a.createElement)("div",{className:`oz-job is-${c} ${e.minimized?"is-min":""}`},(0,a.createElement)("div",{className:"oz-job__head"},"running"===c?(0,a.createElement)(o.Spinner,null):(0,a.createElement)("span",{className:`oz-job__icon-status oz-job__icon-status--${c} dashicons dashicons-${{done:"yes-alt",stopped:"dismiss",error:"warning"}[c]||"yes-alt"}`}),(0,a.createElement)("div",{className:"oz-job__title"},(0,a.createElement)("strong",null,d),(0,a.createElement)("span",null,e.minimized?`${e.pct}%${e.step?` · ${e.step}`:""}`:e.subtitle)),(0,a.createElement)("button",{className:"oz-job__icon","aria-label":e.minimized?(0,i.__)("Expand","optimazz"):(0,i.__)("Minimize","optimazz"),onClick:s},(0,a.createElement)("span",{className:"dashicons dashicons-"+(e.minimized?"editor-expand":"minus")})),(0,a.createElement)("button",{className:"oz-job__icon","aria-label":(0,i.__)("Close","optimazz"),onClick:l},(0,a.createElement)("span",{className:"dashicons dashicons-no-alt"}))),e.minimized?(0,a.createElement)("div",{className:"oz-job__bar "+(e.indeterminate&&e.running?"is-indeterminate":"")},(0,a.createElement)("div",{style:{width:`${e.pct}%`}})):(0,a.createElement)("div",{className:"oz-job__body"},(0,a.createElement)("div",{className:"oz-job__row"},(0,a.createElement)("span",null,e.label),(0,a.createElement)("strong",null,e.pct,"%")),(0,a.createElement)("div",{className:"oz-job__bar "+(e.indeterminate&&e.running?"is-indeterminate":"")},(0,a.createElement)("div",{style:{width:`${e.pct}%`}})),e.step&&(0,a.createElement)("div",{className:"oz-job__step"},(0,i.__)("Step","optimazz")," ",e.step),e.running&&(0,a.createElement)("button",{className:"oz-job__stop",onClick:n},(0,a.createElement)("span",{className:"dashicons dashicons-controls-pause"}),(0,i.__)("Stop","optimazz")),(0,a.createElement)("div",{className:"oz-job__log",ref:r},e.logs.map((e,t)=>{const o=e.level||"INFO";return(0,a.createElement)("div",{key:t,className:`oz-logline oz-logline--${o.toLowerCase()}`},(0,a.createElement)("span",{className:"oz-logline__tag"},o),(0,a.createElement)("span",{className:"oz-logline__text"},e.text))}))))}let B=0;function P({label:e,value:o,options:n,onChange:l,compact:i=!1,disabled:s=!1}){const[r,c]=(0,t.useState)(!1),m=Math.max(0,n.findIndex(e=>e.value===o)),[d,p]=(0,t.useState)(m),_=(0,t.useRef)(null),z=(0,t.useRef)("oz-sel-"+ ++B).current;(0,t.useEffect)(()=>{const e=e=>{_.current&&!_.current.contains(e.target)&&c(!1)};return document.addEventListener("mousedown",e),()=>document.removeEventListener("mousedown",e)},[]);const h=n.find(e=>e.value===o)||n[0],u=e=>{l(n[e].value),c(!1)};return(0,a.createElement)("div",{className:"components-base-control oz-select"+(i?" oz-select--compact":""),ref:_},(0,a.createElement)("span",{className:i?"screen-reader-text":"components-base-control__label",id:`${z}-label`},e),(0,a.createElement)("button",{type:"button",className:"oz-select__btn",disabled:s,role:"combobox","aria-haspopup":"listbox","aria-expanded":r,"aria-labelledby":`${z}-label`,"aria-activedescendant":r?`${z}-opt-${d}`:void 0,onClick:()=>c(e=>!e),onKeyDown:e=>{r?"Escape"===e.key||"Tab"===e.key?c(!1):"ArrowDown"===e.key?(e.preventDefault(),p(e=>Math.min(n.length-1,e+1))):"ArrowUp"===e.key?(e.preventDefault(),p(e=>Math.max(0,e-1))):"Home"===e.key?(e.preventDefault(),p(0)):"End"===e.key?(e.preventDefault(),p(n.length-1)):"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),u(d)):["ArrowDown","ArrowUp","Enter"," "].includes(e.key)&&(e.preventDefault(),p(m),c(!0))}},(0,a.createElement)("span",null,h?.label),(0,a.createElement)("span",{className:"dashicons dashicons-arrow-down-alt2"})),r&&(0,a.createElement)("ul",{className:"oz-select__menu",role:"listbox","aria-labelledby":`${z}-label`},n.map((e,t)=>(0,a.createElement)("li",{key:e.value,id:`${z}-opt-${t}`,role:"option","aria-selected":e.value===o,className:`oz-select__item ${e.value===o?"is-selected":""} ${t===d?"is-active":""}`,onMouseEnter:()=>p(t),onClick:()=>u(t)},(0,a.createElement)("span",null,e.label),e.value===o&&(0,a.createElement)("span",{className:"dashicons dashicons-yes"})))))}const O={dashboard:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"M3 9.6 12 3l9 6.6"}),(0,a.createElement)("path",{d:"M5 9.1V20h5v-5h4v5h5V9.1"})),cache:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"M12 3 2.5 8 12 13l9.5-5L12 3Z"}),(0,a.createElement)("path",{d:"M2.5 12 12 17l9.5-5"})),files:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"m9 8-4 4 4 4"}),(0,a.createElement)("path",{d:"m15 8 4 4-4 4"})),media:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("rect",{x:"3",y:"4",width:"18",height:"16",rx:"2"}),(0,a.createElement)("circle",{cx:"8.5",cy:"9.5",r:"1.5"}),(0,a.createElement)("path",{d:"m21 16-5-5-6 6"})),fonts:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"M5 6V4h14v2"}),(0,a.createElement)("path",{d:"M12 4v16"}),(0,a.createElement)("path",{d:"M9 20h6"})),preload:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"M21 12a9 9 0 1 1-2.6-6.4"}),(0,a.createElement)("path",{d:"M21 4v5h-5"})),advanced:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"M4 8h9"}),(0,a.createElement)("circle",{cx:"16",cy:"8",r:"2.1"}),(0,a.createElement)("path",{d:"M19 8h1"}),(0,a.createElement)("path",{d:"M4 16h1"}),(0,a.createElement)("circle",{cx:"8",cy:"16",r:"2.1"}),(0,a.createElement)("path",{d:"M11 16h9"})),database:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("ellipse",{cx:"12",cy:"5",rx:"8",ry:"3"}),(0,a.createElement)("path",{d:"M4 5v14c0 1.6 3.6 3 8 3s8-1.4 8-3V5"}),(0,a.createElement)("path",{d:"M4 12c0 1.6 3.6 3 8 3s8-1.4 8-3"})),heartbeat:(0,a.createElement)("path",{d:"M3 12h4l2 6 4-14 2 8h6"}),tweaks:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"m12 4 1.5 4.5L18 10l-4.5 1.5L12 16l-1.5-4.5L6 10l4.5-1.5Z"}),(0,a.createElement)("path",{d:"M18.5 15v3"}),(0,a.createElement)("path",{d:"M17 16.5h3"})),cdn:(0,a.createElement)("path",{d:"M17 18H7a4 4 0 0 1-.3-8A6 6 0 0 1 18.3 9 3.5 3.5 0 0 1 17 18Z"}),tools:(0,a.createElement)("path",{d:"M14.5 5.5a4 4 0 0 0-5 5L4 16v4h4l5.5-5.5a4 4 0 0 0 5-5l-2.6 2.6-2-.4-.4-2 2.5-2.6Z"})};function R({name:e,paths:t}){return(0,a.createElement)("svg",{className:"oz-ico",viewBox:"0 0 24 24",width:"20",height:"20",fill:"none",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",focusable:"false"},t||O[e]||O.dashboard)}function D({ok:e,warn:t,label:o}){const n=e?"ok":t?"warn":"bad",l=e?"yes-alt":t?"warning":"dismiss";return(0,a.createElement)("li",{className:`oz-health__row is-${n}`},(0,a.createElement)("span",{className:`dashicons dashicons-${l}`}),(0,a.createElement)("span",null,o))}function F({label:e,help:t,value:o,options:n,onChange:l}){return(0,a.createElement)("div",{className:"components-base-control oz-pillgroup"},e&&(0,a.createElement)("span",{className:"components-base-control__label"},e),(0,a.createElement)("div",{className:"oz-pillgroup__row",role:"radiogroup","aria-label":e,onKeyDown:e=>{const a=n.findIndex(e=>e.value===o);"ArrowRight"===e.key||"ArrowDown"===e.key?(e.preventDefault(),l(n[Math.min(n.length-1,a+1)].value)):"ArrowLeft"!==e.key&&"ArrowUp"!==e.key||(e.preventDefault(),l(n[Math.max(0,a-1)].value))}},n.map(e=>{const t=e.value===o;return(0,a.createElement)("button",{key:String(e.value),type:"button",role:"radio","aria-checked":t,tabIndex:t?0:-1,className:"oz-pillbtn "+(t?"is-on":""),onClick:()=>l(e.value)},e.label)})),t&&(0,a.createElement)("span",{className:"components-base-control__help"},t))}function H({title:e,description:t,children:o}){return(0,a.createElement)("section",{className:"oz-panel"},(0,a.createElement)("header",{className:"oz-panel__head"},(0,a.createElement)("h2",null,e),t&&(0,a.createElement)("p",null,t)),(0,a.createElement)("div",{className:"oz-panel__body"},o))}function A({className:e="",items:t,nav:n,onNav:l,foot:s,topIcon:r,title:c,sub:m,dirty:d,saving:p,onSave:_,toast:z,onToastRemove:h,children:u,after:g}){return(0,a.createElement)("div",{className:`oz-shell ${e}`},(0,a.createElement)("aside",{className:"oz-sidebar"},(0,a.createElement)("div",{className:"oz-sidebar__brand"},window.optimazzData?.logoUrl?(0,a.createElement)("img",{className:"oz-logo-img",src:window.optimazzData.logoUrl,alt:"Optimazz"}):(0,a.createElement)("div",{className:"oz-sidebar__brand-fallback"},(0,a.createElement)("span",{className:"oz-logo dashicons dashicons-performance"}),(0,a.createElement)("strong",null,"Optimazz"))),(0,a.createElement)("nav",null,t.map(e=>(0,a.createElement)("button",{key:e.key,className:"oz-navitem "+(n===e.key?"is-active":""),onClick:()=>l(e.key)},(0,a.createElement)(R,{name:e.svg||e.key,paths:e.iconSvg}),(0,a.createElement)("span",{className:"oz-navitem__text"},(0,a.createElement)("strong",null,e.title),(0,a.createElement)("small",null,e.sub)),e.dot&&(0,a.createElement)("i",{className:"oz-navitem__dot"})))),(0,a.createElement)("div",{className:"oz-sidebar__foot"},s)),(0,a.createElement)("div",{className:"oz-content"},(0,a.createElement)("header",{className:"oz-topbar"},(0,a.createElement)("span",{className:`oz-topbar__icon dashicons dashicons-${r}`}),(0,a.createElement)("div",null,(0,a.createElement)("h1",null,c),(0,a.createElement)("p",null,m))),(0,a.createElement)("main",{className:"oz-main"},u)),(0,a.createElement)("div",{className:"oz-savebar "+(d?"is-dirty":"")},(0,a.createElement)("span",{className:"oz-savebar__hint"},d?(0,i.__)("You have unsaved changes","optimazz"):(0,i.__)("All changes saved","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-primary",isBusy:p,disabled:!d||p,onClick:_},(0,i.__)("Save changes","optimazz"))),z&&(0,a.createElement)("div",{className:"oz-toast"},(0,a.createElement)(o.Snackbar,{status:z.status,onRemove:h},z.text)),g)}const L=document.getElementById("optimazz-app");L&&(0,t.createRoot)(L).render((0,a.createElement)(function(){var e,n,s,c,f,E,w,$;const[B,O]=(0,t.useState)(m||{}),[L,W]=(0,t.useState)(m||{}),[j,I]=(0,t.useState)(null),[q,U]=(0,t.useState)(null),[J,G]=(0,t.useState)(!1),[V,K]=(0,t.useState)("dashboard"),[Y,Z]=(0,t.useState)(!1),[Q,X]=(0,t.useState)(null),[ee,ae]=(0,t.useState)(!(!window.optimazzData||!window.optimazzData.hasBackup)),te=(0,t.useRef)(null);(0,t.useEffect)(()=>{const e=e=>{const a=e&&e.detail&&e.detail.key;a&&K(a)};return window.addEventListener("optimazz:navigate",e),()=>window.removeEventListener("optimazz:navigate",e)},[]);const oe=JSON.stringify(B)!==JSON.stringify(L),ne=(0,t.useCallback)((e,a,t)=>O(o=>({...o,[e]:{...o[e],[a]:t}})),[]),le=(e,a)=>ne(e,a,!B[e]?.[a]),ie=(0,t.useCallback)(async()=>{try{I(await l()({url:`${r}/cache/stats`}))}catch(e){}},[]),[se,re]=(0,t.useState)(null),ce=(0,t.useCallback)(async()=>{try{re(await l()({url:`${r}/system?_=${Date.now()}`}))}catch(e){re({error:!0})}},[]);(0,t.useEffect)(()=>{"tools"!==V||se||ce()},[V,se,ce]);const[me,de]=(0,t.useState)(null),pe=(0,t.useCallback)(async()=>{try{de(await l()({url:`${r}/recommendations?_=${Date.now()}`}))}catch(e){de({error:!0})}},[]);(0,t.useEffect)(()=>{"dashboard"!==V||me||pe()},[V,me,pe]);const _e=(0,t.useCallback)(async()=>{G(!0);try{U(await l()({url:`${r}/cache/health`}))}catch(e){U({error:!0})}G(!1)},[]),ze=(0,t.useCallback)(async(e,a)=>{if(window.confirm(`${(0,i.__)("Deactivate","optimazz")} ${a}? ${(0,i.__)("Optimazz will keep handling caching.","optimazz")}`)){G(!0);try{const t=await l()({url:`${r}/cache/deactivate-conflict`,method:"POST",data:{plugin:e}});U(t),ue(`${a} ${(0,i.__)("deactivated — Optimazz is now the only cache.","optimazz")}`,"success")}catch(e){ue((0,i.__)("Could not deactivate the plugin.","optimazz"),"error")}G(!1)}},[]),he=(0,t.useCallback)(async()=>{if(window.confirm((0,i.__)("Make Optimazz the caching plugin? This replaces the current advanced-cache.php (from another caching plugin) and turns on WP_CACHE. Deactivate the other caching plugin afterwards to avoid conflicts.","optimazz"))){G(!0);try{const e=await l()({url:`${r}/cache/takeover`,method:"POST"});U(e),ue("hit"===e.live?(0,i.__)("Optimazz is now serving pages from cache.","optimazz"):e.dropin_ours?(0,i.__)("Drop-in installed. Preload the cache, then check again.","optimazz"):(0,i.__)("Could not fully take over — see the cache health details.","optimazz"),e.dropin_ours?"success":"error")}catch(e){ue((0,i.__)("Take-over failed. Check file permissions.","optimazz"),"error")}G(!1)}},[]);(0,t.useEffect)(()=>{ie()},[ie]);const ue=(e,a="success")=>{X({text:e,status:a}),window.clearTimeout(ue._t),ue._t=window.setTimeout(()=>X(null),3e3)};(0,t.useEffect)(()=>{try{const e=new URLSearchParams(window.location.search),a=e.get("optimazz_notice"),t={purged:(0,i.__)("Cache purged.","optimazz"),preloaded:(0,i.__)("Cache preload started in the background.","optimazz"),cleared:(0,i.__)("Cache cleared; preload started in the background.","optimazz")};if(a&&t[a]){ue(t[a]),e.delete("optimazz_notice");const o=e.toString();window.history.replaceState({},"",window.location.pathname+(o?`?${o}`:""))}window.optimazzData?.wpconfigUnwritable&&ue((0,i.__)("wp-config.php is not writable — add WP_CACHE manually to enable page caching.","optimazz"),"error")}catch(e){}},[]);const ge=async e=>{Z(!0);try{const a=await l()({url:`${r}/settings`,method:"POST",data:e});W(a.settings),O(a.settings),ae(!!a.hasBackup),de(null),ue((0,i.__)("Settings saved.","optimazz")),ie()}catch(e){ue(e.message||(0,i.__)("Save failed.","optimazz"),"error")}Z(!1)},be=async()=>{await l()({url:`${r}/cache/purge`,method:"POST"}),ue((0,i.__)("Cache purged.","optimazz")),ie()},fe=()=>Me("preload"),[Ee,ve]=(0,t.useState)(null),[ye,we]=(0,t.useState)(null),[ke,Ce]=(0,t.useState)(null),Ne=(0,t.useRef)(!1),xe=(0,t.useCallback)(async()=>{try{we(await l()({url:`${r}/media/stats`}))}catch(e){}},[]);(0,t.useEffect)(()=>{"media"!==V||ye||xe()},[V,ye,xe]);const Se={convert:{title:(0,i.__)("Converting images to WebP/AVIF","optimazz"),subtitle:(0,i.__)("Next-gen format generation","optimazz"),endpoint:`${r}/media/convert`,refresh:xe,summarize:e=>`${(0,i.__)("Generated","optimazz")} ${e.generated||0} ${(0,i.__)("files in this batch","optimazz")}${e.skipped?` · ${e.skipped} ${(0,i.__)("skipped","optimazz")}`:""}`},compress:{title:(0,i.__)("Compressing images","optimazz"),subtitle:(0,i.__)("Recompress, resize & strip metadata","optimazz"),endpoint:`${r}/media/optimize`,refresh:xe,summarize:e=>{const a=Math.max(0,(e.before||0)-(e.after||0)),t=e.skipped?` · ${e.skipped} ${(0,i.__)("skipped","optimazz")}`:"";return`${(0,i.__)("Processed","optimazz")} ${e.processed||0} — ${Pe(a)} ${(0,i.__)("saved","optimazz")}${t}`}},preload:{title:(0,i.__)("Preloading cache","optimazz"),subtitle:(0,i.__)("Warming pages","optimazz"),endpoint:`${r}/cache/preload`,refresh:ie,onBatch:ie,summarize:(e,a)=>`${(0,i.__)("Warmed","optimazz")} ${Math.min(e.next_offset||0,e.total||0)} / ${e.total||0} ${(0,i.__)("pages","optimazz")} · ${a.cached} ${(0,i.__)("cached","optimazz")}${a.missed?` · ${a.missed} ${(0,i.__)("not cached","optimazz")}`:""}`}},$e=e=>!e||e<1?"":e>=3600?`~${Math.round(e/3600)}h`:e>=60?`~${Math.round(e/60)}m`:`~${Math.round(e)}s`,Te=()=>l()({url:`${r}/media/job`,method:"DELETE"}).catch(()=>{}),Me=async(e,{startOffset:a=0,reconnect:t=!1}={})=>{const o=Se[e];if(!o||ke?.running)return;Ne.current=!1;const n=t?(0,i.__)("Resuming…","optimazz"):`${o.title}…`;Ce({title:o.title,subtitle:o.subtitle,pct:0,label:n,step:"",logs:[{level:"INFO",text:n}],running:!0,status:"running",minimized:!1});let s=a,r=0,c=0;const m={cached:0,missed:0,diagnosis:""},d=Date.now();try{for(;;){var p;if(Ne.current){await Te(),Ce(e=>e&&{...e,running:!1,status:"stopped",logs:[...e.logs,{level:"WARN",text:(0,i.__)("Stopped by user.","optimazz")}]});break}const a=s;let t;try{t=await l()({url:o.endpoint,method:"POST",data:{offset:s}})}catch(e){if(c++,c>20)throw e;if(s=a+1,Ce(e=>e&&{...e,logs:[...e.logs.slice(-60),{level:"WARN",text:(0,i.__)("A batch failed (likely one slow/large image); skipped it and continued.","optimazz")}]}),r&&s>=r){Ce(e=>e&&{...e,running:!1,status:"done",pct:100,logs:[...e.logs,{level:"DONE",text:(0,i.__)("Completed (some items skipped).","optimazz")}]});break}await new Promise(e=>setTimeout(e,600));continue}if(c=0,s=null!==(p=t.next_offset)&&void 0!==p?p:s+(t.processed||0),r=t.total||r,!t.done&&s<=a){await Te(),Ce(e=>e&&{...e,running:!1,status:"error",logs:[...e.logs,{level:"ERROR",text:(0,i.__)("Job stalled — no progress.","optimazz")}]});break}m.cached+=t.cached||0,m.missed+=t.missed||0,m.diagnosis=t.diagnosis||m.diagnosis;const n=r?Math.min(100,Math.round(s/r*100)):100,_=o.summarize(t,m),z=(Date.now()-d)/1e3,h=s>0?s/Math.max(z,.001):0,u=h>0&&r&&s<r?(r-s)/h:0,g=u?` · ${$e(u)} ${(0,i.__)("left","optimazz")}`:"";if(Ce(e=>e&&{...e,pct:n,label:_,step:r?`${Math.min(s,r)} / ${r}${g}`:"",logs:[...e.logs.slice(-60),...t.breakdown?[{level:"INFO",text:`${(0,i.sprintf)(/* translators: %d: number of pages to warm. */ /* translators: %d: number of pages to warm. */ 3 (0,i.__)("Found %d pages to warm:","optimazz"),t.total||0)} ${t.breakdown}.`}]:[],{level:"INFO",text:_}]}),o.onBatch&&o.onBatch(),t.unsupported){await Te(),Ce(e=>e&&{...e,running:!1,status:"error",logs:[...e.logs,{level:"ERROR",text:t.message||(0,i.__)("Image processing is unavailable on this server (no GD or ImageMagick). See Tools → Server Requirements.","optimazz")}]});break}if(t.blocked){await Te(),Ce(e=>e&&{...e,running:!1,status:"error",logs:[...e.logs,{level:"ERROR",text:(0,i.__)('Warming stopped: the server returned 403/429, so a firewall or security plugin is blocking the preload requests. Cached what we could; allow-list the "Optimazz-CacheWarmer" user-agent in your firewall or security plugin (e.g. All-In-One Security, Wordfence, Cloudflare), or slow warming with the optimazz_preload_delay_ms filter.',"optimazz")}]});break}if("preload"===e&&!t.done&&0===m.cached&&m.missed>=3){await Te(),Ce(e=>e&&{...e,running:!1,status:"error",label:(0,i.__)("Pages are not being cached","optimazz"),logs:[...e.logs,{level:"ERROR",text:`${(0,i.__)("Stopped: pages are loading but none are being saved to the cache.","optimazz")} ${m.diagnosis}`}]});break}if(t.done){const e=[{level:"DONE",text:(0,i.__)("Completed.","optimazz")}];m.missed&&e.unshift({level:"WARN",text:`${(0,i.sprintf)(/* translators: %d: number of pages. */ /* translators: %d: number of pages. */ 4 (0,i._n)("%d page was not cached.","%d pages were not cached.",m.missed,"optimazz"),m.missed)} ${m.diagnosis}`}),Ce(a=>a&&{...a,running:!1,status:"done",pct:100,logs:[...a.logs,...e]});break}}}catch(e){Ce(a=>a&&{...a,running:!1,status:"error",logs:[...a.logs,{level:"ERROR",text:e.message||"Error"}]})}(o.refresh||xe)()};(0,t.useEffect)(()=>{let e=!1;return(async()=>{try{const a=await l()({url:`${r}/media/job`});!e&&a&&a.running&&Se[a.type]&&Me(a.type,{startOffset:a.offset||0,reconnect:!0})}catch(e){}})(),()=>{e=!0}},[]);const Be=(0,t.useCallback)(async()=>{try{ve(await l()({url:`${r}/database/counts`}))}catch(e){}},[]);(0,t.useEffect)(()=>{"database"!==V||Ee||Be()},[V,Ee,Be]);const Pe=v,Oe=h?N:N.filter(e=>"cache"!==e),Re=Oe.filter(e=>B[e]?.enabled).length,De=B.cache||{},Fe=B.js||{},He=B.css||{},Ae=B.media||{},Le=B.fonts||{},We=B.database||{},je=B.heartbeat||{},Ie=B.tweaks||{},qe=B.cdn||{},Ue=_.find(e=>e.key===V),Je=[{on:!!De.enabled,w:30,label:(0,i.__)("Page caching","optimazz"),server:!0},{on:!(!Fe.enabled||!Fe.delay),w:18,label:(0,i.__)("Delay JavaScript","optimazz")},{on:!(!Fe.enabled||!Fe.defer),w:8,label:(0,i.__)("Defer JavaScript","optimazz")},{on:!(!He.enabled||!He.minify),w:10,label:(0,i.__)("Minify CSS","optimazz")},{on:!(!Ae.enabled||!Ae.lazy_load),w:12,label:(0,i.__)("Lazy-load images","optimazz")},{on:!(!Ae.enabled||!Ae.webp&&!Ae.webp_generate),w:8,label:(0,i.__)("Next-gen images","optimazz")},{on:!(!Le.enabled||!Le.display_swap),w:6,label:(0,i.__)("Font optimization","optimazz")},{on:!!De.preload,w:5,label:(0,i.__)("Cache preloading","optimazz"),server:!0},{on:!!je.enabled,w:3,label:(0,i.__)("Heartbeat control","optimazz")}],Ge=b?Je:Je.filter(e=>!0!==e.server),Ve=Ge.reduce((e,a)=>e+a.w,0)||1,Ke=Math.round(Ge.reduce((e,a)=>e+(a.on?a.w:0),0)/Ve*100),Ye=Ge.filter(e=>!e.on).sort((e,a)=>a.w-e.w)[0],Ze=Ke>=90?"A":Ke>=75?"B":Ke>=55?"C":Ke>=35?"D":"E",Qe=2*Math.PI*52,Xe=h&&(Fe.enabled||He.enabled||Ae.enabled)&&!De.enabled,ea=(0,a.createElement)(a.Fragment,null,Xe&&(0,a.createElement)("div",{className:"oz-cachewarn",role:"status"},(0,a.createElement)("span",{className:"dashicons dashicons-info-outline"}),(0,a.createElement)("div",{className:"oz-cachewarn__text"},(0,a.createElement)("strong",null,(0,i.__)("Turn on Page Cache for the real speed gain","optimazz")),(0,a.createElement)("p",null,(0,i.__)("File and media optimizations are on, but Page Cache is off — so the server rebuilds every page on every request. Without caching this can make a site feel slower, not faster, especially on mobile. Enable Page Cache to serve ready-made HTML.","optimazz"))),(0,a.createElement)(o.Button,{className:"oz-btn-primary",onClick:()=>K("cache")},(0,i.__)("Enable Page Cache","optimazz"))),(0,a.createElement)("div",{className:"oz-score"},(0,a.createElement)("div",{className:"oz-score__gauge"},(0,a.createElement)("svg",{viewBox:"0 0 120 120",width:"132",height:"132"},(0,a.createElement)("circle",{cx:"60",cy:"60",r:"52",fill:"none",stroke:"rgba(255,255,255,.25)",strokeWidth:"10"}),(0,a.createElement)("circle",{cx:"60",cy:"60",r:"52",fill:"none",stroke:"#fff",strokeWidth:"10",strokeLinecap:"round",strokeDasharray:Qe,strokeDashoffset:Qe-Qe*Ke/100,transform:"rotate(-90 60 60)",style:{transition:"stroke-dashoffset .6s ease"}}),(0,a.createElement)("text",{x:"60",y:"56",textAnchor:"middle",fontSize:"30",fontWeight:"800",fill:"#fff"},Ke),(0,a.createElement)("text",{x:"60",y:"78",textAnchor:"middle",fontSize:"12",fill:"rgba(255,255,255,.85)"},(0,i.__)("SCORE","optimazz"))),(0,a.createElement)("span",{className:"oz-score__grade"},Ze)),(0,a.createElement)("div",{className:"oz-score__body"},(0,a.createElement)("h2",null,(0,i.__)("Performance score","optimazz")),(0,a.createElement)("p",null,Ye?`${(0,i.__)("Biggest remaining win:","optimazz")} ${Ye.label} (+${Ye.w})`:(0,i.__)("Every optimization is enabled. Nicely done!","optimazz")),(0,a.createElement)("div",{className:"oz-score__chips"},Ge.map(e=>(0,a.createElement)("span",{key:e.label,className:"oz-chip "+(e.on?"is-on":"")},(0,a.createElement)("span",{className:"dashicons dashicons-"+(e.on?"yes-alt":"marker")}),e.label))))),(0,a.createElement)("div",{className:"oz-kpis"+(b?"":" oz-kpis--single")},b&&(0,a.createElement)("div",{className:"oz-kpi"},(0,a.createElement)("span",{className:"dashicons dashicons-database"}),(0,a.createElement)("strong",null,j?null!==(e=j.pages)&&void 0!==e?e:j.files:(0,a.createElement)(o.Spinner,null)),(0,a.createElement)("span",null,(0,i.__)("Cached pages","optimazz"))),b&&(0,a.createElement)("div",{className:"oz-kpi"},(0,a.createElement)("span",{className:"dashicons dashicons-chart-area"}),(0,a.createElement)("strong",null,j?Pe(j.bytes):(0,a.createElement)(o.Spinner,null)),(0,a.createElement)("span",null,(0,i.__)("Cache size","optimazz"))),(0,a.createElement)("div",{className:"oz-kpi"},(0,a.createElement)("span",{className:"dashicons dashicons-yes-alt"}),(0,a.createElement)("strong",null,Re,"/",Oe.length),(0,a.createElement)("span",null,(0,i.__)("Modules active","optimazz")))),me&&!me.error&&(me.items||[]).length>0&&(0,a.createElement)(H,{title:(0,i.__)("Recommendations","optimazz"),description:(0,i.__)("What to fix next to speed up your site — one click each.","optimazz")},(0,a.createElement)("ul",{className:"oz-recs"},(me.items||[]).map(e=>(0,a.createElement)("li",{key:e.id,className:`oz-rec oz-rec--${e.severity}`},(0,a.createElement)("span",{className:"oz-rec__dot dashicons "+("high"===e.severity?"dashicons-warning":"medium"===e.severity?"dashicons-info":"dashicons-marker")}),(0,a.createElement)("span",{className:"oz-rec__text"},(0,a.createElement)("strong",null,e.title),(0,a.createElement)("small",null,e.desc)),e.fix?(0,a.createElement)(o.Button,{className:"oz-btn-primary oz-rec__btn",isBusy:Y,onClick:()=>(async e=>{if(!e||!e.module||!e.key)return;const a={...B,[e.module]:{...B[e.module],[e.key]:e.value}};await ge(a),de(null)})(e.fix)},(0,i.__)("Fix it","optimazz")):e.link?(0,a.createElement)(o.Button,{className:"oz-btn-ghost oz-rec__btn",onClick:()=>K(e.link)},(0,i.__)("Open","optimazz")):null)))),me&&!me.error&&0===(me.items||[]).length&&(0,a.createElement)(H,{title:(0,i.__)("Recommendations","optimazz")},(0,a.createElement)("p",{className:"oz-recs-clear"},(0,a.createElement)("span",{className:"dashicons dashicons-yes-alt"}),(0,i.__)("Nice — everything we recommend is already on. Your site is well optimized.","optimazz"))),(0,a.createElement)("div",{className:"oz-quick"},b&&(0,a.createElement)(H,{title:(0,i.__)("Cache Files","optimazz"),description:(0,i.__)("Clear and preload all cache files.","optimazz")},(0,a.createElement)("div",{className:"oz-quick__actions"},(0,a.createElement)(o.Button,{className:"oz-btn-primary",icon:"trash",isBusy:!!ke?.running,onClick:async()=>{await be(),fe()}},(0,i.__)("Clear & Preload","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"update",onClick:fe},(0,i.__)("Preload","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost oz-btn--x",icon:"dismiss",onClick:be},(0,i.__)("Purge cache","optimazz"))),(0,a.createElement)("ul",{className:"oz-cachelist"},(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-media-text"}),(0,i.__)("Cached pages","optimazz"),(0,a.createElement)("strong",null,j?null!==(n=j.pages)&&void 0!==n?n:j.files:"—")),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-database"}),(0,i.__)("Page cache size","optimazz"),(0,a.createElement)("strong",null,j?Pe(j.bytes):"—")),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-admin-appearance"}),(0,i.__)("Optimized CSS","optimazz"),(0,a.createElement)("strong",null,j?`${j.css_files} (${Pe(j.css_bytes)})`:"—")),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-clock"}),(0,i.__)("Cache lifespan","optimazz"),(0,a.createElement)(P,{compact:!0,disabled:Y,label:(0,i.__)("Cache lifespan","optimazz"),value:Number(null!==(s=De.lifespan_hours)&&void 0!==s?s:0),options:(aa=[{value:0,label:(0,i.__)("Never expire","optimazz")},{value:12,label:(0,i.__)("12 hours","optimazz")},{value:24,label:(0,i.__)("1 day","optimazz")},{value:168,label:(0,i.__)("1 week","optimazz")},{value:720,label:(0,i.__)("1 month","optimazz")}],ta=Number(null!==(c=De.lifespan_hours)&&void 0!==c?c:0),oa=e=>(0,i.sprintf)(/* translators: %d: number of hours. */ /* translators: %d: number of hours. */ 5 (0,i._n)("%d hour","%d hours",e,"optimazz"),e),aa.some(e=>e.value===ta)?aa:[...aa,{value:ta,label:oa(ta)}].sort((e,a)=>e.value-a.value)),onChange:e=>ge({...B,cache:{...B.cache,lifespan_hours:e}})})),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-update"}),(0,a.createElement)("span",{className:"oz-row-label"},(0,i.__)("Auto-preload","optimazz"),(0,a.createElement)("small",null,j?j.preload&&j.next_preload?j.next_preload_overdue?(0,i.__)("next run: due now","optimazz"):`${(0,i.__)("next run in","optimazz")} ${j.next_preload}`:(0,i.__)("off","optimazz"):"")),(0,a.createElement)(P,{compact:!0,disabled:Y,label:(0,i.__)("Auto-preload frequency","optimazz"),value:De.preload?De.preload_interval||"twicedaily":"off",options:[{value:"off",label:(0,i.__)("Off","optimazz")},{value:"hourly",label:(0,i.__)("Every hour","optimazz")},{value:"twicedaily",label:(0,i.__)("Twice a day","optimazz")},{value:"daily",label:(0,i.__)("Once a day","optimazz")}],onChange:e=>{ge({...B,cache:{...B.cache,preload:"off"!==e,preload_interval:"off"===e?De.preload_interval||"twicedaily":e}})}})),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-backup"}),(0,i.__)("Last auto-preload","optimazz"),(0,a.createElement)("strong",null,j?j.last_preload?(0,i.sprintf)(/* translators: 1: time ago, e.g. "2 hours", 2: number of pages */ /* translators: 1: time ago, e.g. "2 hours", 2: number of pages */ 6 (0,i.__)("%1$s ago · %2$d pages","optimazz"),j.last_preload.ago,j.last_preload.warmed)+(j.last_preload.blocked?` · ${(0,i.__)("blocked by firewall","optimazz")}`:""):(0,i.__)("not yet","optimazz"):"—")))),(0,a.createElement)(H,{title:(0,i.__)("Module Status","optimazz"),description:(0,i.__)("Enable or disable any module right here.","optimazz")},(0,a.createElement)("ul",{className:"oz-status"},d.filter(e=>e.mod&&(h||"cache"!==e.mod)).map(e=>(0,a.createElement)("li",{key:e.key},(0,a.createElement)(R,{name:e.key}),(0,a.createElement)("button",{className:"oz-status__name",onClick:()=>K(e.key)},e.title),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,checked:!!B[e.mod]?.enabled,onChange:()=>le(e.mod,"enabled")}))),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-editor-code"}),(0,a.createElement)("button",{className:"oz-status__name",onClick:()=>K("files")},(0,i.__)("File Optimization","optimazz")),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,checked:!(!Fe.enabled&&!He.enabled),onChange:()=>{const e=!(Fe.enabled||He.enabled);ne("js","enabled",e),ne("css","enabled",e)}}))))));var aa,ta,oa;const na=h?(0,a.createElement)(T,{icon:"database",title:(0,i.__)("Page Cache","optimazz"),description:(0,i.__)("Serve static HTML instead of running PHP on every request.","optimazz"),enabled:!!De.enabled,onToggle:()=>le("cache","enabled")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Cache mobile devices separately","optimazz"),checked:!!De.cache_mobile,onChange:()=>le("cache","cache_mobile")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Cache logged-in users","optimazz"),help:(0,i.__)("Leave off unless you know what you are doing.","optimazz"),checked:!!De.cache_logged_in,onChange:()=>le("cache","cache_logged_in")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Browser cache headers","optimazz"),help:(0,i.__)("Add Expires / Cache-Control rules so repeat visits load CSS, JS, images and fonts from the browser cache. Apache and LiteSpeed only (written to .htaccess); ignored on Nginx.","optimazz"),checked:!!De.browser_cache,onChange:()=>le("cache","browser_cache")}),(0,a.createElement)(F,{label:(0,i.__)("Cache lifetime","optimazz"),help:(0,i.__)("How long a cached page is kept before it is refreshed. Pages are always purged immediately when their content changes.","optimazz"),value:null!==(f=De.lifespan_hours)&&void 0!==f?f:0,options:[{value:0,label:(0,i.__)("Never","optimazz")},{value:12,label:(0,i.__)("12 hours","optimazz")},{value:24,label:(0,i.__)("1 day","optimazz")},{value:168,label:(0,i.__)("1 week","optimazz")},{value:720,label:(0,i.__)("1 month","optimazz")}],onChange:e=>ne("cache","lifespan_hours",e)}),(0,a.createElement)("div",{className:"oz-health"},(0,a.createElement)("div",{className:"oz-health__head"},(0,a.createElement)("strong",null,(0,i.__)("Cache health","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost",isBusy:J,onClick:_e},(0,i.__)("Check now","optimazz"))),(0,a.createElement)("p",{className:"oz-health__hint"},(0,i.__)("Confirms caching is actually serving pages. If it is not, the plugin rebuilds every page on each request — which is slower, not faster.","optimazz")),q&&!q.error&&(0,a.createElement)("ul",{className:"oz-health__list"},(0,a.createElement)(D,{ok:"hit"===q.live,warn:"miss"===q.live,label:"hit"===q.live?(0,i.__)("Pages ARE being served from cache (HIT)","optimazz"):"miss"===q.live?(0,i.__)("Pages are NOT being served from cache (MISS) — see below","optimazz"):(0,i.__)("Live check unavailable","optimazz")}),(0,a.createElement)(D,{ok:q.dropin_installed&&q.dropin_ours,warn:q.conflict,label:q.conflict?q.conflict_name?`${(0,i.__)("advanced-cache.php belongs to","optimazz")} ${q.conflict_name} — ${(0,i.__)("take it over below","optimazz")}`:(0,i.__)("advanced-cache.php belongs to ANOTHER plugin — take it over below","optimazz"):q.dropin_ours?(0,i.__)("advanced-cache.php drop-in installed","optimazz"):(0,i.__)("advanced-cache.php drop-in is missing","optimazz")}),(0,a.createElement)(D,{ok:q.wp_cache,label:q.wp_cache?(0,i.__)("WP_CACHE is enabled in wp-config.php","optimazz"):(0,i.__)("WP_CACHE is not enabled in wp-config.php","optimazz")}),!q.wpconfig_writable&&(0,a.createElement)(D,{warn:!0,label:(0,i.__)("wp-config.php is not writable — add the WP_CACHE line manually (below)","optimazz")}),(0,a.createElement)(D,{ok:q.warmed>0,label:`${q.warmed} ${(0,i.__)("pages warmed in cache","optimazz")}`})),q&&!q.error&&(q.conflict||!q.wp_cache&&q.wpconfig_writable||!q.dropin_ours)&&q.enabled&&(0,a.createElement)("div",{className:"oz-health__fix"},(0,a.createElement)(o.Button,{className:"oz-btn-primary",isBusy:J,onClick:he},(0,i.__)("Take over caching","optimazz")),(0,a.createElement)("span",null,(0,i.__)("Make Optimazz the caching plugin — installs its drop-in and enables WP_CACHE.","optimazz"))),q&&!q.error&&Array.isArray(q.active_conflicts)&&q.active_conflicts.length>0&&(0,a.createElement)("div",{className:"oz-health__conflict"},q.active_conflicts.map(e=>(0,a.createElement)("div",{className:"oz-health__conflict-row",key:e.file},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,`${e.name} ${(0,i.__)("is still active and may reclaim caching. Deactivate it so Optimazz keeps serving the cache.","optimazz")}`),(0,a.createElement)(o.Button,{className:"oz-btn-danger",isBusy:J,onClick:()=>ze(e.file,e.name)},`${(0,i.__)("Deactivate","optimazz")} ${e.name}`)))),q&&!q.error&&!q.wp_cache&&!q.wpconfig_writable&&(0,a.createElement)("div",{className:"oz-health__manual"},(0,a.createElement)("p",null,(0,i.__)("wp-config.php is not writable. Add this line just after the opening <?php tag:","optimazz")),(0,a.createElement)("code",null,"define( 'WP_CACHE', true );")),q&&q.error&&(0,a.createElement)("p",{className:"oz-health__err"},(0,i.__)("Could not run the check. Try reloading the page.","optimazz")))):g?(0,a.createElement)(C,null):(0,a.createElement)(k,null),la=(0,a.createElement)(a.Fragment,null,(0,a.createElement)(T,{icon:"editor-code",title:(0,i.__)("JavaScript","optimazz"),description:(0,i.__)("Defer and delay scripts to cut Total Blocking Time.","optimazz"),enabled:!!Fe.enabled,onToggle:()=>le("js","enabled")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Minify JavaScript","optimazz"),help:(0,i.__)("Strip comments and whitespace from inline scripts. Line breaks are kept for safety; test your site after enabling.","optimazz"),checked:!!Fe.minify,onChange:()=>le("js","minify")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Defer JavaScript","optimazz"),help:(0,i.__)("Load scripts without blocking page render.","optimazz"),checked:!!Fe.defer,onChange:()=>le("js","defer")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Delay JS until interaction","optimazz"),help:(0,i.__)("Run scripts only after the first user interaction.","optimazz"),checked:!!Fe.delay,onChange:()=>le("js","delay")}),Fe.delay&&(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Delay all scripts","optimazz"),help:(0,i.__)("Delay every script until interaction. jQuery, your theme, and Elementor are always kept safe. Still, on sites where content is rendered by JavaScript this can delay the visible page — test before keeping it on.","optimazz"),checked:!!Fe.delay_all,onChange:()=>le("js","delay_all")}),Fe.delay&&Fe.delay_all&&(0,a.createElement)("div",{className:"oz-dbwarn"},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,(0,i.__)('Delaying all scripts can slow the first paint on JavaScript-rendered sites (Elementor, sliders). If your PageSpeed FCP/LCP get worse, switch to "Delay selected" and delay only tracking scripts.',"optimazz"))),Fe.delay&&!Fe.delay_all&&(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Delay these scripts","optimazz"),help:(0,i.__)("Only scripts matching these keywords are delayed. Match by filename, handle or URL — e.g. gtag, analytics, facebook.","optimazz"),placeholder:(0,i.__)("e.g. gtag, analytics","optimazz"),value:Fe.delay_list||[],onChange:e=>ne("js","delay_list",e)}),(Fe.defer||Fe.delay)&&(0,a.createElement)("div",{className:"oz-fieldgroup"},(0,a.createElement)("h4",{className:"oz-fieldgroup__title"},(0,i.__)("Exclude specific scripts","optimazz")),(0,a.createElement)("p",{className:"oz-fieldgroup__desc"},(0,i.__)("If a script breaks, exclude it from just one optimization instead of both. Match by filename, script handle, or part of the URL.","optimazz")),Fe.defer&&(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never defer","optimazz"),help:(0,i.__)("Keep loading these render-blocking, but still allow delay. Use for scripts that must run during page load.","optimazz"),placeholder:(0,i.__)("e.g. jquery.min.js","optimazz"),value:Fe.no_defer||[],onChange:e=>ne("js","no_defer",e)}),Fe.delay&&(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never delay","optimazz"),help:(0,i.__)("Run these before any interaction, but still allow defer. Use for sliders, above-the-fold, or consent scripts.","optimazz"),placeholder:(0,i.__)("e.g. slider, swiper","optimazz"),value:Fe.no_delay||[],onChange:e=>ne("js","no_delay",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never optimize","optimazz"),help:(0,i.__)("Skip both defer and delay — leave these scripts exactly as they are.","optimazz"),placeholder:(0,i.__)("e.g. recaptcha","optimazz"),value:Fe.exclusions||[],onChange:e=>ne("js","exclusions",e)}))),(0,a.createElement)(T,{icon:"admin-appearance",title:(0,i.__)("CSS","optimazz"),description:(0,i.__)("Minify, combine, and remove render-blocking stylesheets.","optimazz"),enabled:!!He.enabled,onToggle:()=>le("css","enabled")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Minify CSS","optimazz"),checked:!!He.minify,onChange:()=>le("css","minify")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Combine CSS files","optimazz"),help:(0,i.__)("Merge stylesheets to reduce requests.","optimazz"),checked:!!He.combine,onChange:()=>le("css","combine")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Load CSS asynchronously","optimazz"),help:(0,i.__)("Removes render-blocking CSS. Paste your above-the-fold (critical) CSS below, or the page may briefly flash unstyled.","optimazz"),checked:!!He.async_load,onChange:()=>le("css","async_load")}),He.async_load&&(0,a.createElement)(a.Fragment,null,!(He.critical_css||"").trim()&&(0,a.createElement)("div",{className:"oz-dbwarn"},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,(0,i.__)("No critical CSS provided. Async loading is on, so visitors may see a flash of unstyled content until you paste critical CSS below.","optimazz"))),(0,a.createElement)(o.TextareaControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Critical CSS (above-the-fold)","optimazz"),help:(0,i.__)("Paste the above-the-fold CSS to inline. Generate it with a free tool such as a critical-CSS generator. Leave empty to disable inlining.","optimazz"),value:He.critical_css||"",onChange:e=>ne("css","critical_css",e),rows:6})),(0,a.createElement)("div",{className:"oz-subhead"},(0,i.__)("Remove unused CSS","optimazz")),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Remove block-editor CSS on the front end","optimazz"),help:(0,i.__)("Dequeues WordPress block-library and global styles. Turn on only if your front end does NOT render Gutenberg blocks (e.g. a page-builder or classic theme). Big byte saving when it applies.","optimazz"),checked:!!He.remove_block_css,onChange:()=>le("css","remove_block_css")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Load WooCommerce CSS only on shop pages","optimazz"),help:(0,i.__)("Dequeues WooCommerce styles on non-shop pages (kept on cart, checkout, account, product and shop). Turn off if you show Woo products or widgets on other pages.","optimazz"),checked:!!He.remove_woo_css,onChange:()=>le("css","remove_woo_css")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Load Easy Digital Downloads CSS only on EDD pages","optimazz"),help:(0,i.__)("Dequeues EDD styles on non-EDD pages (kept on checkout, purchase-confirmation, purchase history and download archives). Turn off if you show download or purchase links elsewhere.","optimazz"),checked:!!He.remove_edd_css,onChange:()=>le("css","remove_edd_css")}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Excluded CSS files (keywords)","optimazz"),value:He.exclusions||[],onChange:e=>ne("css","exclusions",e)}))),ia=(0,a.createElement)(T,{icon:"format-image",title:(0,i.__)("Image Optimization","optimazz"),description:(0,i.__)("Lazy-load, prevent layout shift, serve next-gen formats, and lighten media.","optimazz"),enabled:!!Ae.enabled,onToggle:()=>le("media","enabled")},ye&&!ye.can_process&&(0,a.createElement)("div",{className:"oz-notice oz-notice--error"},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,(0,i.__)('This server has no GD or ImageMagick extension, so image compression and WebP/AVIF cannot run — every image will be skipped. Ask your host to enable "php-gd" (or "php-imagick"). See Tools → Server Requirements.',"optimazz"))),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Lazy-load images","optimazz"),help:(0,i.__)("Load off-screen images only when scrolled into view.","optimazz"),checked:!!Ae.lazy_load,onChange:()=>le("media","lazy_load")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Lazy-load iframes","optimazz"),checked:!!Ae.lazy_iframes,onChange:()=>le("media","lazy_iframes")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Lazy-load CSS background images","optimazz"),help:(0,i.__)("Defer inline background-image elements (test with page builders).","optimazz"),checked:!!Ae.lazy_bg,onChange:()=>le("media","lazy_bg")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Replace YouTube embeds with a preview","optimazz"),help:(0,i.__)("Load the video iframe only when the visitor clicks play.","optimazz"),checked:!!Ae.youtube_preview,onChange:()=>le("media","youtube_preview")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Add missing image dimensions","optimazz"),help:(0,i.__)("Set width/height to prevent layout shift (CLS).","optimazz"),checked:!!Ae.add_dimensions,onChange:()=>le("media","add_dimensions")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Preload the LCP image","optimazz"),help:(0,i.__)("Preload the first/hero image with high priority.","optimazz"),checked:!!Ae.preload_lcp,onChange:()=>le("media","preload_lcp")}),Ae.lazy_load&&(0,a.createElement)(F,{label:(0,i.__)("Keep first images eager (LCP)","optimazz"),help:(0,i.__)("Do not lazy-load the first N images, to protect LCP.","optimazz"),value:null!==(E=Ae.skip_first)&&void 0!==E?E:1,onChange:e=>ne("media","skip_first",e),options:[0,1,2,3,4,5].map(e=>({label:String(e),value:e}))}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Serve WebP via <picture>","optimazz"),help:(0,i.__)("Deliver WebP to supporting browsers, original as fallback.","optimazz"),checked:!!Ae.webp,onChange:()=>le("media","webp")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Generate WebP/AVIF on upload","optimazz"),help:ye&&!ye.can_webp?(0,i.__)("WebP generation is not available on this server.","optimazz"):(0,i.__)("Auto-create next-gen copies for newly uploaded images.","optimazz"),checked:!!Ae.webp_generate,onChange:()=>le("media","webp_generate")}),ye?.can_avif&&(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Also generate/serve AVIF","optimazz"),help:(0,i.__)("Smaller than WebP; served first when supported.","optimazz"),checked:!!Ae.avif,onChange:()=>le("media","avif")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Compress & resize on upload","optimazz"),help:(0,i.__)("Recompress, cap dimensions, and strip metadata from new uploads.","optimazz"),checked:!!Ae.compress,onChange:()=>le("media","compress")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Convert opaque PNG to JPEG","optimazz"),help:(0,i.__)("Re-save non-transparent PNGs as much smaller JPEGs during bulk optimization.","optimazz"),checked:!!Ae.png_to_jpg,onChange:()=>le("media","png_to_jpg")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Keep a backup of originals","optimazz"),help:(0,i.__)("Copy originals to uploads/optimazz-backup before optimizing.","optimazz"),checked:!!Ae.backup_originals,onChange:()=>le("media","backup_originals")}),(0,a.createElement)(o.RangeControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Image quality","optimazz"),value:null!==(w=Ae.quality)&&void 0!==w?w:82,onChange:e=>ne("media","quality",e),min:40,max:100,help:(0,i.__)("Used for compression and WebP/AVIF generation.","optimazz")}),(0,a.createElement)(o.RangeControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Max image width (px)","optimazz"),value:null!==($=Ae.max_width)&&void 0!==$?$:2560,onChange:e=>ne("media","max_width",e),min:800,max:4e3,step:80}),(0,a.createElement)("div",{className:"oz-convert"},(0,a.createElement)("div",{className:"oz-convert__info"},(0,a.createElement)("strong",null,(0,i.__)("WebP coverage","optimazz")),ye?(0,a.createElement)("span",null,ye.converted," / ",ye.images," ",(0,i.__)("images converted","optimazz")):(0,a.createElement)("span",null,(0,i.__)("Loading…","optimazz"))),(0,a.createElement)(o.Button,{className:"oz-btn-primary",icon:"images-alt2",isBusy:ke?.running,disabled:ye&&!ye.can_webp||ke?.running,onClick:()=>Me("convert")},(0,i.__)("Convert to WebP","optimazz"))),(0,a.createElement)("div",{className:"oz-convert"},(0,a.createElement)("div",{className:"oz-convert__info"},(0,a.createElement)("strong",null,(0,i.__)("Bulk compression","optimazz")),ye?(0,a.createElement)("span",null,ye.optimized," ",(0,i.__)("optimized","optimazz")," · ",Pe(ye.saved)," ",(0,i.__)("saved","optimazz")):(0,a.createElement)("span",null,(0,i.__)("Loading…","optimazz"))),(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"filter",isBusy:ke?.running,disabled:ke?.running,onClick:()=>Me("compress")},(0,i.__)("Compress existing images","optimazz")))),sa=b?(0,a.createElement)(H,{title:(0,i.__)("Preload Cache","optimazz"),description:(0,i.__)("Warm the cache automatically so visitors never hit a cold page.","optimazz")},h?(0,a.createElement)(a.Fragment,null,(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Activate preloading","optimazz"),help:(0,i.__)("Warms the cache on a schedule and right after every full purge (for example after changing a setting). Warming follows the sitemap — home, pages, posts, custom post types, then the busiest category and tag archives — most-important first, for both mobile and desktop.","optimazz"),checked:!!De.preload,onChange:()=>le("cache","preload")}),(0,a.createElement)(o.SelectControl,{__nextHasNoMarginBottom:!0,__next40pxDefaultSize:!0,label:(0,i.__)("Automatic preload","optimazz"),help:(0,i.__)("How often to re-warm the whole site. Choose “Every hour” if your content changes often or you use a short cache lifespan.","optimazz"),value:De.preload_interval||"twicedaily",disabled:!De.preload,options:[{label:(0,i.__)("Every hour","optimazz"),value:"hourly"},{label:(0,i.__)("Twice a day","optimazz"),value:"twicedaily"},{label:(0,i.__)("Once a day","optimazz"),value:"daily"}],onChange:e=>ne("cache","preload_interval",e)})):(0,a.createElement)("p",{className:"oz-muted"},z(u?.preload)?(0,i.sprintf)(/* translators: %s: preload frequency, e.g. "twice a day". */ /* translators: %s: preload frequency, e.g. "twice a day". */ 7 (0,i.__)("Automatic preload runs %s for this site (set for the whole network in Network Admin → Optimazz).","optimazz"),y(u?.preload_interval)):(0,i.__)("Automatic preload is off for the network. You can still preload this site now.","optimazz")),j&&j.wp_cron_disabled&&(0,a.createElement)("div",{className:"oz-notice oz-notice--warn",style:{marginTop:12}},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,(0,i.__)("WP-Cron is disabled on this site (DISABLE_WP_CRON). Scheduled preloads only run if a server cron job calls wp-cron.php — ask your host to set one up (e.g. every 5 minutes).","optimazz"))),(0,a.createElement)("div",{className:"oz-inline-actions"},(0,a.createElement)(o.Button,{className:"oz-btn-primary",icon:"update",isBusy:!!ke?.running,onClick:fe},(0,i.__)("Preload now","optimazz")))):(0,a.createElement)(k,null),ra=b?(0,a.createElement)(H,{title:(0,i.__)("Advanced Rules","optimazz"),description:(0,i.__)("Fine-tune which requests are cached.","optimazz")},(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never cache these URLs (contains)","optimazz"),value:De.excluded_uris||[],onChange:e=>ne("cache","excluded_uris",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never cache when these cookies are set","optimazz"),value:De.excluded_cookies||[],onChange:e=>ne("cache","excluded_cookies",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never cache for these user agents","optimazz"),value:De.excluded_user_agents||[],onChange:e=>ne("cache","excluded_user_agents",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Ignore these query arguments","optimazz"),value:De.ignored_query_args||[],onChange:e=>ne("cache","ignored_query_args",e)})):(0,a.createElement)(k,null),ca=!(!B.tools||!B.tools.safe_mode),ma=(0,a.createElement)(a.Fragment,null,(0,a.createElement)(H,{title:(0,i.__)("Safe Mode","optimazz"),description:(0,i.__)("If a theme or page builder looks broken after optimizing, turn this on. It pauses the riskiest optimizations — combine CSS, async CSS, remove block CSS and JavaScript delay — while keeping caching, minify, defer and lazy-load. Your saved settings are not changed; they simply pause until you turn Safe Mode off.","optimazz")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Pause risky optimizations (Safe Mode)","optimazz"),checked:ca,onChange:e=>{const a={...B,tools:{...B.tools||{},safe_mode:e}};O(a),ge(a)}}),ca&&(0,a.createElement)("div",{className:"oz-notice oz-notice--warn",style:{marginTop:12}},(0,a.createElement)("span",{className:"dashicons dashicons-shield"}),(0,a.createElement)("span",null,(0,i.__)("Safe Mode is ON — combine/async CSS, remove-block-CSS and JavaScript delay are paused site-wide. Turn it off to resume full optimization.","optimazz"))),(0,a.createElement)("div",{className:"oz-inline-actions",style:{marginTop:14}},(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"undo",disabled:!ee,onClick:async()=>{try{const e=await l()({url:`${r}/settings/revert`,method:"POST"});if("nothing"===e.message)return ae(!1),void ue((0,i.__)("Nothing to undo.","optimazz"),"error");W(e.settings),O(e.settings),ae(!!e.hasBackup),de(null),ue((0,i.__)("Reverted to the previous settings.","optimazz")),ie()}catch(e){ue(e.message||(0,i.__)("Undo failed.","optimazz"),"error")}}},(0,i.__)("Undo last change","optimazz")),(0,a.createElement)("span",{className:"oz-muted",style:{fontSize:12}},ee?(0,i.__)("Restores the settings from just before your last save, reset or Safe Mode toggle.","optimazz"):(0,i.__)("Nothing to undo yet — this becomes available after your next change.","optimazz")))),(0,a.createElement)(H,{title:(0,i.__)("Server Requirements","optimazz"),description:(0,i.__)("What your host supports. Warnings here explain why a feature (like WebP/AVIF) may be unavailable or slow.","optimazz")},!se&&(0,a.createElement)("p",{className:"oz-muted"},(0,i.__)("Checking your server…","optimazz")),se&&se.error&&(0,a.createElement)("p",{className:"oz-muted"},(0,i.__)("Could not read the server report.","optimazz")),se&&!se.error&&(0,a.createElement)("ul",{className:"oz-reqs"},(se.checks||[]).map((e,t)=>(0,a.createElement)("li",{key:t,className:`oz-req oz-req--${e.status}`},(0,a.createElement)("span",{className:"oz-req__dot dashicons "+("ok"===e.status?"dashicons-yes-alt":"warn"===e.status?"dashicons-warning":"info"===e.status?"dashicons-minus":"dashicons-dismiss")}),(0,a.createElement)("span",{className:"oz-req__label"},e.label),(0,a.createElement)("span",{className:"oz-req__value"},e.value),e.note&&(0,a.createElement)("span",{className:"oz-req__note"},e.note)))),(0,a.createElement)("div",{className:"oz-inline-actions",style:{marginTop:14}},(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"update",isBusy:!se,onClick:()=>{re(null),ce()}},(0,i.__)("Re-check","optimazz")))),(0,a.createElement)(H,{title:(0,i.__)("Import / Export Settings","optimazz"),description:(0,i.__)("Back up your configuration or copy it to another site.","optimazz")},(0,a.createElement)("div",{className:"oz-inline-actions"},(0,a.createElement)(o.Button,{className:"oz-btn-primary",icon:"download",onClick:()=>{const e=new window.Blob([JSON.stringify(B,null,2)],{type:"application/json"}),a=document.createElement("a");a.href=URL.createObjectURL(e),a.download="optimazz-settings.json",a.click(),URL.revokeObjectURL(a.href)}},(0,i.__)("Export settings","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"upload",onClick:()=>te.current?.click()},(0,i.__)("Import settings","optimazz")),(0,a.createElement)("input",{ref:te,type:"file",accept:"application/json",style:{display:"none"},onChange:e=>{const a=e.target.files?.[0];if(!a)return;const t=new window.FileReader;t.onload=()=>{try{const e=JSON.parse(t.result);ge(e)}catch(e){ue((0,i.__)("Invalid settings file.","optimazz"),"error")}},t.readAsText(a),e.target.value=""}}))),(0,a.createElement)(H,{title:(0,i.__)("Reset","optimazz"),description:(0,i.__)("Restore every module to its default configuration. This cannot be undone.","optimazz")},(0,a.createElement)("div",{className:"oz-inline-actions"},(0,a.createElement)(o.Button,{className:"oz-btn-danger",icon:"image-rotate",onClick:async()=>{if(window.confirm((0,i.__)('Reset all Optimazz settings to defaults? You can undo this with "Undo last change".',"optimazz")))try{const e=await l()({url:`${r}/settings/reset`,method:"POST"});W(e.settings),O(e.settings),ae(!!e.hasBackup),ue((0,i.__)("Settings reset to defaults.","optimazz")),ie()}catch(e){ue(e.message||(0,i.__)("Reset failed.","optimazz"),"error")}}},(0,i.__)("Reset all settings to defaults","optimazz"))))),da=(0,a.createElement)(T,{icon:"editor-textcolor",title:(0,i.__)("Fonts","optimazz"),description:(0,i.__)("Swap, preconnect, and preload fonts to remove render-blocking requests.","optimazz"),enabled:!!Le.enabled,onToggle:()=>le("fonts","enabled")},(0,a.createElement)(o.SelectControl,{__nextHasNoMarginBottom:!0,__next40pxDefaultSize:!0,label:(0,i.__)("Font loading (font-display)","optimazz"),help:(0,i.__)("Optional = no layout shift (best for CLS): the web font is skipped on the first uncached load and used once cached. Swap = text stays visible but the web font swaps in and can shift layout (higher CLS). Block = brief invisible text, then the web font.","optimazz"),value:Le.font_display||(Le.display_swap,"swap"),options:[{label:(0,i.__)("Optional — no layout shift (recommended for CLS)","optimazz"),value:"optional"},{label:(0,i.__)("Swap — always show text (may shift layout)","optimazz"),value:"swap"},{label:(0,i.__)("Block — brief hidden text, then web font","optimazz"),value:"block"}],onChange:e=>ne("fonts","font_display",e)}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Combine Google Fonts into one request","optimazz"),help:(0,i.__)("Page builders often load each Google Font family separately. This merges them into a single request.","optimazz"),checked:!!Le.combine_google,onChange:e=>ne("fonts","combine_google",e)}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Load only the font weights your pages use","optimazz"),help:(0,i.__)("Drops unused weights (e.g. 100, 200, 900) and italics your pages never use — often cutting font CSS by more than half. Weights 400 and 700 are always kept. Turn off if some text looks thinner or bolder than intended.","optimazz"),checked:!!Le.trim_weights,disabled:!Le.combine_google,onChange:e=>ne("fonts","trim_weights",e)}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Load Google Fonts without blocking the page","optimazz"),help:(0,i.__)("The page appears right away in a fallback font and switches to your Google Font as soon as it arrives, instead of waiting for Google first. A big win for the first paint on mobile. Applies when font display is Swap or Fallback; with Optional the stylesheet keeps loading first so text never shifts.","optimazz"),checked:!1!==Le.async_google,disabled:!Le.combine_google,onChange:e=>ne("fonts","async_google",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Preload these font files (URLs)","optimazz"),value:Le.preload||[],onChange:e=>ne("fonts","preload",e)})),pa=e=>{var a;if(!Ee||void 0===Ee[e.key])return e.help;const t=null!==(a=Ee[e.key])&&void 0!==a?a:0; 8 /* translators: %d: number of items found in the database. */return`${(0,i.sprintf)((0,i._n)("%d item in your database.","%d items in your database.",t,"optimazz"),t)} ${e.help}`},_a=(0,a.createElement)(H,{title:(0,i.__)("Database Optimization","optimazz"),description:(0,i.__)("Reduce bloat and reclaim space. Once a cleanup runs it cannot be undone, so back up your database first.","optimazz")},x.map(e=>(0,a.createElement)("div",{key:e.title,className:"oz-dbgroup"},(0,a.createElement)("h3",{className:"oz-dbgroup__title"},e.title),(0,a.createElement)("p",{className:"oz-dbgroup__desc"},e.desc),e.items.map(e=>{var t;return(0,a.createElement)(o.ToggleControl,{key:e.key,__nextHasNoMarginBottom:!0,label:Ee?`${e.label} (${null!==(t=Ee[e.key])&&void 0!==t?t:0})`:e.label,help:pa(e),checked:!!We[e.key],onChange:()=>le("database",e.key)})}))),(0,a.createElement)("div",{className:"oz-dbgroup"},(0,a.createElement)("h3",{className:"oz-dbgroup__title"},(0,i.__)("Set it and forget it","optimazz")),(0,a.createElement)("p",{className:"oz-dbgroup__desc"},(0,i.__)("Let Optimazz repeat the cleanups you picked above on a schedule, so your database stays lean without you lifting a finger.","optimazz")),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Schedule automatic cleanup","optimazz"),help:(0,i.__)("Uses WP-Cron to repeat the cleanups you enabled above.","optimazz"),checked:!!We.enabled,onChange:()=>le("database","enabled")}),We.enabled&&(0,a.createElement)(F,{label:(0,i.__)("Cleanup frequency","optimazz"),value:We.schedule||"off",onChange:e=>ne("database","schedule",e),options:[{label:(0,i.__)("Off","optimazz"),value:"off"},{label:(0,i.__)("Daily","optimazz"),value:"daily"},{label:(0,i.__)("Weekly","optimazz"),value:"weekly"},{label:(0,i.__)("Monthly","optimazz"),value:"monthly"}]})),(0,a.createElement)("div",{className:"oz-dbwarn"},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,(0,i.__)("Back up your database before you run a cleanup — a database optimization cannot be undone.","optimazz"))),(0,a.createElement)("div",{className:"oz-inline-actions"},(0,a.createElement)(o.Button,{className:"oz-btn-primary",icon:"trash",isBusy:!!ke?.running,onClick:async()=>{if(ke?.running)return;const e=(0,i.__)("Cleaning and optimizing database…","optimazz");Ce({title:(0,i.__)("Optimizing database","optimazz"),subtitle:(0,i.__)("Cleanup & table optimization","optimazz"),pct:100,indeterminate:!0,label:e,step:"",logs:[{level:"INFO",text:e}],running:!0,status:"running",minimized:!1});try{const e=await l()({url:`${r}/database/optimize`,method:"POST",data:B.database||{}});ve(e.counts);const a=Object.values(e.removed||{}).reduce((e,a)=>e+a,0),t=`${(0,i.__)("Cleaned","optimazz")} ${a} ${(0,i.__)("rows,","optimazz")} ${e.tables} ${(0,i.__)("tables optimized.","optimazz")}`;Ce(e=>e&&{...e,running:!1,status:"done",indeterminate:!1,pct:100,label:t,logs:[...e.logs,{level:"INFO",text:t},{level:"DONE",text:(0,i.__)("Completed.","optimazz")}]}),ue(t)}catch(e){Ce(a=>a&&{...a,running:!1,status:"error",indeterminate:!1,logs:[...a.logs,{level:"ERROR",text:e.message||"Error"}]}),ue(e.message||(0,i.__)("Optimize failed.","optimazz"),"error")}}},(0,i.__)("Optimize now","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"update",onClick:Be},(0,i.__)("Refresh counts","optimazz")))),za={dashboard:ea,cache:na,files:la,media:ia,fonts:da,preload:sa,advanced:ra,database:_a,heartbeat:(0,a.createElement)(H,{title:(0,i.__)("Heartbeat Control","optimazz"),description:(0,i.__)("Reduce or disable the WordPress Heartbeat API to lower server load.","optimazz")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Enable Heartbeat control","optimazz"),checked:!!je.enabled,onChange:()=>le("heartbeat","enabled")}),je.enabled&&(0,a.createElement)(a.Fragment,null,(0,a.createElement)(F,{label:(0,i.__)("Behavior in admin dashboard","optimazz"),value:je.backend||"default",options:S,onChange:e=>ne("heartbeat","backend",e)}),(0,a.createElement)(F,{label:(0,i.__)("Behavior in post editor","optimazz"),value:je.editor||"default",options:S,onChange:e=>ne("heartbeat","editor",e)}),(0,a.createElement)(F,{label:(0,i.__)("Behavior on front-end","optimazz"),value:je.frontend||"default",options:S,onChange:e=>ne("heartbeat","frontend",e)}))),tweaks:(0,a.createElement)(T,{icon:"admin-generic",title:(0,i.__)("Tweaks","optimazz"),description:(0,i.__)("Strip unnecessary WordPress front-end weight.","optimazz"),enabled:!!Ie.enabled,onToggle:()=>le("tweaks","enabled")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Disable emojis","optimazz"),help:(0,i.__)("Remove the emoji detection script and styles.","optimazz"),checked:!!Ie.disable_emojis,onChange:()=>le("tweaks","disable_emojis")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Disable embeds (oEmbed)","optimazz"),checked:!!Ie.disable_embeds,onChange:()=>le("tweaks","disable_embeds")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Disable dashicons on front-end","optimazz"),help:(0,i.__)("Only for anonymous visitors.","optimazz"),checked:!!Ie.disable_dashicons,onChange:()=>le("tweaks","disable_dashicons")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Remove query strings from assets","optimazz"),help:(0,i.__)("Strip ?ver= for better proxy caching.","optimazz"),checked:!!Ie.remove_query_strings,onChange:()=>le("tweaks","remove_query_strings")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Disable XML-RPC","optimazz"),checked:!!Ie.disable_xmlrpc,onChange:()=>le("tweaks","disable_xmlrpc")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Disable self-pingbacks","optimazz"),checked:!!Ie.disable_pingback,onChange:()=>le("tweaks","disable_pingback")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Remove head meta (generator, RSD, shortlink)","optimazz"),checked:!!Ie.remove_meta,onChange:()=>le("tweaks","remove_meta")}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("DNS-prefetch domains","optimazz"),value:Ie.dns_prefetch||[],onChange:e=>ne("tweaks","dns_prefetch",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Preconnect origins","optimazz"),value:Ie.preconnect||[],onChange:e=>ne("tweaks","preconnect",e)})),cdn:(0,a.createElement)(T,{icon:"cloud",title:(0,i.__)("Content Delivery Network","optimazz"),description:(0,i.__)("Rewrite static asset URLs to your CDN hostname.","optimazz"),enabled:!!qe.enabled,onToggle:()=>le("cdn","enabled")},(0,a.createElement)(o.TextControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("CDN URL (CNAME)","optimazz"),placeholder:"https://cdn.example.com",value:qe.url||"",onChange:e=>ne("cdn","url",e),help:(0,i.__)("CSS, JS, image, and font URLs will be served from here.","optimazz")}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Exclude paths containing","optimazz"),value:qe.exclusions||[],onChange:e=>ne("cdn","exclusions",e)})),tools:ma};return p.forEach(e=>{za[e.key]="function"==typeof e.render?e.render():e.render}),(0,a.createElement)(A,{items:_.map(e=>({...e,dot:!(!e.mod||!B[e.mod]?.enabled)})),nav:V,onNav:K,foot:`v${window.optimazzData?.version}`,topIcon:Ue.icon,title:Ue.title,sub:Ue.sub,dirty:oe,saving:Y,onSave:()=>ge(B),toast:Q,onToastRemove:()=>X(null),after:(0,a.createElement)(M,{job:ke,onStop:()=>{Ne.current=!0},onClose:()=>{Ne.current=!0,Ce(null)},onToggleMin:()=>Ce(e=>e&&{...e,minimized:!e.minimized})})},za[V])},null));const W={enabled:!1,cache_mobile:!0,cache_logged_in:!1,lifespan_hours:0,preload:!0,preload_interval:"twicedaily",browser_cache:!1},j=document.getElementById("optimazz-network-app");j&&(0,t.createRoot)(j).render((0,a.createElement)(function(){const[e,n]=(0,t.useState)(null),[s,c]=(0,t.useState)(W),[m,d]=(0,t.useState)(!1),[p,_]=(0,t.useState)(!1),[z,h]=(0,t.useState)(""),[u,g]=(0,t.useState)(null),[b,f]=(0,t.useState)("cache"),[E,y]=(0,t.useState)(null),w=(0,t.useRef)(!1),k=async e=>{if(E?.running)return;w.current=!1;const a=(0,i.__)("Preloading cache…","optimazz");y({title:(0,i.__)("Preloading cache","optimazz"),subtitle:e.name||e.url,pct:0,label:a,step:"",logs:[{level:"INFO",text:a}],running:!0,status:"running",minimized:!1});const t={cached:0,missed:0,diagnosis:""},o=Date.now();let s=0,c=0;const m=(e,a,t,o={})=>y(n=>n&&{...n,running:!1,status:e,...o,logs:[...n.logs,{level:a,text:t}]});try{for(;;){var d;if(w.current){m("stopped","WARN",(0,i.__)("Stopped by user.","optimazz"));break}const a=await l()({url:`${r}/network/preload-batch`,method:"POST",data:{site_id:e.id,offset:s}}),n=s;s=null!==(d=a.next_offset)&&void 0!==d?d:s+(a.processed||0),c=a.total||c,t.cached+=a.cached||0,t.missed+=a.missed||0,t.diagnosis=a.diagnosis||t.diagnosis;const p=c?Math.min(100,Math.round(s/c*100)):100,_=`${(0,i.__)("Warmed","optimazz")} ${Math.min(s,c)} / ${c} ${(0,i.__)("pages","optimazz")} · ${t.cached} ${(0,i.__)("cached","optimazz")}${t.missed?` · ${t.missed} ${(0,i.__)("not cached","optimazz")}`:""}`,z=s/Math.max((Date.now()-o)/1e3,.001),h=z>0&&c&&s<c?(c-s)/z:0,u=h>=60?` · ~${Math.round(h/60)}m ${(0,i.__)("left","optimazz")}`:h>=1?` · ~${Math.round(h)}s ${(0,i.__)("left","optimazz")}`:"";if(y(e=>e&&{...e,pct:p,label:_,step:c?`${Math.min(s,c)} / ${c}${u}`:"",logs:[...e.logs.slice(-60),...a.breakdown?[{level:"INFO",text:`${(0,i.sprintf)(/* translators: %d: number of pages to warm. */ /* translators: %d: number of pages to warm. */ 9 (0,i.__)("Found %d pages to warm:","optimazz"),a.total||0)} ${a.breakdown}.`}]:[],{level:"INFO",text:_}]}),a.blocked){m("error","ERROR",(0,i.__)('Warming stopped: the server returned 403/429, so a firewall or security plugin is blocking the preload requests. Cached what we could; allow-list the "Optimazz-CacheWarmer" user-agent in your firewall or security plugin (e.g. All-In-One Security, Wordfence, Cloudflare), or slow warming with the optimazz_preload_delay_ms filter.',"optimazz"));break}if(!a.done&&0===t.cached&&t.missed>=3){m("error","ERROR",`${(0,i.__)("Stopped: pages are loading but none are being saved to the cache.","optimazz")} ${t.diagnosis}`,{label:(0,i.__)("Pages are not being cached","optimazz")});break}if(a.done||s<=n){t.missed&&y(e=>e&&{...e,logs:[...e.logs,{level:"WARN",text:`${(0,i.sprintf)(/* translators: %d: number of pages. */ /* translators: %d: number of pages. */ 10 (0,i._n)("%d page was not cached.","%d pages were not cached.",t.missed,"optimazz"),t.missed)} ${t.diagnosis}`}]}),m("done","DONE",(0,i.__)("Completed.","optimazz"),{pct:100});break}}}catch(e){m("error","ERROR",e?.message||(0,i.__)("Something went wrong.","optimazz"))}l()({url:`${r}/network`}).then(e=>{n(e)}).catch(()=>{})},C=e=>{n(e),c({...W,...e.settings||{}}),d(!1)},N=e=>g({status:"error",text:e?.message||(0,i.__)("Something went wrong.","optimazz")}),x=async(e,a,t,o)=>{h(t);try{const t=await l()({url:`${r}${e}`,method:"POST",data:a});return n(t),m||c({...W,...t.settings||{}}),o&&g({status:"success",text:o}),h(""),!0}catch(e){N(e)}return h(""),!1};(0,t.useEffect)(()=>{l()({url:`${r}/network`}).then(C).catch(N)},[]);const S=(e,a)=>{c(t=>({...t,[e]:a})),d(!0)};if(!e)return(0,a.createElement)("div",{className:"oz-shell oz-shell--network-app"},(0,a.createElement)("div",{className:"oz-main"},(0,a.createElement)(o.Spinner,null)));const $=e.health||{},B=e.sites||[],P=B.filter(e=>e.active),O=!!e.settings?.enabled,R=[{key:"cache",svg:"cache",icon:"database",title:(0,i.__)("Page Cache","optimazz"),sub:(0,i.__)("Caching for every site","optimazz"),dot:O},{key:"sites",svg:"dashboard",icon:"admin-multisite",title:(0,i.__)("Sites","optimazz"),sub:(0,i.__)("Cache per site","optimazz")}],L=R.find(e=>e.key===b)||R[0],j=(0,a.createElement)(a.Fragment,null,!e.network_active&&(0,a.createElement)("div",{className:"oz-cachewarn",role:"status"},(0,a.createElement)("span",{className:"dashicons dashicons-info-outline"}),(0,a.createElement)("div",{className:"oz-cachewarn__text"},(0,a.createElement)("strong",null,(0,i.__)("Optimazz is not network-activated","optimazz")),(0,a.createElement)("p",null,(0,i.__)("Only the sites where Optimazz is activated are cached. Network-activate it on Network Admin → Plugins to cache every site.","optimazz")))),(0,a.createElement)(T,{icon:"database",title:(0,i.__)("Page Cache","optimazz"),description:(0,i.__)("Serve ready-made HTML for every site instead of running PHP on each request. Each site keeps its own cache and its own exclusions.","optimazz"),enabled:!!s.enabled,onToggle:()=>S("enabled",!s.enabled)},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Cache mobile devices separately","optimazz"),checked:!!s.cache_mobile,onChange:()=>S("cache_mobile",!s.cache_mobile)}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Cache logged-in users","optimazz"),help:(0,i.__)("Leave off unless you know what you are doing.","optimazz"),checked:!!s.cache_logged_in,onChange:()=>S("cache_logged_in",!s.cache_logged_in)}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Browser cache headers","optimazz"),help:(0,i.__)("Add Expires / Cache-Control rules to the shared .htaccess so repeat visits load CSS, JS, images and fonts from the browser cache. Apache and LiteSpeed only.","optimazz"),checked:!!s.browser_cache,onChange:()=>S("browser_cache",!s.browser_cache)}),(0,a.createElement)(F,{label:(0,i.__)("Cache lifetime","optimazz"),help:(0,i.__)("How long a cached page is kept. Pages are always purged as soon as their content changes.","optimazz"),value:Number(s.lifespan_hours)||0,options:[{value:0,label:(0,i.__)("Never","optimazz")},{value:12,label:(0,i.__)("12 hours","optimazz")},{value:24,label:(0,i.__)("1 day","optimazz")},{value:168,label:(0,i.__)("1 week","optimazz")},{value:720,label:(0,i.__)("1 month","optimazz")}],onChange:e=>S("lifespan_hours",e)}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Preload every site automatically","optimazz"),help:(0,i.__)("Each site warms its own pages on a schedule and right after its cache is cleared.","optimazz"),checked:!!s.preload,onChange:()=>S("preload",!s.preload)}),(0,a.createElement)(o.SelectControl,{__nextHasNoMarginBottom:!0,__next40pxDefaultSize:!0,label:(0,i.__)("Automatic preload","optimazz"),value:s.preload_interval||"twicedaily",disabled:!s.preload,options:[{label:(0,i.__)("Every hour","optimazz"),value:"hourly"},{label:(0,i.__)("Twice a day","optimazz"),value:"twicedaily"},{label:(0,i.__)("Once a day","optimazz"),value:"daily"}],onChange:e=>S("preload_interval",e)})),(O||$.conflict)&&(0,a.createElement)(H,{title:(0,i.__)("Cache health","optimazz")},$.conflict?(0,a.createElement)("div",{className:"oz-cachewarn",role:"status"},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("div",{className:"oz-cachewarn__text"},(0,a.createElement)("strong",null,(0,i.sprintf)(/* translators: %s: name of another caching plugin. */ /* translators: %s: name of another caching plugin. */ 11 (0,i.__)("%s controls page caching (advanced-cache.php)","optimazz"),$.conflict_name||(0,i.__)("Another plugin","optimazz"))),(0,a.createElement)("p",null,(0,i.__)("Optimazz cannot cache while another plugin owns the cache file. Deactivate that plugin, or let Optimazz take over.","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-primary",isBusy:"takeover"===z,onClick:()=>x("/network/takeover",{},"takeover",(0,i.__)("Optimazz now handles page caching.","optimazz"))},(0,i.__)("Use Optimazz for caching","optimazz")))):(0,a.createElement)(a.Fragment,null,(0,a.createElement)(D,{ok:$.dropin_ours,label:(0,i.__)("Cache engine installed (advanced-cache.php)","optimazz")}),(0,a.createElement)(D,{ok:$.wp_cache,label:(0,i.__)("WP_CACHE is on in wp-config.php","optimazz")}),!$.wpconfig_writable&&!$.wp_cache&&(0,a.createElement)(D,{ok:!1,label:(0,i.__)("wp-config.php is not writable — add define( 'WP_CACHE', true ); yourself","optimazz")}),!$.content_writable&&(0,a.createElement)(D,{ok:!1,label:(0,i.__)("The wp-content folder is not writable","optimazz")})))),I=(0,a.createElement)(a.Fragment,null,(0,a.createElement)(H,{title:(0,i.__)("All sites","optimazz"),description:(0,i.sprintf)(/* translators: 1: sites with Optimazz, 2: all sites. */ /* translators: 1: sites with Optimazz, 2: all sites. */ 12 (0,i.__)("Optimazz runs on %1$d of %2$d sites. Each site has its own cache.","optimazz"),P.length,B.length)},!O&&(0,a.createElement)("p",{className:"oz-muted"},(0,i.__)("Page caching is off for the network — turn it on under Page Cache to purge or preload sites.","optimazz")),(0,a.createElement)("div",{className:"oz-quick__actions"},(0,a.createElement)(o.Button,{className:"oz-btn-ghost oz-btn--x",icon:"dismiss",disabled:!O,isBusy:"purge-all"===z,onClick:()=>x("/network/purge",{},"purge-all",(0,i.__)("Every site’s cache was purged.","optimazz"))},(0,i.__)("Purge all sites","optimazz")))),(0,a.createElement)("div",{className:"oz-quick oz-network__sites"},B.map(e=>(0,a.createElement)(H,{key:e.id,title:e.name||e.url,description:e.url},e.active?(0,a.createElement)(a.Fragment,null,(0,a.createElement)("div",{className:"oz-quick__actions"},(0,a.createElement)(o.Button,{className:"oz-btn-primary",icon:"trash",disabled:!O||!!E?.running,isBusy:`clear-${e.id}`===z,onClick:()=>(async e=>{await x("/network/purge",{site_id:e.id},`clear-${e.id}`)&&k(e)})(e)},(0,i.__)("Clear & Preload","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"update",disabled:!O||!!E?.running,onClick:()=>k(e)},(0,i.__)("Preload","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost oz-btn--x",icon:"dismiss",disabled:!O,isBusy:`purge-${e.id}`===z,onClick:()=>x("/network/purge",{site_id:e.id},`purge-${e.id}`,(0,i.__)("Site cache purged.","optimazz"))},(0,i.__)("Purge cache","optimazz"))),(0,a.createElement)("ul",{className:"oz-cachelist"},(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-media-text"}),(0,i.__)("Cached pages","optimazz"),(0,a.createElement)("strong",null,e.pages)),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-database"}),(0,i.__)("Page cache size","optimazz"),(0,a.createElement)("strong",null,v(e.bytes))),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-admin-generic"}),(0,i.__)("Optimazz settings","optimazz"),(0,a.createElement)("a",{className:"oz-cachelist__link",href:e.admin_url},(0,i.__)("Open","optimazz")," →")))):(0,a.createElement)("p",{className:"oz-muted"},(0,i.__)("Optimazz is not active on this site, so its pages are not cached.","optimazz"))))));return(0,a.createElement)(A,{className:"oz-shell--network-app",items:R,nav:b,onNav:f,foot:`${(0,i.__)("Network","optimazz")} · v${window.optimazzData?.version}`,topIcon:L.icon,title:L.title,sub:L.sub,dirty:m,saving:p,onSave:async()=>{_(!0);try{C(await l()({url:`${r}/network/settings`,method:"POST",data:{settings:s}})),g({status:"success",text:(0,i.__)("Network settings saved.","optimazz")})}catch(e){N(e)}_(!1)},toast:u,onToastRemove:()=>g(null),after:(0,a.createElement)(M,{job:E,onStop:()=>{w.current=!0},onClose:()=>{w.current=!0,y(null)},onToggleMin:()=>y(e=>e&&{...e,minimized:!e.minimized})})},"sites"===b?I:j)},null))})(); -
optimazz/tags/1.1.0/changelog.txt
r3723776 r3743505 1 1 Optimazz – Website Optimization and Speedup — full changelog 2 3 = 1.1.0 = 4 * Fixed: Google Fonts never loaded on sites whose theme or plugin requests them in the old weight format through the new Google Fonts address (for example "Open Sans:400,700" or "w400,700"). Google rejects those requests, so the font was missing while the broken stylesheet still held back the first paint on mobile by most of a second. Optimazz now repairs them into a valid request. 5 * New: Google Fonts load without blocking the page (on by default, Fonts settings). With font display Swap or Fallback the page appears right away in a fallback font and switches when the web font arrives, instead of waiting for Google first. With Optional (zero layout shift) the stylesheet still loads first, so text never shifts. 6 * Improved: sites using Jetpack's Site Accelerator load WordPress core stylesheets from c0.wp.com, which Optimazz used to skip as external, so they stayed render-blocking. When the version matches your WordPress, they are now minified, combined and loaded like local files. 7 * Improved: more chat widgets are delayed until the visitor interacts — OpenWidget, Zendesk, Drift, Freshchat / Freshworks, JivoChat, Smartsupp and Small Chat — cutting mobile Total Blocking Time. 8 * New: full multisite support. Network-activate Optimazz and manage page caching for the whole network in Network Admin → Optimazz: switch it on once, choose mobile and logged-in caching, cache lifetime, browser-cache rules and automatic preload, and see every site with its cached pages and its own Purge and Preload buttons. Each site gets its own cache — on subdirectory and subdomain networks — keeps its own exclusions (Advanced Rules) and all its other Optimazz settings, and can purge and preload its own pages. 9 * Improved: on multisite only the network administrator controls the files every site shares (wp-config.php, advanced-cache.php, .htaccess); a site's own settings never change them, so one site can't switch caching on or off for the others. 10 * Fixed: before this version, on multisite one site's settings controlled caching for every site, only one site of a subdomain network was actually cached, any site administrator could rewrite wp-config.php and .htaccess, and deactivating Optimazz on one site removed the cache for the whole network. 11 * Fixed: deleting Optimazz from a multisite network only cleaned up the main site; settings, scheduled tasks, image data and generated CSS of the other sites were left behind. Uninstall now cleans every site. 12 * Fixed: on sites using plain permalinks (addresses like ?page_id=5) the "Cached pages" count showed 1 no matter how many pages were cached. 13 * Improved: the preload summary now names the home page and counts each address once, so its parts add up to the total. 14 * Improved: changing a cache rule (for example a new exclusion) now also clears pages that were cached under the old rules. 15 * Improved: the Changelog section of this readme now lists recent versions only; the complete history is in changelog.txt. 2 16 3 17 = 1.0.24 = -
optimazz/tags/1.1.0/includes/Admin/Admin.php
r3715895 r3743505 18 18 19 19 /** 20 * Network Admin page slug (multisite). 21 */ 22 const NETWORK_SLUG = 'optimazz-network'; 23 24 /** 20 25 * @var Settings 21 26 */ … … 40 45 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) ); 41 46 add_action( 'admin_notices', array( $this, 'render_notices' ) ); 47 add_action( 'network_admin_menu', array( $this, 'add_network_menu' ) ); 42 48 // Hide third-party admin notices on the Optimazz screen to keep it clean. 43 49 add_action( 'in_admin_header', array( $this, 'suppress_foreign_notices' ), 1000 ); … … 120 126 } 121 127 $screen = get_current_screen(); 122 return $screen && 'toplevel_page_' . self::MENU_SLUG === $screen->id;128 return $screen && in_array( $screen->id, array( 'toplevel_page_' . self::MENU_SLUG, 'toplevel_page_' . self::NETWORK_SLUG . '-network' ), true ); 123 129 } 124 130 … … 276 282 277 283 /** 284 * Network Admin → Optimazz (super admins): network page caching and sites. 285 * 286 * @return void 287 */ 288 public function add_network_menu() { 289 add_menu_page( 290 __( 'Optimazz', 'optimazz' ), 291 __( 'Optimazz', 'optimazz' ), 292 'manage_network_options', 293 self::NETWORK_SLUG, 294 array( $this, 'render_network' ), 295 'dashicons-performance', 296 81 297 ); 298 } 299 300 /** 301 * Render the Network Admin mount node. 302 * 303 * @return void 304 */ 305 public function render_network() { 306 echo '<div class="wrap"><div id="optimazz-network-app"></div></div>'; 307 } 308 309 /** 278 310 * Enqueue the built React app on our page only. 279 311 * … … 282 314 */ 283 315 public function enqueue( $hook ) { 284 if ( 'toplevel_page_' . self::MENU_SLUG !== $hook ) { 316 $network = 'toplevel_page_' . self::NETWORK_SLUG === $hook; 317 if ( 'toplevel_page_' . self::MENU_SLUG !== $hook && ! $network ) { 285 318 return; 286 319 } … … 356 389 'wpconfigUnwritable' => (bool) get_transient( 'optimazz_wpconfig_unwritable' ), 357 390 'hasBackup' => $this->settings->has_backup(), 391 'serverFeatures' => \Optimazz\Core\Settings::server_features_allowed(), 392 'multisite' => is_multisite(), 393 // 'network' screen = Network Admin → Optimazz; 'site' = a site's own screen. 394 'context' => $network ? 'network' : 'site', 395 'networkCache' => is_multisite() ? \Optimazz\Core\Network::settings() : null, 396 'networkAdminUrl' => is_multisite() ? network_admin_url( 'admin.php?page=' . self::NETWORK_SLUG ) : '', 397 'isSuperAdmin' => is_super_admin(), 358 398 ) 359 399 ); -
optimazz/tags/1.1.0/includes/Admin/AdminBar.php
r3617648 r3743505 50 50 51 51 $items = array( 52 'optimazz-settings' => array( __( 'Settings', 'optimazz' ), $settings ), 53 'optimazz-clear-preload' => array( __( 'Clear & Preload Cache', 'optimazz' ), $this->action_url( 'optimazz_clear_preload' ) ), 54 'optimazz-preload' => array( __( 'Preload Cache', 'optimazz' ), $this->action_url( 'optimazz_preload' ) ), 55 'optimazz-purge' => array( __( 'Purge Cache', 'optimazz' ), $this->action_url( 'optimazz_purge' ) ), 52 'optimazz-settings' => array( __( 'Settings', 'optimazz' ), $settings ), 56 53 ); 54 // Cache actions only where this site has a page cache (on multisite: when 55 // the network admin has switched network caching on). 56 if ( ! is_multisite() || \Optimazz\Core\Network::cache_enabled() ) { 57 $items['optimazz-clear-preload'] = array( __( 'Clear & Preload Cache', 'optimazz' ), $this->action_url( 'optimazz_clear_preload' ) ); 58 $items['optimazz-preload'] = array( __( 'Preload Cache', 'optimazz' ), $this->action_url( 'optimazz_preload' ) ); 59 $items['optimazz-purge'] = array( __( 'Purge Cache', 'optimazz' ), $this->action_url( 'optimazz_purge' ) ); 60 } 57 61 foreach ( $items as $id => $item ) { 58 62 $bar->add_node( … … 95 99 */ 96 100 private function guard( $action ) { 97 if ( ! current_user_can( 'manage_options' ) || ! check_admin_referer( $action ) ) {101 if ( ( is_multisite() && ! \Optimazz\Core\Network::cache_enabled() ) || ! current_user_can( 'manage_options' ) || ! check_admin_referer( $action ) ) { 98 102 wp_die( esc_html__( 'Not allowed.', 'optimazz' ) ); 99 103 } -
optimazz/tags/1.1.0/includes/Admin/CacheConflictNotice.php
r3723128 r3743505 59 59 */ 60 60 public function render() { 61 if ( ! current_user_can( 'manage_options' ) ) {61 if ( is_multisite() || ! current_user_can( 'manage_options' ) ) { 62 62 return; 63 63 } … … 121 121 */ 122 122 private function guard( $action ) { 123 if ( ! current_user_can( 'manage_options' ) || ! check_admin_referer( $action ) ) { 123 // Multisite: never take over the shared drop-in or (network-)deactivate 124 // another plugin from a site — the same rule as the REST endpoints. 125 if ( ! \Optimazz\Core\Settings::server_features_allowed() || ! current_user_can( 'manage_options' ) || ! check_admin_referer( $action ) ) { 124 126 wp_die( esc_html__( 'Not allowed.', 'optimazz' ) ); 125 127 } -
optimazz/tags/1.1.0/includes/Admin/RestController.php
r3723128 r3743505 278 278 // modules emit into the page, so stale cached HTML must be cleared for the 279 279 // change to take effect immediately (the whole point of Safe Mode). 280 // 'cache' is included so a new exclusion (or lifetime, mobile split …) 281 // also drops pages that were cached under the old rules. 280 282 $purged = false; 281 foreach ( array( 'c ss', 'js', 'media', 'fonts', 'cdn', 'tweaks', 'tools' ) as $mod ) {283 foreach ( array( 'cache', 'css', 'js', 'media', 'fonts', 'cdn', 'tweaks', 'tools' ) as $mod ) { 282 284 $old = isset( $before[ $mod ] ) ? $before[ $mod ] : array(); 283 285 $new = isset( $saved[ $mod ] ) ? $saved[ $mod ] : array(); 286 if ( 'cache' === $mod ) { 287 // Preload schedule and browser-cache headers don't change the 288 // cached HTML — changing them must not throw the cache away. 289 foreach ( array( 'preload', 'preload_interval', 'browser_cache' ) as $key ) { 290 unset( $old[ $key ], $new[ $key ] ); 291 } 292 } 284 293 if ( wp_json_encode( $old ) !== wp_json_encode( $new ) ) { 285 294 $purged = true; … … 373 382 */ 374 383 public function purge_cache() { 384 if ( is_multisite() && ! \Optimazz\Core\Network::cache_enabled() ) { 385 return new WP_REST_Response( array( 'message' => __( 'Page caching is not available on multisite networks.', 'optimazz' ) ), 400 ); 386 } 375 387 Manager::storage()->purge_all(); 376 388 return new WP_REST_Response( array( 'message' => 'purged' ), 200 ); … … 573 585 */ 574 586 public function cache_takeover( WP_REST_Request $request ) { 587 if ( ! \Optimazz\Core\Settings::server_features_allowed() ) { 588 return new WP_REST_Response( array( 'message' => __( 'Page caching is not available on multisite networks.', 'optimazz' ) ), 400 ); 589 } 575 590 // Make sure the page-cache module is on, then force our drop-in in. 576 591 $cache = $this->settings->all(); … … 596 611 */ 597 612 public function cache_deactivate_conflict( WP_REST_Request $request ) { 613 if ( ! \Optimazz\Core\Settings::server_features_allowed() ) { 614 return new WP_REST_Response( array( 'message' => __( 'Page caching is not available on multisite networks.', 'optimazz' ) ), 400 ); 615 } 598 616 $plugin = (string) $request->get_param( 'plugin' ); 599 617 if ( '' === $plugin || '' === Dropin::known_cache_plugin_name( $plugin ) ) { -
optimazz/tags/1.1.0/includes/Core/Lifecycle.php
r3723128 r3743505 19 19 * actually enables page caching (handled by the Cache module). 20 20 * 21 * @param bool $network_wide Whether this is a network-wide activation. 21 22 * @return void 22 23 */ 23 public static function activate() { 24 public static function activate( $network_wide = false ) { 25 // Multisite: add this site (or every site) to the cache site map. The 26 // activated_plugin listener isn't registered yet in the activation 27 // request, so queue the sync here. 28 if ( is_multisite() ) { 29 Network::queue_sync(); 30 } 31 // Network activation: every site starts from the defaults the first time 32 // its settings are read, and the shared cache files follow the network 33 // settings (Network::on_plugin_change() re-syncs the site map). 34 24 35 if ( false === get_option( Settings::OPTION_KEY, false ) ) { 25 36 add_option( Settings::OPTION_KEY, Settings::defaults() ); … … 68 79 update_option( self::VERSION_KEY, OPTIMAZZ_VERSION, false ); 69 80 81 // Multisite: the shared files follow the network settings. Sync them once 82 // per network per version (this also removes a drop-in that a pre-1.1.0 83 // site installed, unless the network turned caching on); then bring this 84 // site's own rules and schedule up to date and rebuild its cache. 85 if ( is_multisite() ) { 86 if ( OPTIMAZZ_VERSION !== get_site_option( Network::VERSION_KEY ) ) { 87 Network::sync(); 88 \Optimazz_Cache_Engine::remove_mixed_case_hosts(); // Orphans of pre-1.1.0 mixed-case Host folders. 89 } 90 if ( class_exists( '\Optimazz\Modules\Cache\Manager' ) ) { 91 \Optimazz\Modules\Cache\Manager::sync( $settings->for_module( 'cache' ) ); 92 \Optimazz\Modules\Cache\Manager::purge_and_rewarm(); 93 } 94 if ( Network::cache_enabled() && class_exists( '\Optimazz\Core\Guard' ) ) { 95 Guard::schedule(); // Same post-update self-check as on a single site. 96 } 97 return; 98 } 99 70 100 // Only touch caching when the user actually has it enabled. 71 101 $cache = $settings->for_module( 'cache' ); … … 91 121 * so a disabled plugin never serves stale cached pages. 92 122 * 123 * @param bool $network_wide Whether this is a network-wide deactivation. 93 124 * @return void 94 125 */ 95 public static function deactivate() { 96 if ( class_exists( '\Optimazz\Modules\Cache\Dropin' ) ) { 97 Dropin::uninstall(); 126 public static function deactivate( $network_wide = false ) { 127 // Multisite, one site: the shared drop-in still serves the other sites. 128 // Clear this site's cache and jobs; Network::on_plugin_change() drops it 129 // from the site map so the engine stops caching it. 130 if ( is_multisite() && ! $network_wide ) { 131 if ( class_exists( '\Optimazz\Modules\Cache\Manager' ) ) { 132 \Optimazz\Modules\Cache\Manager::storage()->purge_all(); 133 } 134 self::clear_events(); 135 return; 98 136 } 99 if ( class_exists( '\Optimazz\Modules\Cache\BrowserCache' ) ) { 100 \Optimazz\Modules\Cache\BrowserCache::apply( false ); 137 Network::with_shared_writes( 138 static function () { 139 if ( class_exists( '\Optimazz\Modules\Cache\Dropin' ) ) { 140 Dropin::uninstall(); 141 } 142 if ( class_exists( '\Optimazz\Modules\Cache\BrowserCache' ) ) { 143 \Optimazz\Modules\Cache\BrowserCache::apply( false ); 144 } 145 } 146 ); 147 // Scheduled events live per site. A network deactivation runs once, so 148 // clear every site. 149 if ( is_multisite() && $network_wide ) { 150 foreach ( get_sites( array( 'fields' => 'ids', 'number' => 0 ) ) as $site_id ) { 151 switch_to_blog( $site_id ); 152 self::clear_events(); 153 restore_current_blog(); 154 } 155 } else { 156 self::clear_events(); 101 157 } 102 wp_clear_scheduled_hook( 'optimazz_preload_cache' ); 103 wp_clear_scheduled_hook( 'optimazz_preload_cache_once' ); 104 wp_clear_scheduled_hook( 'optimazz_purge_expired' ); 105 wp_clear_scheduled_hook( 'optimazz_db_cleanup' ); 106 wp_clear_scheduled_hook( 'optimazz_health_check' ); 158 } 159 160 /** 161 * Remove this site's Optimazz scheduled events. 162 * 163 * @return void 164 */ 165 private static function clear_events() { 166 foreach ( array( 'optimazz_preload_cache', 'optimazz_preload_cache_once', 'optimazz_purge_expired', 'optimazz_db_cleanup', 'optimazz_health_check' ) as $hook ) { 167 wp_clear_scheduled_hook( $hook ); 168 } 107 169 } 108 170 } -
optimazz/tags/1.1.0/includes/Core/Paths.php
r3617648 r3743505 25 25 public static function url_to_path( $url ) { 26 26 $host = wp_parse_url( $url, PHP_URL_HOST ); 27 if ( 'c0.wp.com' === $host ) { 28 return self::jetpack_core_path( $url ); 29 } 27 30 if ( $host && $host !== wp_parse_url( home_url(), PHP_URL_HOST ) ) { 28 31 return ''; … … 48 51 return ABSPATH . ltrim( $path, '/' ); 49 52 } 53 54 /** 55 * Jetpack's Site Accelerator serves WordPress core files from 56 * c0.wp.com/c/<version>/wp-includes/… . When that version is the one 57 * installed here, the file is identical to our own copy, so it can be 58 * minified, combined and loaded without blocking like any local file — 59 * otherwise these core stylesheets stay render-blocking on every page. 60 * 61 * @param string $url c0.wp.com URL. 62 * @return string Local path, or '' when it is not a matching core file. 63 */ 64 private static function jetpack_core_path( $url ) { 65 $path = (string) wp_parse_url( strtok( (string) $url, '?' ), PHP_URL_PATH ); 66 if ( false !== strpos( $path, '..' ) || ! preg_match( '#^/c/([^/]+)/((?:wp-includes|wp-admin)/.+)$#', $path, $m ) ) { 67 return ''; 68 } 69 if ( $m[1] !== get_bloginfo( 'version' ) ) { 70 return ''; // A different WordPress version: the files may differ. 71 } 72 return ABSPATH . $m[2]; 73 } 50 74 } -
optimazz/tags/1.1.0/includes/Core/Plugin.php
r3723128 r3743505 11 11 use Optimazz\Admin\AdminBar; 12 12 use Optimazz\Admin\CacheConflictNotice; 13 use Optimazz\Admin\NetworkRestController; 13 14 use Optimazz\Admin\RestController; 14 15 use Optimazz\Modules\Cache\CacheModule; … … 95 96 // Mode if a post-update front page renders unstyled. 96 97 Guard::register(); 98 Network::register(); // Multisite: keep the site map in step with the network. 97 99 98 100 // One-time reconciliation after a version change (update). Clears cached … … 109 111 110 112 ( new RestController( $this->settings, $this->loader ) )->register(); 113 ( new NetworkRestController() )->register(); // Multisite: Network Admin → Optimazz. 111 114 112 115 // Translations are auto-loaded by WordPress for .org-hosted plugins -
optimazz/tags/1.1.0/includes/Core/Recommendations.php
r3700289 r3743505 56 56 } 57 57 58 // ---- Page cache (biggest single win) ---- 59 if ( ! $on( $cache, 'enabled' ) ) { 58 // ---- Page cache (biggest single win; not available on multisite) ---- 59 if ( ! \Optimazz\Core\Settings::server_features_allowed() ) { 60 // Nothing to recommend — see Settings::server_features_allowed(). 61 } elseif ( ! $on( $cache, 'enabled' ) ) { 60 62 $recs[] = self::rec( 'page_cache', 'high', 61 63 __( 'Enable Page Cache', 'optimazz' ), … … 63 65 array( 'cache', 'enabled', true ) 64 66 ); 65 } elseif ( ! $on( $cache, 'preload' ) ) {67 } elseif ( ! $on( $cache, 'preload' ) && ! is_multisite() ) { 66 68 $recs[] = self::rec( 'preload', 'medium', 67 69 __( 'Turn on cache preloading', 'optimazz' ), -
optimazz/tags/1.1.0/includes/Core/Settings.php
r3723128 r3743505 114 114 'combine_google' => true, // Merge all Google Fonts requests into one. 115 115 'trim_weights' => true, // Request only the font weights the page uses. 116 'async_google' => true, // Load the Google Fonts stylesheet without blocking rendering. 116 117 'preload' => array(), 117 118 ), … … 153 154 if ( null === $this->cache ) { 154 155 $stored = get_option( self::OPTION_KEY, array() ); 155 $this->cache = $this->merge_deep( self::defaults(), is_array( $stored ) ? $stored : array() );156 $this->cache = self::enforce_platform( $this->merge_deep( self::defaults(), is_array( $stored ) ? $stored : array() ) ); 156 157 } 157 158 return $this->cache; 159 } 160 161 /** 162 * Whether the current code may write SHARED server files. 163 * 164 * Page cache (wp-config.php WP_CACHE, wp-content/advanced-cache.php and the 165 * cache folder) and browser-cache rules (.htaccess) are one-per-install. On 166 * a single site its own settings control them. On multisite only a network 167 * action (Network::sync() and friends, run for the super admin) may write 168 * them — never a site's own settings save, so one site can't change caching 169 * for the others and a site admin can't rewrite wp-config.php / .htaccess. 170 * 171 * @return bool 172 */ 173 public static function server_features_allowed() { 174 return ! is_multisite() || Network::is_writing(); 175 } 176 177 /** 178 * On multisite the page-cache switches come from the network settings 179 * (Network Admin → Optimazz); each site keeps its own exclusions. 180 * 181 * @param array $settings Settings. 182 * @return array 183 */ 184 private static function enforce_platform( array $settings ) { 185 if ( is_multisite() && isset( $settings['cache'] ) && is_array( $settings['cache'] ) ) { 186 $settings['cache'] = array_merge( $settings['cache'], Network::settings() ); 187 } 188 return $settings; 158 189 } 159 190 … … 214 245 $defaults = self::defaults(); 215 246 update_option( self::OPTION_KEY, $defaults ); 216 $this->cache = $defaults;217 return $ defaults;247 $this->cache = self::enforce_platform( $defaults ); // On multisite: network cache switches stay. 248 return $this->cache; 218 249 } 219 250 … … 285 316 } 286 317 } 287 return $clean;318 return self::enforce_platform( $clean ); 288 319 } 289 320 -
optimazz/tags/1.1.0/includes/Modules/Cache/BrowserCache.php
r3617648 r3743505 25 25 */ 26 26 public static function apply( $enabled ) { 27 if ( $enabled && ! \Optimazz\Core\Settings::server_features_allowed() ) { 28 $enabled = false; // One .htaccess serves the whole network — never written from a subsite. 29 } 27 30 $htaccess = ABSPATH . '.htaccess'; 28 31 -
optimazz/tags/1.1.0/includes/Modules/Cache/Dropin.php
r3723128 r3743505 53 53 */ 54 54 public static function install() { 55 if ( ! \Optimazz\Core\Settings::server_features_allowed() ) { 56 return false; // advanced-cache.php is shared by every site of a network. 57 } 55 58 $fs = self::filesystem(); 56 59 if ( ! $fs || ! $fs->is_readable( self::template() ) ) { … … 65 68 66 69 $ok = $fs->put_contents( self::DROPIN_PATH, $contents ); 70 self::forget_compiled( self::DROPIN_PATH ); 67 71 if ( $ok ) { 68 72 self::set_wp_cache( true ); … … 322 326 323 327 /** 328 * Make PHP's opcode cache drop its compiled copy of a file we rewrote 329 * (wp-config.php, advanced-cache.php), so the change applies on the very 330 * next request instead of after the next timestamp check. 331 * 332 * @param string $file Absolute path. 333 * @return void 334 */ 335 private static function forget_compiled( $file ) { 336 if ( function_exists( 'opcache_invalidate' ) ) { 337 @opcache_invalidate( $file, true ); // phpcs:ignore WordPress.PHP.NoSilencedErrors -- may be restricted by opcache.restrict_api. 338 } 339 } 340 341 /** 324 342 * Forcefully make Optimazz the caching drop-in — overwrite whatever 325 343 * advanced-cache.php is present and enable WP_CACHE. Used by the admin … … 371 389 */ 372 390 private static function set_wp_cache( $enabled ) { 391 // Multisite: wp-config.php is shared by the whole network, so a subsite 392 // never edits it — not even to turn WP_CACHE off. Removing our drop-in is 393 // enough to stop caching (WordPress only loads advanced-cache.php if the 394 // file exists). 395 if ( ! \Optimazz\Core\Settings::server_features_allowed() ) { 396 return false; 397 } 373 398 $config = ABSPATH . 'wp-config.php'; 374 399 if ( ! is_writable( $config ) ) { // phpcs:ignore WordPress.WP.AlternativeFunctions … … 405 430 // core caching plugins — we toggle that single line here via 406 431 // WP_Filesystem. Nothing else in the file is touched. 407 return $fs->put_contents( $config, $contents ); 432 $ok = $fs->put_contents( $config, $contents ); 433 self::forget_compiled( $config ); // WP_CACHE must take effect on the next request. 434 return $ok; 408 435 } 409 436 } -
optimazz/tags/1.1.0/includes/Modules/Cache/Manager.php
r3723128 r3743505 54 54 } 55 55 56 // Multisite: the drop-in, WP_CACHE, the engine config and .htaccess are 57 // shared by the whole network and only Network::sync() (a network admin 58 // action) writes them. A site writes just its own rules file and keeps 59 // its own preload schedule in step with the network settings. 60 if ( is_multisite() && ! \Optimazz\Core\Network::is_writing() ) { 61 \Optimazz\Core\Network::write_current_site_rules(); 62 $on = ! empty( $cache_settings['enabled'] ); 63 Preloader::schedule( $on && ! empty( $cache_settings['preload'] ), isset( $cache_settings['preload_interval'] ) ? (string) $cache_settings['preload_interval'] : 'twicedaily' ); 64 if ( ! $on ) { 65 wp_clear_scheduled_hook( Preloader::HOOK . '_once' ); 66 wp_clear_scheduled_hook( 'optimazz_purge_expired' ); 67 } 68 Preloader::sync_due(); 69 return; 70 } 71 56 72 // The engine config is a trimmed, disk-readable copy of the settings. 57 73 \Optimazz_Cache_Engine::write_config( … … 107 123 */ 108 124 public static function purge_and_rewarm() { 109 self::storage()->purge_all(); 125 if ( is_multisite() && ! \Optimazz\Core\Network::cache_enabled() ) { 126 return; // Page caching is off for this network — nothing to rebuild. 127 } 128 self::storage()->purge_all(); // On multisite: this site's pages only. 110 129 111 130 $cache = ( new \Optimazz\Core\Settings() )->for_module( 'cache' ); … … 128 147 } 129 148 149 if ( is_multisite() ) { 150 // A new site has no rules file yet; write its own. 151 if ( ! is_readable( \Optimazz_Cache_Engine::site_rules_file( get_current_blog_id() ) ) ) { 152 \Optimazz\Core\Network::write_current_site_rules(); 153 } 154 // Network caching is on but the shared drop-in or engine config is 155 // missing/stale (plugin folder moved, a host tool emptied the cache 156 // folder): heal it for the whole network — at most every 10 minutes, 157 // so a drop-in that can't be installed (another plugin owns it, the 158 // folder isn't writable) never makes every request run a full sync. 159 if ( ! empty( $cache_settings['enabled'] ) 160 && null === Dropin::conflict_owner() // Another plugin's drop-in: nothing to heal (Network screen offers take-over). 161 && ( ! Dropin::is_current() || ! is_readable( \Optimazz_Cache_Engine::config_file() ) ) 162 && ! get_site_transient( 'optimazz_heal' ) ) { 163 set_site_transient( 'optimazz_heal', 1, 10 * MINUTE_IN_SECONDS ); 164 \Optimazz\Core\Network::queue_sync(); 165 } 166 return; 167 } 168 130 169 if ( ! is_readable( \Optimazz_Cache_Engine::config_file() ) ) { 131 170 self::sync( $cache_settings ); -
optimazz/tags/1.1.0/includes/Modules/Cache/Preloader.php
r3723128 r3743505 113 113 } 114 114 $parts = array(); 115 if ( ! empty( $counts['__home'] ) ) { 116 $parts[] = __( 'the home page', 'optimazz' ); 117 } 115 118 foreach ( $counts as $group => $n ) { 116 if ( ! $n || '__terms' === $group ) {119 if ( ! $n || '__terms' === $group || '__home' === $group ) { 117 120 continue; 118 121 } … … 140 143 $max = max( 1, (int) apply_filters( 'optimazz_preload_max', 300 ) ); 141 144 $urls = array( home_url( '/' ) ); 142 $groups = array( ); // Source of each URL, for breakdown().145 $groups = array( '__home' => array( home_url( '/' ) ) ); // Source of each URL, for breakdown(). 143 146 144 147 // A static front page and/or posts page first, when configured. … … 214 217 215 218 // How many of the FINAL list came from each source (for breakdown()). 219 // Each URL counts once, under the first source that listed it (the home 220 // page may also be the static front Page). 221 $owner = array(); 222 foreach ( $groups as $group => $links ) { 223 foreach ( $links as $link ) { 224 if ( ! isset( $owner[ $link ] ) ) { 225 $owner[ $link ] = $group; 226 } 227 } 228 } 216 229 $counts = array(); 217 foreach ( $groups as $group => $links ) { 218 $counts[ $group ] = count( array_intersect( array_unique( $links ), $urls ) ); 230 foreach ( $urls as $url ) { 231 if ( isset( $owner[ $url ] ) ) { 232 $counts[ $owner[ $url ] ] = ( isset( $counts[ $owner[ $url ] ] ) ? $counts[ $owner[ $url ] ] : 0 ) + 1; 233 } 219 234 } 220 235 set_transient( self::URLS_CACHE . '_breakdown', $counts, HOUR_IN_SECONDS ); … … 275 290 require_once OPTIMAZZ_DIR . 'includes/Modules/Cache/engine.php'; 276 291 } 277 $config = \Optimazz_Cache_Engine::load_config();292 $config = self::engine_config(); 278 293 // A new manual run (offset 0) rebuilds the list; later steps reuse it. 279 294 $urls = self::collect_all_urls( 0 === (int) $offset ); … … 341 356 342 357 /** 358 * Engine config for this site: on a network, the network settings merged 359 * with this site's own rules (ignored query args decide the cache key). 360 * 361 * @return array 362 */ 363 private static function engine_config() { 364 $home = (array) wp_parse_url( home_url( '/' ) ); 365 return \Optimazz_Cache_Engine::config_for( \Optimazz_Cache_Engine::url_host( $home ), isset( $home['path'] ) ? $home['path'] : '/' ); 366 } 367 368 /** 343 369 * Device copies to warm: desktop, plus mobile when a separate mobile cache is on. 344 370 * … … 383 409 require_once OPTIMAZZ_DIR . 'includes/Modules/Cache/engine.php'; 384 410 } 385 $config = \Optimazz_Cache_Engine::load_config();411 $config = self::engine_config(); 386 412 if ( empty( $config['enabled'] ) ) { 387 413 delete_option( self::CURSOR ); … … 472 498 $parts = wp_parse_url( $url ); 473 499 $scheme = isset( $parts['scheme'] ) ? $parts['scheme'] : 'https'; 474 $host = isset( $parts['host'] ) ? $parts['host'] : '';500 $host = \Optimazz_Cache_Engine::url_host( (array) $parts ); 475 501 $uri = ( isset( $parts['path'] ) ? $parts['path'] : '/' ) . ( isset( $parts['query'] ) ? '?' . $parts['query'] : '' ); 476 502 $file = \Optimazz_Cache_Engine::path_for( $scheme, $host, $uri, $device, $config ); … … 848 874 require_once OPTIMAZZ_DIR . 'includes/Modules/Cache/engine.php'; 849 875 } 850 \Optimazz_Cache_Engine::write_cron_due( self::next_due(), site_url( 'wp-cron.php' ) );876 \Optimazz_Cache_Engine::write_cron_due( self::next_due(), site_url( 'wp-cron.php' ), is_multisite() ? get_current_blog_id() : 0 ); 851 877 } 852 878 -
optimazz/tags/1.1.0/includes/Modules/Cache/Storage/DiskStorage.php
r3723128 r3743505 29 29 30 30 public function purge_all() { 31 if ( is_multisite() ) { 32 \Optimazz_Cache_Engine::purge_site_dir( self::site_dir() ); // This site only. 33 return; 34 } 31 35 \Optimazz_Cache_Engine::purge_all(); 36 } 37 38 /** 39 * Cache folder of the current network site. 40 * 41 * @return string 42 */ 43 private static function site_dir() { 44 $site = get_site(); 45 return \Optimazz_Cache_Engine::site_dir( $site ? $site->domain : (string) wp_parse_url( home_url(), PHP_URL_HOST ), $site ? $site->path : '/' ); 32 46 } 33 47 … … 39 53 public function stats() { 40 54 $root = \Optimazz_Cache_Engine::cache_root(); 55 $skip = array(); 56 if ( is_multisite() ) { 57 // Count this site's folder only, not other sites nested inside it. 58 $root = self::site_dir(); 59 $skip = array_values( array_diff( \Optimazz_Cache_Engine::site_roots(), array( $root ) ) ); 60 } 41 61 if ( ! is_dir( $root ) ) { 42 62 return array( … … 49 69 $files = 0; 50 70 $bytes = 0; 51 // A page (one URL) is stored as several variants in its own folder —52 // desktop/mobile, http/https, query strings. "Cached pages" counts the53 // pages, so it matches what the preloader reports; 'files' is kept for54 // the rawvariant count.71 // A page (one URL) is stored as several variants — desktop/mobile, 72 // http/https, logged-in buckets. "Cached pages" counts the pages, so it 73 // matches what the preloader reports; 'files' is kept for the raw 74 // variant count. 55 75 $dirs = array(); 56 76 $it = new \RecursiveIteratorIterator( 57 new \RecursiveDirectoryIterator( $root, \FilesystemIterator::SKIP_DOTS ) 77 new \RecursiveCallbackFilterIterator( 78 new \RecursiveDirectoryIterator( $root, \FilesystemIterator::SKIP_DOTS ), 79 static function ( $file ) use ( $skip ) { 80 // Skip other network sites' folders (empty on a single site). 81 return ! ( $file->isDir() && in_array( rtrim( $file->getPathname(), '/' ) . '/', $skip, true ) ); 82 } 83 ) 58 84 ); 59 85 foreach ( $it as $file ) { … … 66 92 ++$files; 67 93 $bytes += (int) $file->getSize(); 68 $dirs[ $file->getPath() ] = true; 94 // File name: {scheme}-{device}-{user}[-{query}].html. One page = its 95 // folder. At the site root the query part counts too, because plain 96 // permalinks (?page_id=5) keep every page there; elsewhere query 97 // variants (e.g. /shop/?orderby=price) are the same page. 98 $parts = explode( '-', $file->getBasename( '.html' ), 4 ); 99 $at_root = false === strpos( trim( substr( $file->getPath(), strlen( $root ) ), '/' ), '/' ); 100 $dirs[ $file->getPath() . '|' . ( $at_root && isset( $parts[3] ) ? $parts[3] : '' ) ] = true; 69 101 } 70 102 } -
optimazz/tags/1.1.0/includes/Modules/Cache/engine.php
r3723128 r3743505 38 38 'site_host' => '', 39 39 'lifespan_hours' => 0, 40 // Multisite: map of "host/path/" => site ID for every site Optimazz 41 // caches, and the site the current request belongs to. 42 'multisite' => false, 43 'sites' => array(), 44 'site_id' => 0, 40 45 ); 41 46 } … … 77 82 78 83 /** 84 * Config for a request to $host + $uri. On a network this resolves the 85 * site (by host and the longest matching path) and merges that site's own 86 * rules — exclusions and ignored query args — over the network settings. 87 * A request for a host/path that no cached site owns gets enabled=false. 88 * 89 * @param string $host Host header. 90 * @param string $uri Request URI. 91 * @return array 92 */ 93 public static function config_for( $host, $uri ) { 94 $config = self::load_config(); 95 if ( empty( $config['multisite'] ) ) { 96 return $config; 97 } 98 $id = self::resolve_site( $config, $host, $uri ); 99 if ( ! $id ) { 100 $config['enabled'] = false; 101 return $config; 102 } 103 $config['site_id'] = $id; 104 $file = self::site_rules_file( $id ); 105 if ( is_readable( $file ) ) { 106 $rules = include $file; 107 if ( is_array( $rules ) ) { 108 foreach ( array( 'ignored_query_args', 'excluded_uris', 'excluded_cookies', 'excluded_user_agents' ) as $key ) { 109 if ( isset( $rules[ $key ] ) && is_array( $rules[ $key ] ) ) { 110 $config[ $key ] = $rules[ $key ]; 111 } 112 } 113 } 114 } 115 return $config; 116 } 117 118 /** 119 * Which network site a host + URI belongs to: the site whose "host/path/" 120 * key is the longest prefix of the request (so /shop/… goes to the /shop/ 121 * site, everything else on that host to the main site). 122 * 123 * @param array $config Config with a 'sites' map. 124 * @param string $host Host header. 125 * @param string $uri Request URI. 126 * @return int Site ID, or 0 when no cached site matches. 127 */ 128 public static function resolve_site( array $config, $host, $uri ) { 129 // Host as sent (a port is kept, matching how the cache folder is named 130 // and how WordPress stores a site's domain). 131 $host = strtolower( (string) $host ); 132 $path = (string) strtok( (string) $uri, '?' ); 133 $path = '/' . ltrim( $path, '/' ); 134 $key = $host . rtrim( $path, '/' ) . '/'; 135 $best = 0; 136 $len = 0; 137 foreach ( (array) $config['sites'] as $prefix => $id ) { 138 $prefix = (string) $prefix; 139 if ( strlen( $prefix ) > $len && 0 === strpos( $key, $prefix ) ) { 140 $best = (int) $id; 141 $len = strlen( $prefix ); 142 } 143 } 144 return $best; 145 } 146 147 /** 148 * Host of a parsed URL as a browser sends it in the Host header — with the 149 * port when there is one. Cache folders and the network site map are keyed 150 * on that, so URL-based lookups (purge, preload) must use it too. 151 * 152 * @param array $parts parse_url() result. 153 * @return string 154 */ 155 public static function url_host( array $parts ) { 156 $host = isset( $parts['host'] ) ? strtolower( (string) $parts['host'] ) : ''; 157 return ( '' !== $host && ! empty( $parts['port'] ) ) ? $host . ':' . (int) $parts['port'] : $host; 158 } 159 160 /** 161 * File holding one network site's own cache rules. 162 * 163 * @param int $id Site ID. 164 * @return string 165 */ 166 public static function site_rules_file( $id ) { 167 return self::cache_root() . 'sites/' . (int) $id . '.php'; 168 } 169 170 /** 171 * Write one network site's own cache rules (only changes when they differ). 172 * 173 * @param int $id Site ID. 174 * @param array $rules Rules: ignored_query_args, excluded_uris, excluded_cookies, excluded_user_agents. 175 * @return bool 176 */ 177 public static function write_site_rules( $id, array $rules ) { 178 $file = self::site_rules_file( $id ); 179 // var_export serialises to a fast include file, not for debugging. 180 $code = "<?php\n// Generated by Optimazz. Do not edit.\nreturn " . var_export( $rules, true ) . ";\n"; // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export 181 if ( is_readable( $file ) && (string) @file_get_contents( $file ) === $code ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors,WordPress.WP.AlternativeFunctions 182 return true; 183 } 184 self::mkdir( dirname( $file ) ); 185 self::protect_dir( self::cache_root() ); 186 return self::atomic_write( $file, $code ); 187 } 188 189 /** 79 190 * Persist config to disk as a returnable PHP array (fast to include). 80 191 * … … 87 198 // var_export is used to serialize the config to a fast include file, not for debugging. 88 199 $code = "<?php\n// Generated by Optimazz. Do not edit.\nreturn " . var_export( $config, true ) . ";\n"; // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export 200 if ( is_readable( self::config_file() ) && (string) @file_get_contents( self::config_file() ) === $code ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors,WordPress.WP.AlternativeFunctions 201 return true; // Unchanged: keep the compiled copy (no opcode-cache churn). 202 } 89 203 return self::atomic_write( self::config_file(), $code ); 90 204 } … … 144 258 */ 145 259 public static function serve() { 146 $config = self::load_config(); 260 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- pre-WP drop-in; only used to pick a site and build a sanitized cache key. 261 $config = self::config_for( isset( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : '', isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '/' ); 147 262 148 263 if ( empty( $config['enabled'] ) ) { … … 167 282 header( 'Content-Type: text/html; charset=UTF-8' ); 168 283 readfile( $file ); // phpcs:ignore WordPress.WP.AlternativeFunctions 169 self::maybe_kick_cron( );284 self::maybe_kick_cron( (int) $config['site_id'] ); 170 285 exit; 171 286 } … … 181 296 * A .php file, so the cache directory's access rules keep it private. 182 297 * 298 * @param int $site_id Network site ID (0 on a single site). 183 299 * @return string 184 300 */ 185 private static function cron_due_file( ) {186 return self::cache_root() . 'cron-due.php';301 private static function cron_due_file( $site_id = 0 ) { 302 return self::cache_root() . ( $site_id ? 'cron-due-' . (int) $site_id . '.php' : 'cron-due.php' ); 187 303 } 188 304 … … 193 309 * @param int $timestamp Unix time of the next run (0 = none pending). 194 310 * @param string $cron_url Absolute URL of wp-cron.php. 195 * @return void 196 */ 197 public static function write_cron_due( $timestamp, $cron_url ) { 311 * @param int $site_id Network site ID (0 on a single site). 312 * @return void 313 */ 314 public static function write_cron_due( $timestamp, $cron_url, $site_id = 0 ) { 198 315 $data = array( 199 316 'due' => (int) $timestamp, 200 317 'url' => (string) $cron_url, 201 318 ); 202 $file = self::cron_due_file( );319 $file = self::cron_due_file( $site_id ); 203 320 if ( is_readable( $file ) ) { 204 321 $current = include $file; … … 224 341 * after the page has been handed to the visitor where the server allows. 225 342 * 226 * @return void 227 */ 228 private static function maybe_kick_cron() { 229 $file = self::cron_due_file(); 343 * @param int $site_id Network site ID of the request (0 on a single site). 344 * @return void 345 */ 346 private static function maybe_kick_cron( $site_id = 0 ) { 347 $file = self::cron_due_file( $site_id ); 230 348 if ( ! is_readable( $file ) ) { 231 349 return; … … 235 353 return; 236 354 } 237 $lock = self::cache_root() . 'cron.lock';355 $lock = self::cache_root() . ( $site_id ? 'cron-' . (int) $site_id . '.lock' : 'cron.lock' ); 238 356 if ( file_exists( $lock ) && ( time() - (int) @filemtime( $lock ) ) < 60 ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors 239 357 return; … … 464 582 public static function path_for_request( array $config ) { 465 583 // phpcs:disable WordPress.Security.ValidatedSanitizedInput -- pre-WP drop-in; values feed a sanitized cache key only. 466 $host = isset( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : 'localhost';584 $host = isset( $_SERVER['HTTP_HOST'] ) ? strtolower( $_SERVER['HTTP_HOST'] ) : 'localhost'; // Hosts are case-insensitive; one folder per host. 467 585 $uri = isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '/'; 468 586 $scheme = ( ! empty( $_SERVER['HTTPS'] ) && 'off' !== $_SERVER['HTTPS'] ) ? 'https' : 'http'; … … 588 706 } 589 707 $parts = function_exists( 'wp_parse_url' ) ? wp_parse_url( $url ) : parse_url( $url ); // phpcs:ignore WordPress.WP.AlternativeFunctions 590 $host = isset( $parts['host'] ) ? $parts['host'] : '';708 $host = self::url_host( $parts ); 591 709 $path = isset( $parts['path'] ) ? $parts['path'] : '/'; 592 710 $path = str_replace( array( '..', "\0" ), '', $path ); … … 595 713 $dir = self::cache_root() . self::sanitize_segment( $host ) . rtrim( $path, '/' ) . '/'; 596 714 597 // The homepage's variants live directly in the host dir alongside every 598 // other page's SUBdirectory. Recursively deleting the host dir would wipe 599 // the whole site cache — so for the homepage, remove only its own .html 600 // variant files and leave the per-page subdirectories intact. 601 if ( $dir === $host_dir ) { 602 foreach ( (array) glob( $host_dir . '*.html' ) as $file ) { 715 // A site's front page has its variants directly in the site's root 716 // folder, alongside every other page's SUBdirectory. Recursively deleting 717 // that folder would wipe the whole site — so for a front page (the host 718 // root, or a network site's own root like /shop/) remove only its own 719 // .html variant files and leave the per-page subdirectories intact. 720 if ( $dir === $host_dir || in_array( $dir, self::site_roots(), true ) ) { 721 foreach ( (array) glob( $dir . '*.html' ) as $file ) { 603 722 wp_delete_file( $file ); // phpcs:ignore WordPress.PHP.NoSilencedErrors 604 723 } … … 628 747 // HTML while keeping the hashed assets keeps both sides consistent. 629 748 // (Stale assets are cleared deliberately when CSS/JS settings change.) 630 $keep = array( '.', '..', 'config.php', 'index.html', '.htaccess', 'css', 'js', 'cron-due.php', 'cron.lock' );749 $keep = array( '.', '..', 'config.php', 'index.html', '.htaccess', 'css', 'js', 'cron-due.php', 'cron.lock', 'sites' ); 631 750 foreach ( (array) scandir( $root ) as $item ) { 632 if ( in_array( $item, $keep, true ) ) {751 if ( in_array( $item, $keep, true ) || preg_match( '/^cron(?:-due)?-\d+\.(?:php|lock)$/', (string) $item ) ) { 633 752 continue; 634 753 } … … 642 761 } 643 762 763 /** 764 * Remove cache folders named after a mixed-case Host header. Since 1.1.0 765 * hosts are lowercased, so such folders are never served or purged again. 766 * 767 * @return void 768 */ 769 public static function remove_mixed_case_hosts() { 770 $root = self::cache_root(); 771 if ( ! is_dir( $root ) ) { 772 return; 773 } 774 foreach ( (array) scandir( $root ) as $item ) { 775 if ( '.' !== $item && '..' !== $item && is_dir( $root . $item ) && strtolower( $item ) !== $item ) { 776 self::rrmdir( $root . $item . '/' ); 777 } 778 } 779 } 780 781 /** 782 * Cache folders of every network site's root ("<root>/<host>/<path>/"). 783 * 784 * @return string[] 785 */ 786 public static function site_roots() { 787 $config = self::load_config(); 788 $roots = array(); 789 foreach ( array_keys( (array) $config['sites'] ) as $key ) { 790 $parts = explode( '/', (string) $key, 2 ); 791 $path = isset( $parts[1] ) ? trim( $parts[1], '/' ) : ''; 792 $roots[] = self::cache_root() . self::sanitize_segment( $parts[0] ) . ( '' !== $path ? '/' . $path : '' ) . '/'; 793 } 794 return $roots; 795 } 796 797 /** 798 * Cache folder of a site (host + path), e.g. "<root>/example.com/shop/". 799 * 800 * @param string $host Site host. 801 * @param string $path Site path ('/' or '/shop/'). 802 * @return string 803 */ 804 public static function site_dir( $host, $path ) { 805 $path = trim( str_replace( array( '..', "\0" ), '', (string) $path ), '/' ); 806 $path = preg_replace( '#[^a-zA-Z0-9/_\-.]#', '-', $path ); 807 return self::cache_root() . self::sanitize_segment( strtolower( (string) $host ) ) . ( '' !== $path ? '/' . $path : '' ) . '/'; 808 } 809 810 /** 811 * Purge one network site's pages without touching sites nested inside its 812 * folder (the main site "/" contains the folder of a subdirectory site). 813 * 814 * @param string $dir Site cache folder (from site_dir()). 815 * @return void 816 */ 817 public static function purge_site_dir( $dir ) { 818 $dir = rtrim( (string) $dir, '/' ) . '/'; 819 if ( 0 !== strpos( $dir, self::cache_root() ) || ! is_dir( $dir ) ) { 820 return; 821 } 822 $others = array_values( array_diff( self::site_roots(), array( $dir ) ) ); 823 self::purge_tree( $dir, $others ); 824 } 825 826 /** 827 * Delete cached pages under $dir, skipping (and descending towards) the 828 * folders of other sites. 829 * 830 * @param string $dir Folder with trailing slash. 831 * @param string[] $others Other sites' root folders. 832 * @return void 833 */ 834 private static function purge_tree( $dir, array $others ) { 835 foreach ( (array) scandir( $dir ) as $item ) { 836 if ( '.' === $item || '..' === $item ) { 837 continue; 838 } 839 $path = $dir . $item; 840 if ( ! is_dir( $path ) ) { 841 if ( '.html' === substr( $item, -5 ) && 'index.html' !== $item ) { 842 wp_delete_file( $path ); // phpcs:ignore WordPress.PHP.NoSilencedErrors 843 } 844 continue; 845 } 846 $sub = $path . '/'; 847 if ( in_array( $sub, $others, true ) ) { 848 continue; // Another site's folder. 849 } 850 $contains_other = false; 851 foreach ( $others as $other ) { 852 if ( 0 === strpos( $other, $sub ) ) { 853 $contains_other = true; 854 break; 855 } 856 } 857 if ( $contains_other ) { 858 self::purge_tree( $sub, $others ); 859 } else { 860 self::rrmdir( $sub ); 861 } 862 } 863 } 864 644 865 /* --------------------------------------------------------------------- * 645 866 * Filesystem helpers … … 684 905 return false; 685 906 } 907 self::forget_compiled( $file ); 686 908 return true; 909 } 910 911 /** 912 * Make PHP's opcode cache drop its compiled copy of a file we just 913 * rewrote. Config and rules files are PHP includes: without this, a server 914 * that only re-checks file times every few seconds (or never, with 915 * opcache.validate_timestamps=0) keeps running the OLD settings — caching 916 * stays off after being switched on, or a new exclusion is ignored. 917 * 918 * @param string $file Absolute path. 919 * @return void 920 */ 921 public static function forget_compiled( $file ) { 922 if ( function_exists( 'opcache_invalidate' ) ) { 923 @opcache_invalidate( $file, true ); // phpcs:ignore WordPress.PHP.NoSilencedErrors -- may be restricted by opcache.restrict_api. 924 } 687 925 } 688 926 -
optimazz/tags/1.1.0/includes/Modules/Fonts/FontsModule.php
r3715895 r3743505 21 21 22 22 class FontsModule extends AbstractModule { 23 24 /** 25 * Whether parse_css2() repaired an invalid (CSS1-style) family request on 26 * the current page, so the rebuilt URL must be output even when nothing 27 * else changes. 28 * 29 * @var bool 30 */ 31 private static $repaired = false; 23 32 24 33 public function id() { … … 157 166 return $html; 158 167 } 168 self::$repaired = false; 159 169 160 170 $links = array(); // Tags we will merge (in document order). … … 173 183 } 174 184 $query = (string) wp_parse_url( html_entity_decode( $m[2], ENT_QUOTES ), PHP_URL_QUERY ); // decode first: "&" contains a "#" that would truncate the query. 185 $was = self::$repaired; 175 186 $parsed = 'css2' === strtolower( $m[3] ) ? self::parse_css2( $query ) : self::parse_css1( $query ); 176 187 if ( null === $parsed ) { 188 self::$repaired = $was; // This link is left as-is, so nothing was repaired. 177 189 continue; // unsupported form — keep as-is. 178 190 } … … 197 209 } 198 210 $trim = (bool) $this->option( 'trim_weights', true ); 199 // One link and nothing to trim — nothing to gain. 200 if ( 1 === count( $links ) && ! $trim ) { 211 $disp = '' !== $display ? $display : $orig_display; 212 // Non-blocking load only with swap / fallback. Those swap the font in 213 // anyway; 'optional' is chosen for zero layout shift, and loading its 214 // stylesheet late would make a cached font swap in after first paint. 215 $async = in_array( $disp, array( 'swap', 'fallback' ), true ) && $this->option( 'async_google', true ); 216 // One link, nothing to trim, nothing repaired and no async loading — 217 // rebuilding the URL would gain nothing (and would drop its other 218 // attributes and parameters). 219 if ( 1 === count( $links ) && ! $trim && ! $async && ! self::$repaired ) { 201 220 return $html; 202 221 } … … 208 227 $params[] = 'family=' . str_replace( '%20', '+', rawurlencode( $name ) ) . self::css2_axis( $spec, $used ); 209 228 } 210 $disp = '' !== $display ? $display : $orig_display;211 229 if ( '' !== $disp ) { 212 230 $params[] = 'display=' . $disp; 213 231 } 214 $url = 'https://fonts.googleapis.com/css2?' . implode( '&', $params ); 215 // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet -- rewriting existing output-buffer markup, not enqueueing. 216 $new = '<link rel="stylesheet" id="optimazz-google-fonts" href="' . esc_url( $url ) . '" media="all">'; 232 $url = esc_url( 'https://fonts.googleapis.com/css2?' . implode( '&', $params ) ); 233 // phpcs:disable WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet -- rewriting existing output-buffer markup, not enqueueing. 234 if ( $async ) { 235 // With swap/fallback the text renders in a fallback font first 236 // anyway, so the font stylesheet need not block the page: on 237 // mobile it otherwise holds back the first paint by the full round 238 // trip to Google. Preload it, apply it when loaded, and keep a 239 // <noscript> copy for visitors without JavaScript. 240 $new = '<link rel="preload" as="style" href="' . $url . '">' 241 . '<link rel="stylesheet" id="optimazz-google-fonts" href="' . $url . '" media="print" onload="this.media=\'all\'">' 242 . '<noscript><link rel="stylesheet" href="' . $url . '"></noscript>'; 243 } else { 244 $new = '<link rel="stylesheet" id="optimazz-google-fonts" href="' . $url . '" media="all">'; 245 } 246 // phpcs:enable WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet 217 247 218 248 // Swap the first link for the merged one; remove the rest (each once). … … 252 282 continue; 253 283 } 254 $spec = array( 'normal' => array(), 'italic' => array(), 'raw' => null ); 255 $vars = isset( $parts[1] ) ? explode( ',', $parts[1] ) : array( '400' ); 256 foreach ( $vars as $v ) { 257 $v = strtolower( trim( $v ) ); 258 if ( '' === $v ) { 259 continue; 260 } 261 $v = strtr( $v, array( 'regular' => '400', 'bold' => '700' ) ); 262 $ital = (bool) preg_match( '/(italic|i)$/', $v ); 263 $w = (int) preg_replace( '/\D/', '', $v ); 264 $w = $w ? $w : 400; // bare "italic". 265 if ( $w < 100 || $w > 900 ) { 266 continue; 267 } 268 $spec[ $ital ? 'italic' : 'normal' ][ $w ] = 1; 269 } 270 if ( empty( $spec['normal'] ) && empty( $spec['italic'] ) ) { 271 $spec['normal'][400] = 1; 272 } 273 $out[ $name ] = $spec; 284 $out[ $name ] = self::parse_variants( isset( $parts[1] ) ? $parts[1] : '400' ); 274 285 } 275 286 } 276 287 return $out ? $out : null; 288 } 289 290 /** 291 * Parse a CSS1-style variant list ("400,700i,bold,italic") into a spec. 292 * 293 * @param string $list Comma-separated variants. 294 * @return array{normal:array,italic:array,raw:null} 295 */ 296 private static function parse_variants( $list ) { 297 $spec = array( 'normal' => array(), 'italic' => array(), 'raw' => null ); 298 foreach ( explode( ',', $list ) as $v ) { 299 $v = strtolower( trim( $v ) ); 300 if ( '' === $v ) { 301 continue; 302 } 303 $v = strtr( $v, array( 'regular' => '400', 'bold' => '700' ) ); 304 $ital = (bool) preg_match( '/(italic|i)$/', $v ); 305 $w = (int) preg_replace( '/\D/', '', $v ); 306 $w = $w ? $w : 400; // bare "italic". 307 if ( $w < 100 || $w > 900 ) { 308 continue; 309 } 310 $spec[ $ital ? 'italic' : 'normal' ][ $w ] = 1; 311 } 312 if ( empty( $spec['normal'] ) && empty( $spec['italic'] ) ) { 313 $spec['normal'][400] = 1; 314 } 315 return $spec; 277 316 } 278 317 … … 314 353 $spec['normal'][ (int) $w ] = 1; 315 354 } 355 } elseif ( preg_match( '/^(?:w?\d{3}i?|regular|bold|italic|bolditalic|\d{3}italic)(?:,(?:w?\d{3}i?|regular|bold|italic|bolditalic|\d{3}italic))*$/i', trim( $parts[1] ) ) ) { 356 // Old CSS1-style weights ("400,700", or the "w400,700" some themes 357 // print) sent to the CSS2 API. Google rejects these with HTTP 400, so 358 // the font never loads while the broken stylesheet still blocks 359 // rendering. Read them the CSS1 way so a valid request is built. 360 $spec = self::parse_variants( str_ireplace( 'w', '', $parts[1] ) ); 361 self::$repaired = true; 316 362 } else { 317 363 $spec['raw'] = $parts[1]; // variable ranges / other axes — keep verbatim. -
optimazz/tags/1.1.0/includes/Modules/Js/JsModule.php
r3692106 r3743505 239 239 'tawk.to', 'crisp.chat', 'widget.intercom.io', 'js.intercomcdn.com', 240 240 'code.tidio.co', 'cdn.livechatinc.com', 'embed.tawk.to', 'messenger/customerchat', 241 'cdn.openwidget.com', 'static.zdassets.com', 'js.driftt.com', 'wchat.freshchat.com', 242 'widget.freshworks.com', 'code.jivosite.com', 'smartsuppchat.com', 'embed.small.chat', 241 243 // Marketing / CRM. 242 244 'js.hs-scripts.com', 'js.hsforms.net', 'cdn.onesignal.com', -
optimazz/tags/1.1.0/languages/optimazz.pot
r3723128 r3743505 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: Optimazz – Website Optimization and Speedup 1. 0.24\n"5 "Project-Id-Version: Optimazz – Website Optimization and Speedup 1.1.0\n" 6 6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/optimazz\n" 7 7 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" … … 10 10 "Content-Type: text/plain; charset=UTF-8\n" 11 11 "Content-Transfer-Encoding: 8bit\n" 12 "POT-Creation-Date: 2026-10- 01T12:01:49+00:00\n"12 "POT-Creation-Date: 2026-10-11T19:05:58+00:00\n" 13 13 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 14 "X-Generator: WP-CLI 2.12.0\n" … … 40 40 msgstr "" 41 41 42 #: includes/Admin/Admin.php:149 43 #: includes/Admin/Admin.php:150 42 #: includes/Admin/Admin.php:155 43 #: includes/Admin/Admin.php:156 44 #: includes/Admin/Admin.php:290 45 #: includes/Admin/Admin.php:291 44 46 msgid "Optimazz" 45 47 msgstr "" 46 48 47 #: includes/Admin/Admin.php:1 6649 #: includes/Admin/Admin.php:172 48 50 #: src/index.js:36 49 51 msgid "Dashboard" 50 52 msgstr "" 51 53 52 #: includes/Admin/Admin.php:1 6754 #: includes/Admin/Admin.php:173 53 55 #: includes/Modules/Cache/CacheModule.php:24 54 56 #: src/index.js:37 55 #: src/index.js:1171 57 #: src/index.js:1317 58 #: src/index.js:2075 59 #: src/index.js:2091 56 60 msgid "Page Cache" 57 61 msgstr "" 58 62 59 #: includes/Admin/Admin.php:1 6863 #: includes/Admin/Admin.php:174 60 64 #: src/index.js:38 61 #: src/index.js:1 15065 #: src/index.js:1296 62 66 msgid "File Optimization" 63 67 msgstr "" 64 68 65 #: includes/Admin/Admin.php:1 6969 #: includes/Admin/Admin.php:175 66 70 #: src/index.js:39 67 71 msgid "Media" 68 72 msgstr "" 69 73 70 #: includes/Admin/Admin.php:17 074 #: includes/Admin/Admin.php:176 71 75 #: includes/Modules/Database/DatabaseModule.php:26 72 76 #: src/index.js:43 … … 74 78 msgstr "" 75 79 76 #: includes/Admin/Admin.php:17 177 #: includes/Admin/Admin.php:2 2978 #: includes/Admin/Admin.php:23 380 #: includes/Admin/Admin.php:177 81 #: includes/Admin/Admin.php:235 82 #: includes/Admin/Admin.php:239 79 83 #: src/index.js:47 80 84 msgid "Tools" 81 85 msgstr "" 82 86 83 #: includes/Admin/Admin.php:19 387 #: includes/Admin/Admin.php:199 84 88 #: includes/Admin/AdminBar.php:52 89 #: src/index.js:2198 85 90 msgid "Settings" 86 91 msgstr "" 87 92 88 #: includes/Admin/Admin.php:2 2493 #: includes/Admin/Admin.php:230 89 94 msgid "Optimazz Safe Mode is on." 90 95 msgstr "" 91 96 92 #: includes/Admin/Admin.php:2 2597 #: includes/Admin/Admin.php:231 93 98 msgid "Some pages were loading without styling — their optimized CSS could not be found, usually because a server or CDN cache was serving old pages. Optimazz paused advanced CSS/JS optimization to keep your site working." 94 99 msgstr "" 95 100 96 #: includes/Admin/Admin.php:2 27101 #: includes/Admin/Admin.php:233 97 102 msgid "It broke again shortly after full optimization was restored, so Safe Mode will stay on until you switch it off in" 98 103 msgstr "" 99 104 100 #: includes/Admin/Admin.php:2 28105 #: includes/Admin/Admin.php:234 101 106 msgid "It re-checks automatically and restores full optimization as soon as pages load correctly — clearing your server/CDN cache speeds that up. You can also manage it in" 102 107 msgstr "" 103 108 104 #: includes/Admin/Admin.php:23 2109 #: includes/Admin/Admin.php:238 105 110 msgid "Your pages are loading correctly again — you can switch Safe Mode off in" 106 111 msgstr "" 107 112 108 #: includes/Admin/Admin.php:2 36113 #: includes/Admin/Admin.php:242 109 114 msgid "Pages are loading correctly again, so Safe Mode was switched off automatically and full optimization is back on." 110 115 msgstr "" 111 116 112 #: includes/Admin/Admin.php:25 0117 #: includes/Admin/Admin.php:256 113 118 msgid "wp-config.php is not writable, so page caching could not be enabled. Add" 114 119 msgstr "" 115 120 116 #: includes/Admin/Admin.php:25 2121 #: includes/Admin/Admin.php:258 117 122 msgid "manually, or make wp-config.php writable." 118 123 msgstr "" 119 124 120 #: includes/Admin/Admin.php:2 59125 #: includes/Admin/Admin.php:265 121 126 msgid "Optimazz cache purged." 122 127 msgstr "" 123 128 124 #: includes/Admin/Admin.php:26 0129 #: includes/Admin/Admin.php:266 125 130 msgid "Optimazz cache preload started in the background." 126 131 msgstr "" 127 132 128 #: includes/Admin/Admin.php:26 1133 #: includes/Admin/Admin.php:267 129 134 msgid "Optimazz cache cleared; preload started in the background." 130 135 msgstr "" 131 136 132 #: includes/Admin/AdminBar.php:5 3137 #: includes/Admin/AdminBar.php:57 133 138 msgid "Clear & Preload Cache" 134 139 msgstr "" 135 140 136 #: includes/Admin/AdminBar.php:5 4137 #: src/index.js:1 451141 #: includes/Admin/AdminBar.php:58 142 #: src/index.js:1597 138 143 msgid "Preload Cache" 139 144 msgstr "" 140 145 141 #: includes/Admin/AdminBar.php:5 5146 #: includes/Admin/AdminBar.php:59 142 147 msgid "Purge Cache" 143 148 msgstr "" 144 149 145 #: includes/Admin/AdminBar.php:7 3150 #: includes/Admin/AdminBar.php:77 146 151 msgid "Documentation" 147 152 msgstr "" 148 153 149 #: includes/Admin/AdminBar.php: 98150 #: includes/Admin/CacheConflictNotice.php:12 4151 #: includes/Admin/CacheConflictNotice.php:15 4154 #: includes/Admin/AdminBar.php:102 155 #: includes/Admin/CacheConflictNotice.php:126 156 #: includes/Admin/CacheConflictNotice.php:156 152 157 msgid "Not allowed." 153 158 msgstr "" … … 162 167 163 168 #: includes/Admin/CacheConflictNotice.php:88 164 #: includes/Modules/Cache/Preloader.php:714 169 #: includes/Modules/Cache/Preloader.php:740 170 #: src/index.js:2136 165 171 msgid "Another plugin" 166 172 msgstr "" … … 180 186 #: includes/Admin/CacheConflictNotice.php:108 181 187 #: includes/Admin/CacheConflictNotice.php:110 188 #: src/index.js:2140 182 189 msgid "Use Optimazz for caching" 183 190 msgstr "" … … 187 194 msgstr "" 188 195 189 #: includes/Admin/RestController.php:467 196 #: includes/Admin/NetworkRestController.php:115 197 #: includes/Admin/NetworkRestController.php:134 198 msgid "Page caching is off for this site." 199 msgstr "" 200 201 #: includes/Admin/NetworkRestController.php:153 202 msgid "Optimazz is not active on any site yet. Network-activate it (or activate it on a site) first." 203 msgstr "" 204 205 #: includes/Admin/RestController.php:385 206 #: includes/Admin/RestController.php:588 207 #: includes/Admin/RestController.php:614 208 msgid "Page caching is not available on multisite networks." 209 msgstr "" 210 211 #: includes/Admin/RestController.php:479 190 212 msgid "PHP version" 191 213 msgstr "" 192 214 193 #: includes/Admin/RestController.php:4 70215 #: includes/Admin/RestController.php:482 194 216 msgid "Optimazz requires PHP 7.4 or newer." 195 217 msgstr "" 196 218 197 #: includes/Admin/RestController.php:4 73219 #: includes/Admin/RestController.php:485 198 220 msgid "WordPress version" 199 221 msgstr "" 200 222 201 #: includes/Admin/RestController.php:4 79223 #: includes/Admin/RestController.php:491 202 224 msgid "GD (image library)" 203 225 msgstr "" 204 226 205 #: includes/Admin/RestController.php:4 80206 #: includes/Admin/RestController.php: 489207 #: includes/Admin/RestController.php:5 25227 #: includes/Admin/RestController.php:492 228 #: includes/Admin/RestController.php:501 229 #: includes/Admin/RestController.php:537 208 230 msgid "Enabled" 209 231 msgstr "" 210 232 211 #: includes/Admin/RestController.php:4 80212 #: includes/Admin/RestController.php: 489233 #: includes/Admin/RestController.php:492 234 #: includes/Admin/RestController.php:501 213 235 msgid "Not installed (optional)" 214 236 msgstr "" 215 237 216 #: includes/Admin/RestController.php:4 84238 #: includes/Admin/RestController.php:496 217 239 msgid "GD is enabled but without WebP support — ask your host to enable WebP in GD." 218 240 msgstr "" 219 241 220 #: includes/Admin/RestController.php:4 85242 #: includes/Admin/RestController.php:497 221 243 msgid "The PHP GD extension is not enabled. GD is the most common engine for image compression and WebP. Ask your host to enable the \"php-gd\" extension." 222 244 msgstr "" 223 245 224 #: includes/Admin/RestController.php: 488246 #: includes/Admin/RestController.php:500 225 247 msgid "ImageMagick (Imagick)" 226 248 msgstr "" 227 249 228 #: includes/Admin/RestController.php: 489250 #: includes/Admin/RestController.php:501 229 251 msgid "Not enabled" 230 252 msgstr "" 231 253 232 #: includes/Admin/RestController.php: 496254 #: includes/Admin/RestController.php:508 233 255 msgid "Optional — GD is already handling images. Imagick can add AVIF and higher-quality output if your host enables the \"php-imagick\" extension." 234 256 msgstr "" 235 257 236 #: includes/Admin/RestController.php: 497258 #: includes/Admin/RestController.php:509 237 259 msgid "The PHP Imagick extension is not enabled either. With neither GD nor Imagick, image compression and WebP/AVIF cannot run. Ask your host to enable \"php-gd\" or \"php-imagick\"." 238 260 msgstr "" 239 261 240 #: includes/Admin/RestController.php:5 00262 #: includes/Admin/RestController.php:512 241 263 msgid "WebP support" 242 264 msgstr "" 243 265 244 #: includes/Admin/RestController.php:5 01245 #: includes/Admin/RestController.php:5 07266 #: includes/Admin/RestController.php:513 267 #: includes/Admin/RestController.php:519 246 268 msgid "Available" 247 269 msgstr "" 248 270 249 #: includes/Admin/RestController.php:5 01250 #: includes/Admin/RestController.php:5 07271 #: includes/Admin/RestController.php:513 272 #: includes/Admin/RestController.php:519 251 273 msgid "Not available" 252 274 msgstr "" 253 275 254 #: includes/Admin/RestController.php:5 03276 #: includes/Admin/RestController.php:515 255 277 msgid "This server cannot generate WebP images. Update GD/Imagick or ask your host to enable WebP." 256 278 msgstr "" 257 279 258 #: includes/Admin/RestController.php:5 06280 #: includes/Admin/RestController.php:518 259 281 msgid "AVIF support" 260 282 msgstr "" 261 283 262 #: includes/Admin/RestController.php:5 09284 #: includes/Admin/RestController.php:521 263 285 msgid "This server cannot generate AVIF images (needs PHP 8.1+ GD with AVIF, or ImageMagick with the AVIF delegate). WebP still works." 264 286 msgstr "" 265 287 266 #: includes/Admin/RestController.php:5 12288 #: includes/Admin/RestController.php:524 267 289 msgid "PHP memory limit" 268 290 msgstr "" 269 291 270 #: includes/Admin/RestController.php:5 15292 #: includes/Admin/RestController.php:527 271 293 msgid "Image optimization works best with 256 MB or more. On lower limits Optimazz processes fewer images per request to stay safe." 272 294 msgstr "" 273 295 274 #: includes/Admin/RestController.php:5 18296 #: includes/Admin/RestController.php:530 275 297 msgid "Max execution time" 276 298 msgstr "" 277 299 278 #: includes/Admin/RestController.php:5 19300 #: includes/Admin/RestController.php:531 279 301 msgid "Unlimited" 280 302 msgstr "" 281 303 282 #: includes/Admin/RestController.php:5 21304 #: includes/Admin/RestController.php:533 283 305 msgid "Under 30 seconds can interrupt bulk AVIF conversion of large images." 284 306 msgstr "" 285 307 286 #: includes/Admin/RestController.php:5 24308 #: includes/Admin/RestController.php:536 287 309 msgid "WP-Cron" 288 310 msgstr "" 289 311 290 #: includes/Admin/RestController.php:5 25312 #: includes/Admin/RestController.php:537 291 313 msgid "Disabled" 292 314 msgstr "" 293 315 294 #: includes/Admin/RestController.php:5 27316 #: includes/Admin/RestController.php:539 295 317 msgid "WP-Cron is disabled, so scheduled cache preloading relies on a real system cron job being configured." 296 318 msgstr "" 297 319 298 #: includes/Admin/RestController.php:5 41320 #: includes/Admin/RestController.php:553 299 321 msgid "GD and Imagick are" 300 322 msgstr "" 301 323 302 #: includes/Admin/RestController.php:5 42324 #: includes/Admin/RestController.php:554 303 325 msgid "GD is" 304 326 msgstr "" 305 327 306 #: includes/Admin/RestController.php:5 42328 #: includes/Admin/RestController.php:554 307 329 msgid "Imagick is" 308 330 msgstr "" 309 331 310 #: includes/Admin/RestController.php:5 44332 #: includes/Admin/RestController.php:556 311 333 msgid "PHP that runs this site" 312 334 msgstr "" 313 335 314 336 #. translators: 1: which engine is missing, 2: php.ini path, 3: PHP version 315 #: includes/Admin/RestController.php:5 50337 #: includes/Admin/RestController.php:562 316 338 #, php-format 317 339 msgid "This is the exact PHP handling your site — it loads its settings from %2$s. If you already enabled an image library in your hosting panel but %1$s still shown as not enabled, you almost certainly enabled it for a different PHP version or handler. It must be enabled for THIS PHP (%3$s). After enabling, restart PHP (or PHP-FPM) and click Re-check." … … 319 341 320 342 #. translators: 1: which engine is missing, 2: PHP version 321 #: includes/Admin/RestController.php:5 57343 #: includes/Admin/RestController.php:569 322 344 #, php-format 323 345 msgid "This is the exact PHP handling your site. If you already enabled an image library in your hosting panel but %1$s still shown as not enabled, you enabled it for a different PHP version. It must be enabled for THIS PHP (%2$s). After enabling, restart PHP and click Re-check." 324 346 msgstr "" 325 347 326 #: includes/Admin/RestController.php:7 07348 #: includes/Admin/RestController.php:725 327 349 msgid "Image processing is unavailable: this server has no GD or ImageMagick extension. Ask your host to enable \"php-gd\" (or \"php-imagick\"), then try again. See Tools → Server Requirements." 328 350 msgstr "" … … 336 358 msgstr "" 337 359 338 #: includes/Core/Recommendations.php:6 1339 #: src/index.js: 971360 #: includes/Core/Recommendations.php:63 361 #: src/index.js:1117 340 362 msgid "Enable Page Cache" 341 363 msgstr "" 342 364 343 #: includes/Core/Recommendations.php:6 2365 #: includes/Core/Recommendations.php:64 344 366 msgid "Serve static HTML instead of running PHP on every request — the single biggest speed win." 345 367 msgstr "" 346 368 347 #: includes/Core/Recommendations.php:6 7369 #: includes/Core/Recommendations.php:69 348 370 msgid "Turn on cache preloading" 349 371 msgstr "" 350 372 351 #: includes/Core/Recommendations.php: 68373 #: includes/Core/Recommendations.php:70 352 374 msgid "Warm the cache automatically so visitors never hit a cold, uncached page." 353 375 msgstr "" 354 376 355 #: includes/Core/Recommendations.php:7 6377 #: includes/Core/Recommendations.php:78 356 378 msgid "Enable JavaScript optimization" 357 379 msgstr "" 358 380 359 #: includes/Core/Recommendations.php:7 7381 #: includes/Core/Recommendations.php:79 360 382 msgid "Defer and delay scripts to cut Total Blocking Time — the heaviest-weighted mobile metric." 361 383 msgstr "" 362 384 363 #: includes/Core/Recommendations.php:8 4385 #: includes/Core/Recommendations.php:86 364 386 msgid "Delay JavaScript until interaction" 365 387 msgstr "" 366 388 367 #: includes/Core/Recommendations.php:8 5389 #: includes/Core/Recommendations.php:87 368 390 msgid "Delay non-critical scripts (analytics, chat, pixels) until the first interaction to reduce blocking time." 369 391 msgstr "" 370 392 371 #: includes/Core/Recommendations.php:9 3393 #: includes/Core/Recommendations.php:95 372 394 msgid "Enable CSS optimization" 373 395 msgstr "" 374 396 375 #: includes/Core/Recommendations.php:9 4397 #: includes/Core/Recommendations.php:96 376 398 msgid "Minify CSS to remove render-blocking bytes and speed up First Contentful Paint." 377 399 msgstr "" 378 400 379 #: includes/Core/Recommendations.php:10 2401 #: includes/Core/Recommendations.php:104 380 402 msgid "Enable Image optimization" 381 403 msgstr "" 382 404 383 #: includes/Core/Recommendations.php:10 3405 #: includes/Core/Recommendations.php:105 384 406 msgid "Lazy-load images, reserve their space (to prevent layout shift) and serve next-gen formats." 385 407 msgstr "" 386 408 387 #: includes/Core/Recommendations.php:1 09409 #: includes/Core/Recommendations.php:111 388 410 msgid "Reserve image space (fix layout shift)" 389 411 msgstr "" 390 412 391 #: includes/Core/Recommendations.php:11 0413 #: includes/Core/Recommendations.php:112 392 414 msgid "Add missing image dimensions and aspect-ratio so images do not push content down as they load (lowers CLS)." 393 415 msgstr "" 394 416 395 #: includes/Core/Recommendations.php:11 6396 #: src/index.js: 948397 #: src/index.js:1 369417 #: includes/Core/Recommendations.php:118 418 #: src/index.js:1092 419 #: src/index.js:1515 398 420 msgid "Lazy-load images" 399 421 msgstr "" 400 422 401 #: includes/Core/Recommendations.php:11 7423 #: includes/Core/Recommendations.php:119 402 424 msgid "Load off-screen images only when scrolled into view to speed up the initial load." 403 425 msgstr "" 404 426 405 #: includes/Core/Recommendations.php:12 4427 #: includes/Core/Recommendations.php:126 406 428 msgid "Serve WebP images" 407 429 msgstr "" 408 430 409 #: includes/Core/Recommendations.php:12 5431 #: includes/Core/Recommendations.php:127 410 432 msgid "Generate and serve WebP copies — typically 25–35% smaller than JPEG/PNG at the same quality." 411 433 msgstr "" 412 434 413 #: includes/Core/Recommendations.php:13 6435 #: includes/Core/Recommendations.php:138 414 436 msgid "Set font loading to \"Optional\" (reduce CLS)" 415 437 msgstr "" 416 438 417 #: includes/Core/Recommendations.php:13 7439 #: includes/Core/Recommendations.php:139 418 440 msgid "The \"swap\" mode lets web fonts reflow the page as they load, raising Cumulative Layout Shift. \"Optional\" removes that shift." 419 441 msgstr "" 420 442 421 #: includes/Core/Recommendations.php:14 3443 #: includes/Core/Recommendations.php:145 422 444 msgid "Enable Font optimization" 423 445 msgstr "" 424 446 425 #: includes/Core/Recommendations.php:14 4447 #: includes/Core/Recommendations.php:146 426 448 msgid "Preconnect to font hosts and control font-display to reduce render-blocking and layout shift." 427 449 msgstr "" 428 450 429 #: includes/Core/Recommendations.php:15 2451 #: includes/Core/Recommendations.php:154 430 452 msgid "Enable Database cleanup" 431 453 msgstr "" 432 454 433 #: includes/Core/Recommendations.php:15 3455 #: includes/Core/Recommendations.php:155 434 456 msgid "Remove revisions, auto-drafts, spam and expired transients, and optimize tables to keep the database lean." 435 457 msgstr "" 436 458 437 #: includes/Core/Recommendations.php:16 1459 #: includes/Core/Recommendations.php:163 438 460 msgid "Enable an image library on your server" 439 461 msgstr "" 440 462 441 #: includes/Core/Recommendations.php:16 2463 #: includes/Core/Recommendations.php:164 442 464 msgid "Your server has neither GD nor ImageMagick, so image compression and WebP/AVIF cannot run. Ask your host to enable the \"php-gd\" (or \"php-imagick\") extension. See Tools → Server Requirements." 443 465 msgstr "" 444 466 467 #: includes/Modules/Cache/Preloader.php:116 468 msgid "the home page" 469 msgstr "" 470 445 471 #. translators: %d: number of category / tag archive pages. 446 #: includes/Modules/Cache/Preloader.php:12 4472 #: includes/Modules/Cache/Preloader.php:127 447 473 #, php-format 448 474 msgid "%d category or tag page" … … 451 477 msgstr[1] "" 452 478 453 #: includes/Modules/Cache/Preloader.php: 681479 #: includes/Modules/Cache/Preloader.php:707 454 480 msgid "your host's server cache" 455 481 msgstr "" 456 482 457 #: includes/Modules/Cache/Preloader.php: 685483 #: includes/Modules/Cache/Preloader.php:711 458 484 msgid "a server or CDN cache" 459 485 msgstr "" 460 486 461 #: includes/Modules/Cache/Preloader.php:7 04487 #: includes/Modules/Cache/Preloader.php:730 462 488 msgid "The site could not load its own pages (the loopback request failed or timed out). Some hosts block a server from requesting its own website — ask your host to allow it." 463 489 msgstr "" 464 490 465 491 #. translators: %s: name of another caching plugin. 466 #: includes/Modules/Cache/Preloader.php:7 13492 #: includes/Modules/Cache/Preloader.php:739 467 493 #, php-format 468 494 msgid "%s controls page caching (advanced-cache.php), so Optimazz cannot save pages. Deactivate it, or use \"Take over caching\" in Cache health." 469 495 msgstr "" 470 496 471 #: includes/Modules/Cache/Preloader.php:7 18497 #: includes/Modules/Cache/Preloader.php:744 472 498 msgid "The cache file advanced-cache.php is missing. Turn Page Cache off and on again to reinstall it, and make sure the wp-content folder is writable." 473 499 msgstr "" 474 500 475 #: includes/Modules/Cache/Preloader.php:7 21501 #: includes/Modules/Cache/Preloader.php:747 476 502 msgid "WP_CACHE is not enabled in wp-config.php, so WordPress never loads the cache. Make wp-config.php writable and save the Cache settings, or add define( 'WP_CACHE', true ); yourself." 477 503 msgstr "" 478 504 479 505 #. translators: %s: name of a server or CDN cache, e.g. "Cloudflare". 480 #: includes/Modules/Cache/Preloader.php:7 26506 #: includes/Modules/Cache/Preloader.php:752 481 507 #, php-format 482 508 msgid "%s answered from its own cache before WordPress ran, so Optimazz never saw these requests. Purge that cache, or turn it off for HTML pages if you want Optimazz to cache them." 483 509 msgstr "" 484 510 485 #: includes/Modules/Cache/Preloader.php:7 30511 #: includes/Modules/Cache/Preloader.php:756 486 512 msgid "The Optimazz cache engine did not run for these requests. Usually a server-level cache or proxy (for example LiteSpeed, Varnish or a host cache) answers before WordPress loads. Check Cache health, or ask your host." 487 513 msgstr "" 488 514 489 515 #. translators: %s: HTTP status code, e.g. 404. 490 #: includes/Modules/Cache/Preloader.php:7 37516 #: includes/Modules/Cache/Preloader.php:763 491 517 #, php-format 492 518 msgid "Pages answered with HTTP %s instead of 200, and only normal pages are cached. Check that these URLs open correctly." 493 519 msgstr "" 494 520 495 #: includes/Modules/Cache/Preloader.php:7 42521 #: includes/Modules/Cache/Preloader.php:768 496 522 msgid "Page Cache is switched off." 497 523 msgstr "" 498 524 499 #: includes/Modules/Cache/Preloader.php:7 43525 #: includes/Modules/Cache/Preloader.php:769 500 526 msgid "Another plugin or the theme marks these pages \"do not cache\" (DONOTCACHEPAGE), so they are skipped. Membership, security or form plugins often do this." 501 527 msgstr "" 502 528 503 #: includes/Modules/Cache/Preloader.php:7 44529 #: includes/Modules/Cache/Preloader.php:770 504 530 msgid "The web address used to reach the site does not match the WordPress Site Address (for example www vs non-www). Check Settings → General." 505 531 msgstr "" 506 532 507 #: includes/Modules/Cache/Preloader.php:7 45533 #: includes/Modules/Cache/Preloader.php:771 508 534 msgid "Cache files could not be written. Make the wp-content/cache folder writable (permissions 755)." 509 535 msgstr "" 510 536 511 #: includes/Modules/Cache/Preloader.php:7 46537 #: includes/Modules/Cache/Preloader.php:772 512 538 msgid "These pages match your \"Never cache these URLs\" setting." 513 539 msgstr "" 514 540 515 #: includes/Modules/Cache/Preloader.php:7 47541 #: includes/Modules/Cache/Preloader.php:773 516 542 msgid "A cookie on the request matches your \"Never cache when these cookies are set\" setting." 517 543 msgstr "" 518 544 519 #: includes/Modules/Cache/Preloader.php:7 48545 #: includes/Modules/Cache/Preloader.php:774 520 546 msgid "The preloader's user-agent matches your \"Never cache for these user agents\" setting." 521 547 msgstr "" 522 548 523 #: includes/Modules/Cache/Preloader.php:7 49549 #: includes/Modules/Cache/Preloader.php:775 524 550 msgid "The pages came back empty or almost empty, so there was nothing to cache." 525 551 msgstr "" 526 552 527 #: includes/Modules/Cache/Preloader.php:7 50553 #: includes/Modules/Cache/Preloader.php:776 528 554 msgid "WordPress treated the request as logged in, so it was not cached." 529 555 msgstr "" 530 556 531 #: includes/Modules/Cache/Preloader.php:7 51557 #: includes/Modules/Cache/Preloader.php:777 532 558 msgid "These URLs show a \"not found\" (404) page, which is never cached." 533 559 msgstr "" 534 560 535 561 #. translators: %s: internal reason code. 536 #: includes/Modules/Cache/Preloader.php:7 57562 #: includes/Modules/Cache/Preloader.php:783 537 563 #, php-format 538 564 msgid "The cache skipped these pages (reason: %s)." … … 552 578 msgstr "" 553 579 554 #: includes/Modules/Fonts/FontsModule.php: 29580 #: includes/Modules/Fonts/FontsModule.php:38 555 581 #: src/index.js:40 556 #: src/index.js:1 570582 #: src/index.js:1726 557 583 msgid "Fonts" 558 584 msgstr "" … … 568 594 569 595 #: includes/Modules/Media/MediaModule.php:75 570 #: src/index.js:1 359596 #: src/index.js:1505 571 597 msgid "Image Optimization" 572 598 msgstr "" … … 578 604 #: includes/Modules/Tweaks/TweaksModule.php:25 579 605 #: src/index.js:45 580 #: src/index.js:1 679606 #: src/index.js:1841 581 607 msgid "Tweaks" 582 608 msgstr "" … … 603 629 604 630 #: src/index.js:41 605 #: src/index.js:1055 631 #: src/index.js:1201 632 #: src/index.js:2191 606 633 msgid "Preload" 607 634 msgstr "" … … 612 639 613 640 #: src/index.js:42 614 #: src/index.js:1 482641 #: src/index.js:1638 615 642 msgid "Advanced Rules" 616 643 msgstr "" … … 640 667 msgstr "" 641 668 642 #: src/index.js:64 669 #: src/index.js:81 670 msgid "every hour" 671 msgstr "" 672 673 #: src/index.js:82 674 #: src/index.js:84 675 msgid "twice a day" 676 msgstr "" 677 678 #: src/index.js:83 679 msgid "once a day" 680 msgstr "" 681 682 #: src/index.js:88 683 msgid "never expires" 684 msgstr "" 685 686 #: src/index.js:89 687 #: src/index.js:1231 688 #: src/index.js:1333 689 #: src/index.js:2107 690 msgid "12 hours" 691 msgstr "" 692 693 #: src/index.js:90 694 #: src/index.js:1232 695 #: src/index.js:1334 696 #: src/index.js:2108 697 msgid "1 day" 698 msgstr "" 699 700 #: src/index.js:91 701 #: src/index.js:1233 702 #: src/index.js:1335 703 #: src/index.js:2109 704 msgid "1 week" 705 msgstr "" 706 707 #: src/index.js:92 708 #: src/index.js:1234 709 #: src/index.js:1336 710 #: src/index.js:2110 711 msgid "1 month" 712 msgstr "" 713 714 #. translators: %d: number of hours. 715 #: src/index.js:95 716 #: src/index.js:1237 717 #, js-format 718 msgid "%d hour" 719 msgid_plural "%d hours" 720 msgstr[0] "" 721 msgstr[1] "" 722 723 #: src/index.js:102 724 msgid "Open Network Admin → Optimazz" 725 msgstr "" 726 727 #: src/index.js:111 728 msgid "Page caching is off for this network" 729 msgstr "" 730 731 #: src/index.js:113 732 msgid "Turn it on for every site in Network Admin → Optimazz. Each site gets its own cache, and keeps its own exclusions here under Advanced Rules." 733 msgstr "" 734 735 #: src/index.js:114 736 msgid "Your network administrator can turn it on for every site in Network Admin → Optimazz. All other optimizations — CSS, JavaScript, images, fonts and more — work normally on this site." 737 msgstr "" 738 739 #: src/index.js:128 740 msgid "Page caching is on — managed for the whole network" 741 msgstr "" 742 743 #. translators: 1: yes/no, 2: cache lifetime, 3: preload frequency. 744 #: src/index.js:131 745 #, js-format 746 msgid "Separate mobile cache: %1$s · Cache lifetime: %2$s · Automatic preload: %3$s." 747 msgstr "" 748 749 #: src/index.js:132 750 msgid "yes" 751 msgstr "" 752 753 #: src/index.js:132 754 msgid "no" 755 msgstr "" 756 757 #: src/index.js:134 758 #: src/index.js:1247 759 msgid "off" 760 msgstr "" 761 762 #: src/index.js:136 763 msgid "This site has its own cache. Purge or preload it from the Dashboard or the Preload screen, and set its own exclusions under Advanced Rules." 764 msgstr "" 765 766 #: src/index.js:146 643 767 msgid "Content leftovers" 644 768 msgstr "" 645 769 646 #: src/index.js: 65770 #: src/index.js:147 647 771 msgid "Old post data that piles up over time. Removing it is permanent, so keep anything you still need." 648 772 msgstr "" 649 773 650 #: src/index.js: 67774 #: src/index.js:149 651 775 msgid "Post revisions" 652 776 msgstr "" 653 777 654 #: src/index.js: 67778 #: src/index.js:149 655 779 msgid "Old copies of posts saved automatically. Deleting them keeps only your current content." 656 780 msgstr "" 657 781 658 #: src/index.js: 68782 #: src/index.js:150 659 783 msgid "Auto drafts" 660 784 msgstr "" 661 785 662 #: src/index.js: 68786 #: src/index.js:150 663 787 msgid "Empty drafts WordPress creates while you write. Safe to remove." 664 788 msgstr "" 665 789 666 #: src/index.js: 69790 #: src/index.js:151 667 791 msgid "Trashed posts" 668 792 msgstr "" 669 793 670 #: src/index.js: 69794 #: src/index.js:151 671 795 msgid "Posts in the trash that have not been permanently deleted yet." 672 796 msgstr "" 673 797 674 #: src/index.js: 73798 #: src/index.js:155 675 799 msgid "Comment clutter" 676 800 msgstr "" 677 801 678 #: src/index.js: 74802 #: src/index.js:156 679 803 msgid "Spam and trashed comments you are never going to approve. Clearing them keeps the comments table light." 680 804 msgstr "" 681 805 682 #: src/index.js: 76806 #: src/index.js:158 683 807 msgid "Spam comments" 684 808 msgstr "" 685 809 686 #: src/index.js: 76810 #: src/index.js:158 687 811 msgid "Comments marked as spam. Removing them frees up the comments table." 688 812 msgstr "" 689 813 690 #: src/index.js: 77814 #: src/index.js:159 691 815 msgid "Trashed comments" 692 816 msgstr "" 693 817 694 #: src/index.js: 77818 #: src/index.js:159 695 819 msgid "Comments in the trash awaiting permanent deletion." 696 820 msgstr "" 697 821 698 #: src/index.js: 81822 #: src/index.js:163 699 823 msgid "Temporary data" 700 824 msgstr "" 701 825 702 #: src/index.js: 82826 #: src/index.js:164 703 827 msgid "Short-lived cached values. They are safe to clear and are rebuilt automatically whenever a plugin needs them." 704 828 msgstr "" 705 829 706 #: src/index.js: 84830 #: src/index.js:166 707 831 msgid "Expired transients" 708 832 msgstr "" 709 833 710 #: src/index.js: 84834 #: src/index.js:166 711 835 msgid "Cached values that have already passed their expiry date." 712 836 msgstr "" 713 837 714 #: src/index.js: 88838 #: src/index.js:170 715 839 msgid "Table maintenance" 716 840 msgstr "" 717 841 718 #: src/index.js: 89842 #: src/index.js:171 719 843 msgid "Defragment your tables to reclaim wasted space and trim database overhead." 720 844 msgstr "" 721 845 722 #: src/index.js: 91846 #: src/index.js:173 723 847 msgid "Optimize database tables" 724 848 msgstr "" 725 849 726 #: src/index.js: 91850 #: src/index.js:173 727 851 msgid "Runs OPTIMIZE on your tables to defragment them and free wasted space." 728 852 msgstr "" 729 853 730 #: src/index.js: 97854 #: src/index.js:179 731 855 msgid "Do not limit" 732 856 msgstr "" 733 857 734 #: src/index.js: 98858 #: src/index.js:180 735 859 msgid "Reduce activity" 736 860 msgstr "" 737 861 738 #: src/index.js: 99862 #: src/index.js:181 739 863 msgid "Disable" 740 864 msgstr "" 741 865 742 #: src/index.js:1 08866 #: src/index.js:190 743 867 msgid "Active" 744 868 msgstr "" 745 869 746 #: src/index.js:1 08747 #: src/index.js:1 110748 #: src/index.js:1 636870 #: src/index.js:190 871 #: src/index.js:1256 872 #: src/index.js:1798 749 873 msgid "Off" 750 874 msgstr "" 751 875 752 #: src/index.js: 150876 #: src/index.js:232 753 877 msgid "Cancelled" 754 878 msgstr "" 755 879 756 #: src/index.js: 151880 #: src/index.js:233 757 881 msgid "Failed" 758 882 msgstr "" 759 883 760 #: src/index.js: 167884 #: src/index.js:249 761 885 msgid "Expand" 762 886 msgstr "" 763 887 764 #: src/index.js: 167888 #: src/index.js:249 765 889 msgid "Minimize" 766 890 msgstr "" 767 891 768 #: src/index.js: 170892 #: src/index.js:252 769 893 msgid "Close" 770 894 msgstr "" 771 895 772 #: src/index.js: 184896 #: src/index.js:266 773 897 msgid "Step" 774 898 msgstr "" 775 899 776 #: src/index.js: 189900 #: src/index.js:271 777 901 msgid "Stop" 778 902 msgstr "" 779 903 780 #: src/index.js:492 781 #: src/index.js:1239 904 #: src/index.js:538 905 msgid "You have unsaved changes" 906 msgstr "" 907 908 #: src/index.js:538 909 msgid "All changes saved" 910 msgstr "" 911 912 #: src/index.js:541 913 msgid "Save changes" 914 msgstr "" 915 916 #: src/index.js:639 917 #: src/index.js:1385 782 918 msgid "Deactivate" 783 919 msgstr "" 784 920 785 #: src/index.js: 492921 #: src/index.js:639 786 922 msgid "Optimazz will keep handling caching." 787 923 msgstr "" 788 924 789 #: src/index.js: 499925 #: src/index.js:646 790 926 msgid "deactivated — Optimazz is now the only cache." 791 927 msgstr "" 792 928 793 #: src/index.js: 501929 #: src/index.js:648 794 930 msgid "Could not deactivate the plugin." 795 931 msgstr "" 796 932 797 #: src/index.js: 508933 #: src/index.js:655 798 934 msgid "Make Optimazz the caching plugin? This replaces the current advanced-cache.php (from another caching plugin) and turns on WP_CACHE. Deactivate the other caching plugin afterwards to avoid conflicts." 799 935 msgstr "" 800 936 801 #: src/index.js: 515937 #: src/index.js:662 802 938 msgid "Optimazz is now serving pages from cache." 803 939 msgstr "" 804 940 805 #: src/index.js: 516941 #: src/index.js:663 806 942 msgid "Drop-in installed. Preload the cache, then check again." 807 943 msgstr "" 808 944 809 #: src/index.js: 517945 #: src/index.js:664 810 946 msgid "Could not fully take over — see the cache health details." 811 947 msgstr "" 812 948 813 #: src/index.js: 520949 #: src/index.js:667 814 950 msgid "Take-over failed. Check file permissions." 815 951 msgstr "" 816 952 817 #: src/index.js: 542818 #: src/index.js: 588953 #: src/index.js:689 954 #: src/index.js:735 819 955 msgid "Cache purged." 820 956 msgstr "" 821 957 822 #: src/index.js: 543958 #: src/index.js:690 823 959 msgid "Cache preload started in the background." 824 960 msgstr "" 825 961 826 #: src/index.js: 544962 #: src/index.js:691 827 963 msgid "Cache cleared; preload started in the background." 828 964 msgstr "" 829 965 830 #: src/index.js: 553966 #: src/index.js:700 831 967 msgid "wp-config.php is not writable — add WP_CACHE manually to enable page caching." 832 968 msgstr "" 833 969 834 #: src/index.js: 567970 #: src/index.js:714 835 971 msgid "Settings saved." 836 972 msgstr "" 837 973 838 #: src/index.js: 570974 #: src/index.js:717 839 975 msgid "Save failed." 840 976 msgstr "" 841 977 842 #: src/index.js: 620978 #: src/index.js:767 843 979 msgid "Converting images to WebP/AVIF" 844 980 msgstr "" 845 981 846 #: src/index.js: 621982 #: src/index.js:768 847 983 msgid "Next-gen format generation" 848 984 msgstr "" 849 985 850 #: src/index.js: 624986 #: src/index.js:771 851 987 msgid "Generated" 852 988 msgstr "" 853 989 854 #: src/index.js: 624990 #: src/index.js:771 855 991 msgid "files in this batch" 856 992 msgstr "" 857 993 858 #: src/index.js: 624859 #: src/index.js: 633994 #: src/index.js:771 995 #: src/index.js:780 860 996 msgid "skipped" 861 997 msgstr "" 862 998 863 #: src/index.js: 627999 #: src/index.js:774 864 1000 msgid "Compressing images" 865 1001 msgstr "" 866 1002 867 #: src/index.js: 6281003 #: src/index.js:775 868 1004 msgid "Recompress, resize & strip metadata" 869 1005 msgstr "" 870 1006 871 #: src/index.js: 6341007 #: src/index.js:781 872 1008 msgid "Processed" 873 1009 msgstr "" 874 1010 875 #: src/index.js: 634876 #: src/index.js:1 4401011 #: src/index.js:781 1012 #: src/index.js:1586 877 1013 msgid "saved" 878 1014 msgstr "" 879 1015 880 #: src/index.js:638 1016 #: src/index.js:785 1017 #: src/index.js:1953 881 1018 msgid "Preloading cache" 882 1019 msgstr "" 883 1020 884 #: src/index.js: 6391021 #: src/index.js:786 885 1022 msgid "Warming pages" 886 1023 msgstr "" 887 1024 888 #: src/index.js:643 1025 #: src/index.js:790 1026 #: src/index.js:1974 889 1027 msgid "Warmed" 890 1028 msgstr "" 891 1029 892 #: src/index.js:643 1030 #: src/index.js:790 1031 #: src/index.js:1974 893 1032 msgid "pages" 894 1033 msgstr "" 895 1034 896 #: src/index.js:643 1035 #: src/index.js:790 1036 #: src/index.js:1974 897 1037 msgid "cached" 898 1038 msgstr "" 899 1039 900 #: src/index.js:643 1040 #: src/index.js:790 1041 #: src/index.js:1974 901 1042 msgid "not cached" 902 1043 msgstr "" 903 1044 904 #: src/index.js: 6641045 #: src/index.js:811 905 1046 msgid "Resuming…" 906 1047 msgstr "" 907 1048 908 #: src/index.js:678 1049 #: src/index.js:825 1050 #: src/index.js:1963 909 1051 msgid "Stopped by user." 910 1052 msgstr "" 911 1053 912 #: src/index.js: 6971054 #: src/index.js:844 913 1055 msgid "A batch failed (likely one slow/large image); skipped it and continued." 914 1056 msgstr "" 915 1057 916 #: src/index.js: 6991058 #: src/index.js:846 917 1059 msgid "Completed (some items skipped)." 918 1060 msgstr "" 919 1061 920 #: src/index.js: 7111062 #: src/index.js:858 921 1063 msgid "Job stalled — no progress." 922 1064 msgstr "" 923 1065 924 #: src/index.js:722 1066 #: src/index.js:869 1067 #: src/index.js:1977 925 1068 msgid "left" 926 1069 msgstr "" 927 1070 928 1071 #. translators: %d: number of pages to warm. 929 #: src/index.js:734 1072 #: src/index.js:881 1073 #: src/index.js:1987 930 1074 #, js-format 931 1075 msgid "Found %d pages to warm:" 932 1076 msgstr "" 933 1077 934 #: src/index.js: 7441078 #: src/index.js:891 935 1079 msgid "Image processing is unavailable on this server (no GD or ImageMagick). See Tools → Server Requirements." 936 1080 msgstr "" 937 1081 938 #: src/index.js:749 1082 #: src/index.js:896 1083 #: src/index.js:1993 939 1084 msgid "Warming stopped: the server returned 403/429, so a firewall or security plugin is blocking the preload requests. Cached what we could; allow-list the \"Optimazz-CacheWarmer\" user-agent in your firewall or security plugin (e.g. All-In-One Security, Wordfence, Cloudflare), or slow warming with the optimazz_preload_delay_ms filter." 940 1085 msgstr "" 941 1086 942 #: src/index.js:756 1087 #: src/index.js:903 1088 #: src/index.js:1997 943 1089 msgid "Pages are not being cached" 944 1090 msgstr "" 945 1091 946 #: src/index.js:756 1092 #: src/index.js:903 1093 #: src/index.js:1997 947 1094 msgid "Stopped: pages are loading but none are being saved to the cache." 948 1095 msgstr "" 949 1096 950 #: src/index.js:760 951 #: src/index.js:841 1097 #: src/index.js:907 1098 #: src/index.js:988 1099 #: src/index.js:2007 952 1100 msgid "Completed." 953 1101 msgstr "" 954 1102 955 1103 #. translators: %d: number of pages. 956 #: src/index.js:764 1104 #: src/index.js:911 1105 #: src/index.js:2004 957 1106 #, js-format 958 1107 msgid "%d page was not cached." … … 961 1110 msgstr[1] "" 962 1111 963 #: src/index.js: 8231112 #: src/index.js:970 964 1113 msgid "Cleaning and optimizing database…" 965 1114 msgstr "" 966 1115 967 #: src/index.js: 8271116 #: src/index.js:974 968 1117 msgid "Optimizing database" 969 1118 msgstr "" 970 1119 971 #: src/index.js: 8281120 #: src/index.js:975 972 1121 msgid "Cleanup & table optimization" 973 1122 msgstr "" 974 1123 975 #: src/index.js: 8401124 #: src/index.js:987 976 1125 msgid "Cleaned" 977 1126 msgstr "" 978 1127 979 #: src/index.js: 8401128 #: src/index.js:987 980 1129 msgid "rows," 981 1130 msgstr "" 982 1131 983 #: src/index.js: 8401132 #: src/index.js:987 984 1133 msgid "tables optimized." 985 1134 msgstr "" 986 1135 987 #: src/index.js: 8451136 #: src/index.js:992 988 1137 msgid "Optimize failed." 989 1138 msgstr "" 990 1139 991 #: src/index.js: 8511140 #: src/index.js:998 992 1141 msgid "Reset all Optimazz settings to defaults? You can undo this with \"Undo last change\"." 993 1142 msgstr "" 994 1143 995 #: src/index.js: 8591144 #: src/index.js:1006 996 1145 msgid "Settings reset to defaults." 997 1146 msgstr "" 998 1147 999 #: src/index.js: 8621148 #: src/index.js:1009 1000 1149 msgid "Reset failed." 1001 1150 msgstr "" 1002 1151 1003 #: src/index.js: 8721152 #: src/index.js:1019 1004 1153 msgid "Nothing to undo." 1005 1154 msgstr "" 1006 1155 1007 #: src/index.js: 8791156 #: src/index.js:1026 1008 1157 msgid "Reverted to the previous settings." 1009 1158 msgstr "" 1010 1159 1011 #: src/index.js: 8821160 #: src/index.js:1029 1012 1161 msgid "Undo failed." 1013 1162 msgstr "" 1014 1163 1015 #: src/index.js: 9141164 #: src/index.js:1061 1016 1165 msgid "Invalid settings file." 1017 1166 msgstr "" 1018 1167 1019 #: src/index.js: 9441168 #: src/index.js:1088 1020 1169 msgid "Page caching" 1021 1170 msgstr "" 1022 1171 1023 #: src/index.js: 9451172 #: src/index.js:1089 1024 1173 msgid "Delay JavaScript" 1025 1174 msgstr "" 1026 1175 1027 #: src/index.js: 9461028 #: src/index.js:1 2661176 #: src/index.js:1090 1177 #: src/index.js:1412 1029 1178 msgid "Defer JavaScript" 1030 1179 msgstr "" 1031 1180 1032 #: src/index.js: 9471033 #: src/index.js:1 3191181 #: src/index.js:1091 1182 #: src/index.js:1465 1034 1183 msgid "Minify CSS" 1035 1184 msgstr "" 1036 1185 1037 #: src/index.js: 9491186 #: src/index.js:1093 1038 1187 msgid "Next-gen images" 1039 1188 msgstr "" 1040 1189 1041 #: src/index.js: 9501190 #: src/index.js:1094 1042 1191 msgid "Font optimization" 1043 1192 msgstr "" 1044 1193 1045 #: src/index.js: 9511194 #: src/index.js:1095 1046 1195 msgid "Cache preloading" 1047 1196 msgstr "" 1048 1197 1049 #: src/index.js: 9521198 #: src/index.js:1096 1050 1199 msgid "Heartbeat control" 1051 1200 msgstr "" 1052 1201 1053 #: src/index.js: 9671202 #: src/index.js:1113 1054 1203 msgid "Turn on Page Cache for the real speed gain" 1055 1204 msgstr "" 1056 1205 1057 #: src/index.js: 9681206 #: src/index.js:1114 1058 1207 msgid "File and media optimizations are on, but Page Cache is off — so the server rebuilds every page on every request. Without caching this can make a site feel slower, not faster, especially on mobile. Enable Page Cache to serve ready-made HTML." 1059 1208 msgstr "" 1060 1209 1061 #: src/index.js: 9871210 #: src/index.js:1133 1062 1211 msgid "SCORE" 1063 1212 msgstr "" 1064 1213 1065 #: src/index.js: 9921214 #: src/index.js:1138 1066 1215 msgid "Performance score" 1067 1216 msgstr "" 1068 1217 1069 #: src/index.js: 9951218 #: src/index.js:1141 1070 1219 msgid "Biggest remaining win:" 1071 1220 msgstr "" 1072 1221 1073 #: src/index.js: 9961222 #: src/index.js:1142 1074 1223 msgid "Every optimization is enabled. Nicely done!" 1075 1224 msgstr "" 1076 1225 1077 #: src/index.js:1013 1078 #: src/index.js:1065 1226 #: src/index.js:1159 1227 #: src/index.js:1211 1228 #: src/index.js:2204 1079 1229 msgid "Cached pages" 1080 1230 msgstr "" 1081 1231 1082 #: src/index.js:1 0181232 #: src/index.js:1164 1083 1233 msgid "Cache size" 1084 1234 msgstr "" 1085 1235 1086 #: src/index.js:1 0231236 #: src/index.js:1169 1087 1237 msgid "Modules active" 1088 1238 msgstr "" 1089 1239 1090 #: src/index.js:1 0281091 #: src/index.js:1 0431240 #: src/index.js:1174 1241 #: src/index.js:1189 1092 1242 msgid "Recommendations" 1093 1243 msgstr "" 1094 1244 1095 #: src/index.js:1 0281245 #: src/index.js:1174 1096 1246 msgid "What to fix next to speed up your site — one click each." 1097 1247 msgstr "" 1098 1248 1099 #: src/index.js:1 0351249 #: src/index.js:1181 1100 1250 msgid "Fix it" 1101 1251 msgstr "" 1102 1252 1103 #: src/index.js:1 0361253 #: src/index.js:1182 1104 1254 msgid "Open" 1105 1255 msgstr "" 1106 1256 1107 #: src/index.js:1 0441257 #: src/index.js:1190 1108 1258 msgid "Nice — everything we recommend is already on. Your site is well optimized." 1109 1259 msgstr "" 1110 1260 1111 #: src/index.js:1 0491261 #: src/index.js:1195 1112 1262 msgid "Cache Files" 1113 1263 msgstr "" 1114 1264 1115 #: src/index.js:1 0491265 #: src/index.js:1195 1116 1266 msgid "Clear and preload all cache files." 1117 1267 msgstr "" 1118 1268 1119 #: src/index.js:1052 1269 #: src/index.js:1198 1270 #: src/index.js:2187 1120 1271 msgid "Clear & Preload" 1121 1272 msgstr "" 1122 1273 1123 #: src/index.js:1058 1274 #: src/index.js:1204 1275 #: src/index.js:2195 1124 1276 msgid "Purge cache" 1125 1277 msgstr "" 1126 1278 1127 #: src/index.js:1070 1279 #: src/index.js:1216 1280 #: src/index.js:2209 1128 1281 msgid "Page cache size" 1129 1282 msgstr "" 1130 1283 1131 #: src/index.js:1 0751284 #: src/index.js:1221 1132 1285 msgid "Optimized CSS" 1133 1286 msgstr "" 1134 1287 1135 #: src/index.js:1 0801136 #: src/index.js:1 0811288 #: src/index.js:1226 1289 #: src/index.js:1227 1137 1290 msgid "Cache lifespan" 1138 1291 msgstr "" 1139 1292 1140 #: src/index.js:1 0841293 #: src/index.js:1230 1141 1294 msgid "Never expire" 1142 1295 msgstr "" 1143 1296 1144 #: src/index.js:1085 1145 #: src/index.js:1187 1146 msgid "12 hours" 1147 msgstr "" 1148 1149 #: src/index.js:1086 1150 #: src/index.js:1188 1151 msgid "1 day" 1152 msgstr "" 1153 1154 #: src/index.js:1087 1155 #: src/index.js:1189 1156 msgid "1 week" 1157 msgstr "" 1158 1159 #: src/index.js:1088 1160 #: src/index.js:1190 1161 msgid "1 month" 1162 msgstr "" 1163 1164 #. translators: %d: number of hours. 1165 #: src/index.js:1091 1166 #, js-format 1167 msgid "%d hour" 1168 msgid_plural "%d hours" 1169 msgstr[0] "" 1170 msgstr[1] "" 1171 1172 #: src/index.js:1098 1297 #: src/index.js:1244 1173 1298 msgid "Auto-preload" 1174 1299 msgstr "" 1175 1300 1176 #: src/index.js:1101 1177 msgid "off" 1178 msgstr "" 1179 1180 #: src/index.js:1103 1301 #: src/index.js:1249 1181 1302 msgid "next run: due now" 1182 1303 msgstr "" 1183 1304 1184 #: src/index.js:1 1041305 #: src/index.js:1250 1185 1306 msgid "next run in" 1186 1307 msgstr "" 1187 1308 1188 #: src/index.js:1 1071309 #: src/index.js:1253 1189 1310 msgid "Auto-preload frequency" 1190 1311 msgstr "" 1191 1312 1192 #: src/index.js:1111 1193 #: src/index.js:1462 1313 #: src/index.js:1257 1314 #: src/index.js:1609 1315 #: src/index.js:2121 1194 1316 msgid "Every hour" 1195 1317 msgstr "" 1196 1318 1197 #: src/index.js:1112 1198 #: src/index.js:1463 1319 #: src/index.js:1258 1320 #: src/index.js:1610 1321 #: src/index.js:2122 1199 1322 msgid "Twice a day" 1200 1323 msgstr "" 1201 1324 1202 #: src/index.js:1113 1203 #: src/index.js:1464 1325 #: src/index.js:1259 1326 #: src/index.js:1611 1327 #: src/index.js:2123 1204 1328 msgid "Once a day" 1205 1329 msgstr "" 1206 1330 1207 #: src/index.js:1 1211331 #: src/index.js:1267 1208 1332 msgid "Last auto-preload" 1209 1333 msgstr "" 1210 1334 1211 #: src/index.js:1 1241335 #: src/index.js:1270 1212 1336 msgid "not yet" 1213 1337 msgstr "" 1214 1338 1215 1339 #. translators: 1: time ago, e.g. "2 hours", 2: number of pages 1216 #: src/index.js:1 1271340 #: src/index.js:1273 1217 1341 #, js-format 1218 1342 msgid "%1$s ago · %2$d pages" 1219 1343 msgstr "" 1220 1344 1221 #: src/index.js:1 1301345 #: src/index.js:1276 1222 1346 msgid "blocked by firewall" 1223 1347 msgstr "" 1224 1348 1225 #: src/index.js:1 1351349 #: src/index.js:1281 1226 1350 msgid "Module Status" 1227 1351 msgstr "" 1228 1352 1229 #: src/index.js:1 1351353 #: src/index.js:1281 1230 1354 msgid "Enable or disable any module right here." 1231 1355 msgstr "" 1232 1356 1233 #: src/index.js:1 1721357 #: src/index.js:1318 1234 1358 msgid "Serve static HTML instead of running PHP on every request." 1235 1359 msgstr "" 1236 1360 1237 #: src/index.js:1174 1361 #: src/index.js:1320 1362 #: src/index.js:2094 1238 1363 msgid "Cache mobile devices separately" 1239 1364 msgstr "" 1240 1365 1241 #: src/index.js:1176 1366 #: src/index.js:1322 1367 #: src/index.js:2096 1242 1368 msgid "Cache logged-in users" 1243 1369 msgstr "" 1244 1370 1245 #: src/index.js:1177 1371 #: src/index.js:1323 1372 #: src/index.js:2097 1246 1373 msgid "Leave off unless you know what you are doing." 1247 1374 msgstr "" 1248 1375 1249 #: src/index.js:1179 1376 #: src/index.js:1325 1377 #: src/index.js:2099 1250 1378 msgid "Browser cache headers" 1251 1379 msgstr "" 1252 1380 1253 #: src/index.js:1 1801381 #: src/index.js:1326 1254 1382 msgid "Add Expires / Cache-Control rules so repeat visits load CSS, JS, images and fonts from the browser cache. Apache and LiteSpeed only (written to .htaccess); ignored on Nginx." 1255 1383 msgstr "" 1256 1384 1257 #: src/index.js:1182 1385 #: src/index.js:1328 1386 #: src/index.js:2102 1258 1387 msgid "Cache lifetime" 1259 1388 msgstr "" 1260 1389 1261 #: src/index.js:1 1831390 #: src/index.js:1329 1262 1391 msgid "How long a cached page is kept before it is refreshed. Pages are always purged immediately when their content changes." 1263 1392 msgstr "" 1264 1393 1265 #: src/index.js:1186 1394 #: src/index.js:1332 1395 #: src/index.js:2106 1266 1396 msgid "Never" 1267 1397 msgstr "" 1268 1398 1269 #: src/index.js:1196 1399 #: src/index.js:1342 1400 #: src/index.js:2128 1270 1401 msgid "Cache health" 1271 1402 msgstr "" 1272 1403 1273 #: src/index.js:1 1981404 #: src/index.js:1344 1274 1405 msgid "Check now" 1275 1406 msgstr "" 1276 1407 1277 #: src/index.js:1 2011408 #: src/index.js:1347 1278 1409 msgid "Confirms caching is actually serving pages. If it is not, the plugin rebuilds every page on each request — which is slower, not faster." 1279 1410 msgstr "" 1280 1411 1281 #: src/index.js:1 2051412 #: src/index.js:1351 1282 1413 msgid "Pages ARE being served from cache (HIT)" 1283 1414 msgstr "" 1284 1415 1285 #: src/index.js:1 2061416 #: src/index.js:1352 1286 1417 msgid "Pages are NOT being served from cache (MISS) — see below" 1287 1418 msgstr "" 1288 1419 1289 #: src/index.js:1 2071420 #: src/index.js:1353 1290 1421 msgid "Live check unavailable" 1291 1422 msgstr "" 1292 1423 1293 #: src/index.js:1 2111424 #: src/index.js:1357 1294 1425 msgid "advanced-cache.php belongs to" 1295 1426 msgstr "" 1296 1427 1297 #: src/index.js:1 2111428 #: src/index.js:1357 1298 1429 msgid "take it over below" 1299 1430 msgstr "" 1300 1431 1301 #: src/index.js:1 2121432 #: src/index.js:1358 1302 1433 msgid "advanced-cache.php belongs to ANOTHER plugin — take it over below" 1303 1434 msgstr "" 1304 1435 1305 #: src/index.js:1 2131436 #: src/index.js:1359 1306 1437 msgid "advanced-cache.php drop-in installed" 1307 1438 msgstr "" 1308 1439 1309 #: src/index.js:1 2141440 #: src/index.js:1360 1310 1441 msgid "advanced-cache.php drop-in is missing" 1311 1442 msgstr "" 1312 1443 1313 #: src/index.js:1 2161444 #: src/index.js:1362 1314 1445 msgid "WP_CACHE is enabled in wp-config.php" 1315 1446 msgstr "" 1316 1447 1317 #: src/index.js:1 2161448 #: src/index.js:1362 1318 1449 msgid "WP_CACHE is not enabled in wp-config.php" 1319 1450 msgstr "" 1320 1451 1321 #: src/index.js:1 2181452 #: src/index.js:1364 1322 1453 msgid "wp-config.php is not writable — add the WP_CACHE line manually (below)" 1323 1454 msgstr "" 1324 1455 1325 #: src/index.js:1 2211456 #: src/index.js:1367 1326 1457 msgid "pages warmed in cache" 1327 1458 msgstr "" 1328 1459 1329 #: src/index.js:1 2271460 #: src/index.js:1373 1330 1461 msgid "Take over caching" 1331 1462 msgstr "" 1332 1463 1333 #: src/index.js:1 2291464 #: src/index.js:1375 1334 1465 msgid "Make Optimazz the caching plugin — installs its drop-in and enables WP_CACHE." 1335 1466 msgstr "" 1336 1467 1337 #: src/index.js:1 2371468 #: src/index.js:1383 1338 1469 msgid "is still active and may reclaim caching. Deactivate it so Optimazz keeps serving the cache." 1339 1470 msgstr "" 1340 1471 1341 #: src/index.js:1 2471472 #: src/index.js:1393 1342 1473 msgid "wp-config.php is not writable. Add this line just after the opening <?php tag:" 1343 1474 msgstr "" 1344 1475 1345 #: src/index.js:1 2521476 #: src/index.js:1398 1346 1477 msgid "Could not run the check. Try reloading the page." 1347 1478 msgstr "" 1348 1479 1349 #: src/index.js:1 2601480 #: src/index.js:1406 1350 1481 msgid "JavaScript" 1351 1482 msgstr "" 1352 1483 1353 #: src/index.js:1 2611484 #: src/index.js:1407 1354 1485 msgid "Defer and delay scripts to cut Total Blocking Time." 1355 1486 msgstr "" 1356 1487 1357 #: src/index.js:1 2631488 #: src/index.js:1409 1358 1489 msgid "Minify JavaScript" 1359 1490 msgstr "" 1360 1491 1361 #: src/index.js:1 2641492 #: src/index.js:1410 1362 1493 msgid "Strip comments and whitespace from inline scripts. Line breaks are kept for safety; test your site after enabling." 1363 1494 msgstr "" 1364 1495 1365 #: src/index.js:1 2671496 #: src/index.js:1413 1366 1497 msgid "Load scripts without blocking page render." 1367 1498 msgstr "" 1368 1499 1369 #: src/index.js:1 2691500 #: src/index.js:1415 1370 1501 msgid "Delay JS until interaction" 1371 1502 msgstr "" 1372 1503 1373 #: src/index.js:1 2701504 #: src/index.js:1416 1374 1505 msgid "Run scripts only after the first user interaction." 1375 1506 msgstr "" 1376 1507 1377 #: src/index.js:1 2731508 #: src/index.js:1419 1378 1509 msgid "Delay all scripts" 1379 1510 msgstr "" 1380 1511 1381 #: src/index.js:1 2741512 #: src/index.js:1420 1382 1513 msgid "Delay every script until interaction. jQuery, your theme, and Elementor are always kept safe. Still, on sites where content is rendered by JavaScript this can delay the visible page — test before keeping it on." 1383 1514 msgstr "" 1384 1515 1385 #: src/index.js:1 2801516 #: src/index.js:1426 1386 1517 msgid "Delaying all scripts can slow the first paint on JavaScript-rendered sites (Elementor, sliders). If your PageSpeed FCP/LCP get worse, switch to \"Delay selected\" and delay only tracking scripts." 1387 1518 msgstr "" 1388 1519 1389 #: src/index.js:1 2841520 #: src/index.js:1430 1390 1521 msgid "Delay these scripts" 1391 1522 msgstr "" 1392 1523 1393 #: src/index.js:1 2851524 #: src/index.js:1431 1394 1525 msgid "Only scripts matching these keywords are delayed. Match by filename, handle or URL — e.g. gtag, analytics, facebook." 1395 1526 msgstr "" 1396 1527 1397 #: src/index.js:1 2861528 #: src/index.js:1432 1398 1529 msgid "e.g. gtag, analytics" 1399 1530 msgstr "" 1400 1531 1401 #: src/index.js:1 2921532 #: src/index.js:1438 1402 1533 msgid "Exclude specific scripts" 1403 1534 msgstr "" 1404 1535 1405 #: src/index.js:1 2941536 #: src/index.js:1440 1406 1537 msgid "If a script breaks, exclude it from just one optimization instead of both. Match by filename, script handle, or part of the URL." 1407 1538 msgstr "" 1408 1539 1409 #: src/index.js:1 2981540 #: src/index.js:1444 1410 1541 msgid "Never defer" 1411 1542 msgstr "" 1412 1543 1413 #: src/index.js:1 2991544 #: src/index.js:1445 1414 1545 msgid "Keep loading these render-blocking, but still allow delay. Use for scripts that must run during page load." 1415 1546 msgstr "" 1416 1547 1417 #: src/index.js:1 3001548 #: src/index.js:1446 1418 1549 msgid "e.g. jquery.min.js" 1419 1550 msgstr "" 1420 1551 1421 #: src/index.js:1 3041552 #: src/index.js:1450 1422 1553 msgid "Never delay" 1423 1554 msgstr "" 1424 1555 1425 #: src/index.js:1 3051556 #: src/index.js:1451 1426 1557 msgid "Run these before any interaction, but still allow defer. Use for sliders, above-the-fold, or consent scripts." 1427 1558 msgstr "" 1428 1559 1429 #: src/index.js:1 3061560 #: src/index.js:1452 1430 1561 msgid "e.g. slider, swiper" 1431 1562 msgstr "" 1432 1563 1433 #: src/index.js:1 3091564 #: src/index.js:1455 1434 1565 msgid "Never optimize" 1435 1566 msgstr "" 1436 1567 1437 #: src/index.js:1 3101568 #: src/index.js:1456 1438 1569 msgid "Skip both defer and delay — leave these scripts exactly as they are." 1439 1570 msgstr "" 1440 1571 1441 #: src/index.js:1 3111572 #: src/index.js:1457 1442 1573 msgid "e.g. recaptcha" 1443 1574 msgstr "" 1444 1575 1445 #: src/index.js:1 3161576 #: src/index.js:1462 1446 1577 msgid "CSS" 1447 1578 msgstr "" 1448 1579 1449 #: src/index.js:1 3171580 #: src/index.js:1463 1450 1581 msgid "Minify, combine, and remove render-blocking stylesheets." 1451 1582 msgstr "" 1452 1583 1453 #: src/index.js:1 3211584 #: src/index.js:1467 1454 1585 msgid "Combine CSS files" 1455 1586 msgstr "" 1456 1587 1457 #: src/index.js:1 3221588 #: src/index.js:1468 1458 1589 msgid "Merge stylesheets to reduce requests." 1459 1590 msgstr "" 1460 1591 1461 #: src/index.js:1 3241592 #: src/index.js:1470 1462 1593 msgid "Load CSS asynchronously" 1463 1594 msgstr "" 1464 1595 1465 #: src/index.js:1 3251596 #: src/index.js:1471 1466 1597 msgid "Removes render-blocking CSS. Paste your above-the-fold (critical) CSS below, or the page may briefly flash unstyled." 1467 1598 msgstr "" 1468 1599 1469 #: src/index.js:1 3321600 #: src/index.js:1478 1470 1601 msgid "No critical CSS provided. Async loading is on, so visitors may see a flash of unstyled content until you paste critical CSS below." 1471 1602 msgstr "" 1472 1603 1473 #: src/index.js:1 3361604 #: src/index.js:1482 1474 1605 msgid "Critical CSS (above-the-fold)" 1475 1606 msgstr "" 1476 1607 1477 #: src/index.js:1 3371608 #: src/index.js:1483 1478 1609 msgid "Paste the above-the-fold CSS to inline. Generate it with a free tool such as a critical-CSS generator. Leave empty to disable inlining." 1479 1610 msgstr "" 1480 1611 1481 #: src/index.js:1 3421612 #: src/index.js:1488 1482 1613 msgid "Remove unused CSS" 1483 1614 msgstr "" 1484 1615 1485 #: src/index.js:1 3431616 #: src/index.js:1489 1486 1617 msgid "Remove block-editor CSS on the front end" 1487 1618 msgstr "" 1488 1619 1489 #: src/index.js:1 3441620 #: src/index.js:1490 1490 1621 msgid "Dequeues WordPress block-library and global styles. Turn on only if your front end does NOT render Gutenberg blocks (e.g. a page-builder or classic theme). Big byte saving when it applies." 1491 1622 msgstr "" 1492 1623 1493 #: src/index.js:1 3461624 #: src/index.js:1492 1494 1625 msgid "Load WooCommerce CSS only on shop pages" 1495 1626 msgstr "" 1496 1627 1497 #: src/index.js:1 3471628 #: src/index.js:1493 1498 1629 msgid "Dequeues WooCommerce styles on non-shop pages (kept on cart, checkout, account, product and shop). Turn off if you show Woo products or widgets on other pages." 1499 1630 msgstr "" 1500 1631 1501 #: src/index.js:1 3491632 #: src/index.js:1495 1502 1633 msgid "Load Easy Digital Downloads CSS only on EDD pages" 1503 1634 msgstr "" 1504 1635 1505 #: src/index.js:1 3501636 #: src/index.js:1496 1506 1637 msgid "Dequeues EDD styles on non-EDD pages (kept on checkout, purchase-confirmation, purchase history and download archives). Turn off if you show download or purchase links elsewhere." 1507 1638 msgstr "" 1508 1639 1509 #: src/index.js:1 3521640 #: src/index.js:1498 1510 1641 msgid "Excluded CSS files (keywords)" 1511 1642 msgstr "" 1512 1643 1513 #: src/index.js:1 3601644 #: src/index.js:1506 1514 1645 msgid "Lazy-load, prevent layout shift, serve next-gen formats, and lighten media." 1515 1646 msgstr "" 1516 1647 1517 #: src/index.js:1 3661648 #: src/index.js:1512 1518 1649 msgid "This server has no GD or ImageMagick extension, so image compression and WebP/AVIF cannot run — every image will be skipped. Ask your host to enable \"php-gd\" (or \"php-imagick\"). See Tools → Server Requirements." 1519 1650 msgstr "" 1520 1651 1521 #: src/index.js:1 3701652 #: src/index.js:1516 1522 1653 msgid "Load off-screen images only when scrolled into view." 1523 1654 msgstr "" 1524 1655 1525 #: src/index.js:1 3721656 #: src/index.js:1518 1526 1657 msgid "Lazy-load iframes" 1527 1658 msgstr "" 1528 1659 1529 #: src/index.js:1 3741660 #: src/index.js:1520 1530 1661 msgid "Lazy-load CSS background images" 1531 1662 msgstr "" 1532 1663 1533 #: src/index.js:1 3751664 #: src/index.js:1521 1534 1665 msgid "Defer inline background-image elements (test with page builders)." 1535 1666 msgstr "" 1536 1667 1537 #: src/index.js:1 3771668 #: src/index.js:1523 1538 1669 msgid "Replace YouTube embeds with a preview" 1539 1670 msgstr "" 1540 1671 1541 #: src/index.js:1 3781672 #: src/index.js:1524 1542 1673 msgid "Load the video iframe only when the visitor clicks play." 1543 1674 msgstr "" 1544 1675 1545 #: src/index.js:1 3801676 #: src/index.js:1526 1546 1677 msgid "Add missing image dimensions" 1547 1678 msgstr "" 1548 1679 1549 #: src/index.js:1 3811680 #: src/index.js:1527 1550 1681 msgid "Set width/height to prevent layout shift (CLS)." 1551 1682 msgstr "" 1552 1683 1553 #: src/index.js:1 3831684 #: src/index.js:1529 1554 1685 msgid "Preload the LCP image" 1555 1686 msgstr "" 1556 1687 1557 #: src/index.js:1 3841688 #: src/index.js:1530 1558 1689 msgid "Preload the first/hero image with high priority." 1559 1690 msgstr "" 1560 1691 1561 #: src/index.js:1 3871692 #: src/index.js:1533 1562 1693 msgid "Keep first images eager (LCP)" 1563 1694 msgstr "" 1564 1695 1565 #: src/index.js:1 3881696 #: src/index.js:1534 1566 1697 msgid "Do not lazy-load the first N images, to protect LCP." 1567 1698 msgstr "" 1568 1699 1569 #: src/index.js:1 3931700 #: src/index.js:1539 1570 1701 msgid "Serve WebP via <picture>" 1571 1702 msgstr "" 1572 1703 1573 #: src/index.js:1 3941704 #: src/index.js:1540 1574 1705 msgid "Deliver WebP to supporting browsers, original as fallback." 1575 1706 msgstr "" 1576 1707 1577 #: src/index.js:1 3961708 #: src/index.js:1542 1578 1709 msgid "Generate WebP/AVIF on upload" 1579 1710 msgstr "" 1580 1711 1581 #: src/index.js:1 3981712 #: src/index.js:1544 1582 1713 msgid "WebP generation is not available on this server." 1583 1714 msgstr "" 1584 1715 1585 #: src/index.js:1 3991716 #: src/index.js:1545 1586 1717 msgid "Auto-create next-gen copies for newly uploaded images." 1587 1718 msgstr "" 1588 1719 1589 #: src/index.js:1 4021720 #: src/index.js:1548 1590 1721 msgid "Also generate/serve AVIF" 1591 1722 msgstr "" 1592 1723 1593 #: src/index.js:1 4031724 #: src/index.js:1549 1594 1725 msgid "Smaller than WebP; served first when supported." 1595 1726 msgstr "" 1596 1727 1597 #: src/index.js:1 4071728 #: src/index.js:1553 1598 1729 msgid "Compress & resize on upload" 1599 1730 msgstr "" 1600 1731 1601 #: src/index.js:1 4081732 #: src/index.js:1554 1602 1733 msgid "Recompress, cap dimensions, and strip metadata from new uploads." 1603 1734 msgstr "" 1604 1735 1605 #: src/index.js:1 4101736 #: src/index.js:1556 1606 1737 msgid "Convert opaque PNG to JPEG" 1607 1738 msgstr "" 1608 1739 1609 #: src/index.js:1 4111740 #: src/index.js:1557 1610 1741 msgid "Re-save non-transparent PNGs as much smaller JPEGs during bulk optimization." 1611 1742 msgstr "" 1612 1743 1613 #: src/index.js:1 4131744 #: src/index.js:1559 1614 1745 msgid "Keep a backup of originals" 1615 1746 msgstr "" 1616 1747 1617 #: src/index.js:1 4141748 #: src/index.js:1560 1618 1749 msgid "Copy originals to uploads/optimazz-backup before optimizing." 1619 1750 msgstr "" 1620 1751 1621 #: src/index.js:1 4161752 #: src/index.js:1562 1622 1753 msgid "Image quality" 1623 1754 msgstr "" 1624 1755 1625 #: src/index.js:1 4181756 #: src/index.js:1564 1626 1757 msgid "Used for compression and WebP/AVIF generation." 1627 1758 msgstr "" 1628 1759 1629 #: src/index.js:1 4191760 #: src/index.js:1565 1630 1761 msgid "Max image width (px)" 1631 1762 msgstr "" 1632 1763 1633 #: src/index.js:1 4241764 #: src/index.js:1570 1634 1765 msgid "WebP coverage" 1635 1766 msgstr "" 1636 1767 1637 #: src/index.js:1 4261768 #: src/index.js:1572 1638 1769 msgid "images converted" 1639 1770 msgstr "" 1640 1771 1641 #: src/index.js:1 4271642 #: src/index.js:1 4411772 #: src/index.js:1573 1773 #: src/index.js:1587 1643 1774 msgid "Loading…" 1644 1775 msgstr "" 1645 1776 1646 #: src/index.js:1 4321777 #: src/index.js:1578 1647 1778 msgid "Convert to WebP" 1648 1779 msgstr "" 1649 1780 1650 #: src/index.js:1 4381781 #: src/index.js:1584 1651 1782 msgid "Bulk compression" 1652 1783 msgstr "" 1653 1784 1654 #: src/index.js:1 4401785 #: src/index.js:1586 1655 1786 msgid "optimized" 1656 1787 msgstr "" 1657 1788 1658 #: src/index.js:1 4441789 #: src/index.js:1590 1659 1790 msgid "Compress existing images" 1660 1791 msgstr "" 1661 1792 1662 #: src/index.js:1 4521793 #: src/index.js:1598 1663 1794 msgid "Warm the cache automatically so visitors never hit a cold page." 1664 1795 msgstr "" 1665 1796 1666 #: src/index.js:1 4531797 #: src/index.js:1600 1667 1798 msgid "Activate preloading" 1668 1799 msgstr "" 1669 1800 1670 #: src/index.js:1 4541801 #: src/index.js:1601 1671 1802 msgid "Warms the cache on a schedule and right after every full purge (for example after changing a setting). Warming follows the sitemap — home, pages, posts, custom post types, then the busiest category and tag archives — most-important first, for both mobile and desktop." 1672 1803 msgstr "" 1673 1804 1674 #: src/index.js:1457 1805 #: src/index.js:1604 1806 #: src/index.js:2117 1675 1807 msgid "Automatic preload" 1676 1808 msgstr "" 1677 1809 1678 #: src/index.js:1 4581810 #: src/index.js:1605 1679 1811 msgid "How often to re-warm the whole site. Choose “Every hour” if your content changes often or you use a short cache lifespan." 1680 1812 msgstr "" 1681 1813 1682 #: src/index.js:1470 1814 #. translators: %s: preload frequency, e.g. "twice a day". 1815 #: src/index.js:1618 1816 #, js-format 1817 msgid "Automatic preload runs %s for this site (set for the whole network in Network Admin → Optimazz)." 1818 msgstr "" 1819 1820 #: src/index.js:1621 1821 msgid "Automatic preload is off for the network. You can still preload this site now." 1822 msgstr "" 1823 1824 #: src/index.js:1626 1683 1825 msgid "WP-Cron is disabled on this site (DISABLE_WP_CRON). Scheduled preloads only run if a server cron job calls wp-cron.php — ask your host to set one up (e.g. every 5 minutes)." 1684 1826 msgstr "" 1685 1827 1686 #: src/index.js:1 4751828 #: src/index.js:1631 1687 1829 msgid "Preload now" 1688 1830 msgstr "" 1689 1831 1690 #: src/index.js:1 4831832 #: src/index.js:1639 1691 1833 msgid "Fine-tune which requests are cached." 1692 1834 msgstr "" 1693 1835 1694 #: src/index.js:1 4841836 #: src/index.js:1640 1695 1837 msgid "Never cache these URLs (contains)" 1696 1838 msgstr "" 1697 1839 1698 #: src/index.js:1 4861840 #: src/index.js:1642 1699 1841 msgid "Never cache when these cookies are set" 1700 1842 msgstr "" 1701 1843 1702 #: src/index.js:1 4881844 #: src/index.js:1644 1703 1845 msgid "Never cache for these user agents" 1704 1846 msgstr "" 1705 1847 1706 #: src/index.js:1 4901848 #: src/index.js:1646 1707 1849 msgid "Ignore these query arguments" 1708 1850 msgstr "" 1709 1851 1710 #: src/index.js:1 4981852 #: src/index.js:1654 1711 1853 msgid "Safe Mode" 1712 1854 msgstr "" 1713 1855 1714 #: src/index.js:1 4991856 #: src/index.js:1655 1715 1857 msgid "If a theme or page builder looks broken after optimizing, turn this on. It pauses the riskiest optimizations — combine CSS, async CSS, remove block CSS and JavaScript delay — while keeping caching, minify, defer and lazy-load. Your saved settings are not changed; they simply pause until you turn Safe Mode off." 1716 1858 msgstr "" 1717 1859 1718 #: src/index.js:1 5021860 #: src/index.js:1658 1719 1861 msgid "Pause risky optimizations (Safe Mode)" 1720 1862 msgstr "" 1721 1863 1722 #: src/index.js:1 5091864 #: src/index.js:1665 1723 1865 msgid "Safe Mode is ON — combine/async CSS, remove-block-CSS and JavaScript delay are paused site-wide. Turn it off to resume full optimization." 1724 1866 msgstr "" 1725 1867 1726 #: src/index.js:1 5141868 #: src/index.js:1670 1727 1869 msgid "Undo last change" 1728 1870 msgstr "" 1729 1871 1730 #: src/index.js:1 5181872 #: src/index.js:1674 1731 1873 msgid "Restores the settings from just before your last save, reset or Safe Mode toggle." 1732 1874 msgstr "" 1733 1875 1734 #: src/index.js:1 5191876 #: src/index.js:1675 1735 1877 msgid "Nothing to undo yet — this becomes available after your next change." 1736 1878 msgstr "" 1737 1879 1738 #: src/index.js:1 5231880 #: src/index.js:1679 1739 1881 msgid "Server Requirements" 1740 1882 msgstr "" 1741 1883 1742 #: src/index.js:1 5241884 #: src/index.js:1680 1743 1885 msgid "What your host supports. Warnings here explain why a feature (like WebP/AVIF) may be unavailable or slow." 1744 1886 msgstr "" 1745 1887 1746 #: src/index.js:1 5251888 #: src/index.js:1681 1747 1889 msgid "Checking your server…" 1748 1890 msgstr "" 1749 1891 1750 #: src/index.js:1 5261892 #: src/index.js:1682 1751 1893 msgid "Could not read the server report." 1752 1894 msgstr "" 1753 1895 1754 #: src/index.js:1 5421896 #: src/index.js:1698 1755 1897 msgid "Re-check" 1756 1898 msgstr "" 1757 1899 1758 #: src/index.js:1 5461900 #: src/index.js:1702 1759 1901 msgid "Import / Export Settings" 1760 1902 msgstr "" 1761 1903 1762 #: src/index.js:1 5471904 #: src/index.js:1703 1763 1905 msgid "Back up your configuration or copy it to another site." 1764 1906 msgstr "" 1765 1907 1766 #: src/index.js:1 5501908 #: src/index.js:1706 1767 1909 msgid "Export settings" 1768 1910 msgstr "" 1769 1911 1770 #: src/index.js:1 5531912 #: src/index.js:1709 1771 1913 msgid "Import settings" 1772 1914 msgstr "" 1773 1915 1774 #: src/index.js:1 5581916 #: src/index.js:1714 1775 1917 msgid "Reset" 1776 1918 msgstr "" 1777 1919 1778 #: src/index.js:1 5591920 #: src/index.js:1715 1779 1921 msgid "Restore every module to its default configuration. This cannot be undone." 1780 1922 msgstr "" 1781 1923 1782 #: src/index.js:1 5621924 #: src/index.js:1718 1783 1925 msgid "Reset all settings to defaults" 1784 1926 msgstr "" 1785 1927 1786 #: src/index.js:1 5711928 #: src/index.js:1727 1787 1929 msgid "Swap, preconnect, and preload fonts to remove render-blocking requests." 1788 1930 msgstr "" 1789 1931 1790 #: src/index.js:1 5741932 #: src/index.js:1730 1791 1933 msgid "Font loading (font-display)" 1792 1934 msgstr "" 1793 1935 1794 #: src/index.js:1 5751936 #: src/index.js:1731 1795 1937 msgid "Optional = no layout shift (best for CLS): the web font is skipped on the first uncached load and used once cached. Swap = text stays visible but the web font swaps in and can shift layout (higher CLS). Block = brief invisible text, then the web font." 1796 1938 msgstr "" 1797 1939 1798 #: src/index.js:1 5781940 #: src/index.js:1734 1799 1941 msgid "Optional — no layout shift (recommended for CLS)" 1800 1942 msgstr "" 1801 1943 1802 #: src/index.js:1 5791944 #: src/index.js:1735 1803 1945 msgid "Swap — always show text (may shift layout)" 1804 1946 msgstr "" 1805 1947 1806 #: src/index.js:1 5801948 #: src/index.js:1736 1807 1949 msgid "Block — brief hidden text, then web font" 1808 1950 msgstr "" 1809 1951 1810 #: src/index.js:1 5841952 #: src/index.js:1740 1811 1953 msgid "Combine Google Fonts into one request" 1812 1954 msgstr "" 1813 1955 1814 #: src/index.js:1 5851956 #: src/index.js:1741 1815 1957 msgid "Page builders often load each Google Font family separately. This merges them into a single request." 1816 1958 msgstr "" 1817 1959 1818 #: src/index.js:1 5891960 #: src/index.js:1745 1819 1961 msgid "Load only the font weights your pages use" 1820 1962 msgstr "" 1821 1963 1822 #: src/index.js:1 5901964 #: src/index.js:1746 1823 1965 msgid "Drops unused weights (e.g. 100, 200, 900) and italics your pages never use — often cutting font CSS by more than half. Weights 400 and 700 are always kept. Turn off if some text looks thinner or bolder than intended." 1824 1966 msgstr "" 1825 1967 1826 #: src/index.js:1594 1968 #: src/index.js:1751 1969 msgid "Load Google Fonts without blocking the page" 1970 msgstr "" 1971 1972 #: src/index.js:1752 1973 msgid "The page appears right away in a fallback font and switches to your Google Font as soon as it arrives, instead of waiting for Google first. A big win for the first paint on mobile. Applies when font display is Swap or Fallback; with Optional the stylesheet keeps loading first so text never shifts." 1974 msgstr "" 1975 1976 #: src/index.js:1756 1827 1977 msgid "Preload these font files (URLs)" 1828 1978 msgstr "" 1829 1979 1830 1980 #. translators: %d: number of items found in the database. 1831 #: src/index.js:1 6051981 #: src/index.js:1767 1832 1982 #, js-format 1833 1983 msgid "%d item in your database." … … 1836 1986 msgstr[1] "" 1837 1987 1838 #: src/index.js:1 6101988 #: src/index.js:1772 1839 1989 msgid "Database Optimization" 1840 1990 msgstr "" 1841 1991 1842 #: src/index.js:1 6111992 #: src/index.js:1773 1843 1993 msgid "Reduce bloat and reclaim space. Once a cleanup runs it cannot be undone, so back up your database first." 1844 1994 msgstr "" 1845 1995 1846 #: src/index.js:1 6271996 #: src/index.js:1789 1847 1997 msgid "Set it and forget it" 1848 1998 msgstr "" 1849 1999 1850 #: src/index.js:1 6282000 #: src/index.js:1790 1851 2001 msgid "Let Optimazz repeat the cleanups you picked above on a schedule, so your database stays lean without you lifting a finger." 1852 2002 msgstr "" 1853 2003 1854 #: src/index.js:1 6292004 #: src/index.js:1791 1855 2005 msgid "Schedule automatic cleanup" 1856 2006 msgstr "" 1857 2007 1858 #: src/index.js:1 6302008 #: src/index.js:1792 1859 2009 msgid "Uses WP-Cron to repeat the cleanups you enabled above." 1860 2010 msgstr "" 1861 2011 1862 #: src/index.js:1 6332012 #: src/index.js:1795 1863 2013 msgid "Cleanup frequency" 1864 2014 msgstr "" 1865 2015 1866 #: src/index.js:1 6372016 #: src/index.js:1799 1867 2017 msgid "Daily" 1868 2018 msgstr "" 1869 2019 1870 #: src/index.js:1 6382020 #: src/index.js:1800 1871 2021 msgid "Weekly" 1872 2022 msgstr "" 1873 2023 1874 #: src/index.js:1 6392024 #: src/index.js:1801 1875 2025 msgid "Monthly" 1876 2026 msgstr "" 1877 2027 1878 #: src/index.js:1 6462028 #: src/index.js:1808 1879 2029 msgid "Back up your database before you run a cleanup — a database optimization cannot be undone." 1880 2030 msgstr "" 1881 2031 1882 #: src/index.js:1 6512032 #: src/index.js:1813 1883 2033 msgid "Optimize now" 1884 2034 msgstr "" 1885 2035 1886 #: src/index.js:1 6542036 #: src/index.js:1816 1887 2037 msgid "Refresh counts" 1888 2038 msgstr "" 1889 2039 1890 #: src/index.js:1 6612040 #: src/index.js:1823 1891 2041 msgid "Heartbeat Control" 1892 2042 msgstr "" 1893 2043 1894 #: src/index.js:1 6622044 #: src/index.js:1824 1895 2045 msgid "Reduce or disable the WordPress Heartbeat API to lower server load." 1896 2046 msgstr "" 1897 2047 1898 #: src/index.js:1 6632048 #: src/index.js:1825 1899 2049 msgid "Enable Heartbeat control" 1900 2050 msgstr "" 1901 2051 1902 #: src/index.js:1 6672052 #: src/index.js:1829 1903 2053 msgid "Behavior in admin dashboard" 1904 2054 msgstr "" 1905 2055 1906 #: src/index.js:1 6692056 #: src/index.js:1831 1907 2057 msgid "Behavior in post editor" 1908 2058 msgstr "" 1909 2059 1910 #: src/index.js:1 6712060 #: src/index.js:1833 1911 2061 msgid "Behavior on front-end" 1912 2062 msgstr "" 1913 2063 1914 #: src/index.js:1 6802064 #: src/index.js:1842 1915 2065 msgid "Strip unnecessary WordPress front-end weight." 1916 2066 msgstr "" 1917 2067 1918 #: src/index.js:1 6822068 #: src/index.js:1844 1919 2069 msgid "Disable emojis" 1920 2070 msgstr "" 1921 2071 1922 #: src/index.js:1 6832072 #: src/index.js:1845 1923 2073 msgid "Remove the emoji detection script and styles." 1924 2074 msgstr "" 1925 2075 1926 #: src/index.js:1 6852076 #: src/index.js:1847 1927 2077 msgid "Disable embeds (oEmbed)" 1928 2078 msgstr "" 1929 2079 1930 #: src/index.js:1 6872080 #: src/index.js:1849 1931 2081 msgid "Disable dashicons on front-end" 1932 2082 msgstr "" 1933 2083 1934 #: src/index.js:1 6882084 #: src/index.js:1850 1935 2085 msgid "Only for anonymous visitors." 1936 2086 msgstr "" 1937 2087 1938 #: src/index.js:1 6902088 #: src/index.js:1852 1939 2089 msgid "Remove query strings from assets" 1940 2090 msgstr "" 1941 2091 1942 #: src/index.js:1 6912092 #: src/index.js:1853 1943 2093 msgid "Strip ?ver= for better proxy caching." 1944 2094 msgstr "" 1945 2095 1946 #: src/index.js:1 6932096 #: src/index.js:1855 1947 2097 msgid "Disable XML-RPC" 1948 2098 msgstr "" 1949 2099 1950 #: src/index.js:1 6952100 #: src/index.js:1857 1951 2101 msgid "Disable self-pingbacks" 1952 2102 msgstr "" 1953 2103 1954 #: src/index.js:1 6972104 #: src/index.js:1859 1955 2105 msgid "Remove head meta (generator, RSD, shortlink)" 1956 2106 msgstr "" 1957 2107 1958 #: src/index.js:1 6992108 #: src/index.js:1861 1959 2109 msgid "DNS-prefetch domains" 1960 2110 msgstr "" 1961 2111 1962 #: src/index.js:1 7012112 #: src/index.js:1863 1963 2113 msgid "Preconnect origins" 1964 2114 msgstr "" 1965 2115 1966 #: src/index.js:1 7072116 #: src/index.js:1869 1967 2117 msgid "Content Delivery Network" 1968 2118 msgstr "" 1969 2119 1970 #: src/index.js:1 7082120 #: src/index.js:1870 1971 2121 msgid "Rewrite static asset URLs to your CDN hostname." 1972 2122 msgstr "" 1973 2123 1974 #: src/index.js:1 7102124 #: src/index.js:1872 1975 2125 msgid "CDN URL (CNAME)" 1976 2126 msgstr "" 1977 2127 1978 #: src/index.js:1 7132128 #: src/index.js:1875 1979 2129 msgid "CSS, JS, image, and font URLs will be served from here." 1980 2130 msgstr "" 1981 2131 1982 #: src/index.js:1 7142132 #: src/index.js:1876 1983 2133 msgid "Exclude paths containing" 1984 2134 msgstr "" 1985 2135 1986 #: src/index.js:1781 1987 msgid "You have unsaved changes" 1988 msgstr "" 1989 1990 #: src/index.js:1781 1991 msgid "All changes saved" 1992 msgstr "" 1993 1994 #: src/index.js:1784 1995 msgid "Save changes" 1996 msgstr "" 2136 #: src/index.js:1952 2137 msgid "Preloading cache…" 2138 msgstr "" 2139 2140 #: src/index.js:2012 2141 #: src/index.js:2025 2142 msgid "Something went wrong." 2143 msgstr "" 2144 2145 #: src/index.js:2059 2146 msgid "Network settings saved." 2147 msgstr "" 2148 2149 #: src/index.js:2075 2150 msgid "Caching for every site" 2151 msgstr "" 2152 2153 #: src/index.js:2076 2154 msgid "Sites" 2155 msgstr "" 2156 2157 #: src/index.js:2076 2158 msgid "Cache per site" 2159 msgstr "" 2160 2161 #: src/index.js:2086 2162 msgid "Optimazz is not network-activated" 2163 msgstr "" 2164 2165 #: src/index.js:2087 2166 msgid "Only the sites where Optimazz is activated are cached. Network-activate it on Network Admin → Plugins to cache every site." 2167 msgstr "" 2168 2169 #: src/index.js:2092 2170 msgid "Serve ready-made HTML for every site instead of running PHP on each request. Each site keeps its own cache and its own exclusions." 2171 msgstr "" 2172 2173 #: src/index.js:2100 2174 msgid "Add Expires / Cache-Control rules to the shared .htaccess so repeat visits load CSS, JS, images and fonts from the browser cache. Apache and LiteSpeed only." 2175 msgstr "" 2176 2177 #: src/index.js:2103 2178 msgid "How long a cached page is kept. Pages are always purged as soon as their content changes." 2179 msgstr "" 2180 2181 #: src/index.js:2113 2182 msgid "Preload every site automatically" 2183 msgstr "" 2184 2185 #: src/index.js:2114 2186 msgid "Each site warms its own pages on a schedule and right after its cache is cleared." 2187 msgstr "" 2188 2189 #. translators: %s: name of another caching plugin. 2190 #: src/index.js:2135 2191 #, js-format 2192 msgid "%s controls page caching (advanced-cache.php)" 2193 msgstr "" 2194 2195 #: src/index.js:2138 2196 msgid "Optimazz cannot cache while another plugin owns the cache file. Deactivate that plugin, or let Optimazz take over." 2197 msgstr "" 2198 2199 #: src/index.js:2139 2200 msgid "Optimazz now handles page caching." 2201 msgstr "" 2202 2203 #: src/index.js:2146 2204 msgid "Cache engine installed (advanced-cache.php)" 2205 msgstr "" 2206 2207 #: src/index.js:2147 2208 msgid "WP_CACHE is on in wp-config.php" 2209 msgstr "" 2210 2211 #: src/index.js:2148 2212 msgid "wp-config.php is not writable — add define( 'WP_CACHE', true ); yourself" 2213 msgstr "" 2214 2215 #: src/index.js:2149 2216 msgid "The wp-content folder is not writable" 2217 msgstr "" 2218 2219 #: src/index.js:2167 2220 msgid "All sites" 2221 msgstr "" 2222 2223 #. translators: 1: sites with Optimazz, 2: all sites. 2224 #: src/index.js:2170 2225 #, js-format 2226 msgid "Optimazz runs on %1$d of %2$d sites. Each site has its own cache." 2227 msgstr "" 2228 2229 #: src/index.js:2172 2230 msgid "Page caching is off for the network — turn it on under Page Cache to purge or preload sites." 2231 msgstr "" 2232 2233 #: src/index.js:2175 2234 msgid "Every site’s cache was purged." 2235 msgstr "" 2236 2237 #: src/index.js:2176 2238 msgid "Purge all sites" 2239 msgstr "" 2240 2241 #: src/index.js:2194 2242 msgid "Site cache purged." 2243 msgstr "" 2244 2245 #: src/index.js:2215 2246 msgid "Optimazz is not active on this site, so its pages are not cached." 2247 msgstr "" 2248 2249 #: src/index.js:2225 2250 msgid "Network" 2251 msgstr "" -
optimazz/tags/1.1.0/optimazz.php
r3723128 r3743505 4 4 * Plugin URI: https://wordpress.org/plugins/optimazz/ 5 5 * Description: All-in-one WordPress performance optimization — page caching, JS delay, and CSS optimization. 6 * Version: 1. 0.246 * Version: 1.1.0 7 7 * Requires at least: 6.0 8 8 * Requires PHP: 7.4 … … 26 26 } 27 27 28 define( 'OPTIMAZZ_VERSION', '1. 0.24' );28 define( 'OPTIMAZZ_VERSION', '1.1.0' ); 29 29 define( 'OPTIMAZZ_FILE', __FILE__ ); 30 30 define( 'OPTIMAZZ_DIR', plugin_dir_path( __FILE__ ) ); -
optimazz/tags/1.1.0/optimazz.pot
r3723128 r3743505 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: Optimazz – Website Optimization and Speedup 1. 0.24\n"5 "Project-Id-Version: Optimazz – Website Optimization and Speedup 1.1.0\n" 6 6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/optimazz\n" 7 7 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" … … 10 10 "Content-Type: text/plain; charset=UTF-8\n" 11 11 "Content-Transfer-Encoding: 8bit\n" 12 "POT-Creation-Date: 2026-10- 01T12:01:49+00:00\n"12 "POT-Creation-Date: 2026-10-11T19:05:58+00:00\n" 13 13 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 14 "X-Generator: WP-CLI 2.12.0\n" … … 40 40 msgstr "" 41 41 42 #: includes/Admin/Admin.php:149 43 #: includes/Admin/Admin.php:150 42 #: includes/Admin/Admin.php:155 43 #: includes/Admin/Admin.php:156 44 #: includes/Admin/Admin.php:290 45 #: includes/Admin/Admin.php:291 44 46 msgid "Optimazz" 45 47 msgstr "" 46 48 47 #: includes/Admin/Admin.php:1 6649 #: includes/Admin/Admin.php:172 48 50 #: src/index.js:36 49 51 msgid "Dashboard" 50 52 msgstr "" 51 53 52 #: includes/Admin/Admin.php:1 6754 #: includes/Admin/Admin.php:173 53 55 #: includes/Modules/Cache/CacheModule.php:24 54 56 #: src/index.js:37 55 #: src/index.js:1171 57 #: src/index.js:1317 58 #: src/index.js:2075 59 #: src/index.js:2091 56 60 msgid "Page Cache" 57 61 msgstr "" 58 62 59 #: includes/Admin/Admin.php:1 6863 #: includes/Admin/Admin.php:174 60 64 #: src/index.js:38 61 #: src/index.js:1 15065 #: src/index.js:1296 62 66 msgid "File Optimization" 63 67 msgstr "" 64 68 65 #: includes/Admin/Admin.php:1 6969 #: includes/Admin/Admin.php:175 66 70 #: src/index.js:39 67 71 msgid "Media" 68 72 msgstr "" 69 73 70 #: includes/Admin/Admin.php:17 074 #: includes/Admin/Admin.php:176 71 75 #: includes/Modules/Database/DatabaseModule.php:26 72 76 #: src/index.js:43 … … 74 78 msgstr "" 75 79 76 #: includes/Admin/Admin.php:17 177 #: includes/Admin/Admin.php:2 2978 #: includes/Admin/Admin.php:23 380 #: includes/Admin/Admin.php:177 81 #: includes/Admin/Admin.php:235 82 #: includes/Admin/Admin.php:239 79 83 #: src/index.js:47 80 84 msgid "Tools" 81 85 msgstr "" 82 86 83 #: includes/Admin/Admin.php:19 387 #: includes/Admin/Admin.php:199 84 88 #: includes/Admin/AdminBar.php:52 89 #: src/index.js:2198 85 90 msgid "Settings" 86 91 msgstr "" 87 92 88 #: includes/Admin/Admin.php:2 2493 #: includes/Admin/Admin.php:230 89 94 msgid "Optimazz Safe Mode is on." 90 95 msgstr "" 91 96 92 #: includes/Admin/Admin.php:2 2597 #: includes/Admin/Admin.php:231 93 98 msgid "Some pages were loading without styling — their optimized CSS could not be found, usually because a server or CDN cache was serving old pages. Optimazz paused advanced CSS/JS optimization to keep your site working." 94 99 msgstr "" 95 100 96 #: includes/Admin/Admin.php:2 27101 #: includes/Admin/Admin.php:233 97 102 msgid "It broke again shortly after full optimization was restored, so Safe Mode will stay on until you switch it off in" 98 103 msgstr "" 99 104 100 #: includes/Admin/Admin.php:2 28105 #: includes/Admin/Admin.php:234 101 106 msgid "It re-checks automatically and restores full optimization as soon as pages load correctly — clearing your server/CDN cache speeds that up. You can also manage it in" 102 107 msgstr "" 103 108 104 #: includes/Admin/Admin.php:23 2109 #: includes/Admin/Admin.php:238 105 110 msgid "Your pages are loading correctly again — you can switch Safe Mode off in" 106 111 msgstr "" 107 112 108 #: includes/Admin/Admin.php:2 36113 #: includes/Admin/Admin.php:242 109 114 msgid "Pages are loading correctly again, so Safe Mode was switched off automatically and full optimization is back on." 110 115 msgstr "" 111 116 112 #: includes/Admin/Admin.php:25 0117 #: includes/Admin/Admin.php:256 113 118 msgid "wp-config.php is not writable, so page caching could not be enabled. Add" 114 119 msgstr "" 115 120 116 #: includes/Admin/Admin.php:25 2121 #: includes/Admin/Admin.php:258 117 122 msgid "manually, or make wp-config.php writable." 118 123 msgstr "" 119 124 120 #: includes/Admin/Admin.php:2 59125 #: includes/Admin/Admin.php:265 121 126 msgid "Optimazz cache purged." 122 127 msgstr "" 123 128 124 #: includes/Admin/Admin.php:26 0129 #: includes/Admin/Admin.php:266 125 130 msgid "Optimazz cache preload started in the background." 126 131 msgstr "" 127 132 128 #: includes/Admin/Admin.php:26 1133 #: includes/Admin/Admin.php:267 129 134 msgid "Optimazz cache cleared; preload started in the background." 130 135 msgstr "" 131 136 132 #: includes/Admin/AdminBar.php:5 3137 #: includes/Admin/AdminBar.php:57 133 138 msgid "Clear & Preload Cache" 134 139 msgstr "" 135 140 136 #: includes/Admin/AdminBar.php:5 4137 #: src/index.js:1 451141 #: includes/Admin/AdminBar.php:58 142 #: src/index.js:1597 138 143 msgid "Preload Cache" 139 144 msgstr "" 140 145 141 #: includes/Admin/AdminBar.php:5 5146 #: includes/Admin/AdminBar.php:59 142 147 msgid "Purge Cache" 143 148 msgstr "" 144 149 145 #: includes/Admin/AdminBar.php:7 3150 #: includes/Admin/AdminBar.php:77 146 151 msgid "Documentation" 147 152 msgstr "" 148 153 149 #: includes/Admin/AdminBar.php: 98150 #: includes/Admin/CacheConflictNotice.php:12 4151 #: includes/Admin/CacheConflictNotice.php:15 4154 #: includes/Admin/AdminBar.php:102 155 #: includes/Admin/CacheConflictNotice.php:126 156 #: includes/Admin/CacheConflictNotice.php:156 152 157 msgid "Not allowed." 153 158 msgstr "" … … 162 167 163 168 #: includes/Admin/CacheConflictNotice.php:88 164 #: includes/Modules/Cache/Preloader.php:714 169 #: includes/Modules/Cache/Preloader.php:740 170 #: src/index.js:2136 165 171 msgid "Another plugin" 166 172 msgstr "" … … 180 186 #: includes/Admin/CacheConflictNotice.php:108 181 187 #: includes/Admin/CacheConflictNotice.php:110 188 #: src/index.js:2140 182 189 msgid "Use Optimazz for caching" 183 190 msgstr "" … … 187 194 msgstr "" 188 195 189 #: includes/Admin/RestController.php:467 196 #: includes/Admin/NetworkRestController.php:115 197 #: includes/Admin/NetworkRestController.php:134 198 msgid "Page caching is off for this site." 199 msgstr "" 200 201 #: includes/Admin/NetworkRestController.php:153 202 msgid "Optimazz is not active on any site yet. Network-activate it (or activate it on a site) first." 203 msgstr "" 204 205 #: includes/Admin/RestController.php:385 206 #: includes/Admin/RestController.php:588 207 #: includes/Admin/RestController.php:614 208 msgid "Page caching is not available on multisite networks." 209 msgstr "" 210 211 #: includes/Admin/RestController.php:479 190 212 msgid "PHP version" 191 213 msgstr "" 192 214 193 #: includes/Admin/RestController.php:4 70215 #: includes/Admin/RestController.php:482 194 216 msgid "Optimazz requires PHP 7.4 or newer." 195 217 msgstr "" 196 218 197 #: includes/Admin/RestController.php:4 73219 #: includes/Admin/RestController.php:485 198 220 msgid "WordPress version" 199 221 msgstr "" 200 222 201 #: includes/Admin/RestController.php:4 79223 #: includes/Admin/RestController.php:491 202 224 msgid "GD (image library)" 203 225 msgstr "" 204 226 205 #: includes/Admin/RestController.php:4 80206 #: includes/Admin/RestController.php: 489207 #: includes/Admin/RestController.php:5 25227 #: includes/Admin/RestController.php:492 228 #: includes/Admin/RestController.php:501 229 #: includes/Admin/RestController.php:537 208 230 msgid "Enabled" 209 231 msgstr "" 210 232 211 #: includes/Admin/RestController.php:4 80212 #: includes/Admin/RestController.php: 489233 #: includes/Admin/RestController.php:492 234 #: includes/Admin/RestController.php:501 213 235 msgid "Not installed (optional)" 214 236 msgstr "" 215 237 216 #: includes/Admin/RestController.php:4 84238 #: includes/Admin/RestController.php:496 217 239 msgid "GD is enabled but without WebP support — ask your host to enable WebP in GD." 218 240 msgstr "" 219 241 220 #: includes/Admin/RestController.php:4 85242 #: includes/Admin/RestController.php:497 221 243 msgid "The PHP GD extension is not enabled. GD is the most common engine for image compression and WebP. Ask your host to enable the \"php-gd\" extension." 222 244 msgstr "" 223 245 224 #: includes/Admin/RestController.php: 488246 #: includes/Admin/RestController.php:500 225 247 msgid "ImageMagick (Imagick)" 226 248 msgstr "" 227 249 228 #: includes/Admin/RestController.php: 489250 #: includes/Admin/RestController.php:501 229 251 msgid "Not enabled" 230 252 msgstr "" 231 253 232 #: includes/Admin/RestController.php: 496254 #: includes/Admin/RestController.php:508 233 255 msgid "Optional — GD is already handling images. Imagick can add AVIF and higher-quality output if your host enables the \"php-imagick\" extension." 234 256 msgstr "" 235 257 236 #: includes/Admin/RestController.php: 497258 #: includes/Admin/RestController.php:509 237 259 msgid "The PHP Imagick extension is not enabled either. With neither GD nor Imagick, image compression and WebP/AVIF cannot run. Ask your host to enable \"php-gd\" or \"php-imagick\"." 238 260 msgstr "" 239 261 240 #: includes/Admin/RestController.php:5 00262 #: includes/Admin/RestController.php:512 241 263 msgid "WebP support" 242 264 msgstr "" 243 265 244 #: includes/Admin/RestController.php:5 01245 #: includes/Admin/RestController.php:5 07266 #: includes/Admin/RestController.php:513 267 #: includes/Admin/RestController.php:519 246 268 msgid "Available" 247 269 msgstr "" 248 270 249 #: includes/Admin/RestController.php:5 01250 #: includes/Admin/RestController.php:5 07271 #: includes/Admin/RestController.php:513 272 #: includes/Admin/RestController.php:519 251 273 msgid "Not available" 252 274 msgstr "" 253 275 254 #: includes/Admin/RestController.php:5 03276 #: includes/Admin/RestController.php:515 255 277 msgid "This server cannot generate WebP images. Update GD/Imagick or ask your host to enable WebP." 256 278 msgstr "" 257 279 258 #: includes/Admin/RestController.php:5 06280 #: includes/Admin/RestController.php:518 259 281 msgid "AVIF support" 260 282 msgstr "" 261 283 262 #: includes/Admin/RestController.php:5 09284 #: includes/Admin/RestController.php:521 263 285 msgid "This server cannot generate AVIF images (needs PHP 8.1+ GD with AVIF, or ImageMagick with the AVIF delegate). WebP still works." 264 286 msgstr "" 265 287 266 #: includes/Admin/RestController.php:5 12288 #: includes/Admin/RestController.php:524 267 289 msgid "PHP memory limit" 268 290 msgstr "" 269 291 270 #: includes/Admin/RestController.php:5 15292 #: includes/Admin/RestController.php:527 271 293 msgid "Image optimization works best with 256 MB or more. On lower limits Optimazz processes fewer images per request to stay safe." 272 294 msgstr "" 273 295 274 #: includes/Admin/RestController.php:5 18296 #: includes/Admin/RestController.php:530 275 297 msgid "Max execution time" 276 298 msgstr "" 277 299 278 #: includes/Admin/RestController.php:5 19300 #: includes/Admin/RestController.php:531 279 301 msgid "Unlimited" 280 302 msgstr "" 281 303 282 #: includes/Admin/RestController.php:5 21304 #: includes/Admin/RestController.php:533 283 305 msgid "Under 30 seconds can interrupt bulk AVIF conversion of large images." 284 306 msgstr "" 285 307 286 #: includes/Admin/RestController.php:5 24308 #: includes/Admin/RestController.php:536 287 309 msgid "WP-Cron" 288 310 msgstr "" 289 311 290 #: includes/Admin/RestController.php:5 25312 #: includes/Admin/RestController.php:537 291 313 msgid "Disabled" 292 314 msgstr "" 293 315 294 #: includes/Admin/RestController.php:5 27316 #: includes/Admin/RestController.php:539 295 317 msgid "WP-Cron is disabled, so scheduled cache preloading relies on a real system cron job being configured." 296 318 msgstr "" 297 319 298 #: includes/Admin/RestController.php:5 41320 #: includes/Admin/RestController.php:553 299 321 msgid "GD and Imagick are" 300 322 msgstr "" 301 323 302 #: includes/Admin/RestController.php:5 42324 #: includes/Admin/RestController.php:554 303 325 msgid "GD is" 304 326 msgstr "" 305 327 306 #: includes/Admin/RestController.php:5 42328 #: includes/Admin/RestController.php:554 307 329 msgid "Imagick is" 308 330 msgstr "" 309 331 310 #: includes/Admin/RestController.php:5 44332 #: includes/Admin/RestController.php:556 311 333 msgid "PHP that runs this site" 312 334 msgstr "" 313 335 314 336 #. translators: 1: which engine is missing, 2: php.ini path, 3: PHP version 315 #: includes/Admin/RestController.php:5 50337 #: includes/Admin/RestController.php:562 316 338 #, php-format 317 339 msgid "This is the exact PHP handling your site — it loads its settings from %2$s. If you already enabled an image library in your hosting panel but %1$s still shown as not enabled, you almost certainly enabled it for a different PHP version or handler. It must be enabled for THIS PHP (%3$s). After enabling, restart PHP (or PHP-FPM) and click Re-check." … … 319 341 320 342 #. translators: 1: which engine is missing, 2: PHP version 321 #: includes/Admin/RestController.php:5 57343 #: includes/Admin/RestController.php:569 322 344 #, php-format 323 345 msgid "This is the exact PHP handling your site. If you already enabled an image library in your hosting panel but %1$s still shown as not enabled, you enabled it for a different PHP version. It must be enabled for THIS PHP (%2$s). After enabling, restart PHP and click Re-check." 324 346 msgstr "" 325 347 326 #: includes/Admin/RestController.php:7 07348 #: includes/Admin/RestController.php:725 327 349 msgid "Image processing is unavailable: this server has no GD or ImageMagick extension. Ask your host to enable \"php-gd\" (or \"php-imagick\"), then try again. See Tools → Server Requirements." 328 350 msgstr "" … … 336 358 msgstr "" 337 359 338 #: includes/Core/Recommendations.php:6 1339 #: src/index.js: 971360 #: includes/Core/Recommendations.php:63 361 #: src/index.js:1117 340 362 msgid "Enable Page Cache" 341 363 msgstr "" 342 364 343 #: includes/Core/Recommendations.php:6 2365 #: includes/Core/Recommendations.php:64 344 366 msgid "Serve static HTML instead of running PHP on every request — the single biggest speed win." 345 367 msgstr "" 346 368 347 #: includes/Core/Recommendations.php:6 7369 #: includes/Core/Recommendations.php:69 348 370 msgid "Turn on cache preloading" 349 371 msgstr "" 350 372 351 #: includes/Core/Recommendations.php: 68373 #: includes/Core/Recommendations.php:70 352 374 msgid "Warm the cache automatically so visitors never hit a cold, uncached page." 353 375 msgstr "" 354 376 355 #: includes/Core/Recommendations.php:7 6377 #: includes/Core/Recommendations.php:78 356 378 msgid "Enable JavaScript optimization" 357 379 msgstr "" 358 380 359 #: includes/Core/Recommendations.php:7 7381 #: includes/Core/Recommendations.php:79 360 382 msgid "Defer and delay scripts to cut Total Blocking Time — the heaviest-weighted mobile metric." 361 383 msgstr "" 362 384 363 #: includes/Core/Recommendations.php:8 4385 #: includes/Core/Recommendations.php:86 364 386 msgid "Delay JavaScript until interaction" 365 387 msgstr "" 366 388 367 #: includes/Core/Recommendations.php:8 5389 #: includes/Core/Recommendations.php:87 368 390 msgid "Delay non-critical scripts (analytics, chat, pixels) until the first interaction to reduce blocking time." 369 391 msgstr "" 370 392 371 #: includes/Core/Recommendations.php:9 3393 #: includes/Core/Recommendations.php:95 372 394 msgid "Enable CSS optimization" 373 395 msgstr "" 374 396 375 #: includes/Core/Recommendations.php:9 4397 #: includes/Core/Recommendations.php:96 376 398 msgid "Minify CSS to remove render-blocking bytes and speed up First Contentful Paint." 377 399 msgstr "" 378 400 379 #: includes/Core/Recommendations.php:10 2401 #: includes/Core/Recommendations.php:104 380 402 msgid "Enable Image optimization" 381 403 msgstr "" 382 404 383 #: includes/Core/Recommendations.php:10 3405 #: includes/Core/Recommendations.php:105 384 406 msgid "Lazy-load images, reserve their space (to prevent layout shift) and serve next-gen formats." 385 407 msgstr "" 386 408 387 #: includes/Core/Recommendations.php:1 09409 #: includes/Core/Recommendations.php:111 388 410 msgid "Reserve image space (fix layout shift)" 389 411 msgstr "" 390 412 391 #: includes/Core/Recommendations.php:11 0413 #: includes/Core/Recommendations.php:112 392 414 msgid "Add missing image dimensions and aspect-ratio so images do not push content down as they load (lowers CLS)." 393 415 msgstr "" 394 416 395 #: includes/Core/Recommendations.php:11 6396 #: src/index.js: 948397 #: src/index.js:1 369417 #: includes/Core/Recommendations.php:118 418 #: src/index.js:1092 419 #: src/index.js:1515 398 420 msgid "Lazy-load images" 399 421 msgstr "" 400 422 401 #: includes/Core/Recommendations.php:11 7423 #: includes/Core/Recommendations.php:119 402 424 msgid "Load off-screen images only when scrolled into view to speed up the initial load." 403 425 msgstr "" 404 426 405 #: includes/Core/Recommendations.php:12 4427 #: includes/Core/Recommendations.php:126 406 428 msgid "Serve WebP images" 407 429 msgstr "" 408 430 409 #: includes/Core/Recommendations.php:12 5431 #: includes/Core/Recommendations.php:127 410 432 msgid "Generate and serve WebP copies — typically 25–35% smaller than JPEG/PNG at the same quality." 411 433 msgstr "" 412 434 413 #: includes/Core/Recommendations.php:13 6435 #: includes/Core/Recommendations.php:138 414 436 msgid "Set font loading to \"Optional\" (reduce CLS)" 415 437 msgstr "" 416 438 417 #: includes/Core/Recommendations.php:13 7439 #: includes/Core/Recommendations.php:139 418 440 msgid "The \"swap\" mode lets web fonts reflow the page as they load, raising Cumulative Layout Shift. \"Optional\" removes that shift." 419 441 msgstr "" 420 442 421 #: includes/Core/Recommendations.php:14 3443 #: includes/Core/Recommendations.php:145 422 444 msgid "Enable Font optimization" 423 445 msgstr "" 424 446 425 #: includes/Core/Recommendations.php:14 4447 #: includes/Core/Recommendations.php:146 426 448 msgid "Preconnect to font hosts and control font-display to reduce render-blocking and layout shift." 427 449 msgstr "" 428 450 429 #: includes/Core/Recommendations.php:15 2451 #: includes/Core/Recommendations.php:154 430 452 msgid "Enable Database cleanup" 431 453 msgstr "" 432 454 433 #: includes/Core/Recommendations.php:15 3455 #: includes/Core/Recommendations.php:155 434 456 msgid "Remove revisions, auto-drafts, spam and expired transients, and optimize tables to keep the database lean." 435 457 msgstr "" 436 458 437 #: includes/Core/Recommendations.php:16 1459 #: includes/Core/Recommendations.php:163 438 460 msgid "Enable an image library on your server" 439 461 msgstr "" 440 462 441 #: includes/Core/Recommendations.php:16 2463 #: includes/Core/Recommendations.php:164 442 464 msgid "Your server has neither GD nor ImageMagick, so image compression and WebP/AVIF cannot run. Ask your host to enable the \"php-gd\" (or \"php-imagick\") extension. See Tools → Server Requirements." 443 465 msgstr "" 444 466 467 #: includes/Modules/Cache/Preloader.php:116 468 msgid "the home page" 469 msgstr "" 470 445 471 #. translators: %d: number of category / tag archive pages. 446 #: includes/Modules/Cache/Preloader.php:12 4472 #: includes/Modules/Cache/Preloader.php:127 447 473 #, php-format 448 474 msgid "%d category or tag page" … … 451 477 msgstr[1] "" 452 478 453 #: includes/Modules/Cache/Preloader.php: 681479 #: includes/Modules/Cache/Preloader.php:707 454 480 msgid "your host's server cache" 455 481 msgstr "" 456 482 457 #: includes/Modules/Cache/Preloader.php: 685483 #: includes/Modules/Cache/Preloader.php:711 458 484 msgid "a server or CDN cache" 459 485 msgstr "" 460 486 461 #: includes/Modules/Cache/Preloader.php:7 04487 #: includes/Modules/Cache/Preloader.php:730 462 488 msgid "The site could not load its own pages (the loopback request failed or timed out). Some hosts block a server from requesting its own website — ask your host to allow it." 463 489 msgstr "" 464 490 465 491 #. translators: %s: name of another caching plugin. 466 #: includes/Modules/Cache/Preloader.php:7 13492 #: includes/Modules/Cache/Preloader.php:739 467 493 #, php-format 468 494 msgid "%s controls page caching (advanced-cache.php), so Optimazz cannot save pages. Deactivate it, or use \"Take over caching\" in Cache health." 469 495 msgstr "" 470 496 471 #: includes/Modules/Cache/Preloader.php:7 18497 #: includes/Modules/Cache/Preloader.php:744 472 498 msgid "The cache file advanced-cache.php is missing. Turn Page Cache off and on again to reinstall it, and make sure the wp-content folder is writable." 473 499 msgstr "" 474 500 475 #: includes/Modules/Cache/Preloader.php:7 21501 #: includes/Modules/Cache/Preloader.php:747 476 502 msgid "WP_CACHE is not enabled in wp-config.php, so WordPress never loads the cache. Make wp-config.php writable and save the Cache settings, or add define( 'WP_CACHE', true ); yourself." 477 503 msgstr "" 478 504 479 505 #. translators: %s: name of a server or CDN cache, e.g. "Cloudflare". 480 #: includes/Modules/Cache/Preloader.php:7 26506 #: includes/Modules/Cache/Preloader.php:752 481 507 #, php-format 482 508 msgid "%s answered from its own cache before WordPress ran, so Optimazz never saw these requests. Purge that cache, or turn it off for HTML pages if you want Optimazz to cache them." 483 509 msgstr "" 484 510 485 #: includes/Modules/Cache/Preloader.php:7 30511 #: includes/Modules/Cache/Preloader.php:756 486 512 msgid "The Optimazz cache engine did not run for these requests. Usually a server-level cache or proxy (for example LiteSpeed, Varnish or a host cache) answers before WordPress loads. Check Cache health, or ask your host." 487 513 msgstr "" 488 514 489 515 #. translators: %s: HTTP status code, e.g. 404. 490 #: includes/Modules/Cache/Preloader.php:7 37516 #: includes/Modules/Cache/Preloader.php:763 491 517 #, php-format 492 518 msgid "Pages answered with HTTP %s instead of 200, and only normal pages are cached. Check that these URLs open correctly." 493 519 msgstr "" 494 520 495 #: includes/Modules/Cache/Preloader.php:7 42521 #: includes/Modules/Cache/Preloader.php:768 496 522 msgid "Page Cache is switched off." 497 523 msgstr "" 498 524 499 #: includes/Modules/Cache/Preloader.php:7 43525 #: includes/Modules/Cache/Preloader.php:769 500 526 msgid "Another plugin or the theme marks these pages \"do not cache\" (DONOTCACHEPAGE), so they are skipped. Membership, security or form plugins often do this." 501 527 msgstr "" 502 528 503 #: includes/Modules/Cache/Preloader.php:7 44529 #: includes/Modules/Cache/Preloader.php:770 504 530 msgid "The web address used to reach the site does not match the WordPress Site Address (for example www vs non-www). Check Settings → General." 505 531 msgstr "" 506 532 507 #: includes/Modules/Cache/Preloader.php:7 45533 #: includes/Modules/Cache/Preloader.php:771 508 534 msgid "Cache files could not be written. Make the wp-content/cache folder writable (permissions 755)." 509 535 msgstr "" 510 536 511 #: includes/Modules/Cache/Preloader.php:7 46537 #: includes/Modules/Cache/Preloader.php:772 512 538 msgid "These pages match your \"Never cache these URLs\" setting." 513 539 msgstr "" 514 540 515 #: includes/Modules/Cache/Preloader.php:7 47541 #: includes/Modules/Cache/Preloader.php:773 516 542 msgid "A cookie on the request matches your \"Never cache when these cookies are set\" setting." 517 543 msgstr "" 518 544 519 #: includes/Modules/Cache/Preloader.php:7 48545 #: includes/Modules/Cache/Preloader.php:774 520 546 msgid "The preloader's user-agent matches your \"Never cache for these user agents\" setting." 521 547 msgstr "" 522 548 523 #: includes/Modules/Cache/Preloader.php:7 49549 #: includes/Modules/Cache/Preloader.php:775 524 550 msgid "The pages came back empty or almost empty, so there was nothing to cache." 525 551 msgstr "" 526 552 527 #: includes/Modules/Cache/Preloader.php:7 50553 #: includes/Modules/Cache/Preloader.php:776 528 554 msgid "WordPress treated the request as logged in, so it was not cached." 529 555 msgstr "" 530 556 531 #: includes/Modules/Cache/Preloader.php:7 51557 #: includes/Modules/Cache/Preloader.php:777 532 558 msgid "These URLs show a \"not found\" (404) page, which is never cached." 533 559 msgstr "" 534 560 535 561 #. translators: %s: internal reason code. 536 #: includes/Modules/Cache/Preloader.php:7 57562 #: includes/Modules/Cache/Preloader.php:783 537 563 #, php-format 538 564 msgid "The cache skipped these pages (reason: %s)." … … 552 578 msgstr "" 553 579 554 #: includes/Modules/Fonts/FontsModule.php: 29580 #: includes/Modules/Fonts/FontsModule.php:38 555 581 #: src/index.js:40 556 #: src/index.js:1 570582 #: src/index.js:1726 557 583 msgid "Fonts" 558 584 msgstr "" … … 568 594 569 595 #: includes/Modules/Media/MediaModule.php:75 570 #: src/index.js:1 359596 #: src/index.js:1505 571 597 msgid "Image Optimization" 572 598 msgstr "" … … 578 604 #: includes/Modules/Tweaks/TweaksModule.php:25 579 605 #: src/index.js:45 580 #: src/index.js:1 679606 #: src/index.js:1841 581 607 msgid "Tweaks" 582 608 msgstr "" … … 603 629 604 630 #: src/index.js:41 605 #: src/index.js:1055 631 #: src/index.js:1201 632 #: src/index.js:2191 606 633 msgid "Preload" 607 634 msgstr "" … … 612 639 613 640 #: src/index.js:42 614 #: src/index.js:1 482641 #: src/index.js:1638 615 642 msgid "Advanced Rules" 616 643 msgstr "" … … 640 667 msgstr "" 641 668 642 #: src/index.js:64 669 #: src/index.js:81 670 msgid "every hour" 671 msgstr "" 672 673 #: src/index.js:82 674 #: src/index.js:84 675 msgid "twice a day" 676 msgstr "" 677 678 #: src/index.js:83 679 msgid "once a day" 680 msgstr "" 681 682 #: src/index.js:88 683 msgid "never expires" 684 msgstr "" 685 686 #: src/index.js:89 687 #: src/index.js:1231 688 #: src/index.js:1333 689 #: src/index.js:2107 690 msgid "12 hours" 691 msgstr "" 692 693 #: src/index.js:90 694 #: src/index.js:1232 695 #: src/index.js:1334 696 #: src/index.js:2108 697 msgid "1 day" 698 msgstr "" 699 700 #: src/index.js:91 701 #: src/index.js:1233 702 #: src/index.js:1335 703 #: src/index.js:2109 704 msgid "1 week" 705 msgstr "" 706 707 #: src/index.js:92 708 #: src/index.js:1234 709 #: src/index.js:1336 710 #: src/index.js:2110 711 msgid "1 month" 712 msgstr "" 713 714 #. translators: %d: number of hours. 715 #: src/index.js:95 716 #: src/index.js:1237 717 #, js-format 718 msgid "%d hour" 719 msgid_plural "%d hours" 720 msgstr[0] "" 721 msgstr[1] "" 722 723 #: src/index.js:102 724 msgid "Open Network Admin → Optimazz" 725 msgstr "" 726 727 #: src/index.js:111 728 msgid "Page caching is off for this network" 729 msgstr "" 730 731 #: src/index.js:113 732 msgid "Turn it on for every site in Network Admin → Optimazz. Each site gets its own cache, and keeps its own exclusions here under Advanced Rules." 733 msgstr "" 734 735 #: src/index.js:114 736 msgid "Your network administrator can turn it on for every site in Network Admin → Optimazz. All other optimizations — CSS, JavaScript, images, fonts and more — work normally on this site." 737 msgstr "" 738 739 #: src/index.js:128 740 msgid "Page caching is on — managed for the whole network" 741 msgstr "" 742 743 #. translators: 1: yes/no, 2: cache lifetime, 3: preload frequency. 744 #: src/index.js:131 745 #, js-format 746 msgid "Separate mobile cache: %1$s · Cache lifetime: %2$s · Automatic preload: %3$s." 747 msgstr "" 748 749 #: src/index.js:132 750 msgid "yes" 751 msgstr "" 752 753 #: src/index.js:132 754 msgid "no" 755 msgstr "" 756 757 #: src/index.js:134 758 #: src/index.js:1247 759 msgid "off" 760 msgstr "" 761 762 #: src/index.js:136 763 msgid "This site has its own cache. Purge or preload it from the Dashboard or the Preload screen, and set its own exclusions under Advanced Rules." 764 msgstr "" 765 766 #: src/index.js:146 643 767 msgid "Content leftovers" 644 768 msgstr "" 645 769 646 #: src/index.js: 65770 #: src/index.js:147 647 771 msgid "Old post data that piles up over time. Removing it is permanent, so keep anything you still need." 648 772 msgstr "" 649 773 650 #: src/index.js: 67774 #: src/index.js:149 651 775 msgid "Post revisions" 652 776 msgstr "" 653 777 654 #: src/index.js: 67778 #: src/index.js:149 655 779 msgid "Old copies of posts saved automatically. Deleting them keeps only your current content." 656 780 msgstr "" 657 781 658 #: src/index.js: 68782 #: src/index.js:150 659 783 msgid "Auto drafts" 660 784 msgstr "" 661 785 662 #: src/index.js: 68786 #: src/index.js:150 663 787 msgid "Empty drafts WordPress creates while you write. Safe to remove." 664 788 msgstr "" 665 789 666 #: src/index.js: 69790 #: src/index.js:151 667 791 msgid "Trashed posts" 668 792 msgstr "" 669 793 670 #: src/index.js: 69794 #: src/index.js:151 671 795 msgid "Posts in the trash that have not been permanently deleted yet." 672 796 msgstr "" 673 797 674 #: src/index.js: 73798 #: src/index.js:155 675 799 msgid "Comment clutter" 676 800 msgstr "" 677 801 678 #: src/index.js: 74802 #: src/index.js:156 679 803 msgid "Spam and trashed comments you are never going to approve. Clearing them keeps the comments table light." 680 804 msgstr "" 681 805 682 #: src/index.js: 76806 #: src/index.js:158 683 807 msgid "Spam comments" 684 808 msgstr "" 685 809 686 #: src/index.js: 76810 #: src/index.js:158 687 811 msgid "Comments marked as spam. Removing them frees up the comments table." 688 812 msgstr "" 689 813 690 #: src/index.js: 77814 #: src/index.js:159 691 815 msgid "Trashed comments" 692 816 msgstr "" 693 817 694 #: src/index.js: 77818 #: src/index.js:159 695 819 msgid "Comments in the trash awaiting permanent deletion." 696 820 msgstr "" 697 821 698 #: src/index.js: 81822 #: src/index.js:163 699 823 msgid "Temporary data" 700 824 msgstr "" 701 825 702 #: src/index.js: 82826 #: src/index.js:164 703 827 msgid "Short-lived cached values. They are safe to clear and are rebuilt automatically whenever a plugin needs them." 704 828 msgstr "" 705 829 706 #: src/index.js: 84830 #: src/index.js:166 707 831 msgid "Expired transients" 708 832 msgstr "" 709 833 710 #: src/index.js: 84834 #: src/index.js:166 711 835 msgid "Cached values that have already passed their expiry date." 712 836 msgstr "" 713 837 714 #: src/index.js: 88838 #: src/index.js:170 715 839 msgid "Table maintenance" 716 840 msgstr "" 717 841 718 #: src/index.js: 89842 #: src/index.js:171 719 843 msgid "Defragment your tables to reclaim wasted space and trim database overhead." 720 844 msgstr "" 721 845 722 #: src/index.js: 91846 #: src/index.js:173 723 847 msgid "Optimize database tables" 724 848 msgstr "" 725 849 726 #: src/index.js: 91850 #: src/index.js:173 727 851 msgid "Runs OPTIMIZE on your tables to defragment them and free wasted space." 728 852 msgstr "" 729 853 730 #: src/index.js: 97854 #: src/index.js:179 731 855 msgid "Do not limit" 732 856 msgstr "" 733 857 734 #: src/index.js: 98858 #: src/index.js:180 735 859 msgid "Reduce activity" 736 860 msgstr "" 737 861 738 #: src/index.js: 99862 #: src/index.js:181 739 863 msgid "Disable" 740 864 msgstr "" 741 865 742 #: src/index.js:1 08866 #: src/index.js:190 743 867 msgid "Active" 744 868 msgstr "" 745 869 746 #: src/index.js:1 08747 #: src/index.js:1 110748 #: src/index.js:1 636870 #: src/index.js:190 871 #: src/index.js:1256 872 #: src/index.js:1798 749 873 msgid "Off" 750 874 msgstr "" 751 875 752 #: src/index.js: 150876 #: src/index.js:232 753 877 msgid "Cancelled" 754 878 msgstr "" 755 879 756 #: src/index.js: 151880 #: src/index.js:233 757 881 msgid "Failed" 758 882 msgstr "" 759 883 760 #: src/index.js: 167884 #: src/index.js:249 761 885 msgid "Expand" 762 886 msgstr "" 763 887 764 #: src/index.js: 167888 #: src/index.js:249 765 889 msgid "Minimize" 766 890 msgstr "" 767 891 768 #: src/index.js: 170892 #: src/index.js:252 769 893 msgid "Close" 770 894 msgstr "" 771 895 772 #: src/index.js: 184896 #: src/index.js:266 773 897 msgid "Step" 774 898 msgstr "" 775 899 776 #: src/index.js: 189900 #: src/index.js:271 777 901 msgid "Stop" 778 902 msgstr "" 779 903 780 #: src/index.js:492 781 #: src/index.js:1239 904 #: src/index.js:538 905 msgid "You have unsaved changes" 906 msgstr "" 907 908 #: src/index.js:538 909 msgid "All changes saved" 910 msgstr "" 911 912 #: src/index.js:541 913 msgid "Save changes" 914 msgstr "" 915 916 #: src/index.js:639 917 #: src/index.js:1385 782 918 msgid "Deactivate" 783 919 msgstr "" 784 920 785 #: src/index.js: 492921 #: src/index.js:639 786 922 msgid "Optimazz will keep handling caching." 787 923 msgstr "" 788 924 789 #: src/index.js: 499925 #: src/index.js:646 790 926 msgid "deactivated — Optimazz is now the only cache." 791 927 msgstr "" 792 928 793 #: src/index.js: 501929 #: src/index.js:648 794 930 msgid "Could not deactivate the plugin." 795 931 msgstr "" 796 932 797 #: src/index.js: 508933 #: src/index.js:655 798 934 msgid "Make Optimazz the caching plugin? This replaces the current advanced-cache.php (from another caching plugin) and turns on WP_CACHE. Deactivate the other caching plugin afterwards to avoid conflicts." 799 935 msgstr "" 800 936 801 #: src/index.js: 515937 #: src/index.js:662 802 938 msgid "Optimazz is now serving pages from cache." 803 939 msgstr "" 804 940 805 #: src/index.js: 516941 #: src/index.js:663 806 942 msgid "Drop-in installed. Preload the cache, then check again." 807 943 msgstr "" 808 944 809 #: src/index.js: 517945 #: src/index.js:664 810 946 msgid "Could not fully take over — see the cache health details." 811 947 msgstr "" 812 948 813 #: src/index.js: 520949 #: src/index.js:667 814 950 msgid "Take-over failed. Check file permissions." 815 951 msgstr "" 816 952 817 #: src/index.js: 542818 #: src/index.js: 588953 #: src/index.js:689 954 #: src/index.js:735 819 955 msgid "Cache purged." 820 956 msgstr "" 821 957 822 #: src/index.js: 543958 #: src/index.js:690 823 959 msgid "Cache preload started in the background." 824 960 msgstr "" 825 961 826 #: src/index.js: 544962 #: src/index.js:691 827 963 msgid "Cache cleared; preload started in the background." 828 964 msgstr "" 829 965 830 #: src/index.js: 553966 #: src/index.js:700 831 967 msgid "wp-config.php is not writable — add WP_CACHE manually to enable page caching." 832 968 msgstr "" 833 969 834 #: src/index.js: 567970 #: src/index.js:714 835 971 msgid "Settings saved." 836 972 msgstr "" 837 973 838 #: src/index.js: 570974 #: src/index.js:717 839 975 msgid "Save failed." 840 976 msgstr "" 841 977 842 #: src/index.js: 620978 #: src/index.js:767 843 979 msgid "Converting images to WebP/AVIF" 844 980 msgstr "" 845 981 846 #: src/index.js: 621982 #: src/index.js:768 847 983 msgid "Next-gen format generation" 848 984 msgstr "" 849 985 850 #: src/index.js: 624986 #: src/index.js:771 851 987 msgid "Generated" 852 988 msgstr "" 853 989 854 #: src/index.js: 624990 #: src/index.js:771 855 991 msgid "files in this batch" 856 992 msgstr "" 857 993 858 #: src/index.js: 624859 #: src/index.js: 633994 #: src/index.js:771 995 #: src/index.js:780 860 996 msgid "skipped" 861 997 msgstr "" 862 998 863 #: src/index.js: 627999 #: src/index.js:774 864 1000 msgid "Compressing images" 865 1001 msgstr "" 866 1002 867 #: src/index.js: 6281003 #: src/index.js:775 868 1004 msgid "Recompress, resize & strip metadata" 869 1005 msgstr "" 870 1006 871 #: src/index.js: 6341007 #: src/index.js:781 872 1008 msgid "Processed" 873 1009 msgstr "" 874 1010 875 #: src/index.js: 634876 #: src/index.js:1 4401011 #: src/index.js:781 1012 #: src/index.js:1586 877 1013 msgid "saved" 878 1014 msgstr "" 879 1015 880 #: src/index.js:638 1016 #: src/index.js:785 1017 #: src/index.js:1953 881 1018 msgid "Preloading cache" 882 1019 msgstr "" 883 1020 884 #: src/index.js: 6391021 #: src/index.js:786 885 1022 msgid "Warming pages" 886 1023 msgstr "" 887 1024 888 #: src/index.js:643 1025 #: src/index.js:790 1026 #: src/index.js:1974 889 1027 msgid "Warmed" 890 1028 msgstr "" 891 1029 892 #: src/index.js:643 1030 #: src/index.js:790 1031 #: src/index.js:1974 893 1032 msgid "pages" 894 1033 msgstr "" 895 1034 896 #: src/index.js:643 1035 #: src/index.js:790 1036 #: src/index.js:1974 897 1037 msgid "cached" 898 1038 msgstr "" 899 1039 900 #: src/index.js:643 1040 #: src/index.js:790 1041 #: src/index.js:1974 901 1042 msgid "not cached" 902 1043 msgstr "" 903 1044 904 #: src/index.js: 6641045 #: src/index.js:811 905 1046 msgid "Resuming…" 906 1047 msgstr "" 907 1048 908 #: src/index.js:678 1049 #: src/index.js:825 1050 #: src/index.js:1963 909 1051 msgid "Stopped by user." 910 1052 msgstr "" 911 1053 912 #: src/index.js: 6971054 #: src/index.js:844 913 1055 msgid "A batch failed (likely one slow/large image); skipped it and continued." 914 1056 msgstr "" 915 1057 916 #: src/index.js: 6991058 #: src/index.js:846 917 1059 msgid "Completed (some items skipped)." 918 1060 msgstr "" 919 1061 920 #: src/index.js: 7111062 #: src/index.js:858 921 1063 msgid "Job stalled — no progress." 922 1064 msgstr "" 923 1065 924 #: src/index.js:722 1066 #: src/index.js:869 1067 #: src/index.js:1977 925 1068 msgid "left" 926 1069 msgstr "" 927 1070 928 1071 #. translators: %d: number of pages to warm. 929 #: src/index.js:734 1072 #: src/index.js:881 1073 #: src/index.js:1987 930 1074 #, js-format 931 1075 msgid "Found %d pages to warm:" 932 1076 msgstr "" 933 1077 934 #: src/index.js: 7441078 #: src/index.js:891 935 1079 msgid "Image processing is unavailable on this server (no GD or ImageMagick). See Tools → Server Requirements." 936 1080 msgstr "" 937 1081 938 #: src/index.js:749 1082 #: src/index.js:896 1083 #: src/index.js:1993 939 1084 msgid "Warming stopped: the server returned 403/429, so a firewall or security plugin is blocking the preload requests. Cached what we could; allow-list the \"Optimazz-CacheWarmer\" user-agent in your firewall or security plugin (e.g. All-In-One Security, Wordfence, Cloudflare), or slow warming with the optimazz_preload_delay_ms filter." 940 1085 msgstr "" 941 1086 942 #: src/index.js:756 1087 #: src/index.js:903 1088 #: src/index.js:1997 943 1089 msgid "Pages are not being cached" 944 1090 msgstr "" 945 1091 946 #: src/index.js:756 1092 #: src/index.js:903 1093 #: src/index.js:1997 947 1094 msgid "Stopped: pages are loading but none are being saved to the cache." 948 1095 msgstr "" 949 1096 950 #: src/index.js:760 951 #: src/index.js:841 1097 #: src/index.js:907 1098 #: src/index.js:988 1099 #: src/index.js:2007 952 1100 msgid "Completed." 953 1101 msgstr "" 954 1102 955 1103 #. translators: %d: number of pages. 956 #: src/index.js:764 1104 #: src/index.js:911 1105 #: src/index.js:2004 957 1106 #, js-format 958 1107 msgid "%d page was not cached." … … 961 1110 msgstr[1] "" 962 1111 963 #: src/index.js: 8231112 #: src/index.js:970 964 1113 msgid "Cleaning and optimizing database…" 965 1114 msgstr "" 966 1115 967 #: src/index.js: 8271116 #: src/index.js:974 968 1117 msgid "Optimizing database" 969 1118 msgstr "" 970 1119 971 #: src/index.js: 8281120 #: src/index.js:975 972 1121 msgid "Cleanup & table optimization" 973 1122 msgstr "" 974 1123 975 #: src/index.js: 8401124 #: src/index.js:987 976 1125 msgid "Cleaned" 977 1126 msgstr "" 978 1127 979 #: src/index.js: 8401128 #: src/index.js:987 980 1129 msgid "rows," 981 1130 msgstr "" 982 1131 983 #: src/index.js: 8401132 #: src/index.js:987 984 1133 msgid "tables optimized." 985 1134 msgstr "" 986 1135 987 #: src/index.js: 8451136 #: src/index.js:992 988 1137 msgid "Optimize failed." 989 1138 msgstr "" 990 1139 991 #: src/index.js: 8511140 #: src/index.js:998 992 1141 msgid "Reset all Optimazz settings to defaults? You can undo this with \"Undo last change\"." 993 1142 msgstr "" 994 1143 995 #: src/index.js: 8591144 #: src/index.js:1006 996 1145 msgid "Settings reset to defaults." 997 1146 msgstr "" 998 1147 999 #: src/index.js: 8621148 #: src/index.js:1009 1000 1149 msgid "Reset failed." 1001 1150 msgstr "" 1002 1151 1003 #: src/index.js: 8721152 #: src/index.js:1019 1004 1153 msgid "Nothing to undo." 1005 1154 msgstr "" 1006 1155 1007 #: src/index.js: 8791156 #: src/index.js:1026 1008 1157 msgid "Reverted to the previous settings." 1009 1158 msgstr "" 1010 1159 1011 #: src/index.js: 8821160 #: src/index.js:1029 1012 1161 msgid "Undo failed." 1013 1162 msgstr "" 1014 1163 1015 #: src/index.js: 9141164 #: src/index.js:1061 1016 1165 msgid "Invalid settings file." 1017 1166 msgstr "" 1018 1167 1019 #: src/index.js: 9441168 #: src/index.js:1088 1020 1169 msgid "Page caching" 1021 1170 msgstr "" 1022 1171 1023 #: src/index.js: 9451172 #: src/index.js:1089 1024 1173 msgid "Delay JavaScript" 1025 1174 msgstr "" 1026 1175 1027 #: src/index.js: 9461028 #: src/index.js:1 2661176 #: src/index.js:1090 1177 #: src/index.js:1412 1029 1178 msgid "Defer JavaScript" 1030 1179 msgstr "" 1031 1180 1032 #: src/index.js: 9471033 #: src/index.js:1 3191181 #: src/index.js:1091 1182 #: src/index.js:1465 1034 1183 msgid "Minify CSS" 1035 1184 msgstr "" 1036 1185 1037 #: src/index.js: 9491186 #: src/index.js:1093 1038 1187 msgid "Next-gen images" 1039 1188 msgstr "" 1040 1189 1041 #: src/index.js: 9501190 #: src/index.js:1094 1042 1191 msgid "Font optimization" 1043 1192 msgstr "" 1044 1193 1045 #: src/index.js: 9511194 #: src/index.js:1095 1046 1195 msgid "Cache preloading" 1047 1196 msgstr "" 1048 1197 1049 #: src/index.js: 9521198 #: src/index.js:1096 1050 1199 msgid "Heartbeat control" 1051 1200 msgstr "" 1052 1201 1053 #: src/index.js: 9671202 #: src/index.js:1113 1054 1203 msgid "Turn on Page Cache for the real speed gain" 1055 1204 msgstr "" 1056 1205 1057 #: src/index.js: 9681206 #: src/index.js:1114 1058 1207 msgid "File and media optimizations are on, but Page Cache is off — so the server rebuilds every page on every request. Without caching this can make a site feel slower, not faster, especially on mobile. Enable Page Cache to serve ready-made HTML." 1059 1208 msgstr "" 1060 1209 1061 #: src/index.js: 9871210 #: src/index.js:1133 1062 1211 msgid "SCORE" 1063 1212 msgstr "" 1064 1213 1065 #: src/index.js: 9921214 #: src/index.js:1138 1066 1215 msgid "Performance score" 1067 1216 msgstr "" 1068 1217 1069 #: src/index.js: 9951218 #: src/index.js:1141 1070 1219 msgid "Biggest remaining win:" 1071 1220 msgstr "" 1072 1221 1073 #: src/index.js: 9961222 #: src/index.js:1142 1074 1223 msgid "Every optimization is enabled. Nicely done!" 1075 1224 msgstr "" 1076 1225 1077 #: src/index.js:1013 1078 #: src/index.js:1065 1226 #: src/index.js:1159 1227 #: src/index.js:1211 1228 #: src/index.js:2204 1079 1229 msgid "Cached pages" 1080 1230 msgstr "" 1081 1231 1082 #: src/index.js:1 0181232 #: src/index.js:1164 1083 1233 msgid "Cache size" 1084 1234 msgstr "" 1085 1235 1086 #: src/index.js:1 0231236 #: src/index.js:1169 1087 1237 msgid "Modules active" 1088 1238 msgstr "" 1089 1239 1090 #: src/index.js:1 0281091 #: src/index.js:1 0431240 #: src/index.js:1174 1241 #: src/index.js:1189 1092 1242 msgid "Recommendations" 1093 1243 msgstr "" 1094 1244 1095 #: src/index.js:1 0281245 #: src/index.js:1174 1096 1246 msgid "What to fix next to speed up your site — one click each." 1097 1247 msgstr "" 1098 1248 1099 #: src/index.js:1 0351249 #: src/index.js:1181 1100 1250 msgid "Fix it" 1101 1251 msgstr "" 1102 1252 1103 #: src/index.js:1 0361253 #: src/index.js:1182 1104 1254 msgid "Open" 1105 1255 msgstr "" 1106 1256 1107 #: src/index.js:1 0441257 #: src/index.js:1190 1108 1258 msgid "Nice — everything we recommend is already on. Your site is well optimized." 1109 1259 msgstr "" 1110 1260 1111 #: src/index.js:1 0491261 #: src/index.js:1195 1112 1262 msgid "Cache Files" 1113 1263 msgstr "" 1114 1264 1115 #: src/index.js:1 0491265 #: src/index.js:1195 1116 1266 msgid "Clear and preload all cache files." 1117 1267 msgstr "" 1118 1268 1119 #: src/index.js:1052 1269 #: src/index.js:1198 1270 #: src/index.js:2187 1120 1271 msgid "Clear & Preload" 1121 1272 msgstr "" 1122 1273 1123 #: src/index.js:1058 1274 #: src/index.js:1204 1275 #: src/index.js:2195 1124 1276 msgid "Purge cache" 1125 1277 msgstr "" 1126 1278 1127 #: src/index.js:1070 1279 #: src/index.js:1216 1280 #: src/index.js:2209 1128 1281 msgid "Page cache size" 1129 1282 msgstr "" 1130 1283 1131 #: src/index.js:1 0751284 #: src/index.js:1221 1132 1285 msgid "Optimized CSS" 1133 1286 msgstr "" 1134 1287 1135 #: src/index.js:1 0801136 #: src/index.js:1 0811288 #: src/index.js:1226 1289 #: src/index.js:1227 1137 1290 msgid "Cache lifespan" 1138 1291 msgstr "" 1139 1292 1140 #: src/index.js:1 0841293 #: src/index.js:1230 1141 1294 msgid "Never expire" 1142 1295 msgstr "" 1143 1296 1144 #: src/index.js:1085 1145 #: src/index.js:1187 1146 msgid "12 hours" 1147 msgstr "" 1148 1149 #: src/index.js:1086 1150 #: src/index.js:1188 1151 msgid "1 day" 1152 msgstr "" 1153 1154 #: src/index.js:1087 1155 #: src/index.js:1189 1156 msgid "1 week" 1157 msgstr "" 1158 1159 #: src/index.js:1088 1160 #: src/index.js:1190 1161 msgid "1 month" 1162 msgstr "" 1163 1164 #. translators: %d: number of hours. 1165 #: src/index.js:1091 1166 #, js-format 1167 msgid "%d hour" 1168 msgid_plural "%d hours" 1169 msgstr[0] "" 1170 msgstr[1] "" 1171 1172 #: src/index.js:1098 1297 #: src/index.js:1244 1173 1298 msgid "Auto-preload" 1174 1299 msgstr "" 1175 1300 1176 #: src/index.js:1101 1177 msgid "off" 1178 msgstr "" 1179 1180 #: src/index.js:1103 1301 #: src/index.js:1249 1181 1302 msgid "next run: due now" 1182 1303 msgstr "" 1183 1304 1184 #: src/index.js:1 1041305 #: src/index.js:1250 1185 1306 msgid "next run in" 1186 1307 msgstr "" 1187 1308 1188 #: src/index.js:1 1071309 #: src/index.js:1253 1189 1310 msgid "Auto-preload frequency" 1190 1311 msgstr "" 1191 1312 1192 #: src/index.js:1111 1193 #: src/index.js:1462 1313 #: src/index.js:1257 1314 #: src/index.js:1609 1315 #: src/index.js:2121 1194 1316 msgid "Every hour" 1195 1317 msgstr "" 1196 1318 1197 #: src/index.js:1112 1198 #: src/index.js:1463 1319 #: src/index.js:1258 1320 #: src/index.js:1610 1321 #: src/index.js:2122 1199 1322 msgid "Twice a day" 1200 1323 msgstr "" 1201 1324 1202 #: src/index.js:1113 1203 #: src/index.js:1464 1325 #: src/index.js:1259 1326 #: src/index.js:1611 1327 #: src/index.js:2123 1204 1328 msgid "Once a day" 1205 1329 msgstr "" 1206 1330 1207 #: src/index.js:1 1211331 #: src/index.js:1267 1208 1332 msgid "Last auto-preload" 1209 1333 msgstr "" 1210 1334 1211 #: src/index.js:1 1241335 #: src/index.js:1270 1212 1336 msgid "not yet" 1213 1337 msgstr "" 1214 1338 1215 1339 #. translators: 1: time ago, e.g. "2 hours", 2: number of pages 1216 #: src/index.js:1 1271340 #: src/index.js:1273 1217 1341 #, js-format 1218 1342 msgid "%1$s ago · %2$d pages" 1219 1343 msgstr "" 1220 1344 1221 #: src/index.js:1 1301345 #: src/index.js:1276 1222 1346 msgid "blocked by firewall" 1223 1347 msgstr "" 1224 1348 1225 #: src/index.js:1 1351349 #: src/index.js:1281 1226 1350 msgid "Module Status" 1227 1351 msgstr "" 1228 1352 1229 #: src/index.js:1 1351353 #: src/index.js:1281 1230 1354 msgid "Enable or disable any module right here." 1231 1355 msgstr "" 1232 1356 1233 #: src/index.js:1 1721357 #: src/index.js:1318 1234 1358 msgid "Serve static HTML instead of running PHP on every request." 1235 1359 msgstr "" 1236 1360 1237 #: src/index.js:1174 1361 #: src/index.js:1320 1362 #: src/index.js:2094 1238 1363 msgid "Cache mobile devices separately" 1239 1364 msgstr "" 1240 1365 1241 #: src/index.js:1176 1366 #: src/index.js:1322 1367 #: src/index.js:2096 1242 1368 msgid "Cache logged-in users" 1243 1369 msgstr "" 1244 1370 1245 #: src/index.js:1177 1371 #: src/index.js:1323 1372 #: src/index.js:2097 1246 1373 msgid "Leave off unless you know what you are doing." 1247 1374 msgstr "" 1248 1375 1249 #: src/index.js:1179 1376 #: src/index.js:1325 1377 #: src/index.js:2099 1250 1378 msgid "Browser cache headers" 1251 1379 msgstr "" 1252 1380 1253 #: src/index.js:1 1801381 #: src/index.js:1326 1254 1382 msgid "Add Expires / Cache-Control rules so repeat visits load CSS, JS, images and fonts from the browser cache. Apache and LiteSpeed only (written to .htaccess); ignored on Nginx." 1255 1383 msgstr "" 1256 1384 1257 #: src/index.js:1182 1385 #: src/index.js:1328 1386 #: src/index.js:2102 1258 1387 msgid "Cache lifetime" 1259 1388 msgstr "" 1260 1389 1261 #: src/index.js:1 1831390 #: src/index.js:1329 1262 1391 msgid "How long a cached page is kept before it is refreshed. Pages are always purged immediately when their content changes." 1263 1392 msgstr "" 1264 1393 1265 #: src/index.js:1186 1394 #: src/index.js:1332 1395 #: src/index.js:2106 1266 1396 msgid "Never" 1267 1397 msgstr "" 1268 1398 1269 #: src/index.js:1196 1399 #: src/index.js:1342 1400 #: src/index.js:2128 1270 1401 msgid "Cache health" 1271 1402 msgstr "" 1272 1403 1273 #: src/index.js:1 1981404 #: src/index.js:1344 1274 1405 msgid "Check now" 1275 1406 msgstr "" 1276 1407 1277 #: src/index.js:1 2011408 #: src/index.js:1347 1278 1409 msgid "Confirms caching is actually serving pages. If it is not, the plugin rebuilds every page on each request — which is slower, not faster." 1279 1410 msgstr "" 1280 1411 1281 #: src/index.js:1 2051412 #: src/index.js:1351 1282 1413 msgid "Pages ARE being served from cache (HIT)" 1283 1414 msgstr "" 1284 1415 1285 #: src/index.js:1 2061416 #: src/index.js:1352 1286 1417 msgid "Pages are NOT being served from cache (MISS) — see below" 1287 1418 msgstr "" 1288 1419 1289 #: src/index.js:1 2071420 #: src/index.js:1353 1290 1421 msgid "Live check unavailable" 1291 1422 msgstr "" 1292 1423 1293 #: src/index.js:1 2111424 #: src/index.js:1357 1294 1425 msgid "advanced-cache.php belongs to" 1295 1426 msgstr "" 1296 1427 1297 #: src/index.js:1 2111428 #: src/index.js:1357 1298 1429 msgid "take it over below" 1299 1430 msgstr "" 1300 1431 1301 #: src/index.js:1 2121432 #: src/index.js:1358 1302 1433 msgid "advanced-cache.php belongs to ANOTHER plugin — take it over below" 1303 1434 msgstr "" 1304 1435 1305 #: src/index.js:1 2131436 #: src/index.js:1359 1306 1437 msgid "advanced-cache.php drop-in installed" 1307 1438 msgstr "" 1308 1439 1309 #: src/index.js:1 2141440 #: src/index.js:1360 1310 1441 msgid "advanced-cache.php drop-in is missing" 1311 1442 msgstr "" 1312 1443 1313 #: src/index.js:1 2161444 #: src/index.js:1362 1314 1445 msgid "WP_CACHE is enabled in wp-config.php" 1315 1446 msgstr "" 1316 1447 1317 #: src/index.js:1 2161448 #: src/index.js:1362 1318 1449 msgid "WP_CACHE is not enabled in wp-config.php" 1319 1450 msgstr "" 1320 1451 1321 #: src/index.js:1 2181452 #: src/index.js:1364 1322 1453 msgid "wp-config.php is not writable — add the WP_CACHE line manually (below)" 1323 1454 msgstr "" 1324 1455 1325 #: src/index.js:1 2211456 #: src/index.js:1367 1326 1457 msgid "pages warmed in cache" 1327 1458 msgstr "" 1328 1459 1329 #: src/index.js:1 2271460 #: src/index.js:1373 1330 1461 msgid "Take over caching" 1331 1462 msgstr "" 1332 1463 1333 #: src/index.js:1 2291464 #: src/index.js:1375 1334 1465 msgid "Make Optimazz the caching plugin — installs its drop-in and enables WP_CACHE." 1335 1466 msgstr "" 1336 1467 1337 #: src/index.js:1 2371468 #: src/index.js:1383 1338 1469 msgid "is still active and may reclaim caching. Deactivate it so Optimazz keeps serving the cache." 1339 1470 msgstr "" 1340 1471 1341 #: src/index.js:1 2471472 #: src/index.js:1393 1342 1473 msgid "wp-config.php is not writable. Add this line just after the opening <?php tag:" 1343 1474 msgstr "" 1344 1475 1345 #: src/index.js:1 2521476 #: src/index.js:1398 1346 1477 msgid "Could not run the check. Try reloading the page." 1347 1478 msgstr "" 1348 1479 1349 #: src/index.js:1 2601480 #: src/index.js:1406 1350 1481 msgid "JavaScript" 1351 1482 msgstr "" 1352 1483 1353 #: src/index.js:1 2611484 #: src/index.js:1407 1354 1485 msgid "Defer and delay scripts to cut Total Blocking Time." 1355 1486 msgstr "" 1356 1487 1357 #: src/index.js:1 2631488 #: src/index.js:1409 1358 1489 msgid "Minify JavaScript" 1359 1490 msgstr "" 1360 1491 1361 #: src/index.js:1 2641492 #: src/index.js:1410 1362 1493 msgid "Strip comments and whitespace from inline scripts. Line breaks are kept for safety; test your site after enabling." 1363 1494 msgstr "" 1364 1495 1365 #: src/index.js:1 2671496 #: src/index.js:1413 1366 1497 msgid "Load scripts without blocking page render." 1367 1498 msgstr "" 1368 1499 1369 #: src/index.js:1 2691500 #: src/index.js:1415 1370 1501 msgid "Delay JS until interaction" 1371 1502 msgstr "" 1372 1503 1373 #: src/index.js:1 2701504 #: src/index.js:1416 1374 1505 msgid "Run scripts only after the first user interaction." 1375 1506 msgstr "" 1376 1507 1377 #: src/index.js:1 2731508 #: src/index.js:1419 1378 1509 msgid "Delay all scripts" 1379 1510 msgstr "" 1380 1511 1381 #: src/index.js:1 2741512 #: src/index.js:1420 1382 1513 msgid "Delay every script until interaction. jQuery, your theme, and Elementor are always kept safe. Still, on sites where content is rendered by JavaScript this can delay the visible page — test before keeping it on." 1383 1514 msgstr "" 1384 1515 1385 #: src/index.js:1 2801516 #: src/index.js:1426 1386 1517 msgid "Delaying all scripts can slow the first paint on JavaScript-rendered sites (Elementor, sliders). If your PageSpeed FCP/LCP get worse, switch to \"Delay selected\" and delay only tracking scripts." 1387 1518 msgstr "" 1388 1519 1389 #: src/index.js:1 2841520 #: src/index.js:1430 1390 1521 msgid "Delay these scripts" 1391 1522 msgstr "" 1392 1523 1393 #: src/index.js:1 2851524 #: src/index.js:1431 1394 1525 msgid "Only scripts matching these keywords are delayed. Match by filename, handle or URL — e.g. gtag, analytics, facebook." 1395 1526 msgstr "" 1396 1527 1397 #: src/index.js:1 2861528 #: src/index.js:1432 1398 1529 msgid "e.g. gtag, analytics" 1399 1530 msgstr "" 1400 1531 1401 #: src/index.js:1 2921532 #: src/index.js:1438 1402 1533 msgid "Exclude specific scripts" 1403 1534 msgstr "" 1404 1535 1405 #: src/index.js:1 2941536 #: src/index.js:1440 1406 1537 msgid "If a script breaks, exclude it from just one optimization instead of both. Match by filename, script handle, or part of the URL." 1407 1538 msgstr "" 1408 1539 1409 #: src/index.js:1 2981540 #: src/index.js:1444 1410 1541 msgid "Never defer" 1411 1542 msgstr "" 1412 1543 1413 #: src/index.js:1 2991544 #: src/index.js:1445 1414 1545 msgid "Keep loading these render-blocking, but still allow delay. Use for scripts that must run during page load." 1415 1546 msgstr "" 1416 1547 1417 #: src/index.js:1 3001548 #: src/index.js:1446 1418 1549 msgid "e.g. jquery.min.js" 1419 1550 msgstr "" 1420 1551 1421 #: src/index.js:1 3041552 #: src/index.js:1450 1422 1553 msgid "Never delay" 1423 1554 msgstr "" 1424 1555 1425 #: src/index.js:1 3051556 #: src/index.js:1451 1426 1557 msgid "Run these before any interaction, but still allow defer. Use for sliders, above-the-fold, or consent scripts." 1427 1558 msgstr "" 1428 1559 1429 #: src/index.js:1 3061560 #: src/index.js:1452 1430 1561 msgid "e.g. slider, swiper" 1431 1562 msgstr "" 1432 1563 1433 #: src/index.js:1 3091564 #: src/index.js:1455 1434 1565 msgid "Never optimize" 1435 1566 msgstr "" 1436 1567 1437 #: src/index.js:1 3101568 #: src/index.js:1456 1438 1569 msgid "Skip both defer and delay — leave these scripts exactly as they are." 1439 1570 msgstr "" 1440 1571 1441 #: src/index.js:1 3111572 #: src/index.js:1457 1442 1573 msgid "e.g. recaptcha" 1443 1574 msgstr "" 1444 1575 1445 #: src/index.js:1 3161576 #: src/index.js:1462 1446 1577 msgid "CSS" 1447 1578 msgstr "" 1448 1579 1449 #: src/index.js:1 3171580 #: src/index.js:1463 1450 1581 msgid "Minify, combine, and remove render-blocking stylesheets." 1451 1582 msgstr "" 1452 1583 1453 #: src/index.js:1 3211584 #: src/index.js:1467 1454 1585 msgid "Combine CSS files" 1455 1586 msgstr "" 1456 1587 1457 #: src/index.js:1 3221588 #: src/index.js:1468 1458 1589 msgid "Merge stylesheets to reduce requests." 1459 1590 msgstr "" 1460 1591 1461 #: src/index.js:1 3241592 #: src/index.js:1470 1462 1593 msgid "Load CSS asynchronously" 1463 1594 msgstr "" 1464 1595 1465 #: src/index.js:1 3251596 #: src/index.js:1471 1466 1597 msgid "Removes render-blocking CSS. Paste your above-the-fold (critical) CSS below, or the page may briefly flash unstyled." 1467 1598 msgstr "" 1468 1599 1469 #: src/index.js:1 3321600 #: src/index.js:1478 1470 1601 msgid "No critical CSS provided. Async loading is on, so visitors may see a flash of unstyled content until you paste critical CSS below." 1471 1602 msgstr "" 1472 1603 1473 #: src/index.js:1 3361604 #: src/index.js:1482 1474 1605 msgid "Critical CSS (above-the-fold)" 1475 1606 msgstr "" 1476 1607 1477 #: src/index.js:1 3371608 #: src/index.js:1483 1478 1609 msgid "Paste the above-the-fold CSS to inline. Generate it with a free tool such as a critical-CSS generator. Leave empty to disable inlining." 1479 1610 msgstr "" 1480 1611 1481 #: src/index.js:1 3421612 #: src/index.js:1488 1482 1613 msgid "Remove unused CSS" 1483 1614 msgstr "" 1484 1615 1485 #: src/index.js:1 3431616 #: src/index.js:1489 1486 1617 msgid "Remove block-editor CSS on the front end" 1487 1618 msgstr "" 1488 1619 1489 #: src/index.js:1 3441620 #: src/index.js:1490 1490 1621 msgid "Dequeues WordPress block-library and global styles. Turn on only if your front end does NOT render Gutenberg blocks (e.g. a page-builder or classic theme). Big byte saving when it applies." 1491 1622 msgstr "" 1492 1623 1493 #: src/index.js:1 3461624 #: src/index.js:1492 1494 1625 msgid "Load WooCommerce CSS only on shop pages" 1495 1626 msgstr "" 1496 1627 1497 #: src/index.js:1 3471628 #: src/index.js:1493 1498 1629 msgid "Dequeues WooCommerce styles on non-shop pages (kept on cart, checkout, account, product and shop). Turn off if you show Woo products or widgets on other pages." 1499 1630 msgstr "" 1500 1631 1501 #: src/index.js:1 3491632 #: src/index.js:1495 1502 1633 msgid "Load Easy Digital Downloads CSS only on EDD pages" 1503 1634 msgstr "" 1504 1635 1505 #: src/index.js:1 3501636 #: src/index.js:1496 1506 1637 msgid "Dequeues EDD styles on non-EDD pages (kept on checkout, purchase-confirmation, purchase history and download archives). Turn off if you show download or purchase links elsewhere." 1507 1638 msgstr "" 1508 1639 1509 #: src/index.js:1 3521640 #: src/index.js:1498 1510 1641 msgid "Excluded CSS files (keywords)" 1511 1642 msgstr "" 1512 1643 1513 #: src/index.js:1 3601644 #: src/index.js:1506 1514 1645 msgid "Lazy-load, prevent layout shift, serve next-gen formats, and lighten media." 1515 1646 msgstr "" 1516 1647 1517 #: src/index.js:1 3661648 #: src/index.js:1512 1518 1649 msgid "This server has no GD or ImageMagick extension, so image compression and WebP/AVIF cannot run — every image will be skipped. Ask your host to enable \"php-gd\" (or \"php-imagick\"). See Tools → Server Requirements." 1519 1650 msgstr "" 1520 1651 1521 #: src/index.js:1 3701652 #: src/index.js:1516 1522 1653 msgid "Load off-screen images only when scrolled into view." 1523 1654 msgstr "" 1524 1655 1525 #: src/index.js:1 3721656 #: src/index.js:1518 1526 1657 msgid "Lazy-load iframes" 1527 1658 msgstr "" 1528 1659 1529 #: src/index.js:1 3741660 #: src/index.js:1520 1530 1661 msgid "Lazy-load CSS background images" 1531 1662 msgstr "" 1532 1663 1533 #: src/index.js:1 3751664 #: src/index.js:1521 1534 1665 msgid "Defer inline background-image elements (test with page builders)." 1535 1666 msgstr "" 1536 1667 1537 #: src/index.js:1 3771668 #: src/index.js:1523 1538 1669 msgid "Replace YouTube embeds with a preview" 1539 1670 msgstr "" 1540 1671 1541 #: src/index.js:1 3781672 #: src/index.js:1524 1542 1673 msgid "Load the video iframe only when the visitor clicks play." 1543 1674 msgstr "" 1544 1675 1545 #: src/index.js:1 3801676 #: src/index.js:1526 1546 1677 msgid "Add missing image dimensions" 1547 1678 msgstr "" 1548 1679 1549 #: src/index.js:1 3811680 #: src/index.js:1527 1550 1681 msgid "Set width/height to prevent layout shift (CLS)." 1551 1682 msgstr "" 1552 1683 1553 #: src/index.js:1 3831684 #: src/index.js:1529 1554 1685 msgid "Preload the LCP image" 1555 1686 msgstr "" 1556 1687 1557 #: src/index.js:1 3841688 #: src/index.js:1530 1558 1689 msgid "Preload the first/hero image with high priority." 1559 1690 msgstr "" 1560 1691 1561 #: src/index.js:1 3871692 #: src/index.js:1533 1562 1693 msgid "Keep first images eager (LCP)" 1563 1694 msgstr "" 1564 1695 1565 #: src/index.js:1 3881696 #: src/index.js:1534 1566 1697 msgid "Do not lazy-load the first N images, to protect LCP." 1567 1698 msgstr "" 1568 1699 1569 #: src/index.js:1 3931700 #: src/index.js:1539 1570 1701 msgid "Serve WebP via <picture>" 1571 1702 msgstr "" 1572 1703 1573 #: src/index.js:1 3941704 #: src/index.js:1540 1574 1705 msgid "Deliver WebP to supporting browsers, original as fallback." 1575 1706 msgstr "" 1576 1707 1577 #: src/index.js:1 3961708 #: src/index.js:1542 1578 1709 msgid "Generate WebP/AVIF on upload" 1579 1710 msgstr "" 1580 1711 1581 #: src/index.js:1 3981712 #: src/index.js:1544 1582 1713 msgid "WebP generation is not available on this server." 1583 1714 msgstr "" 1584 1715 1585 #: src/index.js:1 3991716 #: src/index.js:1545 1586 1717 msgid "Auto-create next-gen copies for newly uploaded images." 1587 1718 msgstr "" 1588 1719 1589 #: src/index.js:1 4021720 #: src/index.js:1548 1590 1721 msgid "Also generate/serve AVIF" 1591 1722 msgstr "" 1592 1723 1593 #: src/index.js:1 4031724 #: src/index.js:1549 1594 1725 msgid "Smaller than WebP; served first when supported." 1595 1726 msgstr "" 1596 1727 1597 #: src/index.js:1 4071728 #: src/index.js:1553 1598 1729 msgid "Compress & resize on upload" 1599 1730 msgstr "" 1600 1731 1601 #: src/index.js:1 4081732 #: src/index.js:1554 1602 1733 msgid "Recompress, cap dimensions, and strip metadata from new uploads." 1603 1734 msgstr "" 1604 1735 1605 #: src/index.js:1 4101736 #: src/index.js:1556 1606 1737 msgid "Convert opaque PNG to JPEG" 1607 1738 msgstr "" 1608 1739 1609 #: src/index.js:1 4111740 #: src/index.js:1557 1610 1741 msgid "Re-save non-transparent PNGs as much smaller JPEGs during bulk optimization." 1611 1742 msgstr "" 1612 1743 1613 #: src/index.js:1 4131744 #: src/index.js:1559 1614 1745 msgid "Keep a backup of originals" 1615 1746 msgstr "" 1616 1747 1617 #: src/index.js:1 4141748 #: src/index.js:1560 1618 1749 msgid "Copy originals to uploads/optimazz-backup before optimizing." 1619 1750 msgstr "" 1620 1751 1621 #: src/index.js:1 4161752 #: src/index.js:1562 1622 1753 msgid "Image quality" 1623 1754 msgstr "" 1624 1755 1625 #: src/index.js:1 4181756 #: src/index.js:1564 1626 1757 msgid "Used for compression and WebP/AVIF generation." 1627 1758 msgstr "" 1628 1759 1629 #: src/index.js:1 4191760 #: src/index.js:1565 1630 1761 msgid "Max image width (px)" 1631 1762 msgstr "" 1632 1763 1633 #: src/index.js:1 4241764 #: src/index.js:1570 1634 1765 msgid "WebP coverage" 1635 1766 msgstr "" 1636 1767 1637 #: src/index.js:1 4261768 #: src/index.js:1572 1638 1769 msgid "images converted" 1639 1770 msgstr "" 1640 1771 1641 #: src/index.js:1 4271642 #: src/index.js:1 4411772 #: src/index.js:1573 1773 #: src/index.js:1587 1643 1774 msgid "Loading…" 1644 1775 msgstr "" 1645 1776 1646 #: src/index.js:1 4321777 #: src/index.js:1578 1647 1778 msgid "Convert to WebP" 1648 1779 msgstr "" 1649 1780 1650 #: src/index.js:1 4381781 #: src/index.js:1584 1651 1782 msgid "Bulk compression" 1652 1783 msgstr "" 1653 1784 1654 #: src/index.js:1 4401785 #: src/index.js:1586 1655 1786 msgid "optimized" 1656 1787 msgstr "" 1657 1788 1658 #: src/index.js:1 4441789 #: src/index.js:1590 1659 1790 msgid "Compress existing images" 1660 1791 msgstr "" 1661 1792 1662 #: src/index.js:1 4521793 #: src/index.js:1598 1663 1794 msgid "Warm the cache automatically so visitors never hit a cold page." 1664 1795 msgstr "" 1665 1796 1666 #: src/index.js:1 4531797 #: src/index.js:1600 1667 1798 msgid "Activate preloading" 1668 1799 msgstr "" 1669 1800 1670 #: src/index.js:1 4541801 #: src/index.js:1601 1671 1802 msgid "Warms the cache on a schedule and right after every full purge (for example after changing a setting). Warming follows the sitemap — home, pages, posts, custom post types, then the busiest category and tag archives — most-important first, for both mobile and desktop." 1672 1803 msgstr "" 1673 1804 1674 #: src/index.js:1457 1805 #: src/index.js:1604 1806 #: src/index.js:2117 1675 1807 msgid "Automatic preload" 1676 1808 msgstr "" 1677 1809 1678 #: src/index.js:1 4581810 #: src/index.js:1605 1679 1811 msgid "How often to re-warm the whole site. Choose “Every hour” if your content changes often or you use a short cache lifespan." 1680 1812 msgstr "" 1681 1813 1682 #: src/index.js:1470 1814 #. translators: %s: preload frequency, e.g. "twice a day". 1815 #: src/index.js:1618 1816 #, js-format 1817 msgid "Automatic preload runs %s for this site (set for the whole network in Network Admin → Optimazz)." 1818 msgstr "" 1819 1820 #: src/index.js:1621 1821 msgid "Automatic preload is off for the network. You can still preload this site now." 1822 msgstr "" 1823 1824 #: src/index.js:1626 1683 1825 msgid "WP-Cron is disabled on this site (DISABLE_WP_CRON). Scheduled preloads only run if a server cron job calls wp-cron.php — ask your host to set one up (e.g. every 5 minutes)." 1684 1826 msgstr "" 1685 1827 1686 #: src/index.js:1 4751828 #: src/index.js:1631 1687 1829 msgid "Preload now" 1688 1830 msgstr "" 1689 1831 1690 #: src/index.js:1 4831832 #: src/index.js:1639 1691 1833 msgid "Fine-tune which requests are cached." 1692 1834 msgstr "" 1693 1835 1694 #: src/index.js:1 4841836 #: src/index.js:1640 1695 1837 msgid "Never cache these URLs (contains)" 1696 1838 msgstr "" 1697 1839 1698 #: src/index.js:1 4861840 #: src/index.js:1642 1699 1841 msgid "Never cache when these cookies are set" 1700 1842 msgstr "" 1701 1843 1702 #: src/index.js:1 4881844 #: src/index.js:1644 1703 1845 msgid "Never cache for these user agents" 1704 1846 msgstr "" 1705 1847 1706 #: src/index.js:1 4901848 #: src/index.js:1646 1707 1849 msgid "Ignore these query arguments" 1708 1850 msgstr "" 1709 1851 1710 #: src/index.js:1 4981852 #: src/index.js:1654 1711 1853 msgid "Safe Mode" 1712 1854 msgstr "" 1713 1855 1714 #: src/index.js:1 4991856 #: src/index.js:1655 1715 1857 msgid "If a theme or page builder looks broken after optimizing, turn this on. It pauses the riskiest optimizations — combine CSS, async CSS, remove block CSS and JavaScript delay — while keeping caching, minify, defer and lazy-load. Your saved settings are not changed; they simply pause until you turn Safe Mode off." 1716 1858 msgstr "" 1717 1859 1718 #: src/index.js:1 5021860 #: src/index.js:1658 1719 1861 msgid "Pause risky optimizations (Safe Mode)" 1720 1862 msgstr "" 1721 1863 1722 #: src/index.js:1 5091864 #: src/index.js:1665 1723 1865 msgid "Safe Mode is ON — combine/async CSS, remove-block-CSS and JavaScript delay are paused site-wide. Turn it off to resume full optimization." 1724 1866 msgstr "" 1725 1867 1726 #: src/index.js:1 5141868 #: src/index.js:1670 1727 1869 msgid "Undo last change" 1728 1870 msgstr "" 1729 1871 1730 #: src/index.js:1 5181872 #: src/index.js:1674 1731 1873 msgid "Restores the settings from just before your last save, reset or Safe Mode toggle." 1732 1874 msgstr "" 1733 1875 1734 #: src/index.js:1 5191876 #: src/index.js:1675 1735 1877 msgid "Nothing to undo yet — this becomes available after your next change." 1736 1878 msgstr "" 1737 1879 1738 #: src/index.js:1 5231880 #: src/index.js:1679 1739 1881 msgid "Server Requirements" 1740 1882 msgstr "" 1741 1883 1742 #: src/index.js:1 5241884 #: src/index.js:1680 1743 1885 msgid "What your host supports. Warnings here explain why a feature (like WebP/AVIF) may be unavailable or slow." 1744 1886 msgstr "" 1745 1887 1746 #: src/index.js:1 5251888 #: src/index.js:1681 1747 1889 msgid "Checking your server…" 1748 1890 msgstr "" 1749 1891 1750 #: src/index.js:1 5261892 #: src/index.js:1682 1751 1893 msgid "Could not read the server report." 1752 1894 msgstr "" 1753 1895 1754 #: src/index.js:1 5421896 #: src/index.js:1698 1755 1897 msgid "Re-check" 1756 1898 msgstr "" 1757 1899 1758 #: src/index.js:1 5461900 #: src/index.js:1702 1759 1901 msgid "Import / Export Settings" 1760 1902 msgstr "" 1761 1903 1762 #: src/index.js:1 5471904 #: src/index.js:1703 1763 1905 msgid "Back up your configuration or copy it to another site." 1764 1906 msgstr "" 1765 1907 1766 #: src/index.js:1 5501908 #: src/index.js:1706 1767 1909 msgid "Export settings" 1768 1910 msgstr "" 1769 1911 1770 #: src/index.js:1 5531912 #: src/index.js:1709 1771 1913 msgid "Import settings" 1772 1914 msgstr "" 1773 1915 1774 #: src/index.js:1 5581916 #: src/index.js:1714 1775 1917 msgid "Reset" 1776 1918 msgstr "" 1777 1919 1778 #: src/index.js:1 5591920 #: src/index.js:1715 1779 1921 msgid "Restore every module to its default configuration. This cannot be undone." 1780 1922 msgstr "" 1781 1923 1782 #: src/index.js:1 5621924 #: src/index.js:1718 1783 1925 msgid "Reset all settings to defaults" 1784 1926 msgstr "" 1785 1927 1786 #: src/index.js:1 5711928 #: src/index.js:1727 1787 1929 msgid "Swap, preconnect, and preload fonts to remove render-blocking requests." 1788 1930 msgstr "" 1789 1931 1790 #: src/index.js:1 5741932 #: src/index.js:1730 1791 1933 msgid "Font loading (font-display)" 1792 1934 msgstr "" 1793 1935 1794 #: src/index.js:1 5751936 #: src/index.js:1731 1795 1937 msgid "Optional = no layout shift (best for CLS): the web font is skipped on the first uncached load and used once cached. Swap = text stays visible but the web font swaps in and can shift layout (higher CLS). Block = brief invisible text, then the web font." 1796 1938 msgstr "" 1797 1939 1798 #: src/index.js:1 5781940 #: src/index.js:1734 1799 1941 msgid "Optional — no layout shift (recommended for CLS)" 1800 1942 msgstr "" 1801 1943 1802 #: src/index.js:1 5791944 #: src/index.js:1735 1803 1945 msgid "Swap — always show text (may shift layout)" 1804 1946 msgstr "" 1805 1947 1806 #: src/index.js:1 5801948 #: src/index.js:1736 1807 1949 msgid "Block — brief hidden text, then web font" 1808 1950 msgstr "" 1809 1951 1810 #: src/index.js:1 5841952 #: src/index.js:1740 1811 1953 msgid "Combine Google Fonts into one request" 1812 1954 msgstr "" 1813 1955 1814 #: src/index.js:1 5851956 #: src/index.js:1741 1815 1957 msgid "Page builders often load each Google Font family separately. This merges them into a single request." 1816 1958 msgstr "" 1817 1959 1818 #: src/index.js:1 5891960 #: src/index.js:1745 1819 1961 msgid "Load only the font weights your pages use" 1820 1962 msgstr "" 1821 1963 1822 #: src/index.js:1 5901964 #: src/index.js:1746 1823 1965 msgid "Drops unused weights (e.g. 100, 200, 900) and italics your pages never use — often cutting font CSS by more than half. Weights 400 and 700 are always kept. Turn off if some text looks thinner or bolder than intended." 1824 1966 msgstr "" 1825 1967 1826 #: src/index.js:1594 1968 #: src/index.js:1751 1969 msgid "Load Google Fonts without blocking the page" 1970 msgstr "" 1971 1972 #: src/index.js:1752 1973 msgid "The page appears right away in a fallback font and switches to your Google Font as soon as it arrives, instead of waiting for Google first. A big win for the first paint on mobile. Applies when font display is Swap or Fallback; with Optional the stylesheet keeps loading first so text never shifts." 1974 msgstr "" 1975 1976 #: src/index.js:1756 1827 1977 msgid "Preload these font files (URLs)" 1828 1978 msgstr "" 1829 1979 1830 1980 #. translators: %d: number of items found in the database. 1831 #: src/index.js:1 6051981 #: src/index.js:1767 1832 1982 #, js-format 1833 1983 msgid "%d item in your database." … … 1836 1986 msgstr[1] "" 1837 1987 1838 #: src/index.js:1 6101988 #: src/index.js:1772 1839 1989 msgid "Database Optimization" 1840 1990 msgstr "" 1841 1991 1842 #: src/index.js:1 6111992 #: src/index.js:1773 1843 1993 msgid "Reduce bloat and reclaim space. Once a cleanup runs it cannot be undone, so back up your database first." 1844 1994 msgstr "" 1845 1995 1846 #: src/index.js:1 6271996 #: src/index.js:1789 1847 1997 msgid "Set it and forget it" 1848 1998 msgstr "" 1849 1999 1850 #: src/index.js:1 6282000 #: src/index.js:1790 1851 2001 msgid "Let Optimazz repeat the cleanups you picked above on a schedule, so your database stays lean without you lifting a finger." 1852 2002 msgstr "" 1853 2003 1854 #: src/index.js:1 6292004 #: src/index.js:1791 1855 2005 msgid "Schedule automatic cleanup" 1856 2006 msgstr "" 1857 2007 1858 #: src/index.js:1 6302008 #: src/index.js:1792 1859 2009 msgid "Uses WP-Cron to repeat the cleanups you enabled above." 1860 2010 msgstr "" 1861 2011 1862 #: src/index.js:1 6332012 #: src/index.js:1795 1863 2013 msgid "Cleanup frequency" 1864 2014 msgstr "" 1865 2015 1866 #: src/index.js:1 6372016 #: src/index.js:1799 1867 2017 msgid "Daily" 1868 2018 msgstr "" 1869 2019 1870 #: src/index.js:1 6382020 #: src/index.js:1800 1871 2021 msgid "Weekly" 1872 2022 msgstr "" 1873 2023 1874 #: src/index.js:1 6392024 #: src/index.js:1801 1875 2025 msgid "Monthly" 1876 2026 msgstr "" 1877 2027 1878 #: src/index.js:1 6462028 #: src/index.js:1808 1879 2029 msgid "Back up your database before you run a cleanup — a database optimization cannot be undone." 1880 2030 msgstr "" 1881 2031 1882 #: src/index.js:1 6512032 #: src/index.js:1813 1883 2033 msgid "Optimize now" 1884 2034 msgstr "" 1885 2035 1886 #: src/index.js:1 6542036 #: src/index.js:1816 1887 2037 msgid "Refresh counts" 1888 2038 msgstr "" 1889 2039 1890 #: src/index.js:1 6612040 #: src/index.js:1823 1891 2041 msgid "Heartbeat Control" 1892 2042 msgstr "" 1893 2043 1894 #: src/index.js:1 6622044 #: src/index.js:1824 1895 2045 msgid "Reduce or disable the WordPress Heartbeat API to lower server load." 1896 2046 msgstr "" 1897 2047 1898 #: src/index.js:1 6632048 #: src/index.js:1825 1899 2049 msgid "Enable Heartbeat control" 1900 2050 msgstr "" 1901 2051 1902 #: src/index.js:1 6672052 #: src/index.js:1829 1903 2053 msgid "Behavior in admin dashboard" 1904 2054 msgstr "" 1905 2055 1906 #: src/index.js:1 6692056 #: src/index.js:1831 1907 2057 msgid "Behavior in post editor" 1908 2058 msgstr "" 1909 2059 1910 #: src/index.js:1 6712060 #: src/index.js:1833 1911 2061 msgid "Behavior on front-end" 1912 2062 msgstr "" 1913 2063 1914 #: src/index.js:1 6802064 #: src/index.js:1842 1915 2065 msgid "Strip unnecessary WordPress front-end weight." 1916 2066 msgstr "" 1917 2067 1918 #: src/index.js:1 6822068 #: src/index.js:1844 1919 2069 msgid "Disable emojis" 1920 2070 msgstr "" 1921 2071 1922 #: src/index.js:1 6832072 #: src/index.js:1845 1923 2073 msgid "Remove the emoji detection script and styles." 1924 2074 msgstr "" 1925 2075 1926 #: src/index.js:1 6852076 #: src/index.js:1847 1927 2077 msgid "Disable embeds (oEmbed)" 1928 2078 msgstr "" 1929 2079 1930 #: src/index.js:1 6872080 #: src/index.js:1849 1931 2081 msgid "Disable dashicons on front-end" 1932 2082 msgstr "" 1933 2083 1934 #: src/index.js:1 6882084 #: src/index.js:1850 1935 2085 msgid "Only for anonymous visitors." 1936 2086 msgstr "" 1937 2087 1938 #: src/index.js:1 6902088 #: src/index.js:1852 1939 2089 msgid "Remove query strings from assets" 1940 2090 msgstr "" 1941 2091 1942 #: src/index.js:1 6912092 #: src/index.js:1853 1943 2093 msgid "Strip ?ver= for better proxy caching." 1944 2094 msgstr "" 1945 2095 1946 #: src/index.js:1 6932096 #: src/index.js:1855 1947 2097 msgid "Disable XML-RPC" 1948 2098 msgstr "" 1949 2099 1950 #: src/index.js:1 6952100 #: src/index.js:1857 1951 2101 msgid "Disable self-pingbacks" 1952 2102 msgstr "" 1953 2103 1954 #: src/index.js:1 6972104 #: src/index.js:1859 1955 2105 msgid "Remove head meta (generator, RSD, shortlink)" 1956 2106 msgstr "" 1957 2107 1958 #: src/index.js:1 6992108 #: src/index.js:1861 1959 2109 msgid "DNS-prefetch domains" 1960 2110 msgstr "" 1961 2111 1962 #: src/index.js:1 7012112 #: src/index.js:1863 1963 2113 msgid "Preconnect origins" 1964 2114 msgstr "" 1965 2115 1966 #: src/index.js:1 7072116 #: src/index.js:1869 1967 2117 msgid "Content Delivery Network" 1968 2118 msgstr "" 1969 2119 1970 #: src/index.js:1 7082120 #: src/index.js:1870 1971 2121 msgid "Rewrite static asset URLs to your CDN hostname." 1972 2122 msgstr "" 1973 2123 1974 #: src/index.js:1 7102124 #: src/index.js:1872 1975 2125 msgid "CDN URL (CNAME)" 1976 2126 msgstr "" 1977 2127 1978 #: src/index.js:1 7132128 #: src/index.js:1875 1979 2129 msgid "CSS, JS, image, and font URLs will be served from here." 1980 2130 msgstr "" 1981 2131 1982 #: src/index.js:1 7142132 #: src/index.js:1876 1983 2133 msgid "Exclude paths containing" 1984 2134 msgstr "" 1985 2135 1986 #: src/index.js:1781 1987 msgid "You have unsaved changes" 1988 msgstr "" 1989 1990 #: src/index.js:1781 1991 msgid "All changes saved" 1992 msgstr "" 1993 1994 #: src/index.js:1784 1995 msgid "Save changes" 1996 msgstr "" 2136 #: src/index.js:1952 2137 msgid "Preloading cache…" 2138 msgstr "" 2139 2140 #: src/index.js:2012 2141 #: src/index.js:2025 2142 msgid "Something went wrong." 2143 msgstr "" 2144 2145 #: src/index.js:2059 2146 msgid "Network settings saved." 2147 msgstr "" 2148 2149 #: src/index.js:2075 2150 msgid "Caching for every site" 2151 msgstr "" 2152 2153 #: src/index.js:2076 2154 msgid "Sites" 2155 msgstr "" 2156 2157 #: src/index.js:2076 2158 msgid "Cache per site" 2159 msgstr "" 2160 2161 #: src/index.js:2086 2162 msgid "Optimazz is not network-activated" 2163 msgstr "" 2164 2165 #: src/index.js:2087 2166 msgid "Only the sites where Optimazz is activated are cached. Network-activate it on Network Admin → Plugins to cache every site." 2167 msgstr "" 2168 2169 #: src/index.js:2092 2170 msgid "Serve ready-made HTML for every site instead of running PHP on each request. Each site keeps its own cache and its own exclusions." 2171 msgstr "" 2172 2173 #: src/index.js:2100 2174 msgid "Add Expires / Cache-Control rules to the shared .htaccess so repeat visits load CSS, JS, images and fonts from the browser cache. Apache and LiteSpeed only." 2175 msgstr "" 2176 2177 #: src/index.js:2103 2178 msgid "How long a cached page is kept. Pages are always purged as soon as their content changes." 2179 msgstr "" 2180 2181 #: src/index.js:2113 2182 msgid "Preload every site automatically" 2183 msgstr "" 2184 2185 #: src/index.js:2114 2186 msgid "Each site warms its own pages on a schedule and right after its cache is cleared." 2187 msgstr "" 2188 2189 #. translators: %s: name of another caching plugin. 2190 #: src/index.js:2135 2191 #, js-format 2192 msgid "%s controls page caching (advanced-cache.php)" 2193 msgstr "" 2194 2195 #: src/index.js:2138 2196 msgid "Optimazz cannot cache while another plugin owns the cache file. Deactivate that plugin, or let Optimazz take over." 2197 msgstr "" 2198 2199 #: src/index.js:2139 2200 msgid "Optimazz now handles page caching." 2201 msgstr "" 2202 2203 #: src/index.js:2146 2204 msgid "Cache engine installed (advanced-cache.php)" 2205 msgstr "" 2206 2207 #: src/index.js:2147 2208 msgid "WP_CACHE is on in wp-config.php" 2209 msgstr "" 2210 2211 #: src/index.js:2148 2212 msgid "wp-config.php is not writable — add define( 'WP_CACHE', true ); yourself" 2213 msgstr "" 2214 2215 #: src/index.js:2149 2216 msgid "The wp-content folder is not writable" 2217 msgstr "" 2218 2219 #: src/index.js:2167 2220 msgid "All sites" 2221 msgstr "" 2222 2223 #. translators: 1: sites with Optimazz, 2: all sites. 2224 #: src/index.js:2170 2225 #, js-format 2226 msgid "Optimazz runs on %1$d of %2$d sites. Each site has its own cache." 2227 msgstr "" 2228 2229 #: src/index.js:2172 2230 msgid "Page caching is off for the network — turn it on under Page Cache to purge or preload sites." 2231 msgstr "" 2232 2233 #: src/index.js:2175 2234 msgid "Every site’s cache was purged." 2235 msgstr "" 2236 2237 #: src/index.js:2176 2238 msgid "Purge all sites" 2239 msgstr "" 2240 2241 #: src/index.js:2194 2242 msgid "Site cache purged." 2243 msgstr "" 2244 2245 #: src/index.js:2215 2246 msgid "Optimazz is not active on this site, so its pages are not cached." 2247 msgstr "" 2248 2249 #: src/index.js:2225 2250 msgid "Network" 2251 msgstr "" -
optimazz/tags/1.1.0/readme.txt
r3723776 r3743505 5 5 Tested up to: 7.1 6 6 Requires PHP: 7.4 7 Stable tag: 1. 0.247 Stable tag: 1.1.0 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 49 49 50 50 == Frequently Asked Questions == 51 52 = Does Optimazz work on WordPress multisite? = 53 54 Yes. Network-activate it, then switch page caching on for every site in Network Admin → Optimazz (super admins only). Each site gets its own cache and keeps its own settings and cache exclusions. You can also activate Optimazz on individual sites only; then only those sites are cached. 51 55 52 56 = Does Optimazz work alongside another caching plugin? = … … 100 104 101 105 == Changelog == 106 107 = 1.1.0 = 108 * Fixed: Google Fonts never loaded on sites whose theme or plugin requests them in the old weight format through the new Google Fonts address (for example "Open Sans:400,700" or "w400,700"). Google rejects those requests, so the font was missing while the broken stylesheet still held back the first paint on mobile by most of a second. Optimazz now repairs them into a valid request. 109 * New: Google Fonts load without blocking the page (on by default, Fonts settings). With font display Swap or Fallback the page appears right away in a fallback font and switches when the web font arrives, instead of waiting for Google first. With Optional (zero layout shift) the stylesheet still loads first, so text never shifts. 110 * Improved: sites using Jetpack's Site Accelerator load WordPress core stylesheets from c0.wp.com, which Optimazz used to skip as external, so they stayed render-blocking. When the version matches your WordPress, they are now minified, combined and loaded like local files. 111 * Improved: more chat widgets are delayed until the visitor interacts — OpenWidget, Zendesk, Drift, Freshchat / Freshworks, JivoChat, Smartsupp and Small Chat — cutting mobile Total Blocking Time. 112 * New: full multisite support. Network-activate Optimazz and manage page caching for the whole network in Network Admin → Optimazz: switch it on once, choose mobile and logged-in caching, cache lifetime, browser-cache rules and automatic preload, and see every site with its cached pages and its own Purge and Preload buttons. Each site gets its own cache — on subdirectory and subdomain networks — keeps its own exclusions (Advanced Rules) and all its other Optimazz settings, and can purge and preload its own pages. 113 * Improved: on multisite only the network administrator controls the files every site shares (wp-config.php, advanced-cache.php, .htaccess); a site's own settings never change them, so one site can't switch caching on or off for the others. 114 * Fixed: before this version, on multisite one site's settings controlled caching for every site, only one site of a subdomain network was actually cached, any site administrator could rewrite wp-config.php and .htaccess, and deactivating Optimazz on one site removed the cache for the whole network. 115 * Fixed: deleting Optimazz from a multisite network only cleaned up the main site; settings, scheduled tasks, image data and generated CSS of the other sites were left behind. Uninstall now cleans every site. 116 * Fixed: on sites using plain permalinks (addresses like ?page_id=5) the "Cached pages" count showed 1 no matter how many pages were cached. 117 * Improved: the preload summary now names the home page and counts each address once, so its parts add up to the total. 118 * Improved: changing a cache rule (for example a new exclusion) now also clears pages that were cached under the old rules. 119 * Improved: the Changelog section of this readme now lists recent versions only; the complete history is in changelog.txt. 102 120 103 121 = 1.0.24 = … … 153 171 == Upgrade Notice == 154 172 173 = 1.1.0 = 174 Multisite network support: manage page caching for every site from Network Admin → Optimazz, with a separate cache per site. Also a faster mobile first paint (Google Fonts) and many fixes. Recommended. 175 155 176 = 1.0.24 = 156 177 Scheduled cache preloading now runs on time even when every visitor gets a cached page, works behind 6G/7G firewalls, and the cache rebuilds itself after you change settings. Also warns you when another caching plugin is blocking the Optimazz page cache. Recommended. -
optimazz/tags/1.1.0/src/index.js
r3723128 r3743505 58 58 const NAV_ALL = [ ...NAV, ...PRO_SECTIONS ]; 59 59 60 // wp_localize_script() sends top-level scalars as strings: false arrives as "" 61 // and true as "1", so test for every "off" form. 62 const isOn = ( v ) => ! [ false, '', '0', 0, undefined, null ].includes( v ); 63 // Single site: this site controls page caching itself. On multisite a super 64 // admin switches it on for the whole network (Network Admin → Optimazz) and 65 // each site keeps its own cache, exclusions, purge and preload. 66 const SERVER_FEATURES = ! [ false, '', '0', 0 ].includes( window.optimazzData?.serverFeatures ); 67 const NETWORK_CACHE = window.optimazzData?.networkCache || null; 68 const NETWORK_CACHE_ON = ! SERVER_FEATURES && !! NETWORK_CACHE && isOn( NETWORK_CACHE.enabled ); 69 const CACHE_AVAILABLE = SERVER_FEATURES || NETWORK_CACHE_ON; 70 const IS_SUPER_ADMIN = isOn( window.optimazzData?.isSuperAdmin ); 71 const NETWORK_URL = window.optimazzData?.networkAdminUrl || ''; 72 73 const formatBytes = ( b ) => { 74 if ( ! b ) return '0 KB'; 75 const u = [ 'B', 'KB', 'MB', 'GB' ]; 76 const i = Math.floor( Math.log( b ) / Math.log( 1024 ) ); 77 return `${ ( b / Math.pow( 1024, i ) ).toFixed( i ? 1 : 0 ) } ${ u[ i ] }`; 78 }; 79 80 const intervalLabel = ( v ) => ( { 81 hourly: __( 'every hour', 'optimazz' ), 82 twicedaily: __( 'twice a day', 'optimazz' ), 83 daily: __( 'once a day', 'optimazz' ), 84 }[ v ] || __( 'twice a day', 'optimazz' ) ); 85 86 const lifespanLabel = ( h ) => { 87 const map = { 88 0: __( 'never expires', 'optimazz' ), 89 12: __( '12 hours', 'optimazz' ), 90 24: __( '1 day', 'optimazz' ), 91 168: __( '1 week', 'optimazz' ), 92 720: __( '1 month', 'optimazz' ), 93 }; 94 /* translators: %d: number of hours. */ 95 return map[ Number( h ) ] ?? sprintf( _n( '%d hour', '%d hours', Number( h ), 'optimazz' ), Number( h ) ); 96 }; 97 98 function NetworkLink() { 99 if ( ! IS_SUPER_ADMIN || ! NETWORK_URL ) { 100 return null; 101 } 102 return <a className="oz-cachewarn__link" href={ NETWORK_URL }>{ __( 'Open Network Admin → Optimazz', 'optimazz' ) }</a>; 103 } 104 105 // Multisite, network caching off. 106 function MultisiteCacheNotice() { 107 return ( 108 <div className="oz-cachewarn" role="status"> 109 <span className="dashicons dashicons-info-outline" /> 110 <div className="oz-cachewarn__text"> 111 <strong>{ __( 'Page caching is off for this network', 'optimazz' ) }</strong> 112 <p>{ IS_SUPER_ADMIN 113 ? __( 'Turn it on for every site in Network Admin → Optimazz. Each site gets its own cache, and keeps its own exclusions here under Advanced Rules.', 'optimazz' ) 114 : __( 'Your network administrator can turn it on for every site in Network Admin → Optimazz. All other optimizations — CSS, JavaScript, images, fonts and more — work normally on this site.', 'optimazz' ) }</p> 115 <NetworkLink /> 116 </div> 117 </div> 118 ); 119 } 120 121 // Multisite, network caching on: this site's Page Cache screen. 122 function NetworkCacheOn() { 123 const n = NETWORK_CACHE || {}; 124 return ( 125 <div className="oz-cachewarn oz-cachewarn--ok" role="status"> 126 <span className="dashicons dashicons-yes-alt" /> 127 <div className="oz-cachewarn__text"> 128 <strong>{ __( 'Page caching is on — managed for the whole network', 'optimazz' ) }</strong> 129 <p>{ sprintf( 130 /* translators: 1: yes/no, 2: cache lifetime, 3: preload frequency. */ 131 __( 'Separate mobile cache: %1$s · Cache lifetime: %2$s · Automatic preload: %3$s.', 'optimazz' ), 132 isOn( n.cache_mobile ) ? __( 'yes', 'optimazz' ) : __( 'no', 'optimazz' ), 133 lifespanLabel( n.lifespan_hours ), 134 isOn( n.preload ) ? intervalLabel( n.preload_interval ) : __( 'off', 'optimazz' ) 135 ) }</p> 136 <p>{ __( 'This site has its own cache. Purge or preload it from the Dashboard or the Preload screen, and set its own exclusions under Advanced Rules.', 'optimazz' ) }</p> 137 <NetworkLink /> 138 </div> 139 </div> 140 ); 141 } 60 142 const MODULES = [ 'cache', 'js', 'css', 'media', 'fonts', 'database', 'heartbeat', 'tweaks', 'cdn' ]; 61 143 … … 407 489 408 490 /* ------------------------------------------------------------------ */ 491 492 /** 493 * The Optimazz admin layout shared by the site screen and Network Admin: 494 * brand + sidebar navigation, page header, content, save bar and toast. 495 */ 496 function AppShell( { className = '', items, nav, onNav, foot, topIcon, title, sub, dirty, saving, onSave, toast, onToastRemove, children, after } ) { 497 return ( 498 <div className={ `oz-shell ${ className }` }> 499 <aside className="oz-sidebar"> 500 <div className="oz-sidebar__brand"> 501 { window.optimazzData?.logoUrl ? ( 502 <img className="oz-logo-img" src={ window.optimazzData.logoUrl } alt="Optimazz" /> 503 ) : ( 504 <div className="oz-sidebar__brand-fallback"> 505 <span className="oz-logo dashicons dashicons-performance" /> 506 <strong>Optimazz</strong> 507 </div> 508 ) } 509 </div> 510 <nav> 511 { items.map( ( n ) => ( 512 <button key={ n.key } className={ `oz-navitem ${ nav === n.key ? 'is-active' : '' }` } onClick={ () => onNav( n.key ) }> 513 <Icon name={ n.svg || n.key } paths={ n.iconSvg } /> 514 <span className="oz-navitem__text"> 515 <strong>{ n.title }</strong> 516 <small>{ n.sub }</small> 517 </span> 518 { n.dot && <i className="oz-navitem__dot" /> } 519 </button> 520 ) ) } 521 </nav> 522 <div className="oz-sidebar__foot">{ foot }</div> 523 </aside> 524 525 <div className="oz-content"> 526 <header className="oz-topbar"> 527 <span className={ `oz-topbar__icon dashicons dashicons-${ topIcon }` } /> 528 <div> 529 <h1>{ title }</h1> 530 <p>{ sub }</p> 531 </div> 532 </header> 533 <main className="oz-main">{ children }</main> 534 </div> 535 536 <div className={ `oz-savebar ${ dirty ? 'is-dirty' : '' }` }> 537 <span className="oz-savebar__hint"> 538 { dirty ? __( 'You have unsaved changes', 'optimazz' ) : __( 'All changes saved', 'optimazz' ) } 539 </span> 540 <Button className="oz-btn-primary" isBusy={ saving } disabled={ ! dirty || saving } onClick={ onSave }> 541 { __( 'Save changes', 'optimazz' ) } 542 </Button> 543 </div> 544 545 { toast && ( 546 <div className="oz-toast"> 547 <Snackbar status={ toast.status } onRemove={ onToastRemove }> 548 { toast.text } 549 </Snackbar> 550 </div> 551 ) } 552 { after } 553 </div> 554 ); 555 } 409 556 410 557 function App() { … … 919 1066 }; 920 1067 921 const fmtBytes = ( b ) => { 922 if ( ! b ) return '0 KB'; 923 const u = [ 'B', 'KB', 'MB', 'GB' ]; 924 const i = Math.floor( Math.log( b ) / Math.log( 1024 ) ); 925 return `${ ( b / Math.pow( 1024, i ) ).toFixed( i ? 1 : 0 ) } ${ u[ i ] }`; 926 }; 927 928 const enabledCount = MODULES.filter( ( k ) => settings[ k ]?.enabled ).length; 1068 const fmtBytes = formatBytes; 1069 1070 // On multisite the page cache is unavailable, so it is not counted. 1071 const usableModules = SERVER_FEATURES ? MODULES : MODULES.filter( ( k ) => k !== 'cache' ); 1072 const enabledCount = usableModules.filter( ( k ) => settings[ k ]?.enabled ).length; 929 1073 const c = settings.cache || {}; 930 1074 const j = settings.js || {}; … … 942 1086 // Live performance score — weighted by which optimizations are enabled. 943 1087 const scoreParts = [ 944 { on: !! c.enabled, w: 30, label: __( 'Page caching', 'optimazz' ) },1088 { on: !! c.enabled, w: 30, label: __( 'Page caching', 'optimazz' ), server: true }, 945 1089 { on: !! ( j.enabled && j.delay ), w: 18, label: __( 'Delay JavaScript', 'optimazz' ) }, 946 1090 { on: !! ( j.enabled && j.defer ), w: 8, label: __( 'Defer JavaScript', 'optimazz' ) }, … … 949 1093 { on: !! ( m.enabled && ( m.webp || m.webp_generate ) ), w: 8, label: __( 'Next-gen images', 'optimazz' ) }, 950 1094 { on: !! ( f.enabled && f.display_swap ), w: 6, label: __( 'Font optimization', 'optimazz' ) }, 951 { on: !! c.preload, w: 5, label: __( 'Cache preloading', 'optimazz' ) },1095 { on: !! c.preload, w: 5, label: __( 'Cache preloading', 'optimazz' ), server: true }, 952 1096 { on: !! h.enabled, w: 3, label: __( 'Heartbeat control', 'optimazz' ) }, 953 1097 ]; 954 const score = scoreParts.reduce( ( a, p ) => a + ( p.on ? p.w : 0 ), 0 ); 955 const nextWin = scoreParts.filter( ( p ) => ! p.on ).sort( ( a, b ) => b.w - a.w )[ 0 ]; 1098 const usableParts = CACHE_AVAILABLE ? scoreParts : scoreParts.filter( ( p ) => p.server !== true ); 1099 const maxScore = usableParts.reduce( ( a, p ) => a + p.w, 0 ) || 1; 1100 const score = Math.round( ( usableParts.reduce( ( a, p ) => a + ( p.on ? p.w : 0 ), 0 ) / maxScore ) * 100 ); 1101 const nextWin = usableParts.filter( ( p ) => ! p.on ).sort( ( a, b ) => b.w - a.w )[ 0 ]; 956 1102 const grade = score >= 90 ? 'A' : score >= 75 ? 'B' : score >= 55 ? 'C' : score >= 35 ? 'D' : 'E'; 957 1103 const ring = 2 * Math.PI * 52; 958 1104 959 const optimizingWithoutCache = ( j.enabled || s.enabled || m.enabled ) && ! c.enabled;1105 const optimizingWithoutCache = SERVER_FEATURES && ( j.enabled || s.enabled || m.enabled ) && ! c.enabled; 960 1106 961 1107 const dashboard = ( … … 997 1143 </p> 998 1144 <div className="oz-score__chips"> 999 { scoreParts.map( ( p ) => (1145 { usableParts.map( ( p ) => ( 1000 1146 <span key={ p.label } className={ `oz-chip ${ p.on ? 'is-on' : '' }` }> 1001 1147 <span className={ `dashicons dashicons-${ p.on ? 'yes-alt' : 'marker' }` } /> … … 1007 1153 </div> 1008 1154 1009 <div className= "oz-kpis">1010 <div className="oz-kpi">1155 <div className={ `oz-kpis${ CACHE_AVAILABLE ? '' : ' oz-kpis--single' }` }> 1156 { CACHE_AVAILABLE && <div className="oz-kpi"> 1011 1157 <span className="dashicons dashicons-database" /> 1012 1158 <strong>{ stats ? ( stats.pages ?? stats.files ) : <Spinner /> }</strong> 1013 1159 <span>{ __( 'Cached pages', 'optimazz' ) }</span> 1014 </div> 1015 <div className="oz-kpi">1160 </div> } 1161 { CACHE_AVAILABLE && <div className="oz-kpi"> 1016 1162 <span className="dashicons dashicons-chart-area" /> 1017 1163 <strong>{ stats ? fmtBytes( stats.bytes ) : <Spinner /> }</strong> 1018 1164 <span>{ __( 'Cache size', 'optimazz' ) }</span> 1019 </div> 1165 </div> } 1020 1166 <div className="oz-kpi"> 1021 1167 <span className="dashicons dashicons-yes-alt" /> 1022 <strong>{ enabledCount }/{ MODULES.length }</strong>1168 <strong>{ enabledCount }/{ usableModules.length }</strong> 1023 1169 <span>{ __( 'Modules active', 'optimazz' ) }</span> 1024 1170 </div> … … 1047 1193 1048 1194 <div className="oz-quick"> 1049 <Panel title={ __( 'Cache Files', 'optimazz' ) } description={ __( 'Clear and preload all cache files.', 'optimazz' ) }>1195 { CACHE_AVAILABLE && <Panel title={ __( 'Cache Files', 'optimazz' ) } description={ __( 'Clear and preload all cache files.', 'optimazz' ) }> 1050 1196 <div className="oz-quick__actions"> 1051 1197 <Button className="oz-btn-primary" icon="trash" isBusy={ !! job?.running } onClick={ async () => { await purge(); preload(); } }> … … 1132 1278 </li> 1133 1279 </ul> 1134 </Panel> 1280 </Panel> } 1135 1281 <Panel title={ __( 'Module Status', 'optimazz' ) } description={ __( 'Enable or disable any module right here.', 'optimazz' ) }> 1136 1282 <ul className="oz-status"> 1137 { NAV.filter( ( n ) => n.mod ).map( ( n ) => (1283 { NAV.filter( ( n ) => n.mod && ( SERVER_FEATURES || n.mod !== 'cache' ) ).map( ( n ) => ( 1138 1284 <li key={ n.key }> 1139 1285 <Icon name={ n.key } /> … … 1168 1314 ); 1169 1315 1170 const cacheSection = (1316 const cacheSection = ! SERVER_FEATURES ? ( NETWORK_CACHE_ON ? <NetworkCacheOn /> : <MultisiteCacheNotice /> ) : ( 1171 1317 <Card icon="database" title={ __( 'Page Cache', 'optimazz' ) } 1172 1318 description={ __( 'Serve static HTML instead of running PHP on every request.', 'optimazz' ) } … … 1448 1594 ); 1449 1595 1450 const preloadSection = (1596 const preloadSection = ! CACHE_AVAILABLE ? <MultisiteCacheNotice /> : ( 1451 1597 <Panel title={ __( 'Preload Cache', 'optimazz' ) } 1452 1598 description={ __( 'Warm the cache automatically so visitors never hit a cold page.', 'optimazz' ) }> 1599 { SERVER_FEATURES ? ( <> 1453 1600 <ToggleControl __nextHasNoMarginBottom label={ __( 'Activate preloading', 'optimazz' ) } 1454 1601 help={ __( 'Warms the cache on a schedule and right after every full purge (for example after changing a setting). Warming follows the sitemap — home, pages, posts, custom post types, then the busiest category and tag archives — most-important first, for both mobile and desktop.', 'optimazz' ) } … … 1465 1612 ] } 1466 1613 onChange={ ( v ) => set( 'cache', 'preload_interval', v ) } /> 1614 </> ) : ( 1615 <p className="oz-muted">{ isOn( NETWORK_CACHE?.preload ) 1616 ? sprintf( 1617 /* translators: %s: preload frequency, e.g. "twice a day". */ 1618 __( 'Automatic preload runs %s for this site (set for the whole network in Network Admin → Optimazz).', 'optimazz' ), 1619 intervalLabel( NETWORK_CACHE?.preload_interval ) 1620 ) 1621 : __( 'Automatic preload is off for the network. You can still preload this site now.', 'optimazz' ) }</p> 1622 ) } 1467 1623 { stats && stats.wp_cron_disabled && ( 1468 1624 <div className="oz-notice oz-notice--warn" style={ { marginTop: 12 } }> … … 1479 1635 ); 1480 1636 1481 const advancedSection = (1637 const advancedSection = ! CACHE_AVAILABLE ? <MultisiteCacheNotice /> : ( 1482 1638 <Panel title={ __( 'Advanced Rules', 'optimazz' ) } 1483 1639 description={ __( 'Fine-tune which requests are cached.', 'optimazz' ) }> … … 1592 1748 disabled={ ! f.combine_google } 1593 1749 onChange={ ( v ) => set( 'fonts', 'trim_weights', v ) } /> 1750 <ToggleControl __nextHasNoMarginBottom 1751 label={ __( 'Load Google Fonts without blocking the page', 'optimazz' ) } 1752 help={ __( 'The page appears right away in a fallback font and switches to your Google Font as soon as it arrives, instead of waiting for Google first. A big win for the first paint on mobile. Applies when font display is Swap or Fallback; with Optional the stylesheet keeps loading first so text never shifts.', 'optimazz' ) } 1753 checked={ f.async_google !== false } 1754 disabled={ ! f.combine_google } 1755 onChange={ ( v ) => set( 'fonts', 'async_google', v ) } /> 1594 1756 <FormTokenField label={ __( 'Preload these font files (URLs)', 'optimazz' ) } 1595 1757 value={ f.preload || [] } onChange={ ( v ) => set( 'fonts', 'preload', v ) } /> … … 1739 1901 1740 1902 return ( 1741 <div className="oz-shell"> 1742 <aside className="oz-sidebar"> 1743 <div className="oz-sidebar__brand"> 1744 { window.optimazzData?.logoUrl ? ( 1745 <img className="oz-logo-img" src={ window.optimazzData.logoUrl } alt="Optimazz" /> 1746 ) : ( 1747 <div className="oz-sidebar__brand-fallback"> 1748 <span className="oz-logo dashicons dashicons-performance" /> 1749 <strong>Optimazz</strong> 1750 </div> 1751 ) } 1752 </div> 1753 <nav> 1754 { NAV_ALL.map( ( n ) => ( 1755 <button key={ n.key } className={ `oz-navitem ${ nav === n.key ? 'is-active' : '' }` } onClick={ () => setNav( n.key ) }> 1756 <Icon name={ n.key } paths={ n.iconSvg } /> 1757 <span className="oz-navitem__text"> 1758 <strong>{ n.title }</strong> 1759 <small>{ n.sub }</small> 1760 </span> 1761 { n.mod && settings[ n.mod ]?.enabled && <i className="oz-navitem__dot" /> } 1762 </button> 1763 ) ) } 1764 </nav> 1765 <div className="oz-sidebar__foot">v{ window.optimazzData?.version }</div> 1766 </aside> 1767 1768 <div className="oz-content"> 1769 <header className="oz-topbar"> 1770 <span className={ `oz-topbar__icon dashicons dashicons-${ active.icon }` } /> 1771 <div> 1772 <h1>{ active.title }</h1> 1773 <p>{ active.sub }</p> 1774 </div> 1775 </header> 1776 <main className="oz-main">{ sections[ nav ] }</main> 1777 </div> 1778 1779 <div className={ `oz-savebar ${ dirty ? 'is-dirty' : '' }` }> 1780 <span className="oz-savebar__hint"> 1781 { dirty ? __( 'You have unsaved changes', 'optimazz' ) : __( 'All changes saved', 'optimazz' ) } 1782 </span> 1783 <Button className="oz-btn-primary" isBusy={ saving } disabled={ ! dirty } onClick={ save }> 1784 { __( 'Save changes', 'optimazz' ) } 1785 </Button> 1786 </div> 1787 1788 { toast && ( 1789 <div className="oz-toast"> 1790 <Snackbar status={ toast.status } onRemove={ () => setToast( null ) }> 1791 { toast.text } 1792 </Snackbar> 1793 </div> 1794 ) } 1795 1796 <JobPanel 1797 job={ job } 1798 onStop={ stopJob } 1799 onClose={ closeJob } 1800 onToggleMin={ () => setJob( ( j ) => j && { ...j, minimized: ! j.minimized } ) } 1801 /> 1802 </div> 1903 <AppShell 1904 items={ NAV_ALL.map( ( n ) => ( { ...n, dot: !! ( n.mod && settings[ n.mod ]?.enabled ) } ) ) } 1905 nav={ nav } onNav={ setNav } 1906 foot={ `v${ window.optimazzData?.version }` } 1907 topIcon={ active.icon } title={ active.title } sub={ active.sub } 1908 dirty={ dirty } saving={ saving } onSave={ save } 1909 toast={ toast } onToastRemove={ () => setToast( null ) } 1910 after={ 1911 <JobPanel 1912 job={ job } 1913 onStop={ stopJob } 1914 onClose={ closeJob } 1915 onToggleMin={ () => setJob( ( j ) => j && { ...j, minimized: ! j.minimized } ) } 1916 /> 1917 } 1918 > 1919 { sections[ nav ] } 1920 </AppShell> 1803 1921 ); 1804 1922 } … … 1808 1926 createRoot( mount ).render( <App /> ); 1809 1927 } 1928 1929 /* ------------------------------------------------------------------ * 1930 * Network Admin → Optimazz (multisite, super admins) 1931 * ------------------------------------------------------------------ */ 1932 1933 const NET_DEFAULTS = { enabled: false, cache_mobile: true, cache_logged_in: false, lifespan_hours: 0, preload: true, preload_interval: 'twicedaily', browser_cache: false }; 1934 1935 function NetworkApp() { 1936 const [ data, setData ] = useState( null ); 1937 const [ draft, setDraft ] = useState( NET_DEFAULTS ); 1938 const [ dirty, setDirty ] = useState( false ); 1939 const [ saving, setSaving ] = useState( false ); 1940 const [ busy, setBusy ] = useState( '' ); 1941 const [ notice, setNotice ] = useState( null ); 1942 const [ nav, setNav ] = useState( 'cache' ); 1943 const [ job, setJob ] = useState( null ); 1944 const stopRef = useRef( false ); 1945 1946 // Live preload of one site, in the same progress panel as the site screen. 1947 const runPreload = async ( site ) => { 1948 if ( job?.running ) { 1949 return; 1950 } 1951 stopRef.current = false; 1952 const opening = __( 'Preloading cache…', 'optimazz' ); 1953 setJob( { title: __( 'Preloading cache', 'optimazz' ), subtitle: site.name || site.url, pct: 0, label: opening, step: '', logs: [ { level: 'INFO', text: opening } ], running: true, status: 'running', minimized: false } ); 1954 const acc = { cached: 0, missed: 0, diagnosis: '' }; 1955 const started = Date.now(); 1956 let offset = 0; 1957 let total = 0; 1958 const end = ( status, level, text, extra = {} ) => setJob( ( j ) => j && { ...j, running: false, status, ...extra, logs: [ ...j.logs, { level, text } ] } ); 1959 try { 1960 // eslint-disable-next-line no-constant-condition 1961 while ( true ) { 1962 if ( stopRef.current ) { 1963 end( 'stopped', 'WARN', __( 'Stopped by user.', 'optimazz' ) ); 1964 break; 1965 } 1966 const res = await apiFetch( { url: `${ restUrl }/network/preload-batch`, method: 'POST', data: { site_id: site.id, offset } } ); 1967 const prev = offset; 1968 offset = res.next_offset ?? offset + ( res.processed || 0 ); 1969 total = res.total || total; 1970 acc.cached += res.cached || 0; 1971 acc.missed += res.missed || 0; 1972 acc.diagnosis = res.diagnosis || acc.diagnosis; 1973 const pct = total ? Math.min( 100, Math.round( ( offset / total ) * 100 ) ) : 100; 1974 const line = `${ __( 'Warmed', 'optimazz' ) } ${ Math.min( offset, total ) } / ${ total } ${ __( 'pages', 'optimazz' ) } · ${ acc.cached } ${ __( 'cached', 'optimazz' ) }${ acc.missed ? ` · ${ acc.missed } ${ __( 'not cached', 'optimazz' ) }` : '' }`; 1975 const rate = offset / Math.max( ( Date.now() - started ) / 1000, 0.001 ); 1976 const left = rate > 0 && total && offset < total ? ( total - offset ) / rate : 0; 1977 const eta = left >= 60 ? ` · ~${ Math.round( left / 60 ) }m ${ __( 'left', 'optimazz' ) }` : ( left >= 1 ? ` · ~${ Math.round( left ) }s ${ __( 'left', 'optimazz' ) }` : '' ); 1978 setJob( ( j ) => j && { 1979 ...j, 1980 pct, 1981 label: line, 1982 step: total ? `${ Math.min( offset, total ) } / ${ total }${ eta }` : '', 1983 logs: [ 1984 ...j.logs.slice( -60 ), 1985 ...( res.breakdown ? [ { level: 'INFO', text: `${ sprintf( 1986 /* translators: %d: number of pages to warm. */ 1987 __( 'Found %d pages to warm:', 'optimazz' ), res.total || 0 1988 ) } ${ res.breakdown }.` } ] : [] ), 1989 { level: 'INFO', text: line }, 1990 ], 1991 } ); 1992 if ( res.blocked ) { 1993 end( 'error', 'ERROR', __( 'Warming stopped: the server returned 403/429, so a firewall or security plugin is blocking the preload requests. Cached what we could; allow-list the "Optimazz-CacheWarmer" user-agent in your firewall or security plugin (e.g. All-In-One Security, Wordfence, Cloudflare), or slow warming with the optimazz_preload_delay_ms filter.', 'optimazz' ) ); 1994 break; 1995 } 1996 if ( ! res.done && acc.cached === 0 && acc.missed >= 3 ) { 1997 end( 'error', 'ERROR', `${ __( 'Stopped: pages are loading but none are being saved to the cache.', 'optimazz' ) } ${ acc.diagnosis }`, { label: __( 'Pages are not being cached', 'optimazz' ) } ); 1998 break; 1999 } 2000 if ( res.done || offset <= prev ) { 2001 if ( acc.missed ) { 2002 setJob( ( j ) => j && { ...j, logs: [ ...j.logs, { level: 'WARN', text: `${ sprintf( 2003 /* translators: %d: number of pages. */ 2004 _n( '%d page was not cached.', '%d pages were not cached.', acc.missed, 'optimazz' ), acc.missed 2005 ) } ${ acc.diagnosis }` } ] } ); 2006 } 2007 end( 'done', 'DONE', __( 'Completed.', 'optimazz' ), { pct: 100 } ); 2008 break; 2009 } 2010 } 2011 } catch ( e ) { 2012 end( 'error', 'ERROR', e?.message || __( 'Something went wrong.', 'optimazz' ) ); 2013 } 2014 // Refresh the cached-page counts on the cards. 2015 apiFetch( { url: `${ restUrl }/network` } ).then( ( res ) => { 2016 setData( res ); 2017 } ).catch( () => {} ); 2018 }; 2019 2020 const apply = ( res ) => { 2021 setData( res ); 2022 setDraft( { ...NET_DEFAULTS, ...( res.settings || {} ) } ); 2023 setDirty( false ); 2024 }; 2025 const fail = ( e ) => setNotice( { status: 'error', text: e?.message || __( 'Something went wrong.', 'optimazz' ) } ); 2026 const call = async ( path, body, key, okText ) => { 2027 setBusy( key ); 2028 try { 2029 const res = await apiFetch( { url: `${ restUrl }${ path }`, method: 'POST', data: body } ); 2030 // Keep any unsaved Page Cache edits when acting on sites. 2031 setData( res ); 2032 if ( ! dirty ) { 2033 setDraft( { ...NET_DEFAULTS, ...( res.settings || {} ) } ); 2034 } 2035 if ( okText ) { 2036 setNotice( { status: 'success', text: okText } ); 2037 } 2038 setBusy( '' ); 2039 return true; 2040 } catch ( e ) { 2041 fail( e ); 2042 } 2043 setBusy( '' ); 2044 return false; 2045 }; 2046 2047 useEffect( () => { 2048 apiFetch( { url: `${ restUrl }/network` } ).then( apply ).catch( fail ); 2049 }, [] ); 2050 2051 const set = ( key, value ) => { 2052 setDraft( ( d ) => ( { ...d, [ key ]: value } ) ); 2053 setDirty( true ); 2054 }; 2055 const save = async () => { 2056 setSaving( true ); 2057 try { 2058 apply( await apiFetch( { url: `${ restUrl }/network/settings`, method: 'POST', data: { settings: draft } } ) ); 2059 setNotice( { status: 'success', text: __( 'Network settings saved.', 'optimazz' ) } ); 2060 } catch ( e ) { 2061 fail( e ); 2062 } 2063 setSaving( false ); 2064 }; 2065 2066 if ( ! data ) { 2067 return <div className="oz-shell oz-shell--network-app"><div className="oz-main"><Spinner /></div></div>; 2068 } 2069 const h = data.health || {}; 2070 const sites = data.sites || []; 2071 const cached = sites.filter( ( s ) => s.active ); 2072 const on = !! data.settings?.enabled; 2073 2074 const NET_NAV = [ 2075 { key: 'cache', svg: 'cache', icon: 'database', title: __( 'Page Cache', 'optimazz' ), sub: __( 'Caching for every site', 'optimazz' ), dot: on }, 2076 { key: 'sites', svg: 'dashboard', icon: 'admin-multisite', title: __( 'Sites', 'optimazz' ), sub: __( 'Cache per site', 'optimazz' ) }, 2077 ]; 2078 const active = NET_NAV.find( ( n ) => n.key === nav ) || NET_NAV[ 0 ]; 2079 2080 const cacheSection = ( 2081 <> 2082 { ! data.network_active && ( 2083 <div className="oz-cachewarn" role="status"> 2084 <span className="dashicons dashicons-info-outline" /> 2085 <div className="oz-cachewarn__text"> 2086 <strong>{ __( 'Optimazz is not network-activated', 'optimazz' ) }</strong> 2087 <p>{ __( 'Only the sites where Optimazz is activated are cached. Network-activate it on Network Admin → Plugins to cache every site.', 'optimazz' ) }</p> 2088 </div> 2089 </div> 2090 ) } 2091 <Card icon="database" title={ __( 'Page Cache', 'optimazz' ) } 2092 description={ __( 'Serve ready-made HTML for every site instead of running PHP on each request. Each site keeps its own cache and its own exclusions.', 'optimazz' ) } 2093 enabled={ !! draft.enabled } onToggle={ () => set( 'enabled', ! draft.enabled ) }> 2094 <ToggleControl __nextHasNoMarginBottom label={ __( 'Cache mobile devices separately', 'optimazz' ) } 2095 checked={ !! draft.cache_mobile } onChange={ () => set( 'cache_mobile', ! draft.cache_mobile ) } /> 2096 <ToggleControl __nextHasNoMarginBottom label={ __( 'Cache logged-in users', 'optimazz' ) } 2097 help={ __( 'Leave off unless you know what you are doing.', 'optimazz' ) } 2098 checked={ !! draft.cache_logged_in } onChange={ () => set( 'cache_logged_in', ! draft.cache_logged_in ) } /> 2099 <ToggleControl __nextHasNoMarginBottom label={ __( 'Browser cache headers', 'optimazz' ) } 2100 help={ __( 'Add Expires / Cache-Control rules to the shared .htaccess so repeat visits load CSS, JS, images and fonts from the browser cache. Apache and LiteSpeed only.', 'optimazz' ) } 2101 checked={ !! draft.browser_cache } onChange={ () => set( 'browser_cache', ! draft.browser_cache ) } /> 2102 <PillGroup label={ __( 'Cache lifetime', 'optimazz' ) } 2103 help={ __( 'How long a cached page is kept. Pages are always purged as soon as their content changes.', 'optimazz' ) } 2104 value={ Number( draft.lifespan_hours ) || 0 } 2105 options={ [ 2106 { value: 0, label: __( 'Never', 'optimazz' ) }, 2107 { value: 12, label: __( '12 hours', 'optimazz' ) }, 2108 { value: 24, label: __( '1 day', 'optimazz' ) }, 2109 { value: 168, label: __( '1 week', 'optimazz' ) }, 2110 { value: 720, label: __( '1 month', 'optimazz' ) }, 2111 ] } 2112 onChange={ ( v ) => set( 'lifespan_hours', v ) } /> 2113 <ToggleControl __nextHasNoMarginBottom label={ __( 'Preload every site automatically', 'optimazz' ) } 2114 help={ __( 'Each site warms its own pages on a schedule and right after its cache is cleared.', 'optimazz' ) } 2115 checked={ !! draft.preload } onChange={ () => set( 'preload', ! draft.preload ) } /> 2116 <SelectControl __nextHasNoMarginBottom __next40pxDefaultSize 2117 label={ __( 'Automatic preload', 'optimazz' ) } 2118 value={ draft.preload_interval || 'twicedaily' } 2119 disabled={ ! draft.preload } 2120 options={ [ 2121 { label: __( 'Every hour', 'optimazz' ), value: 'hourly' }, 2122 { label: __( 'Twice a day', 'optimazz' ), value: 'twicedaily' }, 2123 { label: __( 'Once a day', 'optimazz' ), value: 'daily' }, 2124 ] } 2125 onChange={ ( v ) => set( 'preload_interval', v ) } /> 2126 </Card> 2127 { ( on || h.conflict ) && ( 2128 <Panel title={ __( 'Cache health', 'optimazz' ) }> 2129 { h.conflict ? ( 2130 <div className="oz-cachewarn" role="status"> 2131 <span className="dashicons dashicons-warning" /> 2132 <div className="oz-cachewarn__text"> 2133 <strong>{ sprintf( 2134 /* translators: %s: name of another caching plugin. */ 2135 __( '%s controls page caching (advanced-cache.php)', 'optimazz' ), 2136 h.conflict_name || __( 'Another plugin', 'optimazz' ) 2137 ) }</strong> 2138 <p>{ __( 'Optimazz cannot cache while another plugin owns the cache file. Deactivate that plugin, or let Optimazz take over.', 'optimazz' ) }</p> 2139 <Button className="oz-btn-primary" isBusy={ 'takeover' === busy } onClick={ () => call( '/network/takeover', {}, 'takeover', __( 'Optimazz now handles page caching.', 'optimazz' ) ) }> 2140 { __( 'Use Optimazz for caching', 'optimazz' ) } 2141 </Button> 2142 </div> 2143 </div> 2144 ) : ( 2145 <> 2146 <HealthRow ok={ h.dropin_ours } label={ __( 'Cache engine installed (advanced-cache.php)', 'optimazz' ) } /> 2147 <HealthRow ok={ h.wp_cache } label={ __( 'WP_CACHE is on in wp-config.php', 'optimazz' ) } /> 2148 { ! h.wpconfig_writable && ! h.wp_cache && <HealthRow ok={ false } label={ __( 'wp-config.php is not writable — add define( \'WP_CACHE\', true ); yourself', 'optimazz' ) } /> } 2149 { ! h.content_writable && <HealthRow ok={ false } label={ __( 'The wp-content folder is not writable', 'optimazz' ) } /> } 2150 </> 2151 ) } 2152 </Panel> 2153 ) } 2154 </> 2155 ); 2156 2157 // Clear this site's cache, then warm it live in the progress panel. 2158 const clearAndPreload = async ( site ) => { 2159 const ok = await call( '/network/purge', { site_id: site.id }, `clear-${ site.id }` ); 2160 if ( ok ) { 2161 runPreload( site ); 2162 } 2163 }; 2164 2165 const sitesSection = ( 2166 <> 2167 <Panel title={ __( 'All sites', 'optimazz' ) } 2168 description={ sprintf( 2169 /* translators: 1: sites with Optimazz, 2: all sites. */ 2170 __( 'Optimazz runs on %1$d of %2$d sites. Each site has its own cache.', 'optimazz' ), cached.length, sites.length 2171 ) }> 2172 { ! on && <p className="oz-muted">{ __( 'Page caching is off for the network — turn it on under Page Cache to purge or preload sites.', 'optimazz' ) }</p> } 2173 <div className="oz-quick__actions"> 2174 <Button className="oz-btn-ghost oz-btn--x" icon="dismiss" disabled={ ! on } isBusy={ 'purge-all' === busy } 2175 onClick={ () => call( '/network/purge', {}, 'purge-all', __( 'Every site’s cache was purged.', 'optimazz' ) ) }> 2176 { __( 'Purge all sites', 'optimazz' ) } 2177 </Button> 2178 </div> 2179 </Panel> 2180 <div className="oz-quick oz-network__sites"> 2181 { sites.map( ( s ) => ( 2182 <Panel key={ s.id } title={ s.name || s.url } description={ s.url }> 2183 { s.active ? ( 2184 <> 2185 <div className="oz-quick__actions"> 2186 <Button className="oz-btn-primary" icon="trash" disabled={ ! on || !! job?.running } isBusy={ `clear-${ s.id }` === busy } onClick={ () => clearAndPreload( s ) }> 2187 { __( 'Clear & Preload', 'optimazz' ) } 2188 </Button> 2189 <Button className="oz-btn-ghost" icon="update" disabled={ ! on || !! job?.running } 2190 onClick={ () => runPreload( s ) }> 2191 { __( 'Preload', 'optimazz' ) } 2192 </Button> 2193 <Button className="oz-btn-ghost oz-btn--x" icon="dismiss" disabled={ ! on } isBusy={ `purge-${ s.id }` === busy } 2194 onClick={ () => call( '/network/purge', { site_id: s.id }, `purge-${ s.id }`, __( 'Site cache purged.', 'optimazz' ) ) }> 2195 { __( 'Purge cache', 'optimazz' ) } 2196 </Button> 2197 <Button className="oz-btn-ghost" icon="admin-generic" href={ s.admin_url }> 2198 { __( 'Settings', 'optimazz' ) } 2199 </Button> 2200 </div> 2201 <ul className="oz-cachelist"> 2202 <li> 2203 <span className="dashicons dashicons-media-text" /> 2204 { __( 'Cached pages', 'optimazz' ) } 2205 <strong>{ s.pages }</strong> 2206 </li> 2207 <li> 2208 <span className="dashicons dashicons-database" /> 2209 { __( 'Page cache size', 'optimazz' ) } 2210 <strong>{ formatBytes( s.bytes ) }</strong> 2211 </li> 2212 </ul> 2213 </> 2214 ) : ( 2215 <p className="oz-muted">{ __( 'Optimazz is not active on this site, so its pages are not cached.', 'optimazz' ) }</p> 2216 ) } 2217 </Panel> 2218 ) ) } 2219 </div> 2220 </> 2221 ); 2222 return ( 2223 <AppShell className="oz-shell--network-app" 2224 items={ NET_NAV } nav={ nav } onNav={ setNav } 2225 foot={ `${ __( 'Network', 'optimazz' ) } · v${ window.optimazzData?.version }` } 2226 topIcon={ active.icon } title={ active.title } sub={ active.sub } 2227 dirty={ dirty } saving={ saving } onSave={ save } 2228 toast={ notice } onToastRemove={ () => setNotice( null ) } 2229 after={ 2230 <JobPanel 2231 job={ job } 2232 onStop={ () => { 2233 stopRef.current = true; 2234 } } 2235 onClose={ () => { 2236 stopRef.current = true; 2237 setJob( null ); 2238 } } 2239 onToggleMin={ () => setJob( ( j ) => j && { ...j, minimized: ! j.minimized } ) } 2240 /> 2241 } 2242 > 2243 { 'sites' === nav ? sitesSection : cacheSection } 2244 </AppShell> 2245 ); 2246 } 2247 2248 const networkMount = document.getElementById( 'optimazz-network-app' ); 2249 if ( networkMount ) { 2250 createRoot( networkMount ).render( <NetworkApp /> ); 2251 } -
optimazz/tags/1.1.0/src/index.scss
r3723128 r3743505 402 402 grid-template-columns: repeat(3, 1fr); 403 403 gap: 14px; 404 405 // Multisite: only "Modules active" remains, so it spans the row. 406 &--single { 407 grid-template-columns: 1fr; 408 } 404 409 } 405 410 … … 1802 1807 .dashicons { color: #e0900f; } 1803 1808 } 1809 &--ok { 1810 background: #eaf8f3; 1811 border-color: #b8e6d6; 1812 color: #0f5e48; 1813 .dashicons { color: #0e9f7e; } 1814 } 1804 1815 } 1805 1816 … … 1882 1893 } 1883 1894 } 1895 1896 /* ------------------------------------------------------------------ * 1897 * Multisite: network-managed cache states + Network Admin screen 1898 * ------------------------------------------------------------------ */ 1899 .oz-cachewarn--ok { 1900 background: #eaf8f3; 1901 border-color: #b8e6d6; 1902 color: #0f5e48; 1903 } 1904 1905 .oz-cachewarn__text p + p { 1906 margin-top: 6px; 1907 } 1908 1909 .oz-cachewarn__link { 1910 display: inline-block; 1911 margin-top: 8px; 1912 font-weight: 600; 1913 color: inherit; 1914 text-decoration: underline; 1915 } 1916 1917 // Network → Sites: one Cache Files-style card per site, two per row. 1918 .oz-network__sites { 1919 margin-top: 16px; 1920 align-items: start; 1921 1922 .oz-panel__head h2, 1923 .oz-panel__head p { 1924 overflow-wrap: anywhere; 1925 } 1926 } -
optimazz/tags/1.1.0/uninstall.php
r3723128 r3743505 18 18 // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_rmdir 19 19 20 global $wpdb; 20 // Per-site data (options, transients, image meta, scheduled events). On a 21 // multisite network uninstall runs once, so it is repeated for every site. 22 $optimazz_cleanup_site = static function () { 23 global $wpdb; 21 24 22 // 1. Settings option (and the single-level undo backup). 23 delete_option( 'optimazz_settings' ); 24 delete_option( 'optimazz_settings_backup' ); 25 delete_option( 'optimazz_version' ); // Post-update reconciliation marker. 26 delete_option( 'optimazz_guard' ); // Update safety guard state. 27 delete_option( 'optimazz_last_preload' ); // Last auto-preload run (dashboard). 28 delete_option( 'optimazz_preload_cursor' ); // Position of a half-finished automatic preload. 29 delete_transient( 'optimazz_preload_urls' ); // Cached preload URL list. 30 delete_transient( 'optimazz_preload_urls_breakdown' ); // Counts behind the preload list. 31 delete_transient( 'optimazz_preload_lock' ); // Running-chunk lock. 32 delete_metadata( 'user', 0, 'optimazz_conflict_dismissed', '', true ); // Dismissed cache-conflict notice. 33 wp_clear_scheduled_hook( 'optimazz_health_check' ); 25 // 1. Settings option (and the single-level undo backup). 26 delete_option( 'optimazz_settings' ); 27 delete_option( 'optimazz_settings_backup' ); 28 delete_option( 'optimazz_version' ); // Post-update reconciliation marker. 29 delete_option( 'optimazz_guard' ); // Update safety guard state. 30 delete_option( 'optimazz_last_preload' ); // Last auto-preload run (dashboard). 31 delete_option( 'optimazz_preload_cursor' ); // Position of a half-finished automatic preload. 32 delete_transient( 'optimazz_preload_urls' ); // Cached preload URL list. 33 delete_transient( 'optimazz_preload_urls_breakdown' ); // Counts behind the preload list. 34 delete_transient( 'optimazz_preload_lock' ); // Running-chunk lock. 35 wp_clear_scheduled_hook( 'optimazz_health_check' ); 34 36 35 // 2. Per-image optimization meta.36 $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => '_optimazz_image' ) ); // phpcs:ignore WordPress.DB.SlowDBQuery37 // 2. Per-image optimization meta. 38 $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => '_optimazz_image' ) ); // phpcs:ignore WordPress.DB.SlowDBQuery 37 39 38 // 3. Scheduled events.39 foreach ( array( 'optimazz_preload_cache', 'optimazz_preload_cache_once', 'optimazz_purge_expired', 'optimazz_db_cleanup' ) as $hook ) {40 wp_clear_scheduled_hook( $hook );41 }40 // 3. Scheduled events. 41 foreach ( array( 'optimazz_preload_cache', 'optimazz_preload_cache_once', 'optimazz_purge_expired', 'optimazz_db_cleanup' ) as $hook ) { 42 wp_clear_scheduled_hook( $hook ); 43 } 42 44 43 // 4. Transients (incl. the per-stylesheet font-weight cache). 44 delete_transient( 'optimazz_wpconfig_unwritable' ); 45 $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- one-off cleanup of our own transients on uninstall. 46 $wpdb->prepare( 47 "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s", 48 $wpdb->esc_like( '_transient_optimazz_fw_' ) . '%', 49 $wpdb->esc_like( '_transient_timeout_optimazz_fw_' ) . '%' 50 ) 51 ); 45 // 4. Transients (incl. the per-stylesheet font-weight cache). 46 delete_transient( 'optimazz_wpconfig_unwritable' ); 47 $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- one-off cleanup of our own transients on uninstall. 48 $wpdb->prepare( 49 "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s", 50 $wpdb->esc_like( '_transient_optimazz_fw_' ) . '%', 51 $wpdb->esc_like( '_transient_timeout_optimazz_fw_' ) . '%' 52 ) 53 ); 54 }; 55 56 delete_metadata( 'user', 0, 'optimazz_conflict_dismissed', '', true ); // Dismissed cache-conflict notice (users are network-wide). 57 delete_site_option( 'optimazz_network' ); // Network page-cache settings (multisite). 58 delete_site_option( 'optimazz_network_version' ); 59 delete_site_transient( 'optimazz_heal' ); // Self-heal throttle (multisite). 52 60 53 61 // 5. advanced-cache.php drop-in (only if it is ours). … … 111 119 $optimazz_rrmdir( WP_CONTENT_DIR . '/cache/optimazz' ); 112 120 113 $uploads = wp_get_upload_dir(); 114 if ( ! empty( $uploads['basedir'] ) ) { 115 $optimazz_rrmdir( trailingslashit( $uploads['basedir'] ) . 'optimazz-backup' ); 116 // Generated CSS now lives under uploads/optimazz (moved out of cache/). 117 $optimazz_rrmdir( trailingslashit( $uploads['basedir'] ) . 'optimazz' ); 121 // Generated CSS (uploads/optimazz) and original-image backups 122 // (uploads/optimazz-backup) live in each site's own uploads folder. 123 $optimazz_clean_uploads = static function () use ( $optimazz_rrmdir ) { 124 $uploads = wp_get_upload_dir(); 125 if ( ! empty( $uploads['basedir'] ) ) { 126 $optimazz_rrmdir( trailingslashit( $uploads['basedir'] ) . 'optimazz-backup' ); 127 $optimazz_rrmdir( trailingslashit( $uploads['basedir'] ) . 'optimazz' ); 128 } 129 }; 130 // 8. Per-site cleanup — every site of a network in one pass (uninstall runs once). 131 if ( is_multisite() ) { 132 foreach ( get_sites( array( 'fields' => 'ids', 'number' => 0 ) ) as $optimazz_site_id ) { 133 switch_to_blog( $optimazz_site_id ); 134 $optimazz_cleanup_site(); 135 $optimazz_clean_uploads(); 136 restore_current_blog(); 137 } 138 } else { 139 $optimazz_cleanup_site(); 140 $optimazz_clean_uploads(); 118 141 } -
optimazz/trunk/build/index-rtl.css
r3723128 r3743505 1 .oz-shell{align-items:flex-start;background:#fff;border:1px solid #e7ecef;border-radius:16px;color:#0f2027;display:flex;font-size:14px;margin:10px 0 130px 20px;min-height:640px;overflow:hidden}.oz-shell *{box-sizing:border-box}.oz-shell .dashicons{font-family:dashicons}.oz-sidebar{align-self:flex-start;background:#fff;display:flex;flex:0 0 264px;flex-direction:column;padding:18px 14px}.oz-sidebar__brand{align-items:center;border-bottom:1px solid #e7ecef;display:flex;gap:12px;margin-bottom:10px;padding:10px 10px 18px}.oz-sidebar__brand strong{display:block;font-size:18px;font-weight:750;letter-spacing:-.01em}.oz-sidebar__brand span{color:#6b7a86;font-size:11px}.oz-sidebar__brand-fallback{align-items:center;display:flex;gap:12px}.oz-sidebar__foot{border-top:1px solid #e7ecef;color:#6b7a86;font-size:11px;margin-top:12px;padding:12px 12px 2px}.oz-logo{background:linear-gradient(-135deg,#1dd1a1,#10ac81);border-radius:12px;color:#fff;display:grid;font-size:26px;height:44px;place-items:center;width:44px}.oz-logo-img{display:block;height:30px;max-width:100%;width:auto}.oz-navitem{align-items:center;background:transparent;border:0;border-right:3px solid transparent;border-radius:10px 0 0 10px;color:#0f2027;cursor:pointer;display:flex;gap:12px;padding:11px 12px;text-align:right;transition:all .14s ease;width:100%}.oz-navitem>.oz-ico{color:#6b7a86;flex:0 0 20px;height:20px;transition:color .14s ease,transform .14s ease;width:20px}.oz-navitem:hover>.oz-ico{color:#0f2027}.oz-navitem.is-active>.oz-ico{color:#0c8f6b}.oz-navitem__text{flex:1;min-width:0}.oz-navitem__text strong{display:block;font-size:13.5px;font-weight:650;line-height:1.2}.oz-navitem__text small{color:#6b7a86;font-size:11px}.oz-navitem__dot{background:#1dd1a1;border-radius:50%;height:8px;width:8px}.oz-navitem:hover{background:#f4f7f8}.oz-navitem.is-active{background:rgba(29,209,161,.12);border-right-color:#1dd1a1}.oz-navitem.is-active .oz-navitem__text strong{color:#0c8f6b}.oz-content{align-self:stretch;background:#f4f7f8;border-right:1px solid #e7ecef;display:flex;flex:1;flex-direction:column;min-width:0}.oz-topbar{align-items:center;background:#fff;border-bottom:1px solid #e7ecef;display:flex;gap:14px;padding:20px 28px}.oz-topbar__icon{background:rgba(29,209,161,.14);border-radius:12px;color:#0c8f6b;display:grid;font-size:22px;height:44px;place-items:center;width:44px}.oz-topbar h1{font-size:20px;font-weight:700;margin:0;padding:0}.oz-topbar p{color:#6b7a86;font-size:12.5px;margin:0}.oz-main{display:flex;flex-direction:column;gap:16px;padding:24px 28px}.oz-welcome{align-items:center;background:linear-gradient(-125deg,rgba(29,209,161,.9),rgba(16,172,129,.92));border-radius:16px;color:#06382c;display:flex;gap:20px;justify-content:space-between;overflow:hidden;padding:22px 26px}.oz-welcome h2{color:#06382c;font-size:18px;font-weight:700;margin:0 0 6px}.oz-welcome p{color:#0c5b46;font-size:13px;margin:0;max-width:620px}.oz-welcome__icon{flex:0 0 auto;font-size:64px;height:64px;opacity:.5;width:64px}.oz-score{align-items:center;background:linear-gradient(-125deg,#1dd1a1,#10ac81 62%,#0c8f6b);border-radius:16px;box-shadow:0 16px 34px -18px rgba(12,143,107,.65);color:#06382c;display:flex;gap:30px;overflow:hidden;padding:28px 32px;position:relative}.oz-score:before{background:radial-gradient(circle,hsla(0,0%,100%,.3),transparent 62%);height:440px;left:-8%;top:-55%;width:440px}.oz-score:after,.oz-score:before{border-radius:50%;content:"";pointer-events:none;position:absolute}.oz-score:after{background:radial-gradient(circle,rgba(0,0,0,.08),transparent 60%);bottom:-70%;height:320px;right:8%;width:320px}.oz-score>*{position:relative;z-index:1}.oz-score__gauge{filter:drop-shadow(0 8px 16px rgba(3,40,30,.28));flex:0 0 auto;line-height:0;position:relative}.oz-score__grade{background:#fff;border-radius:50%;box-shadow:0 4px 10px rgba(3,40,30,.28);color:#0c8f6b;display:grid;font-size:14px;font-weight:800;height:30px;place-items:center;position:absolute;left:2px;top:8px;width:30px}.oz-score__body{flex:1;min-width:0}.oz-score__body h2{color:#06382c;font-size:21px;font-weight:750;letter-spacing:-.02em;margin:0 0 4px}.oz-score__body p{color:rgba(6,56,44,.82);font-size:13px;font-weight:500;margin:0 0 15px}.oz-score__chips{display:flex;flex-wrap:wrap;gap:8px}.oz-chip{align-items:center;background:hsla(0,0%,100%,.18);border:1px solid hsla(0,0%,100%,.28);border-radius:999px;color:rgba(6,56,44,.72);display:inline-flex;font-size:11.5px;font-weight:650;gap:6px;padding:5px 9px 5px 12px;transition:transform .15s ease,box-shadow .15s ease}.oz-chip .dashicons{font-size:15px;height:15px;opacity:.65;width:15px}.oz-chip.is-on{background:#fff;border-color:#fff;box-shadow:0 3px 8px -2px rgba(3,40,30,.22);color:#0c8f6b}.oz-chip.is-on .dashicons{color:#1dd1a1;opacity:1}.oz-chip.is-on:hover{box-shadow:0 6px 14px -4px rgba(3,40,30,.3);transform:translateY(-1px)}.oz-convert{align-items:center;background:#f4f7f8;border-radius:12px;display:flex;gap:16px;justify-content:space-between;margin-top:16px;padding:16px}.oz-convert__info{color:#6b7a86;display:flex;flex-direction:column;font-size:12.5px;gap:3px}.oz-convert__info strong{color:#0f2027;font-size:13px}.oz-progress{background:#dfe6e8;border-radius:999px;height:6px;margin-top:4px;overflow:hidden;width:200px}.oz-progress div{background:linear-gradient(-90deg,#1dd1a1,#10ac81);height:100%;transition:width .3s ease}.oz-kpis{display:grid;gap:14px;grid-template-columns:repeat(3,1fr)}.oz-kpi {background:#fff;border:1px solid #e7ecef;border-radius:14px;display:flex;flex-direction:column;padding:16px 18px}.oz-kpi>.dashicons{color:#10ac81;font-size:20px;margin-bottom:8px}.oz-kpi strong{font-size:24px;font-weight:750;min-height:28px}.oz-kpi span{color:#6b7a86;font-size:12px}.oz-quick{display:grid;gap:16px;grid-template-columns:1fr 1fr}.oz-quick__actions{display:flex;flex-wrap:wrap;gap:10px}.oz-cachelist{border-top:1px solid #e7ecef;margin:18px 0 0}.oz-cachelist>li{align-items:center;border-bottom:1px solid #f1f4f5;color:#6b7a86;display:flex;font-size:13px;gap:10px;padding:11px 2px}.oz-cachelist>li:last-child{border-bottom:0}.oz-cachelist>li .dashicons{color:#10ac81;font-size:17px}.oz-cachelist>li strong{color:#0f2027;font-weight:650;margin-right:auto}.oz-cachelist>li .oz-row-label{display:flex;flex-direction:column;line-height:1.3}.oz-cachelist>li .oz-row-label small{color:#6b7a86;font-size:11.5px;opacity:.85}.oz-status{margin:0}.oz-status li{align-items:center;border-bottom:1px solid #e7ecef;display:flex;font-size:13.5px;font-weight:600;gap:10px;padding:9px 6px}.oz-status li:last-child{border-bottom:0}.oz-status li>.oz-ico{color:#6b7a86;flex:0 0 18px;height:18px;width:18px}.oz-status__name{background:transparent;border:0;color:#0f2027;cursor:pointer;flex:1;font:inherit;font-weight:600;padding:4px 0;text-align:right}.oz-status__name:hover{color:#0c8f6b}.oz-status .components-toggle-control{margin-right:auto}.oz-card,.oz-panel{background:#fff;border:1px solid #e7ecef;border-radius:16px;overflow:visible}.oz-card .oz-card__head{border-radius:16px 16px 0 0}.oz-panel__head{padding:18px 22px 0}.oz-panel__head h2{font-size:16px;font-weight:650;margin:0}.oz-panel__head p{color:#6b7a86;font-size:12.5px;margin:4px 0 0}.oz-panel__body{padding:8px 22px 20px}.oz-panel__body .components-base-control{border-bottom:1px solid #f1f4f5;padding:14px 0}.oz-panel__body .components-base-control:last-child{border-bottom:0}.oz-card{transition:box-shadow .18s ease,border-color .18s ease}.oz-card.is-enabled{border-color:rgba(29,209,161,.5);box-shadow:0 0 0 1px rgba(29,209,161,.22)}.oz-card__head{align-items:flex-start;display:flex;gap:15px;padding:20px 22px}.oz-card__icon{background:#f4f7f8;border-radius:12px;color:#6b7a86;display:grid;flex:0 0 46px;font-size:22px;height:46px;place-items:center;width:46px}.oz-card__title{flex:1;min-width:0}.oz-card__title h2{align-items:center;display:flex;font-size:16px;font-weight:650;gap:10px;margin:0 0 3px}.oz-card__title p{color:#6b7a86;font-size:12.5px;margin:0}.oz-card__body{border-top:1px solid #e7ecef;padding:4px 22px 16px}.oz-card__body .components-base-control{border-bottom:1px solid #f1f4f5;padding:14px 0}.oz-card__body .components-base-control:last-child{border-bottom:0}.is-enabled .oz-card__icon{background:linear-gradient(-135deg,#1dd1a1,#10ac81);color:#fff}.oz-inline-actions{display:flex;gap:10px;padding-top:16px}.oz-dbgroup{border-top:1px solid #eef0f2;padding:18px 0}.oz-dbgroup:first-of-type{border-top:0;padding-top:4px}.oz-dbgroup__title{color:#0e1116;font-size:14px;font-weight:700;margin:0 0 4px}.oz-dbgroup__desc{color:#6b7280;font-size:13px;line-height:1.5;margin:0 0 14px}.oz-dbgroup .components-toggle-control{margin-bottom:14px}.oz-dbgroup .components-toggle-control:last-child{margin-bottom:0}.oz-dbwarn{align-items:flex-start;background:#fff5f5;border:1px solid #fcd9d9;border-radius:10px;color:#b42318;display:flex;font-size:13px;font-weight:600;gap:8px;line-height:1.5;margin:4px 0;padding:12px 14px}.oz-dbwarn .dashicons{color:#e05242;flex:0 0 auto}.oz-cachewarn{align-items:center;background:#fff8ec;border:1px solid #f4dcae;border-radius:12px;color:#7a4b00;display:flex;gap:13px;margin:0 0 18px;padding:14px 16px}.oz-cachewarn .dashicons{color:#e0900f;flex:0 0 auto;font-size:22px;height:22px;width:22px}.oz-cachewarn__text{flex:1 1 auto;min-width:0}.oz-cachewarn__text strong{color:#6b4200;display:block;font-size:14px;font-weight:700}.oz-cachewarn__text p{color:#8a6410;font-size:12.5px;line-height:1.5;margin:3px 0 0}.oz-cachewarn .oz-btn-primary{flex:0 0 auto}@media(max-width:640px){.oz-cachewarn{flex-wrap:wrap}.oz-cachewarn .oz-btn-primary{justify-content:center;width:100%}}.oz-subhead{color:#8a96a0;font-size:11px;font-weight:700;letter-spacing:.08em;margin:18px 0 2px;padding-top:14px;text-transform:uppercase}.oz-health,.oz-subhead{border-top:1px solid #e7ecf0}.oz-health{margin-top:18px;padding-top:16px}.oz-health__head{align-items:center;display:flex;gap:12px;justify-content:space-between}.oz-health__head strong{font-size:14px}.oz-health__hint{color:#6b7280;font-size:12px;line-height:1.5;margin:4px 0 10px}.oz-health__list{display:flex;flex-direction:column;gap:6px;margin:0}.oz-health__row{align-items:center;display:flex;font-size:12.5px;gap:8px;line-height:1.4;margin:0}.oz-health__row .dashicons{flex:0 0 auto;font-size:17px;height:17px;width:17px}.oz-health__row.is-ok{color:#216e4e}.oz-health__row.is-ok .dashicons{color:#1dd1a1}.oz-health__row.is-warn{color:#8a5a00}.oz-health__row.is-warn .dashicons{color:#e0900f}.oz-health__row.is-bad{color:#b42318}.oz-health__row.is-bad .dashicons{color:#e05242}.oz-health__err{color:#b42318;font-size:12.5px}.oz-health__fix{align-items:center;display:flex;flex-wrap:wrap;gap:12px;margin-top:14px}.oz-health__fix span{color:#6b7280;flex:1 1 160px;font-size:12px}.oz-health__conflict{display:flex;flex-direction:column;gap:8px;margin-top:12px}.oz-health__conflict-row{align-items:center;background:#fff5f5;border:1px solid #fcd9d9;border-radius:9px;display:flex;flex-wrap:wrap;gap:10px;padding:10px 12px}.oz-health__conflict-row .dashicons{color:#e05242;flex:0 0 auto}.oz-health__conflict-row span:not(.dashicons){color:#8a2f22;flex:1 1 200px;font-size:12.5px}.oz-health__conflict-row .oz-btn-danger.components-button{flex:0 0 auto;height:32px}.oz-health__manual{background:#fff8ec;border:1px solid #f4dcae;border-radius:9px;margin-top:12px;padding:10px 12px}.oz-health__manual p{color:#7a4b00;font-size:12px;margin:0 0 6px}.oz-health__manual code{background:#2b2f36;border-radius:6px;color:#e7f0ec;display:inline-block;font-size:12.5px;padding:6px 10px}.oz-btn-ghost.components-button{border:1px solid #d5dbe0;border-radius:8px;color:#384049;font-size:12.5px;height:30px}.oz-btn-ghost.components-button:hover:not(:disabled){border-color:#1dd1a1;color:#0b7a5c}.oz-pill{align-items:center;border-radius:999px;display:inline-flex;font-size:11px;font-weight:700;gap:5px;padding:3px 9px}.oz-pill i{background:currentColor;border-radius:50%;height:6px;width:6px}.oz-pill.is-on{background:rgba(29,209,161,.16);color:#0c8f6b}.oz-pill.is-off{background:#eef1f2;color:#6b7a86}.oz-shell .components-range-control__thumb,.oz-shell .components-range-control__track{background-color:#1dd1a1!important}.oz-fieldgroup{background:rgba(29,209,161,.035);border:1px solid #e7ecef;border-radius:12px;margin-top:18px;padding:16px 16px 4px}.oz-fieldgroup__title{color:#0e1116;font-size:13px;font-weight:700;margin:0 0 4px}.oz-fieldgroup__desc{color:#6b7280;font-size:12.5px;line-height:1.5;margin:0 0 14px}.oz-fieldgroup .components-base-control{margin-bottom:14px}.oz-shell .components-form-token-field__input-container{background:#fff;border:1px solid #e7ecef;border-radius:10px;min-height:44px;padding:5px 8px;transition:border-color .15s ease,box-shadow .15s ease}.oz-shell .components-form-token-field__input-container.is-active,.oz-shell .components-form-token-field__input-container:focus-within{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)}.oz-shell .components-form-token-field__token{border-radius:8px;font-size:12.5px;margin:3px;overflow:hidden}.oz-shell .components-form-token-field__token-text{background:rgba(29,209,161,.14);border-radius:0 8px 8px 0;color:#0c8f6b;font-weight:650;line-height:1.7;padding:2px 11px 2px 4px}.oz-shell .components-form-token-field__remove-token{background:rgba(29,209,161,.14);border-radius:8px 0 0 8px;color:#0c8f6b;min-width:24px;padding:0 7px;transition:background .15s ease,color .15s ease}.oz-shell .components-form-token-field__remove-token svg{fill:currentColor}.oz-shell .components-form-token-field__remove-token:hover{background:#10ac81;color:#fff}.oz-shell .components-form-token-field__input{font-size:13.5px;margin:4px;min-height:28px}.oz-shell .components-input-control__backdrop{display:none!important}.oz-shell .components-input-control__input,.oz-shell .components-select-control__input,.oz-shell .components-text-control__input{background:#fff!important;border:1px solid #e7ecef!important;border-radius:10px!important;box-shadow:none!important;color:#0f2027;font-size:13.5px;min-height:42px;padding:0 14px}.oz-shell .components-input-control__input:hover,.oz-shell .components-select-control__input:hover,.oz-shell .components-text-control__input:hover{border-color:#bcc6cc!important}.oz-shell .components-input-control__input:focus,.oz-shell .components-select-control__input:focus,.oz-shell .components-text-control__input:focus{border-color:#1dd1a1!important;box-shadow:0 0 0 3px rgba(29,209,161,.16)!important;outline:none!important}.oz-shell select.components-select-control__input{cursor:pointer}.oz-pillgroup .components-base-control__label{display:block;font-weight:600;margin-bottom:8px}.oz-pillgroup__row{background:#f0f2f4;border-radius:12px;display:inline-flex;flex-wrap:wrap;gap:6px;padding:4px}.oz-pillgroup .components-base-control__help{display:block;margin-top:8px}.oz-pillbtn{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;border-radius:9px;color:#4b5563;cursor:pointer;font-size:13px;font-weight:600;min-width:44px;padding:7px 16px;transition:background .12s ease,color .12s ease,box-shadow .12s ease}.oz-pillbtn:hover{color:#0e1116}.oz-pillbtn.is-on{background:#fff;box-shadow:0 1px 3px rgba(4,56,44,.14);color:#0c8f6b}.oz-pillbtn:focus-visible{outline:2px solid #1dd1a1;outline-offset:1px}.oz-select{position:relative}.oz-select .components-base-control__label{display:block;margin-bottom:8px}.oz-select__btn{align-items:center;background:#fff;border:1px solid #e7ecef;border-radius:10px;color:#0f2027;cursor:pointer;display:flex;font-size:13.5px;height:42px;justify-content:space-between;padding:0 14px;transition:border-color .15s ease,box-shadow .15s ease;width:100%}.oz-select__btn .dashicons{color:#0c8f6b;transition:transform .18s ease}.oz-select__btn:hover{border-color:#bcc6cc}.oz-select__btn:focus,.oz-select__btn:focus-visible{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)!important;outline:none!important}.oz-select__btn[aria-expanded=true]{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)}.oz-select__btn[aria-expanded=true] .dashicons{transform:rotate(-180deg)}.oz-select__menu{background:#fff;border:1px solid #e7ecef;border-radius:12px;box-shadow:0 14px 34px rgba(15,32,39,.18);right:0;list-style:none;margin:0;padding:6px;position:absolute;left:0;top:calc(100% + 6px);z-index:30}.oz-select__item{align-items:center;border-radius:8px;color:#0f2027;cursor:pointer;display:flex;font-size:13.5px;justify-content:space-between;padding:9px 12px}.oz-select__item .dashicons{color:#10ac81;font-size:18px}.oz-select__item.is-active,.oz-select__item:hover{background:rgba(29,209,161,.12);color:#0c8f6b}.oz-select__item.is-selected{color:#0c8f6b;font-weight:650}.oz-shell .oz-btn-danger.components-button,.oz-shell .oz-btn-ghost.components-button,.oz-shell .oz-btn-primary.components-button{border-radius:10px;font-weight:650;height:38px;transition:transform .16s cubic-bezier(.2,.7,.3,1),box-shadow .18s ease,background .16s ease,border-color .16s ease,color .16s ease}.oz-shell .oz-btn-danger.components-button .dashicons,.oz-shell .oz-btn-danger.components-button svg,.oz-shell .oz-btn-ghost.components-button .dashicons,.oz-shell .oz-btn-ghost.components-button svg,.oz-shell .oz-btn-primary.components-button .dashicons,.oz-shell .oz-btn-primary.components-button svg{transition:transform .22s cubic-bezier(.2,.7,.3,1)}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled),.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled),.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){transform:translateY(-1px)}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) svg{transform:scale(1.14)}.oz-shell .oz-btn-danger.components-button:active:not(:disabled),.oz-shell .oz-btn-ghost.components-button:active:not(:disabled),.oz-shell .oz-btn-primary.components-button:active:not(:disabled){transform:translateY(0) scale(.975);transition-duration:.05s}.oz-shell .oz-btn-danger.components-button:focus-visible,.oz-shell .oz-btn-ghost.components-button:focus-visible,.oz-shell .oz-btn-primary.components-button:focus-visible{outline:2px solid #1dd1a1;outline-offset:2px}.oz-shell .oz-btn-danger.components-button:disabled,.oz-shell .oz-btn-ghost.components-button:disabled,.oz-shell .oz-btn-primary.components-button:disabled{opacity:.5;transform:none}.oz-shell .oz-btn-primary.components-button{background:linear-gradient(-135deg,#1dd1a1,#10ac81);border:0;box-shadow:0 1px 2px rgba(20,60,48,.12);color:#fff;padding:0 18px}.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){background:linear-gradient(-135deg,#10ac81,#0c8f6b);box-shadow:0 8px 20px -7px rgba(29,209,161,.65);color:#fff}.oz-shell .oz-btn-primary.components-button.is-busy{animation:oz-btn-sheen 1.4s linear infinite;background-size:200% 100%}.oz-shell .oz-btn-ghost.components-button{background:#fff;border:1px solid rgba(29,209,161,.5);box-shadow:none;color:#0c8f6b}.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled){background:rgba(29,209,161,.08);border-color:#1dd1a1;box-shadow:0 6px 16px -8px rgba(29,209,161,.5);color:#0c8f6b}.oz-shell .oz-btn-danger.components-button{background:#fff;border:1px solid rgba(214,54,56,.5);color:#d63638}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled){background:rgba(214,54,56,.08);border-color:#d63638;box-shadow:0 6px 16px -8px rgba(214,54,56,.5);color:#b32d2e}.oz-shell .oz-btn--x.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn--x.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg{transform:scale(1.1) rotate(-90deg)}@media(prefers-reduced-motion:reduce){.oz-shell .oz-btn--x.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn--x.components-button:hover:not(:disabled) svg{transform:none}}@keyframes oz-btn-sheen{0%{background-image:linear-gradient(-135deg,#1dd1a1,#10ac81,#1dd1a1);background-position:-100% 0}to{background-image:linear-gradient(-135deg,#1dd1a1,#10ac81,#1dd1a1);background-position:300% 0}}@media(prefers-reduced-motion:reduce){.oz-shell .oz-btn-danger.components-button,.oz-shell .oz-btn-ghost.components-button,.oz-shell .oz-btn-primary.components-button{transition:background .16s ease,color .16s ease,border-color .16s ease}.oz-shell .oz-btn-danger.components-button:active:not(:disabled),.oz-shell .oz-btn-danger.components-button:hover:not(:disabled),.oz-shell .oz-btn-ghost.components-button:active:not(:disabled),.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled),.oz-shell .oz-btn-primary.components-button:active:not(:disabled),.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){transform:none}.oz-shell .oz-btn-danger.components-button .dashicons,.oz-shell .oz-btn-danger.components-button svg,.oz-shell .oz-btn-ghost.components-button .dashicons,.oz-shell .oz-btn-ghost.components-button svg,.oz-shell .oz-btn-primary.components-button .dashicons,.oz-shell .oz-btn-primary.components-button svg{transition:none}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) svg{transform:none}.oz-shell .oz-btn-danger.components-button.is-busy,.oz-shell .oz-btn-ghost.components-button.is-busy,.oz-shell .oz-btn-primary.components-button.is-busy{animation:none}}.oz-shell .components-button.is-primary:focus:not(:disabled),.oz-shell .components-button:focus,.oz-shell .components-button:focus:not(:disabled){box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle,.oz-shell .components-form-toggle__input,.oz-shell .components-form-toggle__input:active,.oz-shell .components-form-toggle__input:focus,.oz-shell .components-form-toggle__input:focus-visible{border:none!important;box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle__track{background-color:#cdd6db!important;border:1px solid transparent!important;box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle.is-checked .components-form-toggle__track{background-color:#1dd1a1!important}.oz-shell .components-form-toggle__input:focus+.components-form-toggle__track,.oz-shell .components-form-toggle__input:focus-visible+.components-form-toggle__track{box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle__thumb{background-color:#fff!important;border:none!important;box-shadow:0 1px 2px rgba(15,32,39,.25)!important}.oz-savebar{align-items:center;background:#fff;border-top:1px solid #e7ecef;bottom:0;box-shadow:0 -6px 18px rgba(15,32,39,.06);display:flex;gap:16px;justify-content:flex-end;right:160px;padding:13px 32px;position:fixed;left:0;z-index:9}.oz-savebar__hint{color:#6b7a86;font-size:13px}.oz-savebar.is-dirty .oz-savebar__hint{color:#0c8f6b;font-weight:650}.folded .oz-savebar{right:36px}.oz-toast{bottom:72px;z-index:99}.oz-job,.oz-toast{position:fixed;left:24px}.oz-job{background:#fff;border:1px solid #e7ecef;border-radius:16px;bottom:84px;box-shadow:0 18px 50px rgba(15,32,39,.22);color:#0f2027;font-size:13px;max-width:calc(100vw - 48px);overflow:hidden;width:420px;z-index:100}.oz-job__head{align-items:center;display:flex;gap:12px;padding:16px 16px 12px}.oz-job__head .components-spinner{margin:0}.oz-job__icon-status{font-size:22px;height:22px;width:22px}.oz-job__icon-status--done{color:#10ac81}.oz-job__icon-status--stopped{color:#6b7a86}.oz-job__icon-status--error{color:#d63638}.oz-job__title{flex:1;min-width:0}.oz-job__title strong{display:block;font-size:15px;font-weight:700;line-height:1.2}.oz-job__title span{color:#6b7a86;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.oz-job__pct{color:#0f2027;font-size:13px;font-variant-numeric:tabular-nums;font-weight:700}.oz-job__icon{background:transparent;border:0;border-radius:6px;color:#6b7a86;cursor:pointer;display:inline-flex;padding:4px}.oz-job__icon:hover{background:#f4f7f8;color:#0f2027}.oz-job__icon .dashicons{font-size:18px;height:18px;width:18px}.oz-job__body{padding:0 16px 16px}.oz-job__row{align-items:baseline;display:flex;gap:12px;justify-content:space-between;margin-bottom:8px}.oz-job__row span{line-height:1.4}.oz-job__row strong{flex:0 0 auto;font-variant-numeric:tabular-nums;font-weight:700}.oz-job__bar{background:#e7ecef;border-radius:999px;height:7px;overflow:hidden}.oz-job__bar div{background:linear-gradient(-90deg,#1dd1a1,#10ac81);border-radius:999px;height:100%;transition:width .35s ease}.oz-job__bar.is-indeterminate div{animation:oz-indeterminate 1.1s ease-in-out infinite;border-radius:999px;width:40%!important}.oz-job.is-min .oz-job__bar{margin:2px 16px 16px}.oz-job.is-min .oz-job__head{padding-bottom:10px}.oz-job.is-done .oz-job__bar div{background:linear-gradient(-90deg,#10ac81,#0c8f6b)}.oz-job.is-stopped .oz-job__bar div{background:#c3ccd2}.oz-job.is-error .oz-job__bar div{background:linear-gradient(-90deg,#f16b6b,#d63638)}.oz-job__step{color:#6b7a86;font-size:12px;margin-top:8px}.oz-job__stop{align-items:center;background:transparent;border:0;color:#d63638;cursor:pointer;display:inline-flex;font-size:13px;font-weight:650;gap:6px;margin-top:14px;padding:4px 0}.oz-job__stop .dashicons{font-size:18px}.oz-job__stop:hover{color:#b32d2e}.oz-job__log{background:#11202b;border-radius:10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.7;margin-top:14px;max-height:150px;overflow-y:auto;padding:10px 12px}.oz-logline{color:#cdd8df;display:flex;gap:8px}.oz-logline__tag{color:#5fd0ad;flex:0 0 auto;font-weight:700;min-width:42px}.oz-logline--warn .oz-logline__tag{color:#f0b429}.oz-logline--error .oz-logline__tag{color:#ff6b6b}.oz-logline--done .oz-logline__tag{color:#1dd1a1}.oz-logline__text{word-break:break-word}@media(max-width:1100px){.oz-kpis,.oz-quick{grid-template-columns:1fr}}@media(max-width:1024px){.oz-score{align-items:flex-start;flex-direction:column;text-align:right}.oz-convert{align-items:stretch;flex-direction:column;gap:12px}.oz-convert .oz-btn-ghost.components-button,.oz-convert .oz-btn-primary.components-button{justify-content:center;width:100%}}.oz-mobile-toggle,.oz-nav-overlay{display:none!important}@media(max-width:1024px){.oz-shell{flex-direction:column;margin:8px 8px 110px}.oz-content{border-right:0;min-width:0;width:100%}.oz-sidebar{border-bottom:1px solid #e7ecef;border-left:0;flex:none;max-height:none;overflow:visible;padding:14px 12px;position:static;width:100%}.oz-sidebar__brand{margin-bottom:10px;padding-bottom:12px}.oz-sidebar nav{display:flex;flex-wrap:nowrap;gap:6px;overflow-x:auto;-webkit-overflow-scrolling:touch;padding-bottom:4px}.oz-navitem{border-bottom:3px solid transparent;border-right:0;border-radius:10px;flex:0 0 auto;padding:8px 12px;width:auto}.oz-navitem.is-active{border-bottom-color:#1dd1a1;border-right-color:transparent}.oz-navitem .oz-navitem__text strong{white-space:nowrap}.oz-navitem .oz-navitem__dot,.oz-navitem small,.oz-sidebar__foot{display:none}.oz-topbar{padding:16px 18px}.oz-topbar h1{font-size:18px}.oz-main{padding:16px 18px}.oz-savebar{right:0;padding:12px 18px}.oz-card__body{padding-right:18px;padding-left:18px}.oz-job{right:8px;left:8px;width:auto}}@media(max-width:560px){.oz-tiles{grid-template-columns:1fr}.oz-hero,.oz-score{padding:18px}.oz-card__head{gap:12px;padding:16px}.oz-card__icon{flex-basis:40px;height:40px;width:40px}.oz-savebar__hint{display:none}}@keyframes oz-indeterminate{0%{transform:translateX(100%)}to{transform:translateX(-350%)}}.oz-muted{color:#6b7a86;font-size:13px}.oz-reqs{list-style:none;margin:0;padding:0}.oz-req{align-items:center;border-bottom:1px solid #e7ecef;border-radius:8px;display:grid;font-size:13.5px;gap:6px 10px;grid-template-columns:20px 1fr auto;padding:10px 6px}.oz-req:last-child{border-bottom:0}.oz-req__dot{font-size:18px;height:18px;width:18px}.oz-req__label{font-weight:650}.oz-req__value{color:#6b7a86;font-weight:600;justify-self:end}.oz-req__note{color:#6b7a86;font-size:12px;font-weight:400;grid-column:2/-1;line-height:1.45;margin-top:2px}.oz-req--ok>.oz-req__dot{color:#0c8f6b}.oz-req--warn>.oz-req__dot{color:#e0900f}.oz-req--error>.oz-req__dot{color:#e05242}.oz-req--info>.oz-req__dot,.oz-req--info>.oz-req__value{color:#6b7a86}.oz-req--warn{background:rgba(224,144,15,.06)}.oz-req--error{background:rgba(224,82,66,.06)}.oz-notice{align-items:flex-start;background:rgba(224,82,66,.08);border:1px solid rgba(224,82,66,.28);border-radius:10px;color:#7a2018;display:flex;font-size:13px;gap:10px;line-height:1.5;margin:0 0 14px;padding:12px 14px}.oz-notice .dashicons{color:#e05242;flex:0 0 20px;margin-top:1px}.oz-notice--warn{background:rgba(224,144,15,.09);border-color:rgba(224,144,15,.3);color:#7a5406}.oz-notice--warn .dashicons{color:#e0900f}.oz-recs{list-style:none;margin:0;padding:0}.oz-rec{align-items:center;border-bottom:1px solid #e7ecef;border-radius:8px;display:flex;gap:12px;padding:12px 8px}.oz-rec:last-child{border-bottom:0}.oz-rec__dot{flex:0 0 20px;font-size:20px;height:20px;margin-top:1px;width:20px}.oz-rec__text{flex:1;min-width:0}.oz-rec__text strong{display:block;font-size:13.5px;font-weight:650}.oz-rec__text small{color:#6b7a86;display:block;font-size:12px;line-height:1.45;margin-top:2px}.oz-rec__btn.components-button{flex:0 0 auto}.oz-rec--high>.oz-rec__dot{color:#e05242}.oz-rec--medium>.oz-rec__dot{color:#e0900f}.oz-rec--low>.oz-rec__dot{color:#6b7a86}.oz-rec--high{background:rgba(224,82,66,.05)}.oz-rec--medium{background:rgba(224,144,15,.05)}.oz-recs-clear{align-items:center;color:#6b7a86;display:flex;font-size:13px;gap:8px}.oz-recs-clear .dashicons{color:#0c8f6b}.oz-select--compact{border-bottom:0!important;flex:none;margin:0 auto 0 0!important;padding:0!important}.oz-select--compact .oz-select__btn{background:#f7faf9;border-radius:9px;font-size:13px;font-weight:650;gap:10px;height:30px;min-width:132px;padding:0 12px 0 10px;width:auto}.oz-select--compact .oz-select__btn:disabled{cursor:progress;opacity:.6}.oz-select--compact .oz-select__btn .dashicons{font-size:16px;height:16px;width:16px}.oz-select--compact .oz-select__menu{right:auto;min-width:190px;left:0}.oz-select--compact .oz-select__item{font-size:13px;padding:8px 10px}1 .oz-shell{align-items:flex-start;background:#fff;border:1px solid #e7ecef;border-radius:16px;color:#0f2027;display:flex;font-size:14px;margin:10px 0 130px 20px;min-height:640px;overflow:hidden}.oz-shell *{box-sizing:border-box}.oz-shell .dashicons{font-family:dashicons}.oz-sidebar{align-self:flex-start;background:#fff;display:flex;flex:0 0 264px;flex-direction:column;padding:18px 14px}.oz-sidebar__brand{align-items:center;border-bottom:1px solid #e7ecef;display:flex;gap:12px;margin-bottom:10px;padding:10px 10px 18px}.oz-sidebar__brand strong{display:block;font-size:18px;font-weight:750;letter-spacing:-.01em}.oz-sidebar__brand span{color:#6b7a86;font-size:11px}.oz-sidebar__brand-fallback{align-items:center;display:flex;gap:12px}.oz-sidebar__foot{border-top:1px solid #e7ecef;color:#6b7a86;font-size:11px;margin-top:12px;padding:12px 12px 2px}.oz-logo{background:linear-gradient(-135deg,#1dd1a1,#10ac81);border-radius:12px;color:#fff;display:grid;font-size:26px;height:44px;place-items:center;width:44px}.oz-logo-img{display:block;height:30px;max-width:100%;width:auto}.oz-navitem{align-items:center;background:transparent;border:0;border-right:3px solid transparent;border-radius:10px 0 0 10px;color:#0f2027;cursor:pointer;display:flex;gap:12px;padding:11px 12px;text-align:right;transition:all .14s ease;width:100%}.oz-navitem>.oz-ico{color:#6b7a86;flex:0 0 20px;height:20px;transition:color .14s ease,transform .14s ease;width:20px}.oz-navitem:hover>.oz-ico{color:#0f2027}.oz-navitem.is-active>.oz-ico{color:#0c8f6b}.oz-navitem__text{flex:1;min-width:0}.oz-navitem__text strong{display:block;font-size:13.5px;font-weight:650;line-height:1.2}.oz-navitem__text small{color:#6b7a86;font-size:11px}.oz-navitem__dot{background:#1dd1a1;border-radius:50%;height:8px;width:8px}.oz-navitem:hover{background:#f4f7f8}.oz-navitem.is-active{background:rgba(29,209,161,.12);border-right-color:#1dd1a1}.oz-navitem.is-active .oz-navitem__text strong{color:#0c8f6b}.oz-content{align-self:stretch;background:#f4f7f8;border-right:1px solid #e7ecef;display:flex;flex:1;flex-direction:column;min-width:0}.oz-topbar{align-items:center;background:#fff;border-bottom:1px solid #e7ecef;display:flex;gap:14px;padding:20px 28px}.oz-topbar__icon{background:rgba(29,209,161,.14);border-radius:12px;color:#0c8f6b;display:grid;font-size:22px;height:44px;place-items:center;width:44px}.oz-topbar h1{font-size:20px;font-weight:700;margin:0;padding:0}.oz-topbar p{color:#6b7a86;font-size:12.5px;margin:0}.oz-main{display:flex;flex-direction:column;gap:16px;padding:24px 28px}.oz-welcome{align-items:center;background:linear-gradient(-125deg,rgba(29,209,161,.9),rgba(16,172,129,.92));border-radius:16px;color:#06382c;display:flex;gap:20px;justify-content:space-between;overflow:hidden;padding:22px 26px}.oz-welcome h2{color:#06382c;font-size:18px;font-weight:700;margin:0 0 6px}.oz-welcome p{color:#0c5b46;font-size:13px;margin:0;max-width:620px}.oz-welcome__icon{flex:0 0 auto;font-size:64px;height:64px;opacity:.5;width:64px}.oz-score{align-items:center;background:linear-gradient(-125deg,#1dd1a1,#10ac81 62%,#0c8f6b);border-radius:16px;box-shadow:0 16px 34px -18px rgba(12,143,107,.65);color:#06382c;display:flex;gap:30px;overflow:hidden;padding:28px 32px;position:relative}.oz-score:before{background:radial-gradient(circle,hsla(0,0%,100%,.3),transparent 62%);height:440px;left:-8%;top:-55%;width:440px}.oz-score:after,.oz-score:before{border-radius:50%;content:"";pointer-events:none;position:absolute}.oz-score:after{background:radial-gradient(circle,rgba(0,0,0,.08),transparent 60%);bottom:-70%;height:320px;right:8%;width:320px}.oz-score>*{position:relative;z-index:1}.oz-score__gauge{filter:drop-shadow(0 8px 16px rgba(3,40,30,.28));flex:0 0 auto;line-height:0;position:relative}.oz-score__grade{background:#fff;border-radius:50%;box-shadow:0 4px 10px rgba(3,40,30,.28);color:#0c8f6b;display:grid;font-size:14px;font-weight:800;height:30px;place-items:center;position:absolute;left:2px;top:8px;width:30px}.oz-score__body{flex:1;min-width:0}.oz-score__body h2{color:#06382c;font-size:21px;font-weight:750;letter-spacing:-.02em;margin:0 0 4px}.oz-score__body p{color:rgba(6,56,44,.82);font-size:13px;font-weight:500;margin:0 0 15px}.oz-score__chips{display:flex;flex-wrap:wrap;gap:8px}.oz-chip{align-items:center;background:hsla(0,0%,100%,.18);border:1px solid hsla(0,0%,100%,.28);border-radius:999px;color:rgba(6,56,44,.72);display:inline-flex;font-size:11.5px;font-weight:650;gap:6px;padding:5px 9px 5px 12px;transition:transform .15s ease,box-shadow .15s ease}.oz-chip .dashicons{font-size:15px;height:15px;opacity:.65;width:15px}.oz-chip.is-on{background:#fff;border-color:#fff;box-shadow:0 3px 8px -2px rgba(3,40,30,.22);color:#0c8f6b}.oz-chip.is-on .dashicons{color:#1dd1a1;opacity:1}.oz-chip.is-on:hover{box-shadow:0 6px 14px -4px rgba(3,40,30,.3);transform:translateY(-1px)}.oz-convert{align-items:center;background:#f4f7f8;border-radius:12px;display:flex;gap:16px;justify-content:space-between;margin-top:16px;padding:16px}.oz-convert__info{color:#6b7a86;display:flex;flex-direction:column;font-size:12.5px;gap:3px}.oz-convert__info strong{color:#0f2027;font-size:13px}.oz-progress{background:#dfe6e8;border-radius:999px;height:6px;margin-top:4px;overflow:hidden;width:200px}.oz-progress div{background:linear-gradient(-90deg,#1dd1a1,#10ac81);height:100%;transition:width .3s ease}.oz-kpis{display:grid;gap:14px;grid-template-columns:repeat(3,1fr)}.oz-kpis--single{grid-template-columns:1fr}.oz-kpi{background:#fff;border:1px solid #e7ecef;border-radius:14px;display:flex;flex-direction:column;padding:16px 18px}.oz-kpi>.dashicons{color:#10ac81;font-size:20px;margin-bottom:8px}.oz-kpi strong{font-size:24px;font-weight:750;min-height:28px}.oz-kpi span{color:#6b7a86;font-size:12px}.oz-quick{display:grid;gap:16px;grid-template-columns:1fr 1fr}.oz-quick__actions{display:flex;flex-wrap:wrap;gap:10px}.oz-cachelist{border-top:1px solid #e7ecef;margin:18px 0 0}.oz-cachelist>li{align-items:center;border-bottom:1px solid #f1f4f5;color:#6b7a86;display:flex;font-size:13px;gap:10px;padding:11px 2px}.oz-cachelist>li:last-child{border-bottom:0}.oz-cachelist>li .dashicons{color:#10ac81;font-size:17px}.oz-cachelist>li strong{color:#0f2027;font-weight:650;margin-right:auto}.oz-cachelist>li .oz-row-label{display:flex;flex-direction:column;line-height:1.3}.oz-cachelist>li .oz-row-label small{color:#6b7a86;font-size:11.5px;opacity:.85}.oz-status{margin:0}.oz-status li{align-items:center;border-bottom:1px solid #e7ecef;display:flex;font-size:13.5px;font-weight:600;gap:10px;padding:9px 6px}.oz-status li:last-child{border-bottom:0}.oz-status li>.oz-ico{color:#6b7a86;flex:0 0 18px;height:18px;width:18px}.oz-status__name{background:transparent;border:0;color:#0f2027;cursor:pointer;flex:1;font:inherit;font-weight:600;padding:4px 0;text-align:right}.oz-status__name:hover{color:#0c8f6b}.oz-status .components-toggle-control{margin-right:auto}.oz-card,.oz-panel{background:#fff;border:1px solid #e7ecef;border-radius:16px;overflow:visible}.oz-card .oz-card__head{border-radius:16px 16px 0 0}.oz-panel__head{padding:18px 22px 0}.oz-panel__head h2{font-size:16px;font-weight:650;margin:0}.oz-panel__head p{color:#6b7a86;font-size:12.5px;margin:4px 0 0}.oz-panel__body{padding:8px 22px 20px}.oz-panel__body .components-base-control{border-bottom:1px solid #f1f4f5;padding:14px 0}.oz-panel__body .components-base-control:last-child{border-bottom:0}.oz-card{transition:box-shadow .18s ease,border-color .18s ease}.oz-card.is-enabled{border-color:rgba(29,209,161,.5);box-shadow:0 0 0 1px rgba(29,209,161,.22)}.oz-card__head{align-items:flex-start;display:flex;gap:15px;padding:20px 22px}.oz-card__icon{background:#f4f7f8;border-radius:12px;color:#6b7a86;display:grid;flex:0 0 46px;font-size:22px;height:46px;place-items:center;width:46px}.oz-card__title{flex:1;min-width:0}.oz-card__title h2{align-items:center;display:flex;font-size:16px;font-weight:650;gap:10px;margin:0 0 3px}.oz-card__title p{color:#6b7a86;font-size:12.5px;margin:0}.oz-card__body{border-top:1px solid #e7ecef;padding:4px 22px 16px}.oz-card__body .components-base-control{border-bottom:1px solid #f1f4f5;padding:14px 0}.oz-card__body .components-base-control:last-child{border-bottom:0}.is-enabled .oz-card__icon{background:linear-gradient(-135deg,#1dd1a1,#10ac81);color:#fff}.oz-inline-actions{display:flex;gap:10px;padding-top:16px}.oz-dbgroup{border-top:1px solid #eef0f2;padding:18px 0}.oz-dbgroup:first-of-type{border-top:0;padding-top:4px}.oz-dbgroup__title{color:#0e1116;font-size:14px;font-weight:700;margin:0 0 4px}.oz-dbgroup__desc{color:#6b7280;font-size:13px;line-height:1.5;margin:0 0 14px}.oz-dbgroup .components-toggle-control{margin-bottom:14px}.oz-dbgroup .components-toggle-control:last-child{margin-bottom:0}.oz-dbwarn{align-items:flex-start;background:#fff5f5;border:1px solid #fcd9d9;border-radius:10px;color:#b42318;display:flex;font-size:13px;font-weight:600;gap:8px;line-height:1.5;margin:4px 0;padding:12px 14px}.oz-dbwarn .dashicons{color:#e05242;flex:0 0 auto}.oz-cachewarn{align-items:center;background:#fff8ec;border:1px solid #f4dcae;border-radius:12px;color:#7a4b00;display:flex;gap:13px;margin:0 0 18px;padding:14px 16px}.oz-cachewarn .dashicons{color:#e0900f;flex:0 0 auto;font-size:22px;height:22px;width:22px}.oz-cachewarn__text{flex:1 1 auto;min-width:0}.oz-cachewarn__text strong{color:#6b4200;display:block;font-size:14px;font-weight:700}.oz-cachewarn__text p{color:#8a6410;font-size:12.5px;line-height:1.5;margin:3px 0 0}.oz-cachewarn .oz-btn-primary{flex:0 0 auto}@media(max-width:640px){.oz-cachewarn{flex-wrap:wrap}.oz-cachewarn .oz-btn-primary{justify-content:center;width:100%}}.oz-subhead{color:#8a96a0;font-size:11px;font-weight:700;letter-spacing:.08em;margin:18px 0 2px;padding-top:14px;text-transform:uppercase}.oz-health,.oz-subhead{border-top:1px solid #e7ecf0}.oz-health{margin-top:18px;padding-top:16px}.oz-health__head{align-items:center;display:flex;gap:12px;justify-content:space-between}.oz-health__head strong{font-size:14px}.oz-health__hint{color:#6b7280;font-size:12px;line-height:1.5;margin:4px 0 10px}.oz-health__list{display:flex;flex-direction:column;gap:6px;margin:0}.oz-health__row{align-items:center;display:flex;font-size:12.5px;gap:8px;line-height:1.4;margin:0}.oz-health__row .dashicons{flex:0 0 auto;font-size:17px;height:17px;width:17px}.oz-health__row.is-ok{color:#216e4e}.oz-health__row.is-ok .dashicons{color:#1dd1a1}.oz-health__row.is-warn{color:#8a5a00}.oz-health__row.is-warn .dashicons{color:#e0900f}.oz-health__row.is-bad{color:#b42318}.oz-health__row.is-bad .dashicons{color:#e05242}.oz-health__err{color:#b42318;font-size:12.5px}.oz-health__fix{align-items:center;display:flex;flex-wrap:wrap;gap:12px;margin-top:14px}.oz-health__fix span{color:#6b7280;flex:1 1 160px;font-size:12px}.oz-health__conflict{display:flex;flex-direction:column;gap:8px;margin-top:12px}.oz-health__conflict-row{align-items:center;background:#fff5f5;border:1px solid #fcd9d9;border-radius:9px;display:flex;flex-wrap:wrap;gap:10px;padding:10px 12px}.oz-health__conflict-row .dashicons{color:#e05242;flex:0 0 auto}.oz-health__conflict-row span:not(.dashicons){color:#8a2f22;flex:1 1 200px;font-size:12.5px}.oz-health__conflict-row .oz-btn-danger.components-button{flex:0 0 auto;height:32px}.oz-health__manual{background:#fff8ec;border:1px solid #f4dcae;border-radius:9px;margin-top:12px;padding:10px 12px}.oz-health__manual p{color:#7a4b00;font-size:12px;margin:0 0 6px}.oz-health__manual code{background:#2b2f36;border-radius:6px;color:#e7f0ec;display:inline-block;font-size:12.5px;padding:6px 10px}.oz-btn-ghost.components-button{border:1px solid #d5dbe0;border-radius:8px;color:#384049;font-size:12.5px;height:30px}.oz-btn-ghost.components-button:hover:not(:disabled){border-color:#1dd1a1;color:#0b7a5c}.oz-pill{align-items:center;border-radius:999px;display:inline-flex;font-size:11px;font-weight:700;gap:5px;padding:3px 9px}.oz-pill i{background:currentColor;border-radius:50%;height:6px;width:6px}.oz-pill.is-on{background:rgba(29,209,161,.16);color:#0c8f6b}.oz-pill.is-off{background:#eef1f2;color:#6b7a86}.oz-shell .components-range-control__thumb,.oz-shell .components-range-control__track{background-color:#1dd1a1!important}.oz-fieldgroup{background:rgba(29,209,161,.035);border:1px solid #e7ecef;border-radius:12px;margin-top:18px;padding:16px 16px 4px}.oz-fieldgroup__title{color:#0e1116;font-size:13px;font-weight:700;margin:0 0 4px}.oz-fieldgroup__desc{color:#6b7280;font-size:12.5px;line-height:1.5;margin:0 0 14px}.oz-fieldgroup .components-base-control{margin-bottom:14px}.oz-shell .components-form-token-field__input-container{background:#fff;border:1px solid #e7ecef;border-radius:10px;min-height:44px;padding:5px 8px;transition:border-color .15s ease,box-shadow .15s ease}.oz-shell .components-form-token-field__input-container.is-active,.oz-shell .components-form-token-field__input-container:focus-within{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)}.oz-shell .components-form-token-field__token{border-radius:8px;font-size:12.5px;margin:3px;overflow:hidden}.oz-shell .components-form-token-field__token-text{background:rgba(29,209,161,.14);border-radius:0 8px 8px 0;color:#0c8f6b;font-weight:650;line-height:1.7;padding:2px 11px 2px 4px}.oz-shell .components-form-token-field__remove-token{background:rgba(29,209,161,.14);border-radius:8px 0 0 8px;color:#0c8f6b;min-width:24px;padding:0 7px;transition:background .15s ease,color .15s ease}.oz-shell .components-form-token-field__remove-token svg{fill:currentColor}.oz-shell .components-form-token-field__remove-token:hover{background:#10ac81;color:#fff}.oz-shell .components-form-token-field__input{font-size:13.5px;margin:4px;min-height:28px}.oz-shell .components-input-control__backdrop{display:none!important}.oz-shell .components-input-control__input,.oz-shell .components-select-control__input,.oz-shell .components-text-control__input{background:#fff!important;border:1px solid #e7ecef!important;border-radius:10px!important;box-shadow:none!important;color:#0f2027;font-size:13.5px;min-height:42px;padding:0 14px}.oz-shell .components-input-control__input:hover,.oz-shell .components-select-control__input:hover,.oz-shell .components-text-control__input:hover{border-color:#bcc6cc!important}.oz-shell .components-input-control__input:focus,.oz-shell .components-select-control__input:focus,.oz-shell .components-text-control__input:focus{border-color:#1dd1a1!important;box-shadow:0 0 0 3px rgba(29,209,161,.16)!important;outline:none!important}.oz-shell select.components-select-control__input{cursor:pointer}.oz-pillgroup .components-base-control__label{display:block;font-weight:600;margin-bottom:8px}.oz-pillgroup__row{background:#f0f2f4;border-radius:12px;display:inline-flex;flex-wrap:wrap;gap:6px;padding:4px}.oz-pillgroup .components-base-control__help{display:block;margin-top:8px}.oz-pillbtn{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;border-radius:9px;color:#4b5563;cursor:pointer;font-size:13px;font-weight:600;min-width:44px;padding:7px 16px;transition:background .12s ease,color .12s ease,box-shadow .12s ease}.oz-pillbtn:hover{color:#0e1116}.oz-pillbtn.is-on{background:#fff;box-shadow:0 1px 3px rgba(4,56,44,.14);color:#0c8f6b}.oz-pillbtn:focus-visible{outline:2px solid #1dd1a1;outline-offset:1px}.oz-select{position:relative}.oz-select .components-base-control__label{display:block;margin-bottom:8px}.oz-select__btn{align-items:center;background:#fff;border:1px solid #e7ecef;border-radius:10px;color:#0f2027;cursor:pointer;display:flex;font-size:13.5px;height:42px;justify-content:space-between;padding:0 14px;transition:border-color .15s ease,box-shadow .15s ease;width:100%}.oz-select__btn .dashicons{color:#0c8f6b;transition:transform .18s ease}.oz-select__btn:hover{border-color:#bcc6cc}.oz-select__btn:focus,.oz-select__btn:focus-visible{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)!important;outline:none!important}.oz-select__btn[aria-expanded=true]{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)}.oz-select__btn[aria-expanded=true] .dashicons{transform:rotate(-180deg)}.oz-select__menu{background:#fff;border:1px solid #e7ecef;border-radius:12px;box-shadow:0 14px 34px rgba(15,32,39,.18);right:0;list-style:none;margin:0;padding:6px;position:absolute;left:0;top:calc(100% + 6px);z-index:30}.oz-select__item{align-items:center;border-radius:8px;color:#0f2027;cursor:pointer;display:flex;font-size:13.5px;justify-content:space-between;padding:9px 12px}.oz-select__item .dashicons{color:#10ac81;font-size:18px}.oz-select__item.is-active,.oz-select__item:hover{background:rgba(29,209,161,.12);color:#0c8f6b}.oz-select__item.is-selected{color:#0c8f6b;font-weight:650}.oz-shell .oz-btn-danger.components-button,.oz-shell .oz-btn-ghost.components-button,.oz-shell .oz-btn-primary.components-button{border-radius:10px;font-weight:650;height:38px;transition:transform .16s cubic-bezier(.2,.7,.3,1),box-shadow .18s ease,background .16s ease,border-color .16s ease,color .16s ease}.oz-shell .oz-btn-danger.components-button .dashicons,.oz-shell .oz-btn-danger.components-button svg,.oz-shell .oz-btn-ghost.components-button .dashicons,.oz-shell .oz-btn-ghost.components-button svg,.oz-shell .oz-btn-primary.components-button .dashicons,.oz-shell .oz-btn-primary.components-button svg{transition:transform .22s cubic-bezier(.2,.7,.3,1)}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled),.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled),.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){transform:translateY(-1px)}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) svg{transform:scale(1.14)}.oz-shell .oz-btn-danger.components-button:active:not(:disabled),.oz-shell .oz-btn-ghost.components-button:active:not(:disabled),.oz-shell .oz-btn-primary.components-button:active:not(:disabled){transform:translateY(0) scale(.975);transition-duration:.05s}.oz-shell .oz-btn-danger.components-button:focus-visible,.oz-shell .oz-btn-ghost.components-button:focus-visible,.oz-shell .oz-btn-primary.components-button:focus-visible{outline:2px solid #1dd1a1;outline-offset:2px}.oz-shell .oz-btn-danger.components-button:disabled,.oz-shell .oz-btn-ghost.components-button:disabled,.oz-shell .oz-btn-primary.components-button:disabled{opacity:.5;transform:none}.oz-shell .oz-btn-primary.components-button{background:linear-gradient(-135deg,#1dd1a1,#10ac81);border:0;box-shadow:0 1px 2px rgba(20,60,48,.12);color:#fff;padding:0 18px}.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){background:linear-gradient(-135deg,#10ac81,#0c8f6b);box-shadow:0 8px 20px -7px rgba(29,209,161,.65);color:#fff}.oz-shell .oz-btn-primary.components-button.is-busy{animation:oz-btn-sheen 1.4s linear infinite;background-size:200% 100%}.oz-shell .oz-btn-ghost.components-button{background:#fff;border:1px solid rgba(29,209,161,.5);box-shadow:none;color:#0c8f6b}.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled){background:rgba(29,209,161,.08);border-color:#1dd1a1;box-shadow:0 6px 16px -8px rgba(29,209,161,.5);color:#0c8f6b}.oz-shell .oz-btn-danger.components-button{background:#fff;border:1px solid rgba(214,54,56,.5);color:#d63638}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled){background:rgba(214,54,56,.08);border-color:#d63638;box-shadow:0 6px 16px -8px rgba(214,54,56,.5);color:#b32d2e}.oz-shell .oz-btn--x.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn--x.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg{transform:scale(1.1) rotate(-90deg)}@media(prefers-reduced-motion:reduce){.oz-shell .oz-btn--x.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn--x.components-button:hover:not(:disabled) svg{transform:none}}@keyframes oz-btn-sheen{0%{background-image:linear-gradient(-135deg,#1dd1a1,#10ac81,#1dd1a1);background-position:-100% 0}to{background-image:linear-gradient(-135deg,#1dd1a1,#10ac81,#1dd1a1);background-position:300% 0}}@media(prefers-reduced-motion:reduce){.oz-shell .oz-btn-danger.components-button,.oz-shell .oz-btn-ghost.components-button,.oz-shell .oz-btn-primary.components-button{transition:background .16s ease,color .16s ease,border-color .16s ease}.oz-shell .oz-btn-danger.components-button:active:not(:disabled),.oz-shell .oz-btn-danger.components-button:hover:not(:disabled),.oz-shell .oz-btn-ghost.components-button:active:not(:disabled),.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled),.oz-shell .oz-btn-primary.components-button:active:not(:disabled),.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){transform:none}.oz-shell .oz-btn-danger.components-button .dashicons,.oz-shell .oz-btn-danger.components-button svg,.oz-shell .oz-btn-ghost.components-button .dashicons,.oz-shell .oz-btn-ghost.components-button svg,.oz-shell .oz-btn-primary.components-button .dashicons,.oz-shell .oz-btn-primary.components-button svg{transition:none}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) svg{transform:none}.oz-shell .oz-btn-danger.components-button.is-busy,.oz-shell .oz-btn-ghost.components-button.is-busy,.oz-shell .oz-btn-primary.components-button.is-busy{animation:none}}.oz-shell .components-button.is-primary:focus:not(:disabled),.oz-shell .components-button:focus,.oz-shell .components-button:focus:not(:disabled){box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle,.oz-shell .components-form-toggle__input,.oz-shell .components-form-toggle__input:active,.oz-shell .components-form-toggle__input:focus,.oz-shell .components-form-toggle__input:focus-visible{border:none!important;box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle__track{background-color:#cdd6db!important;border:1px solid transparent!important;box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle.is-checked .components-form-toggle__track{background-color:#1dd1a1!important}.oz-shell .components-form-toggle__input:focus+.components-form-toggle__track,.oz-shell .components-form-toggle__input:focus-visible+.components-form-toggle__track{box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle__thumb{background-color:#fff!important;border:none!important;box-shadow:0 1px 2px rgba(15,32,39,.25)!important}.oz-savebar{align-items:center;background:#fff;border-top:1px solid #e7ecef;bottom:0;box-shadow:0 -6px 18px rgba(15,32,39,.06);display:flex;gap:16px;justify-content:flex-end;right:160px;padding:13px 32px;position:fixed;left:0;z-index:9}.oz-savebar__hint{color:#6b7a86;font-size:13px}.oz-savebar.is-dirty .oz-savebar__hint{color:#0c8f6b;font-weight:650}.folded .oz-savebar{right:36px}.oz-toast{bottom:72px;z-index:99}.oz-job,.oz-toast{position:fixed;left:24px}.oz-job{background:#fff;border:1px solid #e7ecef;border-radius:16px;bottom:84px;box-shadow:0 18px 50px rgba(15,32,39,.22);color:#0f2027;font-size:13px;max-width:calc(100vw - 48px);overflow:hidden;width:420px;z-index:100}.oz-job__head{align-items:center;display:flex;gap:12px;padding:16px 16px 12px}.oz-job__head .components-spinner{margin:0}.oz-job__icon-status{font-size:22px;height:22px;width:22px}.oz-job__icon-status--done{color:#10ac81}.oz-job__icon-status--stopped{color:#6b7a86}.oz-job__icon-status--error{color:#d63638}.oz-job__title{flex:1;min-width:0}.oz-job__title strong{display:block;font-size:15px;font-weight:700;line-height:1.2}.oz-job__title span{color:#6b7a86;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.oz-job__pct{color:#0f2027;font-size:13px;font-variant-numeric:tabular-nums;font-weight:700}.oz-job__icon{background:transparent;border:0;border-radius:6px;color:#6b7a86;cursor:pointer;display:inline-flex;padding:4px}.oz-job__icon:hover{background:#f4f7f8;color:#0f2027}.oz-job__icon .dashicons{font-size:18px;height:18px;width:18px}.oz-job__body{padding:0 16px 16px}.oz-job__row{align-items:baseline;display:flex;gap:12px;justify-content:space-between;margin-bottom:8px}.oz-job__row span{line-height:1.4}.oz-job__row strong{flex:0 0 auto;font-variant-numeric:tabular-nums;font-weight:700}.oz-job__bar{background:#e7ecef;border-radius:999px;height:7px;overflow:hidden}.oz-job__bar div{background:linear-gradient(-90deg,#1dd1a1,#10ac81);border-radius:999px;height:100%;transition:width .35s ease}.oz-job__bar.is-indeterminate div{animation:oz-indeterminate 1.1s ease-in-out infinite;border-radius:999px;width:40%!important}.oz-job.is-min .oz-job__bar{margin:2px 16px 16px}.oz-job.is-min .oz-job__head{padding-bottom:10px}.oz-job.is-done .oz-job__bar div{background:linear-gradient(-90deg,#10ac81,#0c8f6b)}.oz-job.is-stopped .oz-job__bar div{background:#c3ccd2}.oz-job.is-error .oz-job__bar div{background:linear-gradient(-90deg,#f16b6b,#d63638)}.oz-job__step{color:#6b7a86;font-size:12px;margin-top:8px}.oz-job__stop{align-items:center;background:transparent;border:0;color:#d63638;cursor:pointer;display:inline-flex;font-size:13px;font-weight:650;gap:6px;margin-top:14px;padding:4px 0}.oz-job__stop .dashicons{font-size:18px}.oz-job__stop:hover{color:#b32d2e}.oz-job__log{background:#11202b;border-radius:10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.7;margin-top:14px;max-height:150px;overflow-y:auto;padding:10px 12px}.oz-logline{color:#cdd8df;display:flex;gap:8px}.oz-logline__tag{color:#5fd0ad;flex:0 0 auto;font-weight:700;min-width:42px}.oz-logline--warn .oz-logline__tag{color:#f0b429}.oz-logline--error .oz-logline__tag{color:#ff6b6b}.oz-logline--done .oz-logline__tag{color:#1dd1a1}.oz-logline__text{word-break:break-word}@media(max-width:1100px){.oz-kpis,.oz-quick{grid-template-columns:1fr}}@media(max-width:1024px){.oz-score{align-items:flex-start;flex-direction:column;text-align:right}.oz-convert{align-items:stretch;flex-direction:column;gap:12px}.oz-convert .oz-btn-ghost.components-button,.oz-convert .oz-btn-primary.components-button{justify-content:center;width:100%}}.oz-mobile-toggle,.oz-nav-overlay{display:none!important}@media(max-width:1024px){.oz-shell{flex-direction:column;margin:8px 8px 110px}.oz-content{border-right:0;min-width:0;width:100%}.oz-sidebar{border-bottom:1px solid #e7ecef;border-left:0;flex:none;max-height:none;overflow:visible;padding:14px 12px;position:static;width:100%}.oz-sidebar__brand{margin-bottom:10px;padding-bottom:12px}.oz-sidebar nav{display:flex;flex-wrap:nowrap;gap:6px;overflow-x:auto;-webkit-overflow-scrolling:touch;padding-bottom:4px}.oz-navitem{border-bottom:3px solid transparent;border-right:0;border-radius:10px;flex:0 0 auto;padding:8px 12px;width:auto}.oz-navitem.is-active{border-bottom-color:#1dd1a1;border-right-color:transparent}.oz-navitem .oz-navitem__text strong{white-space:nowrap}.oz-navitem .oz-navitem__dot,.oz-navitem small,.oz-sidebar__foot{display:none}.oz-topbar{padding:16px 18px}.oz-topbar h1{font-size:18px}.oz-main{padding:16px 18px}.oz-savebar{right:0;padding:12px 18px}.oz-card__body{padding-right:18px;padding-left:18px}.oz-job{right:8px;left:8px;width:auto}}@media(max-width:560px){.oz-tiles{grid-template-columns:1fr}.oz-hero,.oz-score{padding:18px}.oz-card__head{gap:12px;padding:16px}.oz-card__icon{flex-basis:40px;height:40px;width:40px}.oz-savebar__hint{display:none}}@keyframes oz-indeterminate{0%{transform:translateX(100%)}to{transform:translateX(-350%)}}.oz-muted{color:#6b7a86;font-size:13px}.oz-reqs{list-style:none;margin:0;padding:0}.oz-req{align-items:center;border-bottom:1px solid #e7ecef;border-radius:8px;display:grid;font-size:13.5px;gap:6px 10px;grid-template-columns:20px 1fr auto;padding:10px 6px}.oz-req:last-child{border-bottom:0}.oz-req__dot{font-size:18px;height:18px;width:18px}.oz-req__label{font-weight:650}.oz-req__value{color:#6b7a86;font-weight:600;justify-self:end}.oz-req__note{color:#6b7a86;font-size:12px;font-weight:400;grid-column:2/-1;line-height:1.45;margin-top:2px}.oz-req--ok>.oz-req__dot{color:#0c8f6b}.oz-req--warn>.oz-req__dot{color:#e0900f}.oz-req--error>.oz-req__dot{color:#e05242}.oz-req--info>.oz-req__dot,.oz-req--info>.oz-req__value{color:#6b7a86}.oz-req--warn{background:rgba(224,144,15,.06)}.oz-req--error{background:rgba(224,82,66,.06)}.oz-notice{align-items:flex-start;background:rgba(224,82,66,.08);border:1px solid rgba(224,82,66,.28);border-radius:10px;color:#7a2018;display:flex;font-size:13px;gap:10px;line-height:1.5;margin:0 0 14px;padding:12px 14px}.oz-notice .dashicons{color:#e05242;flex:0 0 20px;margin-top:1px}.oz-notice--warn{background:rgba(224,144,15,.09);border-color:rgba(224,144,15,.3);color:#7a5406}.oz-notice--warn .dashicons{color:#e0900f}.oz-notice--ok{background:#eaf8f3;border-color:#b8e6d6;color:#0f5e48}.oz-notice--ok .dashicons{color:#0e9f7e}.oz-recs{list-style:none;margin:0;padding:0}.oz-rec{align-items:center;border-bottom:1px solid #e7ecef;border-radius:8px;display:flex;gap:12px;padding:12px 8px}.oz-rec:last-child{border-bottom:0}.oz-rec__dot{flex:0 0 20px;font-size:20px;height:20px;margin-top:1px;width:20px}.oz-rec__text{flex:1;min-width:0}.oz-rec__text strong{display:block;font-size:13.5px;font-weight:650}.oz-rec__text small{color:#6b7a86;display:block;font-size:12px;line-height:1.45;margin-top:2px}.oz-rec__btn.components-button{flex:0 0 auto}.oz-rec--high>.oz-rec__dot{color:#e05242}.oz-rec--medium>.oz-rec__dot{color:#e0900f}.oz-rec--low>.oz-rec__dot{color:#6b7a86}.oz-rec--high{background:rgba(224,82,66,.05)}.oz-rec--medium{background:rgba(224,144,15,.05)}.oz-recs-clear{align-items:center;color:#6b7a86;display:flex;font-size:13px;gap:8px}.oz-recs-clear .dashicons{color:#0c8f6b}.oz-select--compact{border-bottom:0!important;flex:none;margin:0 auto 0 0!important;padding:0!important}.oz-select--compact .oz-select__btn{background:#f7faf9;border-radius:9px;font-size:13px;font-weight:650;gap:10px;height:30px;min-width:132px;padding:0 12px 0 10px;width:auto}.oz-select--compact .oz-select__btn:disabled{cursor:progress;opacity:.6}.oz-select--compact .oz-select__btn .dashicons{font-size:16px;height:16px;width:16px}.oz-select--compact .oz-select__menu{right:auto;min-width:190px;left:0}.oz-select--compact .oz-select__item{font-size:13px;padding:8px 10px}.oz-cachewarn--ok{background:#eaf8f3;border-color:#b8e6d6;color:#0f5e48}.oz-cachewarn__text p+p{margin-top:6px}.oz-cachewarn__link{color:inherit;display:inline-block;font-weight:600;margin-top:8px;text-decoration:underline}.oz-network__sites{align-items:start;margin-top:16px}.oz-network__sites .oz-panel__head h2,.oz-network__sites .oz-panel__head p{overflow-wrap:anywhere}.oz-cachelist__link{color:#1dd1a1;font-weight:600;margin-right:auto;text-decoration:none}.oz-cachelist__link:focus,.oz-cachelist__link:hover{text-decoration:underline} -
optimazz/trunk/build/index.asset.php
r3723128 r3743505 1 <?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => ' e40d7f0648a583c6302d');1 <?php return array('dependencies' => array('react', 'wp-api-fetch', 'wp-components', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => '5a501c583130cba224b3'); -
optimazz/trunk/build/index.css
r3723128 r3743505 1 .oz-shell{align-items:flex-start;background:#fff;border:1px solid #e7ecef;border-radius:16px;color:#0f2027;display:flex;font-size:14px;margin:10px 20px 130px 0;min-height:640px;overflow:hidden}.oz-shell *{box-sizing:border-box}.oz-shell .dashicons{font-family:dashicons}.oz-sidebar{align-self:flex-start;background:#fff;display:flex;flex:0 0 264px;flex-direction:column;padding:18px 14px}.oz-sidebar__brand{align-items:center;border-bottom:1px solid #e7ecef;display:flex;gap:12px;margin-bottom:10px;padding:10px 10px 18px}.oz-sidebar__brand strong{display:block;font-size:18px;font-weight:750;letter-spacing:-.01em}.oz-sidebar__brand span{color:#6b7a86;font-size:11px}.oz-sidebar__brand-fallback{align-items:center;display:flex;gap:12px}.oz-sidebar__foot{border-top:1px solid #e7ecef;color:#6b7a86;font-size:11px;margin-top:12px;padding:12px 12px 2px}.oz-logo{background:linear-gradient(135deg,#1dd1a1,#10ac81);border-radius:12px;color:#fff;display:grid;font-size:26px;height:44px;place-items:center;width:44px}.oz-logo-img{display:block;height:30px;max-width:100%;width:auto}.oz-navitem{align-items:center;background:transparent;border:0;border-left:3px solid transparent;border-radius:0 10px 10px 0;color:#0f2027;cursor:pointer;display:flex;gap:12px;padding:11px 12px;text-align:left;transition:all .14s ease;width:100%}.oz-navitem>.oz-ico{color:#6b7a86;flex:0 0 20px;height:20px;transition:color .14s ease,transform .14s ease;width:20px}.oz-navitem:hover>.oz-ico{color:#0f2027}.oz-navitem.is-active>.oz-ico{color:#0c8f6b}.oz-navitem__text{flex:1;min-width:0}.oz-navitem__text strong{display:block;font-size:13.5px;font-weight:650;line-height:1.2}.oz-navitem__text small{color:#6b7a86;font-size:11px}.oz-navitem__dot{background:#1dd1a1;border-radius:50%;height:8px;width:8px}.oz-navitem:hover{background:#f4f7f8}.oz-navitem.is-active{background:rgba(29,209,161,.12);border-left-color:#1dd1a1}.oz-navitem.is-active .oz-navitem__text strong{color:#0c8f6b}.oz-content{align-self:stretch;background:#f4f7f8;border-left:1px solid #e7ecef;display:flex;flex:1;flex-direction:column;min-width:0}.oz-topbar{align-items:center;background:#fff;border-bottom:1px solid #e7ecef;display:flex;gap:14px;padding:20px 28px}.oz-topbar__icon{background:rgba(29,209,161,.14);border-radius:12px;color:#0c8f6b;display:grid;font-size:22px;height:44px;place-items:center;width:44px}.oz-topbar h1{font-size:20px;font-weight:700;margin:0;padding:0}.oz-topbar p{color:#6b7a86;font-size:12.5px;margin:0}.oz-main{display:flex;flex-direction:column;gap:16px;padding:24px 28px}.oz-welcome{align-items:center;background:linear-gradient(125deg,rgba(29,209,161,.9),rgba(16,172,129,.92));border-radius:16px;color:#06382c;display:flex;gap:20px;justify-content:space-between;overflow:hidden;padding:22px 26px}.oz-welcome h2{color:#06382c;font-size:18px;font-weight:700;margin:0 0 6px}.oz-welcome p{color:#0c5b46;font-size:13px;margin:0;max-width:620px}.oz-welcome__icon{flex:0 0 auto;font-size:64px;height:64px;opacity:.5;width:64px}.oz-score{align-items:center;background:linear-gradient(125deg,#1dd1a1,#10ac81 62%,#0c8f6b);border-radius:16px;box-shadow:0 16px 34px -18px rgba(12,143,107,.65);color:#06382c;display:flex;gap:30px;overflow:hidden;padding:28px 32px;position:relative}.oz-score:before{background:radial-gradient(circle,hsla(0,0%,100%,.3),transparent 62%);height:440px;right:-8%;top:-55%;width:440px}.oz-score:after,.oz-score:before{border-radius:50%;content:"";pointer-events:none;position:absolute}.oz-score:after{background:radial-gradient(circle,rgba(0,0,0,.08),transparent 60%);bottom:-70%;height:320px;left:8%;width:320px}.oz-score>*{position:relative;z-index:1}.oz-score__gauge{filter:drop-shadow(0 8px 16px rgba(3,40,30,.28));flex:0 0 auto;line-height:0;position:relative}.oz-score__grade{background:#fff;border-radius:50%;box-shadow:0 4px 10px rgba(3,40,30,.28);color:#0c8f6b;display:grid;font-size:14px;font-weight:800;height:30px;place-items:center;position:absolute;right:2px;top:8px;width:30px}.oz-score__body{flex:1;min-width:0}.oz-score__body h2{color:#06382c;font-size:21px;font-weight:750;letter-spacing:-.02em;margin:0 0 4px}.oz-score__body p{color:rgba(6,56,44,.82);font-size:13px;font-weight:500;margin:0 0 15px}.oz-score__chips{display:flex;flex-wrap:wrap;gap:8px}.oz-chip{align-items:center;background:hsla(0,0%,100%,.18);border:1px solid hsla(0,0%,100%,.28);border-radius:999px;color:rgba(6,56,44,.72);display:inline-flex;font-size:11.5px;font-weight:650;gap:6px;padding:5px 12px 5px 9px;transition:transform .15s ease,box-shadow .15s ease}.oz-chip .dashicons{font-size:15px;height:15px;opacity:.65;width:15px}.oz-chip.is-on{background:#fff;border-color:#fff;box-shadow:0 3px 8px -2px rgba(3,40,30,.22);color:#0c8f6b}.oz-chip.is-on .dashicons{color:#1dd1a1;opacity:1}.oz-chip.is-on:hover{box-shadow:0 6px 14px -4px rgba(3,40,30,.3);transform:translateY(-1px)}.oz-convert{align-items:center;background:#f4f7f8;border-radius:12px;display:flex;gap:16px;justify-content:space-between;margin-top:16px;padding:16px}.oz-convert__info{color:#6b7a86;display:flex;flex-direction:column;font-size:12.5px;gap:3px}.oz-convert__info strong{color:#0f2027;font-size:13px}.oz-progress{background:#dfe6e8;border-radius:999px;height:6px;margin-top:4px;overflow:hidden;width:200px}.oz-progress div{background:linear-gradient(90deg,#1dd1a1,#10ac81);height:100%;transition:width .3s ease}.oz-kpis{display:grid;gap:14px;grid-template-columns:repeat(3,1fr)}.oz-kpi {background:#fff;border:1px solid #e7ecef;border-radius:14px;display:flex;flex-direction:column;padding:16px 18px}.oz-kpi>.dashicons{color:#10ac81;font-size:20px;margin-bottom:8px}.oz-kpi strong{font-size:24px;font-weight:750;min-height:28px}.oz-kpi span{color:#6b7a86;font-size:12px}.oz-quick{display:grid;gap:16px;grid-template-columns:1fr 1fr}.oz-quick__actions{display:flex;flex-wrap:wrap;gap:10px}.oz-cachelist{border-top:1px solid #e7ecef;margin:18px 0 0}.oz-cachelist>li{align-items:center;border-bottom:1px solid #f1f4f5;color:#6b7a86;display:flex;font-size:13px;gap:10px;padding:11px 2px}.oz-cachelist>li:last-child{border-bottom:0}.oz-cachelist>li .dashicons{color:#10ac81;font-size:17px}.oz-cachelist>li strong{color:#0f2027;font-weight:650;margin-left:auto}.oz-cachelist>li .oz-row-label{display:flex;flex-direction:column;line-height:1.3}.oz-cachelist>li .oz-row-label small{color:#6b7a86;font-size:11.5px;opacity:.85}.oz-status{margin:0}.oz-status li{align-items:center;border-bottom:1px solid #e7ecef;display:flex;font-size:13.5px;font-weight:600;gap:10px;padding:9px 6px}.oz-status li:last-child{border-bottom:0}.oz-status li>.oz-ico{color:#6b7a86;flex:0 0 18px;height:18px;width:18px}.oz-status__name{background:transparent;border:0;color:#0f2027;cursor:pointer;flex:1;font:inherit;font-weight:600;padding:4px 0;text-align:left}.oz-status__name:hover{color:#0c8f6b}.oz-status .components-toggle-control{margin-left:auto}.oz-card,.oz-panel{background:#fff;border:1px solid #e7ecef;border-radius:16px;overflow:visible}.oz-card .oz-card__head{border-radius:16px 16px 0 0}.oz-panel__head{padding:18px 22px 0}.oz-panel__head h2{font-size:16px;font-weight:650;margin:0}.oz-panel__head p{color:#6b7a86;font-size:12.5px;margin:4px 0 0}.oz-panel__body{padding:8px 22px 20px}.oz-panel__body .components-base-control{border-bottom:1px solid #f1f4f5;padding:14px 0}.oz-panel__body .components-base-control:last-child{border-bottom:0}.oz-card{transition:box-shadow .18s ease,border-color .18s ease}.oz-card.is-enabled{border-color:rgba(29,209,161,.5);box-shadow:0 0 0 1px rgba(29,209,161,.22)}.oz-card__head{align-items:flex-start;display:flex;gap:15px;padding:20px 22px}.oz-card__icon{background:#f4f7f8;border-radius:12px;color:#6b7a86;display:grid;flex:0 0 46px;font-size:22px;height:46px;place-items:center;width:46px}.oz-card__title{flex:1;min-width:0}.oz-card__title h2{align-items:center;display:flex;font-size:16px;font-weight:650;gap:10px;margin:0 0 3px}.oz-card__title p{color:#6b7a86;font-size:12.5px;margin:0}.oz-card__body{border-top:1px solid #e7ecef;padding:4px 22px 16px}.oz-card__body .components-base-control{border-bottom:1px solid #f1f4f5;padding:14px 0}.oz-card__body .components-base-control:last-child{border-bottom:0}.is-enabled .oz-card__icon{background:linear-gradient(135deg,#1dd1a1,#10ac81);color:#fff}.oz-inline-actions{display:flex;gap:10px;padding-top:16px}.oz-dbgroup{border-top:1px solid #eef0f2;padding:18px 0}.oz-dbgroup:first-of-type{border-top:0;padding-top:4px}.oz-dbgroup__title{color:#0e1116;font-size:14px;font-weight:700;margin:0 0 4px}.oz-dbgroup__desc{color:#6b7280;font-size:13px;line-height:1.5;margin:0 0 14px}.oz-dbgroup .components-toggle-control{margin-bottom:14px}.oz-dbgroup .components-toggle-control:last-child{margin-bottom:0}.oz-dbwarn{align-items:flex-start;background:#fff5f5;border:1px solid #fcd9d9;border-radius:10px;color:#b42318;display:flex;font-size:13px;font-weight:600;gap:8px;line-height:1.5;margin:4px 0;padding:12px 14px}.oz-dbwarn .dashicons{color:#e05242;flex:0 0 auto}.oz-cachewarn{align-items:center;background:#fff8ec;border:1px solid #f4dcae;border-radius:12px;color:#7a4b00;display:flex;gap:13px;margin:0 0 18px;padding:14px 16px}.oz-cachewarn .dashicons{color:#e0900f;flex:0 0 auto;font-size:22px;height:22px;width:22px}.oz-cachewarn__text{flex:1 1 auto;min-width:0}.oz-cachewarn__text strong{color:#6b4200;display:block;font-size:14px;font-weight:700}.oz-cachewarn__text p{color:#8a6410;font-size:12.5px;line-height:1.5;margin:3px 0 0}.oz-cachewarn .oz-btn-primary{flex:0 0 auto}@media(max-width:640px){.oz-cachewarn{flex-wrap:wrap}.oz-cachewarn .oz-btn-primary{justify-content:center;width:100%}}.oz-subhead{color:#8a96a0;font-size:11px;font-weight:700;letter-spacing:.08em;margin:18px 0 2px;padding-top:14px;text-transform:uppercase}.oz-health,.oz-subhead{border-top:1px solid #e7ecf0}.oz-health{margin-top:18px;padding-top:16px}.oz-health__head{align-items:center;display:flex;gap:12px;justify-content:space-between}.oz-health__head strong{font-size:14px}.oz-health__hint{color:#6b7280;font-size:12px;line-height:1.5;margin:4px 0 10px}.oz-health__list{display:flex;flex-direction:column;gap:6px;margin:0}.oz-health__row{align-items:center;display:flex;font-size:12.5px;gap:8px;line-height:1.4;margin:0}.oz-health__row .dashicons{flex:0 0 auto;font-size:17px;height:17px;width:17px}.oz-health__row.is-ok{color:#216e4e}.oz-health__row.is-ok .dashicons{color:#1dd1a1}.oz-health__row.is-warn{color:#8a5a00}.oz-health__row.is-warn .dashicons{color:#e0900f}.oz-health__row.is-bad{color:#b42318}.oz-health__row.is-bad .dashicons{color:#e05242}.oz-health__err{color:#b42318;font-size:12.5px}.oz-health__fix{align-items:center;display:flex;flex-wrap:wrap;gap:12px;margin-top:14px}.oz-health__fix span{color:#6b7280;flex:1 1 160px;font-size:12px}.oz-health__conflict{display:flex;flex-direction:column;gap:8px;margin-top:12px}.oz-health__conflict-row{align-items:center;background:#fff5f5;border:1px solid #fcd9d9;border-radius:9px;display:flex;flex-wrap:wrap;gap:10px;padding:10px 12px}.oz-health__conflict-row .dashicons{color:#e05242;flex:0 0 auto}.oz-health__conflict-row span:not(.dashicons){color:#8a2f22;flex:1 1 200px;font-size:12.5px}.oz-health__conflict-row .oz-btn-danger.components-button{flex:0 0 auto;height:32px}.oz-health__manual{background:#fff8ec;border:1px solid #f4dcae;border-radius:9px;margin-top:12px;padding:10px 12px}.oz-health__manual p{color:#7a4b00;font-size:12px;margin:0 0 6px}.oz-health__manual code{background:#2b2f36;border-radius:6px;color:#e7f0ec;display:inline-block;font-size:12.5px;padding:6px 10px}.oz-btn-ghost.components-button{border:1px solid #d5dbe0;border-radius:8px;color:#384049;font-size:12.5px;height:30px}.oz-btn-ghost.components-button:hover:not(:disabled){border-color:#1dd1a1;color:#0b7a5c}.oz-pill{align-items:center;border-radius:999px;display:inline-flex;font-size:11px;font-weight:700;gap:5px;padding:3px 9px}.oz-pill i{background:currentColor;border-radius:50%;height:6px;width:6px}.oz-pill.is-on{background:rgba(29,209,161,.16);color:#0c8f6b}.oz-pill.is-off{background:#eef1f2;color:#6b7a86}.oz-shell .components-range-control__thumb,.oz-shell .components-range-control__track{background-color:#1dd1a1!important}.oz-fieldgroup{background:rgba(29,209,161,.035);border:1px solid #e7ecef;border-radius:12px;margin-top:18px;padding:16px 16px 4px}.oz-fieldgroup__title{color:#0e1116;font-size:13px;font-weight:700;margin:0 0 4px}.oz-fieldgroup__desc{color:#6b7280;font-size:12.5px;line-height:1.5;margin:0 0 14px}.oz-fieldgroup .components-base-control{margin-bottom:14px}.oz-shell .components-form-token-field__input-container{background:#fff;border:1px solid #e7ecef;border-radius:10px;min-height:44px;padding:5px 8px;transition:border-color .15s ease,box-shadow .15s ease}.oz-shell .components-form-token-field__input-container.is-active,.oz-shell .components-form-token-field__input-container:focus-within{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)}.oz-shell .components-form-token-field__token{border-radius:8px;font-size:12.5px;margin:3px;overflow:hidden}.oz-shell .components-form-token-field__token-text{background:rgba(29,209,161,.14);border-radius:8px 0 0 8px;color:#0c8f6b;font-weight:650;line-height:1.7;padding:2px 4px 2px 11px}.oz-shell .components-form-token-field__remove-token{background:rgba(29,209,161,.14);border-radius:0 8px 8px 0;color:#0c8f6b;min-width:24px;padding:0 7px;transition:background .15s ease,color .15s ease}.oz-shell .components-form-token-field__remove-token svg{fill:currentColor}.oz-shell .components-form-token-field__remove-token:hover{background:#10ac81;color:#fff}.oz-shell .components-form-token-field__input{font-size:13.5px;margin:4px;min-height:28px}.oz-shell .components-input-control__backdrop{display:none!important}.oz-shell .components-input-control__input,.oz-shell .components-select-control__input,.oz-shell .components-text-control__input{background:#fff!important;border:1px solid #e7ecef!important;border-radius:10px!important;box-shadow:none!important;color:#0f2027;font-size:13.5px;min-height:42px;padding:0 14px}.oz-shell .components-input-control__input:hover,.oz-shell .components-select-control__input:hover,.oz-shell .components-text-control__input:hover{border-color:#bcc6cc!important}.oz-shell .components-input-control__input:focus,.oz-shell .components-select-control__input:focus,.oz-shell .components-text-control__input:focus{border-color:#1dd1a1!important;box-shadow:0 0 0 3px rgba(29,209,161,.16)!important;outline:none!important}.oz-shell select.components-select-control__input{cursor:pointer}.oz-pillgroup .components-base-control__label{display:block;font-weight:600;margin-bottom:8px}.oz-pillgroup__row{background:#f0f2f4;border-radius:12px;display:inline-flex;flex-wrap:wrap;gap:6px;padding:4px}.oz-pillgroup .components-base-control__help{display:block;margin-top:8px}.oz-pillbtn{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;border-radius:9px;color:#4b5563;cursor:pointer;font-size:13px;font-weight:600;min-width:44px;padding:7px 16px;transition:background .12s ease,color .12s ease,box-shadow .12s ease}.oz-pillbtn:hover{color:#0e1116}.oz-pillbtn.is-on{background:#fff;box-shadow:0 1px 3px rgba(4,56,44,.14);color:#0c8f6b}.oz-pillbtn:focus-visible{outline:2px solid #1dd1a1;outline-offset:1px}.oz-select{position:relative}.oz-select .components-base-control__label{display:block;margin-bottom:8px}.oz-select__btn{align-items:center;background:#fff;border:1px solid #e7ecef;border-radius:10px;color:#0f2027;cursor:pointer;display:flex;font-size:13.5px;height:42px;justify-content:space-between;padding:0 14px;transition:border-color .15s ease,box-shadow .15s ease;width:100%}.oz-select__btn .dashicons{color:#0c8f6b;transition:transform .18s ease}.oz-select__btn:hover{border-color:#bcc6cc}.oz-select__btn:focus,.oz-select__btn:focus-visible{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)!important;outline:none!important}.oz-select__btn[aria-expanded=true]{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)}.oz-select__btn[aria-expanded=true] .dashicons{transform:rotate(180deg)}.oz-select__menu{background:#fff;border:1px solid #e7ecef;border-radius:12px;box-shadow:0 14px 34px rgba(15,32,39,.18);left:0;list-style:none;margin:0;padding:6px;position:absolute;right:0;top:calc(100% + 6px);z-index:30}.oz-select__item{align-items:center;border-radius:8px;color:#0f2027;cursor:pointer;display:flex;font-size:13.5px;justify-content:space-between;padding:9px 12px}.oz-select__item .dashicons{color:#10ac81;font-size:18px}.oz-select__item.is-active,.oz-select__item:hover{background:rgba(29,209,161,.12);color:#0c8f6b}.oz-select__item.is-selected{color:#0c8f6b;font-weight:650}.oz-shell .oz-btn-danger.components-button,.oz-shell .oz-btn-ghost.components-button,.oz-shell .oz-btn-primary.components-button{border-radius:10px;font-weight:650;height:38px;transition:transform .16s cubic-bezier(.2,.7,.3,1),box-shadow .18s ease,background .16s ease,border-color .16s ease,color .16s ease}.oz-shell .oz-btn-danger.components-button .dashicons,.oz-shell .oz-btn-danger.components-button svg,.oz-shell .oz-btn-ghost.components-button .dashicons,.oz-shell .oz-btn-ghost.components-button svg,.oz-shell .oz-btn-primary.components-button .dashicons,.oz-shell .oz-btn-primary.components-button svg{transition:transform .22s cubic-bezier(.2,.7,.3,1)}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled),.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled),.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){transform:translateY(-1px)}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) svg{transform:scale(1.14)}.oz-shell .oz-btn-danger.components-button:active:not(:disabled),.oz-shell .oz-btn-ghost.components-button:active:not(:disabled),.oz-shell .oz-btn-primary.components-button:active:not(:disabled){transform:translateY(0) scale(.975);transition-duration:.05s}.oz-shell .oz-btn-danger.components-button:focus-visible,.oz-shell .oz-btn-ghost.components-button:focus-visible,.oz-shell .oz-btn-primary.components-button:focus-visible{outline:2px solid #1dd1a1;outline-offset:2px}.oz-shell .oz-btn-danger.components-button:disabled,.oz-shell .oz-btn-ghost.components-button:disabled,.oz-shell .oz-btn-primary.components-button:disabled{opacity:.5;transform:none}.oz-shell .oz-btn-primary.components-button{background:linear-gradient(135deg,#1dd1a1,#10ac81);border:0;box-shadow:0 1px 2px rgba(20,60,48,.12);color:#fff;padding:0 18px}.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){background:linear-gradient(135deg,#10ac81,#0c8f6b);box-shadow:0 8px 20px -7px rgba(29,209,161,.65);color:#fff}.oz-shell .oz-btn-primary.components-button.is-busy{animation:oz-btn-sheen 1.4s linear infinite;background-size:200% 100%}.oz-shell .oz-btn-ghost.components-button{background:#fff;border:1px solid rgba(29,209,161,.5);box-shadow:none;color:#0c8f6b}.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled){background:rgba(29,209,161,.08);border-color:#1dd1a1;box-shadow:0 6px 16px -8px rgba(29,209,161,.5);color:#0c8f6b}.oz-shell .oz-btn-danger.components-button{background:#fff;border:1px solid rgba(214,54,56,.5);color:#d63638}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled){background:rgba(214,54,56,.08);border-color:#d63638;box-shadow:0 6px 16px -8px rgba(214,54,56,.5);color:#b32d2e}.oz-shell .oz-btn--x.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn--x.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg{transform:scale(1.1) rotate(90deg)}@media(prefers-reduced-motion:reduce){.oz-shell .oz-btn--x.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn--x.components-button:hover:not(:disabled) svg{transform:none}}@keyframes oz-btn-sheen{0%{background-image:linear-gradient(135deg,#1dd1a1,#10ac81,#1dd1a1);background-position:200% 0}to{background-image:linear-gradient(135deg,#1dd1a1,#10ac81,#1dd1a1);background-position:-200% 0}}@media(prefers-reduced-motion:reduce){.oz-shell .oz-btn-danger.components-button,.oz-shell .oz-btn-ghost.components-button,.oz-shell .oz-btn-primary.components-button{transition:background .16s ease,color .16s ease,border-color .16s ease}.oz-shell .oz-btn-danger.components-button:active:not(:disabled),.oz-shell .oz-btn-danger.components-button:hover:not(:disabled),.oz-shell .oz-btn-ghost.components-button:active:not(:disabled),.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled),.oz-shell .oz-btn-primary.components-button:active:not(:disabled),.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){transform:none}.oz-shell .oz-btn-danger.components-button .dashicons,.oz-shell .oz-btn-danger.components-button svg,.oz-shell .oz-btn-ghost.components-button .dashicons,.oz-shell .oz-btn-ghost.components-button svg,.oz-shell .oz-btn-primary.components-button .dashicons,.oz-shell .oz-btn-primary.components-button svg{transition:none}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) svg{transform:none}.oz-shell .oz-btn-danger.components-button.is-busy,.oz-shell .oz-btn-ghost.components-button.is-busy,.oz-shell .oz-btn-primary.components-button.is-busy{animation:none}}.oz-shell .components-button.is-primary:focus:not(:disabled),.oz-shell .components-button:focus,.oz-shell .components-button:focus:not(:disabled){box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle,.oz-shell .components-form-toggle__input,.oz-shell .components-form-toggle__input:active,.oz-shell .components-form-toggle__input:focus,.oz-shell .components-form-toggle__input:focus-visible{border:none!important;box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle__track{background-color:#cdd6db!important;border:1px solid transparent!important;box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle.is-checked .components-form-toggle__track{background-color:#1dd1a1!important}.oz-shell .components-form-toggle__input:focus+.components-form-toggle__track,.oz-shell .components-form-toggle__input:focus-visible+.components-form-toggle__track{box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle__thumb{background-color:#fff!important;border:none!important;box-shadow:0 1px 2px rgba(15,32,39,.25)!important}.oz-savebar{align-items:center;background:#fff;border-top:1px solid #e7ecef;bottom:0;box-shadow:0 -6px 18px rgba(15,32,39,.06);display:flex;gap:16px;justify-content:flex-end;left:160px;padding:13px 32px;position:fixed;right:0;z-index:9}.oz-savebar__hint{color:#6b7a86;font-size:13px}.oz-savebar.is-dirty .oz-savebar__hint{color:#0c8f6b;font-weight:650}.folded .oz-savebar{left:36px}.oz-toast{bottom:72px;z-index:99}.oz-job,.oz-toast{position:fixed;right:24px}.oz-job{background:#fff;border:1px solid #e7ecef;border-radius:16px;bottom:84px;box-shadow:0 18px 50px rgba(15,32,39,.22);color:#0f2027;font-size:13px;max-width:calc(100vw - 48px);overflow:hidden;width:420px;z-index:100}.oz-job__head{align-items:center;display:flex;gap:12px;padding:16px 16px 12px}.oz-job__head .components-spinner{margin:0}.oz-job__icon-status{font-size:22px;height:22px;width:22px}.oz-job__icon-status--done{color:#10ac81}.oz-job__icon-status--stopped{color:#6b7a86}.oz-job__icon-status--error{color:#d63638}.oz-job__title{flex:1;min-width:0}.oz-job__title strong{display:block;font-size:15px;font-weight:700;line-height:1.2}.oz-job__title span{color:#6b7a86;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.oz-job__pct{color:#0f2027;font-size:13px;font-variant-numeric:tabular-nums;font-weight:700}.oz-job__icon{background:transparent;border:0;border-radius:6px;color:#6b7a86;cursor:pointer;display:inline-flex;padding:4px}.oz-job__icon:hover{background:#f4f7f8;color:#0f2027}.oz-job__icon .dashicons{font-size:18px;height:18px;width:18px}.oz-job__body{padding:0 16px 16px}.oz-job__row{align-items:baseline;display:flex;gap:12px;justify-content:space-between;margin-bottom:8px}.oz-job__row span{line-height:1.4}.oz-job__row strong{flex:0 0 auto;font-variant-numeric:tabular-nums;font-weight:700}.oz-job__bar{background:#e7ecef;border-radius:999px;height:7px;overflow:hidden}.oz-job__bar div{background:linear-gradient(90deg,#1dd1a1,#10ac81);border-radius:999px;height:100%;transition:width .35s ease}.oz-job__bar.is-indeterminate div{animation:oz-indeterminate 1.1s ease-in-out infinite;border-radius:999px;width:40%!important}.oz-job.is-min .oz-job__bar{margin:2px 16px 16px}.oz-job.is-min .oz-job__head{padding-bottom:10px}.oz-job.is-done .oz-job__bar div{background:linear-gradient(90deg,#10ac81,#0c8f6b)}.oz-job.is-stopped .oz-job__bar div{background:#c3ccd2}.oz-job.is-error .oz-job__bar div{background:linear-gradient(90deg,#f16b6b,#d63638)}.oz-job__step{color:#6b7a86;font-size:12px;margin-top:8px}.oz-job__stop{align-items:center;background:transparent;border:0;color:#d63638;cursor:pointer;display:inline-flex;font-size:13px;font-weight:650;gap:6px;margin-top:14px;padding:4px 0}.oz-job__stop .dashicons{font-size:18px}.oz-job__stop:hover{color:#b32d2e}.oz-job__log{background:#11202b;border-radius:10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.7;margin-top:14px;max-height:150px;overflow-y:auto;padding:10px 12px}.oz-logline{color:#cdd8df;display:flex;gap:8px}.oz-logline__tag{color:#5fd0ad;flex:0 0 auto;font-weight:700;min-width:42px}.oz-logline--warn .oz-logline__tag{color:#f0b429}.oz-logline--error .oz-logline__tag{color:#ff6b6b}.oz-logline--done .oz-logline__tag{color:#1dd1a1}.oz-logline__text{word-break:break-word}@media(max-width:1100px){.oz-kpis,.oz-quick{grid-template-columns:1fr}}@media(max-width:1024px){.oz-score{align-items:flex-start;flex-direction:column;text-align:left}.oz-convert{align-items:stretch;flex-direction:column;gap:12px}.oz-convert .oz-btn-ghost.components-button,.oz-convert .oz-btn-primary.components-button{justify-content:center;width:100%}}.oz-mobile-toggle,.oz-nav-overlay{display:none!important}@media(max-width:1024px){.oz-shell{flex-direction:column;margin:8px 8px 110px}.oz-content{border-left:0;min-width:0;width:100%}.oz-sidebar{border-bottom:1px solid #e7ecef;border-right:0;flex:none;max-height:none;overflow:visible;padding:14px 12px;position:static;width:100%}.oz-sidebar__brand{margin-bottom:10px;padding-bottom:12px}.oz-sidebar nav{display:flex;flex-wrap:nowrap;gap:6px;overflow-x:auto;-webkit-overflow-scrolling:touch;padding-bottom:4px}.oz-navitem{border-bottom:3px solid transparent;border-left:0;border-radius:10px;flex:0 0 auto;padding:8px 12px;width:auto}.oz-navitem.is-active{border-bottom-color:#1dd1a1;border-left-color:transparent}.oz-navitem .oz-navitem__text strong{white-space:nowrap}.oz-navitem .oz-navitem__dot,.oz-navitem small,.oz-sidebar__foot{display:none}.oz-topbar{padding:16px 18px}.oz-topbar h1{font-size:18px}.oz-main{padding:16px 18px}.oz-savebar{left:0;padding:12px 18px}.oz-card__body{padding-left:18px;padding-right:18px}.oz-job{left:8px;right:8px;width:auto}}@media(max-width:560px){.oz-tiles{grid-template-columns:1fr}.oz-hero,.oz-score{padding:18px}.oz-card__head{gap:12px;padding:16px}.oz-card__icon{flex-basis:40px;height:40px;width:40px}.oz-savebar__hint{display:none}}@keyframes oz-indeterminate{0%{transform:translateX(-100%)}to{transform:translateX(350%)}}.oz-muted{color:#6b7a86;font-size:13px}.oz-reqs{list-style:none;margin:0;padding:0}.oz-req{align-items:center;border-bottom:1px solid #e7ecef;border-radius:8px;display:grid;font-size:13.5px;gap:6px 10px;grid-template-columns:20px 1fr auto;padding:10px 6px}.oz-req:last-child{border-bottom:0}.oz-req__dot{font-size:18px;height:18px;width:18px}.oz-req__label{font-weight:650}.oz-req__value{color:#6b7a86;font-weight:600;justify-self:end}.oz-req__note{color:#6b7a86;font-size:12px;font-weight:400;grid-column:2/-1;line-height:1.45;margin-top:2px}.oz-req--ok>.oz-req__dot{color:#0c8f6b}.oz-req--warn>.oz-req__dot{color:#e0900f}.oz-req--error>.oz-req__dot{color:#e05242}.oz-req--info>.oz-req__dot,.oz-req--info>.oz-req__value{color:#6b7a86}.oz-req--warn{background:rgba(224,144,15,.06)}.oz-req--error{background:rgba(224,82,66,.06)}.oz-notice{align-items:flex-start;background:rgba(224,82,66,.08);border:1px solid rgba(224,82,66,.28);border-radius:10px;color:#7a2018;display:flex;font-size:13px;gap:10px;line-height:1.5;margin:0 0 14px;padding:12px 14px}.oz-notice .dashicons{color:#e05242;flex:0 0 20px;margin-top:1px}.oz-notice--warn{background:rgba(224,144,15,.09);border-color:rgba(224,144,15,.3);color:#7a5406}.oz-notice--warn .dashicons{color:#e0900f}.oz-recs{list-style:none;margin:0;padding:0}.oz-rec{align-items:center;border-bottom:1px solid #e7ecef;border-radius:8px;display:flex;gap:12px;padding:12px 8px}.oz-rec:last-child{border-bottom:0}.oz-rec__dot{flex:0 0 20px;font-size:20px;height:20px;margin-top:1px;width:20px}.oz-rec__text{flex:1;min-width:0}.oz-rec__text strong{display:block;font-size:13.5px;font-weight:650}.oz-rec__text small{color:#6b7a86;display:block;font-size:12px;line-height:1.45;margin-top:2px}.oz-rec__btn.components-button{flex:0 0 auto}.oz-rec--high>.oz-rec__dot{color:#e05242}.oz-rec--medium>.oz-rec__dot{color:#e0900f}.oz-rec--low>.oz-rec__dot{color:#6b7a86}.oz-rec--high{background:rgba(224,82,66,.05)}.oz-rec--medium{background:rgba(224,144,15,.05)}.oz-recs-clear{align-items:center;color:#6b7a86;display:flex;font-size:13px;gap:8px}.oz-recs-clear .dashicons{color:#0c8f6b}.oz-select--compact{border-bottom:0!important;flex:none;margin:0 0 0 auto!important;padding:0!important}.oz-select--compact .oz-select__btn{background:#f7faf9;border-radius:9px;font-size:13px;font-weight:650;gap:10px;height:30px;min-width:132px;padding:0 10px 0 12px;width:auto}.oz-select--compact .oz-select__btn:disabled{cursor:progress;opacity:.6}.oz-select--compact .oz-select__btn .dashicons{font-size:16px;height:16px;width:16px}.oz-select--compact .oz-select__menu{left:auto;min-width:190px;right:0}.oz-select--compact .oz-select__item{font-size:13px;padding:8px 10px}1 .oz-shell{align-items:flex-start;background:#fff;border:1px solid #e7ecef;border-radius:16px;color:#0f2027;display:flex;font-size:14px;margin:10px 20px 130px 0;min-height:640px;overflow:hidden}.oz-shell *{box-sizing:border-box}.oz-shell .dashicons{font-family:dashicons}.oz-sidebar{align-self:flex-start;background:#fff;display:flex;flex:0 0 264px;flex-direction:column;padding:18px 14px}.oz-sidebar__brand{align-items:center;border-bottom:1px solid #e7ecef;display:flex;gap:12px;margin-bottom:10px;padding:10px 10px 18px}.oz-sidebar__brand strong{display:block;font-size:18px;font-weight:750;letter-spacing:-.01em}.oz-sidebar__brand span{color:#6b7a86;font-size:11px}.oz-sidebar__brand-fallback{align-items:center;display:flex;gap:12px}.oz-sidebar__foot{border-top:1px solid #e7ecef;color:#6b7a86;font-size:11px;margin-top:12px;padding:12px 12px 2px}.oz-logo{background:linear-gradient(135deg,#1dd1a1,#10ac81);border-radius:12px;color:#fff;display:grid;font-size:26px;height:44px;place-items:center;width:44px}.oz-logo-img{display:block;height:30px;max-width:100%;width:auto}.oz-navitem{align-items:center;background:transparent;border:0;border-left:3px solid transparent;border-radius:0 10px 10px 0;color:#0f2027;cursor:pointer;display:flex;gap:12px;padding:11px 12px;text-align:left;transition:all .14s ease;width:100%}.oz-navitem>.oz-ico{color:#6b7a86;flex:0 0 20px;height:20px;transition:color .14s ease,transform .14s ease;width:20px}.oz-navitem:hover>.oz-ico{color:#0f2027}.oz-navitem.is-active>.oz-ico{color:#0c8f6b}.oz-navitem__text{flex:1;min-width:0}.oz-navitem__text strong{display:block;font-size:13.5px;font-weight:650;line-height:1.2}.oz-navitem__text small{color:#6b7a86;font-size:11px}.oz-navitem__dot{background:#1dd1a1;border-radius:50%;height:8px;width:8px}.oz-navitem:hover{background:#f4f7f8}.oz-navitem.is-active{background:rgba(29,209,161,.12);border-left-color:#1dd1a1}.oz-navitem.is-active .oz-navitem__text strong{color:#0c8f6b}.oz-content{align-self:stretch;background:#f4f7f8;border-left:1px solid #e7ecef;display:flex;flex:1;flex-direction:column;min-width:0}.oz-topbar{align-items:center;background:#fff;border-bottom:1px solid #e7ecef;display:flex;gap:14px;padding:20px 28px}.oz-topbar__icon{background:rgba(29,209,161,.14);border-radius:12px;color:#0c8f6b;display:grid;font-size:22px;height:44px;place-items:center;width:44px}.oz-topbar h1{font-size:20px;font-weight:700;margin:0;padding:0}.oz-topbar p{color:#6b7a86;font-size:12.5px;margin:0}.oz-main{display:flex;flex-direction:column;gap:16px;padding:24px 28px}.oz-welcome{align-items:center;background:linear-gradient(125deg,rgba(29,209,161,.9),rgba(16,172,129,.92));border-radius:16px;color:#06382c;display:flex;gap:20px;justify-content:space-between;overflow:hidden;padding:22px 26px}.oz-welcome h2{color:#06382c;font-size:18px;font-weight:700;margin:0 0 6px}.oz-welcome p{color:#0c5b46;font-size:13px;margin:0;max-width:620px}.oz-welcome__icon{flex:0 0 auto;font-size:64px;height:64px;opacity:.5;width:64px}.oz-score{align-items:center;background:linear-gradient(125deg,#1dd1a1,#10ac81 62%,#0c8f6b);border-radius:16px;box-shadow:0 16px 34px -18px rgba(12,143,107,.65);color:#06382c;display:flex;gap:30px;overflow:hidden;padding:28px 32px;position:relative}.oz-score:before{background:radial-gradient(circle,hsla(0,0%,100%,.3),transparent 62%);height:440px;right:-8%;top:-55%;width:440px}.oz-score:after,.oz-score:before{border-radius:50%;content:"";pointer-events:none;position:absolute}.oz-score:after{background:radial-gradient(circle,rgba(0,0,0,.08),transparent 60%);bottom:-70%;height:320px;left:8%;width:320px}.oz-score>*{position:relative;z-index:1}.oz-score__gauge{filter:drop-shadow(0 8px 16px rgba(3,40,30,.28));flex:0 0 auto;line-height:0;position:relative}.oz-score__grade{background:#fff;border-radius:50%;box-shadow:0 4px 10px rgba(3,40,30,.28);color:#0c8f6b;display:grid;font-size:14px;font-weight:800;height:30px;place-items:center;position:absolute;right:2px;top:8px;width:30px}.oz-score__body{flex:1;min-width:0}.oz-score__body h2{color:#06382c;font-size:21px;font-weight:750;letter-spacing:-.02em;margin:0 0 4px}.oz-score__body p{color:rgba(6,56,44,.82);font-size:13px;font-weight:500;margin:0 0 15px}.oz-score__chips{display:flex;flex-wrap:wrap;gap:8px}.oz-chip{align-items:center;background:hsla(0,0%,100%,.18);border:1px solid hsla(0,0%,100%,.28);border-radius:999px;color:rgba(6,56,44,.72);display:inline-flex;font-size:11.5px;font-weight:650;gap:6px;padding:5px 12px 5px 9px;transition:transform .15s ease,box-shadow .15s ease}.oz-chip .dashicons{font-size:15px;height:15px;opacity:.65;width:15px}.oz-chip.is-on{background:#fff;border-color:#fff;box-shadow:0 3px 8px -2px rgba(3,40,30,.22);color:#0c8f6b}.oz-chip.is-on .dashicons{color:#1dd1a1;opacity:1}.oz-chip.is-on:hover{box-shadow:0 6px 14px -4px rgba(3,40,30,.3);transform:translateY(-1px)}.oz-convert{align-items:center;background:#f4f7f8;border-radius:12px;display:flex;gap:16px;justify-content:space-between;margin-top:16px;padding:16px}.oz-convert__info{color:#6b7a86;display:flex;flex-direction:column;font-size:12.5px;gap:3px}.oz-convert__info strong{color:#0f2027;font-size:13px}.oz-progress{background:#dfe6e8;border-radius:999px;height:6px;margin-top:4px;overflow:hidden;width:200px}.oz-progress div{background:linear-gradient(90deg,#1dd1a1,#10ac81);height:100%;transition:width .3s ease}.oz-kpis{display:grid;gap:14px;grid-template-columns:repeat(3,1fr)}.oz-kpis--single{grid-template-columns:1fr}.oz-kpi{background:#fff;border:1px solid #e7ecef;border-radius:14px;display:flex;flex-direction:column;padding:16px 18px}.oz-kpi>.dashicons{color:#10ac81;font-size:20px;margin-bottom:8px}.oz-kpi strong{font-size:24px;font-weight:750;min-height:28px}.oz-kpi span{color:#6b7a86;font-size:12px}.oz-quick{display:grid;gap:16px;grid-template-columns:1fr 1fr}.oz-quick__actions{display:flex;flex-wrap:wrap;gap:10px}.oz-cachelist{border-top:1px solid #e7ecef;margin:18px 0 0}.oz-cachelist>li{align-items:center;border-bottom:1px solid #f1f4f5;color:#6b7a86;display:flex;font-size:13px;gap:10px;padding:11px 2px}.oz-cachelist>li:last-child{border-bottom:0}.oz-cachelist>li .dashicons{color:#10ac81;font-size:17px}.oz-cachelist>li strong{color:#0f2027;font-weight:650;margin-left:auto}.oz-cachelist>li .oz-row-label{display:flex;flex-direction:column;line-height:1.3}.oz-cachelist>li .oz-row-label small{color:#6b7a86;font-size:11.5px;opacity:.85}.oz-status{margin:0}.oz-status li{align-items:center;border-bottom:1px solid #e7ecef;display:flex;font-size:13.5px;font-weight:600;gap:10px;padding:9px 6px}.oz-status li:last-child{border-bottom:0}.oz-status li>.oz-ico{color:#6b7a86;flex:0 0 18px;height:18px;width:18px}.oz-status__name{background:transparent;border:0;color:#0f2027;cursor:pointer;flex:1;font:inherit;font-weight:600;padding:4px 0;text-align:left}.oz-status__name:hover{color:#0c8f6b}.oz-status .components-toggle-control{margin-left:auto}.oz-card,.oz-panel{background:#fff;border:1px solid #e7ecef;border-radius:16px;overflow:visible}.oz-card .oz-card__head{border-radius:16px 16px 0 0}.oz-panel__head{padding:18px 22px 0}.oz-panel__head h2{font-size:16px;font-weight:650;margin:0}.oz-panel__head p{color:#6b7a86;font-size:12.5px;margin:4px 0 0}.oz-panel__body{padding:8px 22px 20px}.oz-panel__body .components-base-control{border-bottom:1px solid #f1f4f5;padding:14px 0}.oz-panel__body .components-base-control:last-child{border-bottom:0}.oz-card{transition:box-shadow .18s ease,border-color .18s ease}.oz-card.is-enabled{border-color:rgba(29,209,161,.5);box-shadow:0 0 0 1px rgba(29,209,161,.22)}.oz-card__head{align-items:flex-start;display:flex;gap:15px;padding:20px 22px}.oz-card__icon{background:#f4f7f8;border-radius:12px;color:#6b7a86;display:grid;flex:0 0 46px;font-size:22px;height:46px;place-items:center;width:46px}.oz-card__title{flex:1;min-width:0}.oz-card__title h2{align-items:center;display:flex;font-size:16px;font-weight:650;gap:10px;margin:0 0 3px}.oz-card__title p{color:#6b7a86;font-size:12.5px;margin:0}.oz-card__body{border-top:1px solid #e7ecef;padding:4px 22px 16px}.oz-card__body .components-base-control{border-bottom:1px solid #f1f4f5;padding:14px 0}.oz-card__body .components-base-control:last-child{border-bottom:0}.is-enabled .oz-card__icon{background:linear-gradient(135deg,#1dd1a1,#10ac81);color:#fff}.oz-inline-actions{display:flex;gap:10px;padding-top:16px}.oz-dbgroup{border-top:1px solid #eef0f2;padding:18px 0}.oz-dbgroup:first-of-type{border-top:0;padding-top:4px}.oz-dbgroup__title{color:#0e1116;font-size:14px;font-weight:700;margin:0 0 4px}.oz-dbgroup__desc{color:#6b7280;font-size:13px;line-height:1.5;margin:0 0 14px}.oz-dbgroup .components-toggle-control{margin-bottom:14px}.oz-dbgroup .components-toggle-control:last-child{margin-bottom:0}.oz-dbwarn{align-items:flex-start;background:#fff5f5;border:1px solid #fcd9d9;border-radius:10px;color:#b42318;display:flex;font-size:13px;font-weight:600;gap:8px;line-height:1.5;margin:4px 0;padding:12px 14px}.oz-dbwarn .dashicons{color:#e05242;flex:0 0 auto}.oz-cachewarn{align-items:center;background:#fff8ec;border:1px solid #f4dcae;border-radius:12px;color:#7a4b00;display:flex;gap:13px;margin:0 0 18px;padding:14px 16px}.oz-cachewarn .dashicons{color:#e0900f;flex:0 0 auto;font-size:22px;height:22px;width:22px}.oz-cachewarn__text{flex:1 1 auto;min-width:0}.oz-cachewarn__text strong{color:#6b4200;display:block;font-size:14px;font-weight:700}.oz-cachewarn__text p{color:#8a6410;font-size:12.5px;line-height:1.5;margin:3px 0 0}.oz-cachewarn .oz-btn-primary{flex:0 0 auto}@media(max-width:640px){.oz-cachewarn{flex-wrap:wrap}.oz-cachewarn .oz-btn-primary{justify-content:center;width:100%}}.oz-subhead{color:#8a96a0;font-size:11px;font-weight:700;letter-spacing:.08em;margin:18px 0 2px;padding-top:14px;text-transform:uppercase}.oz-health,.oz-subhead{border-top:1px solid #e7ecf0}.oz-health{margin-top:18px;padding-top:16px}.oz-health__head{align-items:center;display:flex;gap:12px;justify-content:space-between}.oz-health__head strong{font-size:14px}.oz-health__hint{color:#6b7280;font-size:12px;line-height:1.5;margin:4px 0 10px}.oz-health__list{display:flex;flex-direction:column;gap:6px;margin:0}.oz-health__row{align-items:center;display:flex;font-size:12.5px;gap:8px;line-height:1.4;margin:0}.oz-health__row .dashicons{flex:0 0 auto;font-size:17px;height:17px;width:17px}.oz-health__row.is-ok{color:#216e4e}.oz-health__row.is-ok .dashicons{color:#1dd1a1}.oz-health__row.is-warn{color:#8a5a00}.oz-health__row.is-warn .dashicons{color:#e0900f}.oz-health__row.is-bad{color:#b42318}.oz-health__row.is-bad .dashicons{color:#e05242}.oz-health__err{color:#b42318;font-size:12.5px}.oz-health__fix{align-items:center;display:flex;flex-wrap:wrap;gap:12px;margin-top:14px}.oz-health__fix span{color:#6b7280;flex:1 1 160px;font-size:12px}.oz-health__conflict{display:flex;flex-direction:column;gap:8px;margin-top:12px}.oz-health__conflict-row{align-items:center;background:#fff5f5;border:1px solid #fcd9d9;border-radius:9px;display:flex;flex-wrap:wrap;gap:10px;padding:10px 12px}.oz-health__conflict-row .dashicons{color:#e05242;flex:0 0 auto}.oz-health__conflict-row span:not(.dashicons){color:#8a2f22;flex:1 1 200px;font-size:12.5px}.oz-health__conflict-row .oz-btn-danger.components-button{flex:0 0 auto;height:32px}.oz-health__manual{background:#fff8ec;border:1px solid #f4dcae;border-radius:9px;margin-top:12px;padding:10px 12px}.oz-health__manual p{color:#7a4b00;font-size:12px;margin:0 0 6px}.oz-health__manual code{background:#2b2f36;border-radius:6px;color:#e7f0ec;display:inline-block;font-size:12.5px;padding:6px 10px}.oz-btn-ghost.components-button{border:1px solid #d5dbe0;border-radius:8px;color:#384049;font-size:12.5px;height:30px}.oz-btn-ghost.components-button:hover:not(:disabled){border-color:#1dd1a1;color:#0b7a5c}.oz-pill{align-items:center;border-radius:999px;display:inline-flex;font-size:11px;font-weight:700;gap:5px;padding:3px 9px}.oz-pill i{background:currentColor;border-radius:50%;height:6px;width:6px}.oz-pill.is-on{background:rgba(29,209,161,.16);color:#0c8f6b}.oz-pill.is-off{background:#eef1f2;color:#6b7a86}.oz-shell .components-range-control__thumb,.oz-shell .components-range-control__track{background-color:#1dd1a1!important}.oz-fieldgroup{background:rgba(29,209,161,.035);border:1px solid #e7ecef;border-radius:12px;margin-top:18px;padding:16px 16px 4px}.oz-fieldgroup__title{color:#0e1116;font-size:13px;font-weight:700;margin:0 0 4px}.oz-fieldgroup__desc{color:#6b7280;font-size:12.5px;line-height:1.5;margin:0 0 14px}.oz-fieldgroup .components-base-control{margin-bottom:14px}.oz-shell .components-form-token-field__input-container{background:#fff;border:1px solid #e7ecef;border-radius:10px;min-height:44px;padding:5px 8px;transition:border-color .15s ease,box-shadow .15s ease}.oz-shell .components-form-token-field__input-container.is-active,.oz-shell .components-form-token-field__input-container:focus-within{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)}.oz-shell .components-form-token-field__token{border-radius:8px;font-size:12.5px;margin:3px;overflow:hidden}.oz-shell .components-form-token-field__token-text{background:rgba(29,209,161,.14);border-radius:8px 0 0 8px;color:#0c8f6b;font-weight:650;line-height:1.7;padding:2px 4px 2px 11px}.oz-shell .components-form-token-field__remove-token{background:rgba(29,209,161,.14);border-radius:0 8px 8px 0;color:#0c8f6b;min-width:24px;padding:0 7px;transition:background .15s ease,color .15s ease}.oz-shell .components-form-token-field__remove-token svg{fill:currentColor}.oz-shell .components-form-token-field__remove-token:hover{background:#10ac81;color:#fff}.oz-shell .components-form-token-field__input{font-size:13.5px;margin:4px;min-height:28px}.oz-shell .components-input-control__backdrop{display:none!important}.oz-shell .components-input-control__input,.oz-shell .components-select-control__input,.oz-shell .components-text-control__input{background:#fff!important;border:1px solid #e7ecef!important;border-radius:10px!important;box-shadow:none!important;color:#0f2027;font-size:13.5px;min-height:42px;padding:0 14px}.oz-shell .components-input-control__input:hover,.oz-shell .components-select-control__input:hover,.oz-shell .components-text-control__input:hover{border-color:#bcc6cc!important}.oz-shell .components-input-control__input:focus,.oz-shell .components-select-control__input:focus,.oz-shell .components-text-control__input:focus{border-color:#1dd1a1!important;box-shadow:0 0 0 3px rgba(29,209,161,.16)!important;outline:none!important}.oz-shell select.components-select-control__input{cursor:pointer}.oz-pillgroup .components-base-control__label{display:block;font-weight:600;margin-bottom:8px}.oz-pillgroup__row{background:#f0f2f4;border-radius:12px;display:inline-flex;flex-wrap:wrap;gap:6px;padding:4px}.oz-pillgroup .components-base-control__help{display:block;margin-top:8px}.oz-pillbtn{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:0;border-radius:9px;color:#4b5563;cursor:pointer;font-size:13px;font-weight:600;min-width:44px;padding:7px 16px;transition:background .12s ease,color .12s ease,box-shadow .12s ease}.oz-pillbtn:hover{color:#0e1116}.oz-pillbtn.is-on{background:#fff;box-shadow:0 1px 3px rgba(4,56,44,.14);color:#0c8f6b}.oz-pillbtn:focus-visible{outline:2px solid #1dd1a1;outline-offset:1px}.oz-select{position:relative}.oz-select .components-base-control__label{display:block;margin-bottom:8px}.oz-select__btn{align-items:center;background:#fff;border:1px solid #e7ecef;border-radius:10px;color:#0f2027;cursor:pointer;display:flex;font-size:13.5px;height:42px;justify-content:space-between;padding:0 14px;transition:border-color .15s ease,box-shadow .15s ease;width:100%}.oz-select__btn .dashicons{color:#0c8f6b;transition:transform .18s ease}.oz-select__btn:hover{border-color:#bcc6cc}.oz-select__btn:focus,.oz-select__btn:focus-visible{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)!important;outline:none!important}.oz-select__btn[aria-expanded=true]{border-color:#1dd1a1;box-shadow:0 0 0 3px rgba(29,209,161,.16)}.oz-select__btn[aria-expanded=true] .dashicons{transform:rotate(180deg)}.oz-select__menu{background:#fff;border:1px solid #e7ecef;border-radius:12px;box-shadow:0 14px 34px rgba(15,32,39,.18);left:0;list-style:none;margin:0;padding:6px;position:absolute;right:0;top:calc(100% + 6px);z-index:30}.oz-select__item{align-items:center;border-radius:8px;color:#0f2027;cursor:pointer;display:flex;font-size:13.5px;justify-content:space-between;padding:9px 12px}.oz-select__item .dashicons{color:#10ac81;font-size:18px}.oz-select__item.is-active,.oz-select__item:hover{background:rgba(29,209,161,.12);color:#0c8f6b}.oz-select__item.is-selected{color:#0c8f6b;font-weight:650}.oz-shell .oz-btn-danger.components-button,.oz-shell .oz-btn-ghost.components-button,.oz-shell .oz-btn-primary.components-button{border-radius:10px;font-weight:650;height:38px;transition:transform .16s cubic-bezier(.2,.7,.3,1),box-shadow .18s ease,background .16s ease,border-color .16s ease,color .16s ease}.oz-shell .oz-btn-danger.components-button .dashicons,.oz-shell .oz-btn-danger.components-button svg,.oz-shell .oz-btn-ghost.components-button .dashicons,.oz-shell .oz-btn-ghost.components-button svg,.oz-shell .oz-btn-primary.components-button .dashicons,.oz-shell .oz-btn-primary.components-button svg{transition:transform .22s cubic-bezier(.2,.7,.3,1)}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled),.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled),.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){transform:translateY(-1px)}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) svg{transform:scale(1.14)}.oz-shell .oz-btn-danger.components-button:active:not(:disabled),.oz-shell .oz-btn-ghost.components-button:active:not(:disabled),.oz-shell .oz-btn-primary.components-button:active:not(:disabled){transform:translateY(0) scale(.975);transition-duration:.05s}.oz-shell .oz-btn-danger.components-button:focus-visible,.oz-shell .oz-btn-ghost.components-button:focus-visible,.oz-shell .oz-btn-primary.components-button:focus-visible{outline:2px solid #1dd1a1;outline-offset:2px}.oz-shell .oz-btn-danger.components-button:disabled,.oz-shell .oz-btn-ghost.components-button:disabled,.oz-shell .oz-btn-primary.components-button:disabled{opacity:.5;transform:none}.oz-shell .oz-btn-primary.components-button{background:linear-gradient(135deg,#1dd1a1,#10ac81);border:0;box-shadow:0 1px 2px rgba(20,60,48,.12);color:#fff;padding:0 18px}.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){background:linear-gradient(135deg,#10ac81,#0c8f6b);box-shadow:0 8px 20px -7px rgba(29,209,161,.65);color:#fff}.oz-shell .oz-btn-primary.components-button.is-busy{animation:oz-btn-sheen 1.4s linear infinite;background-size:200% 100%}.oz-shell .oz-btn-ghost.components-button{background:#fff;border:1px solid rgba(29,209,161,.5);box-shadow:none;color:#0c8f6b}.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled){background:rgba(29,209,161,.08);border-color:#1dd1a1;box-shadow:0 6px 16px -8px rgba(29,209,161,.5);color:#0c8f6b}.oz-shell .oz-btn-danger.components-button{background:#fff;border:1px solid rgba(214,54,56,.5);color:#d63638}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled){background:rgba(214,54,56,.08);border-color:#d63638;box-shadow:0 6px 16px -8px rgba(214,54,56,.5);color:#b32d2e}.oz-shell .oz-btn--x.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn--x.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg{transform:scale(1.1) rotate(90deg)}@media(prefers-reduced-motion:reduce){.oz-shell .oz-btn--x.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn--x.components-button:hover:not(:disabled) svg{transform:none}}@keyframes oz-btn-sheen{0%{background-image:linear-gradient(135deg,#1dd1a1,#10ac81,#1dd1a1);background-position:200% 0}to{background-image:linear-gradient(135deg,#1dd1a1,#10ac81,#1dd1a1);background-position:-200% 0}}@media(prefers-reduced-motion:reduce){.oz-shell .oz-btn-danger.components-button,.oz-shell .oz-btn-ghost.components-button,.oz-shell .oz-btn-primary.components-button{transition:background .16s ease,color .16s ease,border-color .16s ease}.oz-shell .oz-btn-danger.components-button:active:not(:disabled),.oz-shell .oz-btn-danger.components-button:hover:not(:disabled),.oz-shell .oz-btn-ghost.components-button:active:not(:disabled),.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled),.oz-shell .oz-btn-primary.components-button:active:not(:disabled),.oz-shell .oz-btn-primary.components-button:hover:not(:disabled){transform:none}.oz-shell .oz-btn-danger.components-button .dashicons,.oz-shell .oz-btn-danger.components-button svg,.oz-shell .oz-btn-ghost.components-button .dashicons,.oz-shell .oz-btn-ghost.components-button svg,.oz-shell .oz-btn-primary.components-button .dashicons,.oz-shell .oz-btn-primary.components-button svg{transition:none}.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-danger.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-ghost.components-button:hover:not(:disabled) svg,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) .dashicons,.oz-shell .oz-btn-primary.components-button:hover:not(:disabled) svg{transform:none}.oz-shell .oz-btn-danger.components-button.is-busy,.oz-shell .oz-btn-ghost.components-button.is-busy,.oz-shell .oz-btn-primary.components-button.is-busy{animation:none}}.oz-shell .components-button.is-primary:focus:not(:disabled),.oz-shell .components-button:focus,.oz-shell .components-button:focus:not(:disabled){box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle,.oz-shell .components-form-toggle__input,.oz-shell .components-form-toggle__input:active,.oz-shell .components-form-toggle__input:focus,.oz-shell .components-form-toggle__input:focus-visible{border:none!important;box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle__track{background-color:#cdd6db!important;border:1px solid transparent!important;box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle.is-checked .components-form-toggle__track{background-color:#1dd1a1!important}.oz-shell .components-form-toggle__input:focus+.components-form-toggle__track,.oz-shell .components-form-toggle__input:focus-visible+.components-form-toggle__track{box-shadow:none!important;outline:none!important}.oz-shell .components-form-toggle__thumb{background-color:#fff!important;border:none!important;box-shadow:0 1px 2px rgba(15,32,39,.25)!important}.oz-savebar{align-items:center;background:#fff;border-top:1px solid #e7ecef;bottom:0;box-shadow:0 -6px 18px rgba(15,32,39,.06);display:flex;gap:16px;justify-content:flex-end;left:160px;padding:13px 32px;position:fixed;right:0;z-index:9}.oz-savebar__hint{color:#6b7a86;font-size:13px}.oz-savebar.is-dirty .oz-savebar__hint{color:#0c8f6b;font-weight:650}.folded .oz-savebar{left:36px}.oz-toast{bottom:72px;z-index:99}.oz-job,.oz-toast{position:fixed;right:24px}.oz-job{background:#fff;border:1px solid #e7ecef;border-radius:16px;bottom:84px;box-shadow:0 18px 50px rgba(15,32,39,.22);color:#0f2027;font-size:13px;max-width:calc(100vw - 48px);overflow:hidden;width:420px;z-index:100}.oz-job__head{align-items:center;display:flex;gap:12px;padding:16px 16px 12px}.oz-job__head .components-spinner{margin:0}.oz-job__icon-status{font-size:22px;height:22px;width:22px}.oz-job__icon-status--done{color:#10ac81}.oz-job__icon-status--stopped{color:#6b7a86}.oz-job__icon-status--error{color:#d63638}.oz-job__title{flex:1;min-width:0}.oz-job__title strong{display:block;font-size:15px;font-weight:700;line-height:1.2}.oz-job__title span{color:#6b7a86;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.oz-job__pct{color:#0f2027;font-size:13px;font-variant-numeric:tabular-nums;font-weight:700}.oz-job__icon{background:transparent;border:0;border-radius:6px;color:#6b7a86;cursor:pointer;display:inline-flex;padding:4px}.oz-job__icon:hover{background:#f4f7f8;color:#0f2027}.oz-job__icon .dashicons{font-size:18px;height:18px;width:18px}.oz-job__body{padding:0 16px 16px}.oz-job__row{align-items:baseline;display:flex;gap:12px;justify-content:space-between;margin-bottom:8px}.oz-job__row span{line-height:1.4}.oz-job__row strong{flex:0 0 auto;font-variant-numeric:tabular-nums;font-weight:700}.oz-job__bar{background:#e7ecef;border-radius:999px;height:7px;overflow:hidden}.oz-job__bar div{background:linear-gradient(90deg,#1dd1a1,#10ac81);border-radius:999px;height:100%;transition:width .35s ease}.oz-job__bar.is-indeterminate div{animation:oz-indeterminate 1.1s ease-in-out infinite;border-radius:999px;width:40%!important}.oz-job.is-min .oz-job__bar{margin:2px 16px 16px}.oz-job.is-min .oz-job__head{padding-bottom:10px}.oz-job.is-done .oz-job__bar div{background:linear-gradient(90deg,#10ac81,#0c8f6b)}.oz-job.is-stopped .oz-job__bar div{background:#c3ccd2}.oz-job.is-error .oz-job__bar div{background:linear-gradient(90deg,#f16b6b,#d63638)}.oz-job__step{color:#6b7a86;font-size:12px;margin-top:8px}.oz-job__stop{align-items:center;background:transparent;border:0;color:#d63638;cursor:pointer;display:inline-flex;font-size:13px;font-weight:650;gap:6px;margin-top:14px;padding:4px 0}.oz-job__stop .dashicons{font-size:18px}.oz-job__stop:hover{color:#b32d2e}.oz-job__log{background:#11202b;border-radius:10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.7;margin-top:14px;max-height:150px;overflow-y:auto;padding:10px 12px}.oz-logline{color:#cdd8df;display:flex;gap:8px}.oz-logline__tag{color:#5fd0ad;flex:0 0 auto;font-weight:700;min-width:42px}.oz-logline--warn .oz-logline__tag{color:#f0b429}.oz-logline--error .oz-logline__tag{color:#ff6b6b}.oz-logline--done .oz-logline__tag{color:#1dd1a1}.oz-logline__text{word-break:break-word}@media(max-width:1100px){.oz-kpis,.oz-quick{grid-template-columns:1fr}}@media(max-width:1024px){.oz-score{align-items:flex-start;flex-direction:column;text-align:left}.oz-convert{align-items:stretch;flex-direction:column;gap:12px}.oz-convert .oz-btn-ghost.components-button,.oz-convert .oz-btn-primary.components-button{justify-content:center;width:100%}}.oz-mobile-toggle,.oz-nav-overlay{display:none!important}@media(max-width:1024px){.oz-shell{flex-direction:column;margin:8px 8px 110px}.oz-content{border-left:0;min-width:0;width:100%}.oz-sidebar{border-bottom:1px solid #e7ecef;border-right:0;flex:none;max-height:none;overflow:visible;padding:14px 12px;position:static;width:100%}.oz-sidebar__brand{margin-bottom:10px;padding-bottom:12px}.oz-sidebar nav{display:flex;flex-wrap:nowrap;gap:6px;overflow-x:auto;-webkit-overflow-scrolling:touch;padding-bottom:4px}.oz-navitem{border-bottom:3px solid transparent;border-left:0;border-radius:10px;flex:0 0 auto;padding:8px 12px;width:auto}.oz-navitem.is-active{border-bottom-color:#1dd1a1;border-left-color:transparent}.oz-navitem .oz-navitem__text strong{white-space:nowrap}.oz-navitem .oz-navitem__dot,.oz-navitem small,.oz-sidebar__foot{display:none}.oz-topbar{padding:16px 18px}.oz-topbar h1{font-size:18px}.oz-main{padding:16px 18px}.oz-savebar{left:0;padding:12px 18px}.oz-card__body{padding-left:18px;padding-right:18px}.oz-job{left:8px;right:8px;width:auto}}@media(max-width:560px){.oz-tiles{grid-template-columns:1fr}.oz-hero,.oz-score{padding:18px}.oz-card__head{gap:12px;padding:16px}.oz-card__icon{flex-basis:40px;height:40px;width:40px}.oz-savebar__hint{display:none}}@keyframes oz-indeterminate{0%{transform:translateX(-100%)}to{transform:translateX(350%)}}.oz-muted{color:#6b7a86;font-size:13px}.oz-reqs{list-style:none;margin:0;padding:0}.oz-req{align-items:center;border-bottom:1px solid #e7ecef;border-radius:8px;display:grid;font-size:13.5px;gap:6px 10px;grid-template-columns:20px 1fr auto;padding:10px 6px}.oz-req:last-child{border-bottom:0}.oz-req__dot{font-size:18px;height:18px;width:18px}.oz-req__label{font-weight:650}.oz-req__value{color:#6b7a86;font-weight:600;justify-self:end}.oz-req__note{color:#6b7a86;font-size:12px;font-weight:400;grid-column:2/-1;line-height:1.45;margin-top:2px}.oz-req--ok>.oz-req__dot{color:#0c8f6b}.oz-req--warn>.oz-req__dot{color:#e0900f}.oz-req--error>.oz-req__dot{color:#e05242}.oz-req--info>.oz-req__dot,.oz-req--info>.oz-req__value{color:#6b7a86}.oz-req--warn{background:rgba(224,144,15,.06)}.oz-req--error{background:rgba(224,82,66,.06)}.oz-notice{align-items:flex-start;background:rgba(224,82,66,.08);border:1px solid rgba(224,82,66,.28);border-radius:10px;color:#7a2018;display:flex;font-size:13px;gap:10px;line-height:1.5;margin:0 0 14px;padding:12px 14px}.oz-notice .dashicons{color:#e05242;flex:0 0 20px;margin-top:1px}.oz-notice--warn{background:rgba(224,144,15,.09);border-color:rgba(224,144,15,.3);color:#7a5406}.oz-notice--warn .dashicons{color:#e0900f}.oz-notice--ok{background:#eaf8f3;border-color:#b8e6d6;color:#0f5e48}.oz-notice--ok .dashicons{color:#0e9f7e}.oz-recs{list-style:none;margin:0;padding:0}.oz-rec{align-items:center;border-bottom:1px solid #e7ecef;border-radius:8px;display:flex;gap:12px;padding:12px 8px}.oz-rec:last-child{border-bottom:0}.oz-rec__dot{flex:0 0 20px;font-size:20px;height:20px;margin-top:1px;width:20px}.oz-rec__text{flex:1;min-width:0}.oz-rec__text strong{display:block;font-size:13.5px;font-weight:650}.oz-rec__text small{color:#6b7a86;display:block;font-size:12px;line-height:1.45;margin-top:2px}.oz-rec__btn.components-button{flex:0 0 auto}.oz-rec--high>.oz-rec__dot{color:#e05242}.oz-rec--medium>.oz-rec__dot{color:#e0900f}.oz-rec--low>.oz-rec__dot{color:#6b7a86}.oz-rec--high{background:rgba(224,82,66,.05)}.oz-rec--medium{background:rgba(224,144,15,.05)}.oz-recs-clear{align-items:center;color:#6b7a86;display:flex;font-size:13px;gap:8px}.oz-recs-clear .dashicons{color:#0c8f6b}.oz-select--compact{border-bottom:0!important;flex:none;margin:0 0 0 auto!important;padding:0!important}.oz-select--compact .oz-select__btn{background:#f7faf9;border-radius:9px;font-size:13px;font-weight:650;gap:10px;height:30px;min-width:132px;padding:0 10px 0 12px;width:auto}.oz-select--compact .oz-select__btn:disabled{cursor:progress;opacity:.6}.oz-select--compact .oz-select__btn .dashicons{font-size:16px;height:16px;width:16px}.oz-select--compact .oz-select__menu{left:auto;min-width:190px;right:0}.oz-select--compact .oz-select__item{font-size:13px;padding:8px 10px}.oz-cachewarn--ok{background:#eaf8f3;border-color:#b8e6d6;color:#0f5e48}.oz-cachewarn__text p+p{margin-top:6px}.oz-cachewarn__link{color:inherit;display:inline-block;font-weight:600;margin-top:8px;text-decoration:underline}.oz-network__sites{align-items:start;margin-top:16px}.oz-network__sites .oz-panel__head h2,.oz-network__sites .oz-panel__head p{overflow-wrap:anywhere}.oz-cachelist__link{color:#1dd1a1;font-weight:600;margin-left:auto;text-decoration:none}.oz-cachelist__link:focus,.oz-cachelist__link:hover{text-decoration:underline} -
optimazz/trunk/build/index.js
r3723128 r3743505 1 (()=>{"use strict";const e={n:a=>{const t=a&&a.__esModule?()=>a.default:()=>a;return e.d(t,{a:t}),t},d:(a,t)=>{for(var o in t)e.o(t,o)&&!e.o(a,o)&&Object.defineProperty(a,o,{enumerable:!0,get:t[o]})},o:(e,a)=>Object.hasOwn(e,a)},a=window.React,t=window.wp.element,o=window.wp.components,n=window.wp.apiFetch;var l=e.n(n);const i=window.wp.i18n,s=window.wp.hooks,{restUrl:r,nonce:c,settings:m}=window.optimazzData||{};l().use(l().createNonceMiddleware(c));const d=[{key:"dashboard",title:(0,i.__)("Dashboard","optimazz"),sub:(0,i.__)("Overview & quick actions","optimazz"),icon:"admin-home"},{key:"cache",title:(0,i.__)("Page Cache","optimazz"),sub:(0,i.__)("Static HTML caching","optimazz"),icon:"database",mod:"cache"},{key:"files",title:(0,i.__)("File Optimization","optimazz"),sub:(0,i.__)("Optimize CSS & JS","optimazz"),icon:"editor-code"},{key:"media",title:(0,i.__)("Media","optimazz"),sub:(0,i.__)("LazyLoad, dimensions, WebP","optimazz"),icon:"format-image",mod:"media"},{key:"fonts",title:(0,i.__)("Fonts","optimazz"),sub:(0,i.__)("Preload, swap, preconnect","optimazz"),icon:"editor-textcolor",mod:"fonts"},{key:"preload",title:(0,i.__)("Preload","optimazz"),sub:(0,i.__)("Generate cache files","optimazz"),icon:"update"},{key:"advanced",title:(0,i.__)("Advanced Rules","optimazz"),sub:(0,i.__)("Fine-tune cache rules","optimazz"),icon:"list-view"},{key:"database",title:(0,i.__)("Database","optimazz"),sub:(0,i.__)("Optimize, reduce bloat","optimazz"),icon:"database-view",mod:"database"},{key:"heartbeat",title:(0,i.__)("Heartbeat","optimazz"),sub:(0,i.__)("Control Heartbeat API","optimazz"),icon:"heart",mod:"heartbeat"},{key:"tweaks",title:(0,i.__)("Tweaks","optimazz"),sub:(0,i.__)("Remove WordPress bloat","optimazz"),icon:"admin-generic",mod:"tweaks"},{key:"cdn",title:(0,i.__)("CDN","optimazz"),sub:(0,i.__)("Rewrite asset URLs","optimazz"),icon:"cloud",mod:"cdn"},{key:"tools",title:(0,i.__)("Tools","optimazz"),sub:(0,i.__)("Import, export","optimazz"),icon:"admin-tools"}],p=(0,s.applyFilters)("optimazz.adminSections",[]),_=[...d,...p],z=["cache","js","css","media","fonts","database","heartbeat","tweaks","cdn"],h=[{title:(0,i.__)("Content leftovers","optimazz"),desc:(0,i.__)("Old post data that piles up over time. Removing it is permanent, so keep anything you still need.","optimazz"),items:[{key:"revisions",label:(0,i.__)("Post revisions","optimazz"),help:(0,i.__)("Old copies of posts saved automatically. Deleting them keeps only your current content.","optimazz")},{key:"auto_drafts",label:(0,i.__)("Auto drafts","optimazz"),help:(0,i.__)("Empty drafts WordPress creates while you write. Safe to remove.","optimazz")},{key:"trashed_posts",label:(0,i.__)("Trashed posts","optimazz"),help:(0,i.__)("Posts in the trash that have not been permanently deleted yet.","optimazz")}]},{title:(0,i.__)("Comment clutter","optimazz"),desc:(0,i.__)("Spam and trashed comments you are never going to approve. Clearing them keeps the comments table light.","optimazz"),items:[{key:"spam_comments",label:(0,i.__)("Spam comments","optimazz"),help:(0,i.__)("Comments marked as spam. Removing them frees up the comments table.","optimazz")},{key:"trashed_comments",label:(0,i.__)("Trashed comments","optimazz"),help:(0,i.__)("Comments in the trash awaiting permanent deletion.","optimazz")}]},{title:(0,i.__)("Temporary data","optimazz"),desc:(0,i.__)("Short-lived cached values. They are safe to clear and are rebuilt automatically whenever a plugin needs them.","optimazz"),items:[{key:"expired_transients",label:(0,i.__)("Expired transients","optimazz"),help:(0,i.__)("Cached values that have already passed their expiry date.","optimazz")}]},{title:(0,i.__)("Table maintenance","optimazz"),desc:(0,i.__)("Defragment your tables to reclaim wasted space and trim database overhead.","optimazz"),items:[{key:"optimize_tables",label:(0,i.__)("Optimize database tables","optimazz"),help:(0,i.__)("Runs OPTIMIZE on your tables to defragment them and free wasted space.","optimazz")}]}],u=[{label:(0,i.__)("Do not limit","optimazz"),value:"default"},{label:(0,i.__)("Reduce activity","optimazz"),value:"reduce"},{label:(0,i.__)("Disable","optimazz"),value:"disable"}];function g({on:e}){return(0,a.createElement)("span",{className:"oz-pill "+(e?"is-on":"is-off")},(0,a.createElement)("i",null),e?(0,i.__)("Active","optimazz"):(0,i.__)("Off","optimazz"))}function b({icon:e,title:t,description:n,enabled:l,onToggle:i,children:s}){return(0,a.createElement)("section",{className:"oz-card "+(l?"is-enabled":"")},(0,a.createElement)("header",{className:"oz-card__head"},(0,a.createElement)("span",{className:`oz-card__icon dashicons dashicons-${e}`}),(0,a.createElement)("div",{className:"oz-card__title"},(0,a.createElement)("h2",null,t," ",(0,a.createElement)(g,{on:l})),(0,a.createElement)("p",null,n)),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,checked:l,onChange:i})),l&&(0,a.createElement)("div",{className:"oz-card__body"},s))}function f({job:e,onStop:n,onClose:l,onToggleMin:s}){const r=(0,t.useRef)(null);if((0,t.useEffect)(()=>{r.current&&(r.current.scrollTop=r.current.scrollHeight)},[e?.logs?.length]),!e)return null;const c=e.status||(e.running?"running":"done"),m={stopped:(0,i.__)("Cancelled","optimazz"),error:(0,i.__)("Failed","optimazz")},d="running"===c||"done"===c?e.title:`${e.title} — ${m[c]}`;return(0,a.createElement)("div",{className:`oz-job is-${c} ${e.minimized?"is-min":""}`},(0,a.createElement)("div",{className:"oz-job__head"},"running"===c?(0,a.createElement)(o.Spinner,null):(0,a.createElement)("span",{className:`oz-job__icon-status oz-job__icon-status--${c} dashicons dashicons-${{done:"yes-alt",stopped:"dismiss",error:"warning"}[c]||"yes-alt"}`}),(0,a.createElement)("div",{className:"oz-job__title"},(0,a.createElement)("strong",null,d),(0,a.createElement)("span",null,e.minimized?`${e.pct}%${e.step?` · ${e.step}`:""}`:e.subtitle)),(0,a.createElement)("button",{className:"oz-job__icon","aria-label":e.minimized?(0,i.__)("Expand","optimazz"):(0,i.__)("Minimize","optimazz"),onClick:s},(0,a.createElement)("span",{className:"dashicons dashicons-"+(e.minimized?"editor-expand":"minus")})),(0,a.createElement)("button",{className:"oz-job__icon","aria-label":(0,i.__)("Close","optimazz"),onClick:l},(0,a.createElement)("span",{className:"dashicons dashicons-no-alt"}))),e.minimized?(0,a.createElement)("div",{className:"oz-job__bar "+(e.indeterminate&&e.running?"is-indeterminate":"")},(0,a.createElement)("div",{style:{width:`${e.pct}%`}})):(0,a.createElement)("div",{className:"oz-job__body"},(0,a.createElement)("div",{className:"oz-job__row"},(0,a.createElement)("span",null,e.label),(0,a.createElement)("strong",null,e.pct,"%")),(0,a.createElement)("div",{className:"oz-job__bar "+(e.indeterminate&&e.running?"is-indeterminate":"")},(0,a.createElement)("div",{style:{width:`${e.pct}%`}})),e.step&&(0,a.createElement)("div",{className:"oz-job__step"},(0,i.__)("Step","optimazz")," ",e.step),e.running&&(0,a.createElement)("button",{className:"oz-job__stop",onClick:n},(0,a.createElement)("span",{className:"dashicons dashicons-controls-pause"}),(0,i.__)("Stop","optimazz")),(0,a.createElement)("div",{className:"oz-job__log",ref:r},e.logs.map((e,t)=>{const o=e.level||"INFO";return(0,a.createElement)("div",{key:t,className:`oz-logline oz-logline--${o.toLowerCase()}`},(0,a.createElement)("span",{className:"oz-logline__tag"},o),(0,a.createElement)("span",{className:"oz-logline__text"},e.text))}))))}let E=0;function v({label:e,value:o,options:n,onChange:l,compact:i=!1,disabled:s=!1}){const[r,c]=(0,t.useState)(!1),m=Math.max(0,n.findIndex(e=>e.value===o)),[d,p]=(0,t.useState)(m),_=(0,t.useRef)(null),z=(0,t.useRef)("oz-sel-"+ ++E).current;(0,t.useEffect)(()=>{const e=e=>{_.current&&!_.current.contains(e.target)&&c(!1)};return document.addEventListener("mousedown",e),()=>document.removeEventListener("mousedown",e)},[]);const h=n.find(e=>e.value===o)||n[0],u=e=>{l(n[e].value),c(!1)};return(0,a.createElement)("div",{className:"components-base-control oz-select"+(i?" oz-select--compact":""),ref:_},(0,a.createElement)("span",{className:i?"screen-reader-text":"components-base-control__label",id:`${z}-label`},e),(0,a.createElement)("button",{type:"button",className:"oz-select__btn",disabled:s,role:"combobox","aria-haspopup":"listbox","aria-expanded":r,"aria-labelledby":`${z}-label`,"aria-activedescendant":r?`${z}-opt-${d}`:void 0,onClick:()=>c(e=>!e),onKeyDown:e=>{r?"Escape"===e.key||"Tab"===e.key?c(!1):"ArrowDown"===e.key?(e.preventDefault(),p(e=>Math.min(n.length-1,e+1))):"ArrowUp"===e.key?(e.preventDefault(),p(e=>Math.max(0,e-1))):"Home"===e.key?(e.preventDefault(),p(0)):"End"===e.key?(e.preventDefault(),p(n.length-1)):"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),u(d)):["ArrowDown","ArrowUp","Enter"," "].includes(e.key)&&(e.preventDefault(),p(m),c(!0))}},(0,a.createElement)("span",null,h?.label),(0,a.createElement)("span",{className:"dashicons dashicons-arrow-down-alt2"})),r&&(0,a.createElement)("ul",{className:"oz-select__menu",role:"listbox","aria-labelledby":`${z}-label`},n.map((e,t)=>(0,a.createElement)("li",{key:e.value,id:`${z}-opt-${t}`,role:"option","aria-selected":e.value===o,className:`oz-select__item ${e.value===o?"is-selected":""} ${t===d?"is-active":""}`,onMouseEnter:()=>p(t),onClick:()=>u(t)},(0,a.createElement)("span",null,e.label),e.value===o&&(0,a.createElement)("span",{className:"dashicons dashicons-yes"})))))}const y={dashboard:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"M3 9.6 12 3l9 6.6"}),(0,a.createElement)("path",{d:"M5 9.1V20h5v-5h4v5h5V9.1"})),cache:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"M12 3 2.5 8 12 13l9.5-5L12 3Z"}),(0,a.createElement)("path",{d:"M2.5 12 12 17l9.5-5"})),files:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"m9 8-4 4 4 4"}),(0,a.createElement)("path",{d:"m15 8 4 4-4 4"})),media:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("rect",{x:"3",y:"4",width:"18",height:"16",rx:"2"}),(0,a.createElement)("circle",{cx:"8.5",cy:"9.5",r:"1.5"}),(0,a.createElement)("path",{d:"m21 16-5-5-6 6"})),fonts:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"M5 6V4h14v2"}),(0,a.createElement)("path",{d:"M12 4v16"}),(0,a.createElement)("path",{d:"M9 20h6"})),preload:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"M21 12a9 9 0 1 1-2.6-6.4"}),(0,a.createElement)("path",{d:"M21 4v5h-5"})),advanced:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"M4 8h9"}),(0,a.createElement)("circle",{cx:"16",cy:"8",r:"2.1"}),(0,a.createElement)("path",{d:"M19 8h1"}),(0,a.createElement)("path",{d:"M4 16h1"}),(0,a.createElement)("circle",{cx:"8",cy:"16",r:"2.1"}),(0,a.createElement)("path",{d:"M11 16h9"})),database:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("ellipse",{cx:"12",cy:"5",rx:"8",ry:"3"}),(0,a.createElement)("path",{d:"M4 5v14c0 1.6 3.6 3 8 3s8-1.4 8-3V5"}),(0,a.createElement)("path",{d:"M4 12c0 1.6 3.6 3 8 3s8-1.4 8-3"})),heartbeat:(0,a.createElement)("path",{d:"M3 12h4l2 6 4-14 2 8h6"}),tweaks:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"m12 4 1.5 4.5L18 10l-4.5 1.5L12 16l-1.5-4.5L6 10l4.5-1.5Z"}),(0,a.createElement)("path",{d:"M18.5 15v3"}),(0,a.createElement)("path",{d:"M17 16.5h3"})),cdn:(0,a.createElement)("path",{d:"M17 18H7a4 4 0 0 1-.3-8A6 6 0 0 1 18.3 9 3.5 3.5 0 0 1 17 18Z"}),tools:(0,a.createElement)("path",{d:"M14.5 5.5a4 4 0 0 0-5 5L4 16v4h4l5.5-5.5a4 4 0 0 0 5-5l-2.6 2.6-2-.4-.4-2 2.5-2.6Z"})};function w({name:e,paths:t}){return(0,a.createElement)("svg",{className:"oz-ico",viewBox:"0 0 24 24",width:"20",height:"20",fill:"none",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",focusable:"false"},t||y[e]||y.dashboard)}function k({ok:e,warn:t,label:o}){const n=e?"ok":t?"warn":"bad",l=e?"yes-alt":t?"warning":"dismiss";return(0,a.createElement)("li",{className:`oz-health__row is-${n}`},(0,a.createElement)("span",{className:`dashicons dashicons-${l}`}),(0,a.createElement)("span",null,o))}function C({label:e,help:t,value:o,options:n,onChange:l}){return(0,a.createElement)("div",{className:"components-base-control oz-pillgroup"},e&&(0,a.createElement)("span",{className:"components-base-control__label"},e),(0,a.createElement)("div",{className:"oz-pillgroup__row",role:"radiogroup","aria-label":e,onKeyDown:e=>{const a=n.findIndex(e=>e.value===o);"ArrowRight"===e.key||"ArrowDown"===e.key?(e.preventDefault(),l(n[Math.min(n.length-1,a+1)].value)):"ArrowLeft"!==e.key&&"ArrowUp"!==e.key||(e.preventDefault(),l(n[Math.max(0,a-1)].value))}},n.map(e=>{const t=e.value===o;return(0,a.createElement)("button",{key:String(e.value),type:"button",role:"radio","aria-checked":t,tabIndex:t?0:-1,className:"oz-pillbtn "+(t?"is-on":""),onClick:()=>l(e.value)},e.label)})),t&&(0,a.createElement)("span",{className:"components-base-control__help"},t))}function N({title:e,description:t,children:o}){return(0,a.createElement)("section",{className:"oz-panel"},(0,a.createElement)("header",{className:"oz-panel__head"},(0,a.createElement)("h2",null,e),t&&(0,a.createElement)("p",null,t)),(0,a.createElement)("div",{className:"oz-panel__body"},o))}const x=document.getElementById("optimazz-app");x&&(0,t.createRoot)(x).render((0,a.createElement)(function(){var e,n,s,c,g,E,y,x;const[S,M]=(0,t.useState)(m||{}),[T,$]=(0,t.useState)(m||{}),[B,R]=(0,t.useState)(null),[P,D]=(0,t.useState)(null),[O,F]=(0,t.useState)(!1),[H,L]=(0,t.useState)("dashboard"),[A,j]=(0,t.useState)(!1),[W,I]=(0,t.useState)(null),[q,U]=(0,t.useState)(!(!window.optimazzData||!window.optimazzData.hasBackup)),J=(0,t.useRef)(null);(0,t.useEffect)(()=>{const e=e=>{const a=e&&e.detail&&e.detail.key;a&&L(a)};return window.addEventListener("optimazz:navigate",e),()=>window.removeEventListener("optimazz:navigate",e)},[]);const G=JSON.stringify(S)!==JSON.stringify(T),V=(0,t.useCallback)((e,a,t)=>M(o=>({...o,[e]:{...o[e],[a]:t}})),[]),K=(e,a)=>V(e,a,!S[e]?.[a]),Y=(0,t.useCallback)(async()=>{try{R(await l()({url:`${r}/cache/stats`}))}catch(e){}},[]),[Z,Q]=(0,t.useState)(null),X=(0,t.useCallback)(async()=>{try{Q(await l()({url:`${r}/system?_=${Date.now()}`}))}catch(e){Q({error:!0})}},[]);(0,t.useEffect)(()=>{"tools"!==H||Z||X()},[H,Z,X]);const[ee,ae]=(0,t.useState)(null),te=(0,t.useCallback)(async()=>{try{ae(await l()({url:`${r}/recommendations?_=${Date.now()}`}))}catch(e){ae({error:!0})}},[]);(0,t.useEffect)(()=>{"dashboard"!==H||ee||te()},[H,ee,te]);const oe=(0,t.useCallback)(async()=>{F(!0);try{D(await l()({url:`${r}/cache/health`}))}catch(e){D({error:!0})}F(!1)},[]),ne=(0,t.useCallback)(async(e,a)=>{if(window.confirm(`${(0,i.__)("Deactivate","optimazz")} ${a}? ${(0,i.__)("Optimazz will keep handling caching.","optimazz")}`)){F(!0);try{const t=await l()({url:`${r}/cache/deactivate-conflict`,method:"POST",data:{plugin:e}});D(t),ie(`${a} ${(0,i.__)("deactivated — Optimazz is now the only cache.","optimazz")}`,"success")}catch(e){ie((0,i.__)("Could not deactivate the plugin.","optimazz"),"error")}F(!1)}},[]),le=(0,t.useCallback)(async()=>{if(window.confirm((0,i.__)("Make Optimazz the caching plugin? This replaces the current advanced-cache.php (from another caching plugin) and turns on WP_CACHE. Deactivate the other caching plugin afterwards to avoid conflicts.","optimazz"))){F(!0);try{const e=await l()({url:`${r}/cache/takeover`,method:"POST"});D(e),ie("hit"===e.live?(0,i.__)("Optimazz is now serving pages from cache.","optimazz"):e.dropin_ours?(0,i.__)("Drop-in installed. Preload the cache, then check again.","optimazz"):(0,i.__)("Could not fully take over — see the cache health details.","optimazz"),e.dropin_ours?"success":"error")}catch(e){ie((0,i.__)("Take-over failed. Check file permissions.","optimazz"),"error")}F(!1)}},[]);(0,t.useEffect)(()=>{Y()},[Y]);const ie=(e,a="success")=>{I({text:e,status:a}),window.clearTimeout(ie._t),ie._t=window.setTimeout(()=>I(null),3e3)};(0,t.useEffect)(()=>{try{const e=new URLSearchParams(window.location.search),a=e.get("optimazz_notice"),t={purged:(0,i.__)("Cache purged.","optimazz"),preloaded:(0,i.__)("Cache preload started in the background.","optimazz"),cleared:(0,i.__)("Cache cleared; preload started in the background.","optimazz")};if(a&&t[a]){ie(t[a]),e.delete("optimazz_notice");const o=e.toString();window.history.replaceState({},"",window.location.pathname+(o?`?${o}`:""))}window.optimazzData?.wpconfigUnwritable&&ie((0,i.__)("wp-config.php is not writable — add WP_CACHE manually to enable page caching.","optimazz"),"error")}catch(e){}},[]);const se=async e=>{j(!0);try{const a=await l()({url:`${r}/settings`,method:"POST",data:e});$(a.settings),M(a.settings),U(!!a.hasBackup),ae(null),ie((0,i.__)("Settings saved.","optimazz")),Y()}catch(e){ie(e.message||(0,i.__)("Save failed.","optimazz"),"error")}j(!1)},re=async()=>{await l()({url:`${r}/cache/purge`,method:"POST"}),ie((0,i.__)("Cache purged.","optimazz")),Y()},ce=()=>ve("preload"),[me,de]=(0,t.useState)(null),[pe,_e]=(0,t.useState)(null),[ze,he]=(0,t.useState)(null),ue=(0,t.useRef)(!1),ge=(0,t.useCallback)(async()=>{try{_e(await l()({url:`${r}/media/stats`}))}catch(e){}},[]);(0,t.useEffect)(()=>{"media"!==H||pe||ge()},[H,pe,ge]);const be={convert:{title:(0,i.__)("Converting images to WebP/AVIF","optimazz"),subtitle:(0,i.__)("Next-gen format generation","optimazz"),endpoint:`${r}/media/convert`,refresh:ge,summarize:e=>`${(0,i.__)("Generated","optimazz")} ${e.generated||0} ${(0,i.__)("files in this batch","optimazz")}${e.skipped?` · ${e.skipped} ${(0,i.__)("skipped","optimazz")}`:""}`},compress:{title:(0,i.__)("Compressing images","optimazz"),subtitle:(0,i.__)("Recompress, resize & strip metadata","optimazz"),endpoint:`${r}/media/optimize`,refresh:ge,summarize:e=>{const a=Math.max(0,(e.before||0)-(e.after||0)),t=e.skipped?` · ${e.skipped} ${(0,i.__)("skipped","optimazz")}`:"";return`${(0,i.__)("Processed","optimazz")} ${e.processed||0} — ${we(a)} ${(0,i.__)("saved","optimazz")}${t}`}},preload:{title:(0,i.__)("Preloading cache","optimazz"),subtitle:(0,i.__)("Warming pages","optimazz"),endpoint:`${r}/cache/preload`,refresh:Y,onBatch:Y,summarize:(e,a)=>`${(0,i.__)("Warmed","optimazz")} ${Math.min(e.next_offset||0,e.total||0)} / ${e.total||0} ${(0,i.__)("pages","optimazz")} · ${a.cached} ${(0,i.__)("cached","optimazz")}${a.missed?` · ${a.missed} ${(0,i.__)("not cached","optimazz")}`:""}`}},fe=e=>!e||e<1?"":e>=3600?`~${Math.round(e/3600)}h`:e>=60?`~${Math.round(e/60)}m`:`~${Math.round(e)}s`,Ee=()=>l()({url:`${r}/media/job`,method:"DELETE"}).catch(()=>{}),ve=async(e,{startOffset:a=0,reconnect:t=!1}={})=>{const o=be[e];if(!o||ze?.running)return;ue.current=!1;const n=t?(0,i.__)("Resuming…","optimazz"):`${o.title}…`;he({title:o.title,subtitle:o.subtitle,pct:0,label:n,step:"",logs:[{level:"INFO",text:n}],running:!0,status:"running",minimized:!1});let s=a,r=0,c=0;const m={cached:0,missed:0,diagnosis:""},d=Date.now();try{for(;;){var p;if(ue.current){await Ee(),he(e=>e&&{...e,running:!1,status:"stopped",logs:[...e.logs,{level:"WARN",text:(0,i.__)("Stopped by user.","optimazz")}]});break}const a=s;let t;try{t=await l()({url:o.endpoint,method:"POST",data:{offset:s}})}catch(e){if(c++,c>20)throw e;if(s=a+1,he(e=>e&&{...e,logs:[...e.logs.slice(-60),{level:"WARN",text:(0,i.__)("A batch failed (likely one slow/large image); skipped it and continued.","optimazz")}]}),r&&s>=r){he(e=>e&&{...e,running:!1,status:"done",pct:100,logs:[...e.logs,{level:"DONE",text:(0,i.__)("Completed (some items skipped).","optimazz")}]});break}await new Promise(e=>setTimeout(e,600));continue}if(c=0,s=null!==(p=t.next_offset)&&void 0!==p?p:s+(t.processed||0),r=t.total||r,!t.done&&s<=a){await Ee(),he(e=>e&&{...e,running:!1,status:"error",logs:[...e.logs,{level:"ERROR",text:(0,i.__)("Job stalled — no progress.","optimazz")}]});break}m.cached+=t.cached||0,m.missed+=t.missed||0,m.diagnosis=t.diagnosis||m.diagnosis;const n=r?Math.min(100,Math.round(s/r*100)):100,_=o.summarize(t,m),z=(Date.now()-d)/1e3,h=s>0?s/Math.max(z,.001):0,u=h>0&&r&&s<r?(r-s)/h:0,g=u?` · ${fe(u)} ${(0,i.__)("left","optimazz")}`:"";if(he(e=>e&&{...e,pct:n,label:_,step:r?`${Math.min(s,r)} / ${r}${g}`:"",logs:[...e.logs.slice(-60),...t.breakdown?[{level:"INFO",text:`${(0,i.sprintf)(/* translators: %d: number of pages to warm. */ /* translators: %d: number of pages to warm. */ 2 (0,i.__)("Found %d pages to warm:","optimazz"),t.total||0)} ${t.breakdown}.`}]:[],{level:"INFO",text:_}]}),o.onBatch&&o.onBatch(),t.unsupported){await Ee(),he(e=>e&&{...e,running:!1,status:"error",logs:[...e.logs,{level:"ERROR",text:t.message||(0,i.__)("Image processing is unavailable on this server (no GD or ImageMagick). See Tools → Server Requirements.","optimazz")}]});break}if(t.blocked){await Ee(),he(e=>e&&{...e,running:!1,status:"error",logs:[...e.logs,{level:"ERROR",text:(0,i.__)('Warming stopped: the server returned 403/429, so a firewall or security plugin is blocking the preload requests. Cached what we could; allow-list the "Optimazz-CacheWarmer" user-agent in your firewall or security plugin (e.g. All-In-One Security, Wordfence, Cloudflare), or slow warming with the optimazz_preload_delay_ms filter.',"optimazz")}]});break}if("preload"===e&&!t.done&&0===m.cached&&m.missed>=3){await Ee(),he(e=>e&&{...e,running:!1,status:"error",label:(0,i.__)("Pages are not being cached","optimazz"),logs:[...e.logs,{level:"ERROR",text:`${(0,i.__)("Stopped: pages are loading but none are being saved to the cache.","optimazz")} ${m.diagnosis}`}]});break}if(t.done){const e=[{level:"DONE",text:(0,i.__)("Completed.","optimazz")}];m.missed&&e.unshift({level:"WARN",text:`${(0,i.sprintf)(/* translators: %d: number of pages. */ /* translators: %d: number of pages. */ 3 (0,i._n)("%d page was not cached.","%d pages were not cached.",m.missed,"optimazz"),m.missed)} ${m.diagnosis}`}),he(a=>a&&{...a,running:!1,status:"done",pct:100,logs:[...a.logs,...e]});break}}}catch(e){he(a=>a&&{...a,running:!1,status:"error",logs:[...a.logs,{level:"ERROR",text:e.message||"Error"}]})}(o.refresh||ge)()};(0,t.useEffect)(()=>{let e=!1;return(async()=>{try{const a=await l()({url:`${r}/media/job`});!e&&a&&a.running&&be[a.type]&&ve(a.type,{startOffset:a.offset||0,reconnect:!0})}catch(e){}})(),()=>{e=!0}},[]);const ye=(0,t.useCallback)(async()=>{try{de(await l()({url:`${r}/database/counts`}))}catch(e){}},[]);(0,t.useEffect)(()=>{"database"!==H||me||ye()},[H,me,ye]);const we=e=>{if(!e)return"0 KB";const a=Math.floor(Math.log(e)/Math.log(1024));return`${(e/Math.pow(1024,a)).toFixed(a?1:0)} ${["B","KB","MB","GB"][a]}`},ke=z.filter(e=>S[e]?.enabled).length,Ce=S.cache||{},Ne=S.js||{},xe=S.css||{},Se=S.media||{},Me=S.fonts||{},Te=S.database||{},$e=S.heartbeat||{},Be=S.tweaks||{},Re=S.cdn||{},Pe=_.find(e=>e.key===H),De=[{on:!!Ce.enabled,w:30,label:(0,i.__)("Page caching","optimazz")},{on:!(!Ne.enabled||!Ne.delay),w:18,label:(0,i.__)("Delay JavaScript","optimazz")},{on:!(!Ne.enabled||!Ne.defer),w:8,label:(0,i.__)("Defer JavaScript","optimazz")},{on:!(!xe.enabled||!xe.minify),w:10,label:(0,i.__)("Minify CSS","optimazz")},{on:!(!Se.enabled||!Se.lazy_load),w:12,label:(0,i.__)("Lazy-load images","optimazz")},{on:!(!Se.enabled||!Se.webp&&!Se.webp_generate),w:8,label:(0,i.__)("Next-gen images","optimazz")},{on:!(!Me.enabled||!Me.display_swap),w:6,label:(0,i.__)("Font optimization","optimazz")},{on:!!Ce.preload,w:5,label:(0,i.__)("Cache preloading","optimazz")},{on:!!$e.enabled,w:3,label:(0,i.__)("Heartbeat control","optimazz")}],Oe=De.reduce((e,a)=>e+(a.on?a.w:0),0),Fe=De.filter(e=>!e.on).sort((e,a)=>a.w-e.w)[0],He=Oe>=90?"A":Oe>=75?"B":Oe>=55?"C":Oe>=35?"D":"E",Le=2*Math.PI*52,Ae=(Ne.enabled||xe.enabled||Se.enabled)&&!Ce.enabled,je=(0,a.createElement)(a.Fragment,null,Ae&&(0,a.createElement)("div",{className:"oz-cachewarn",role:"status"},(0,a.createElement)("span",{className:"dashicons dashicons-info-outline"}),(0,a.createElement)("div",{className:"oz-cachewarn__text"},(0,a.createElement)("strong",null,(0,i.__)("Turn on Page Cache for the real speed gain","optimazz")),(0,a.createElement)("p",null,(0,i.__)("File and media optimizations are on, but Page Cache is off — so the server rebuilds every page on every request. Without caching this can make a site feel slower, not faster, especially on mobile. Enable Page Cache to serve ready-made HTML.","optimazz"))),(0,a.createElement)(o.Button,{className:"oz-btn-primary",onClick:()=>L("cache")},(0,i.__)("Enable Page Cache","optimazz"))),(0,a.createElement)("div",{className:"oz-score"},(0,a.createElement)("div",{className:"oz-score__gauge"},(0,a.createElement)("svg",{viewBox:"0 0 120 120",width:"132",height:"132"},(0,a.createElement)("circle",{cx:"60",cy:"60",r:"52",fill:"none",stroke:"rgba(255,255,255,.25)",strokeWidth:"10"}),(0,a.createElement)("circle",{cx:"60",cy:"60",r:"52",fill:"none",stroke:"#fff",strokeWidth:"10",strokeLinecap:"round",strokeDasharray:Le,strokeDashoffset:Le-Le*Oe/100,transform:"rotate(-90 60 60)",style:{transition:"stroke-dashoffset .6s ease"}}),(0,a.createElement)("text",{x:"60",y:"56",textAnchor:"middle",fontSize:"30",fontWeight:"800",fill:"#fff"},Oe),(0,a.createElement)("text",{x:"60",y:"78",textAnchor:"middle",fontSize:"12",fill:"rgba(255,255,255,.85)"},(0,i.__)("SCORE","optimazz"))),(0,a.createElement)("span",{className:"oz-score__grade"},He)),(0,a.createElement)("div",{className:"oz-score__body"},(0,a.createElement)("h2",null,(0,i.__)("Performance score","optimazz")),(0,a.createElement)("p",null,Fe?`${(0,i.__)("Biggest remaining win:","optimazz")} ${Fe.label} (+${Fe.w})`:(0,i.__)("Every optimization is enabled. Nicely done!","optimazz")),(0,a.createElement)("div",{className:"oz-score__chips"},De.map(e=>(0,a.createElement)("span",{key:e.label,className:"oz-chip "+(e.on?"is-on":"")},(0,a.createElement)("span",{className:"dashicons dashicons-"+(e.on?"yes-alt":"marker")}),e.label))))),(0,a.createElement)("div",{className:"oz-kpis"},(0,a.createElement)("div",{className:"oz-kpi"},(0,a.createElement)("span",{className:"dashicons dashicons-database"}),(0,a.createElement)("strong",null,B?null!==(e=B.pages)&&void 0!==e?e:B.files:(0,a.createElement)(o.Spinner,null)),(0,a.createElement)("span",null,(0,i.__)("Cached pages","optimazz"))),(0,a.createElement)("div",{className:"oz-kpi"},(0,a.createElement)("span",{className:"dashicons dashicons-chart-area"}),(0,a.createElement)("strong",null,B?we(B.bytes):(0,a.createElement)(o.Spinner,null)),(0,a.createElement)("span",null,(0,i.__)("Cache size","optimazz"))),(0,a.createElement)("div",{className:"oz-kpi"},(0,a.createElement)("span",{className:"dashicons dashicons-yes-alt"}),(0,a.createElement)("strong",null,ke,"/",z.length),(0,a.createElement)("span",null,(0,i.__)("Modules active","optimazz")))),ee&&!ee.error&&(ee.items||[]).length>0&&(0,a.createElement)(N,{title:(0,i.__)("Recommendations","optimazz"),description:(0,i.__)("What to fix next to speed up your site — one click each.","optimazz")},(0,a.createElement)("ul",{className:"oz-recs"},(ee.items||[]).map(e=>(0,a.createElement)("li",{key:e.id,className:`oz-rec oz-rec--${e.severity}`},(0,a.createElement)("span",{className:"oz-rec__dot dashicons "+("high"===e.severity?"dashicons-warning":"medium"===e.severity?"dashicons-info":"dashicons-marker")}),(0,a.createElement)("span",{className:"oz-rec__text"},(0,a.createElement)("strong",null,e.title),(0,a.createElement)("small",null,e.desc)),e.fix?(0,a.createElement)(o.Button,{className:"oz-btn-primary oz-rec__btn",isBusy:A,onClick:()=>(async e=>{if(!e||!e.module||!e.key)return;const a={...S,[e.module]:{...S[e.module],[e.key]:e.value}};await se(a),ae(null)})(e.fix)},(0,i.__)("Fix it","optimazz")):e.link?(0,a.createElement)(o.Button,{className:"oz-btn-ghost oz-rec__btn",onClick:()=>L(e.link)},(0,i.__)("Open","optimazz")):null)))),ee&&!ee.error&&0===(ee.items||[]).length&&(0,a.createElement)(N,{title:(0,i.__)("Recommendations","optimazz")},(0,a.createElement)("p",{className:"oz-recs-clear"},(0,a.createElement)("span",{className:"dashicons dashicons-yes-alt"}),(0,i.__)("Nice — everything we recommend is already on. Your site is well optimized.","optimazz"))),(0,a.createElement)("div",{className:"oz-quick"},(0,a.createElement)(N,{title:(0,i.__)("Cache Files","optimazz"),description:(0,i.__)("Clear and preload all cache files.","optimazz")},(0,a.createElement)("div",{className:"oz-quick__actions"},(0,a.createElement)(o.Button,{className:"oz-btn-primary",icon:"trash",isBusy:!!ze?.running,onClick:async()=>{await re(),ce()}},(0,i.__)("Clear & Preload","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"update",onClick:ce},(0,i.__)("Preload","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost oz-btn--x",icon:"dismiss",onClick:re},(0,i.__)("Purge cache","optimazz"))),(0,a.createElement)("ul",{className:"oz-cachelist"},(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-media-text"}),(0,i.__)("Cached pages","optimazz"),(0,a.createElement)("strong",null,B?null!==(n=B.pages)&&void 0!==n?n:B.files:"—")),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-database"}),(0,i.__)("Page cache size","optimazz"),(0,a.createElement)("strong",null,B?we(B.bytes):"—")),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-admin-appearance"}),(0,i.__)("Optimized CSS","optimazz"),(0,a.createElement)("strong",null,B?`${B.css_files} (${we(B.css_bytes)})`:"—")),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-clock"}),(0,i.__)("Cache lifespan","optimazz"),(0,a.createElement)(v,{compact:!0,disabled:A,label:(0,i.__)("Cache lifespan","optimazz"),value:Number(null!==(s=Ce.lifespan_hours)&&void 0!==s?s:0),options:(We=[{value:0,label:(0,i.__)("Never expire","optimazz")},{value:12,label:(0,i.__)("12 hours","optimazz")},{value:24,label:(0,i.__)("1 day","optimazz")},{value:168,label:(0,i.__)("1 week","optimazz")},{value:720,label:(0,i.__)("1 month","optimazz")}],Ie=Number(null!==(c=Ce.lifespan_hours)&&void 0!==c?c:0),qe=e=>(0,i.sprintf)(/* translators: %d: number of hours. */ /* translators: %d: number of hours. */ 4 (0,i._n)("%d hour","%d hours",e,"optimazz"),e),We.some(e=>e.value===Ie)?We:[...We,{value:Ie,label:qe(Ie)}].sort((e,a)=>e.value-a.value)),onChange:e=>se({...S,cache:{...S.cache,lifespan_hours:e}})})),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-update"}),(0,a.createElement)("span",{className:"oz-row-label"},(0,i.__)("Auto-preload","optimazz"),(0,a.createElement)("small",null,B?B.preload&&B.next_preload?B.next_preload_overdue?(0,i.__)("next run: due now","optimazz"):`${(0,i.__)("next run in","optimazz")} ${B.next_preload}`:(0,i.__)("off","optimazz"):"")),(0,a.createElement)(v,{compact:!0,disabled:A,label:(0,i.__)("Auto-preload frequency","optimazz"),value:Ce.preload?Ce.preload_interval||"twicedaily":"off",options:[{value:"off",label:(0,i.__)("Off","optimazz")},{value:"hourly",label:(0,i.__)("Every hour","optimazz")},{value:"twicedaily",label:(0,i.__)("Twice a day","optimazz")},{value:"daily",label:(0,i.__)("Once a day","optimazz")}],onChange:e=>{se({...S,cache:{...S.cache,preload:"off"!==e,preload_interval:"off"===e?Ce.preload_interval||"twicedaily":e}})}})),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-backup"}),(0,i.__)("Last auto-preload","optimazz"),(0,a.createElement)("strong",null,B?B.last_preload?(0,i.sprintf)(/* translators: 1: time ago, e.g. "2 hours", 2: number of pages */ /* translators: 1: time ago, e.g. "2 hours", 2: number of pages */ 5 (0,i.__)("%1$s ago · %2$d pages","optimazz"),B.last_preload.ago,B.last_preload.warmed)+(B.last_preload.blocked?` · ${(0,i.__)("blocked by firewall","optimazz")}`:""):(0,i.__)("not yet","optimazz"):"—")))),(0,a.createElement)(N,{title:(0,i.__)("Module Status","optimazz"),description:(0,i.__)("Enable or disable any module right here.","optimazz")},(0,a.createElement)("ul",{className:"oz-status"},d.filter(e=>e.mod).map(e=>(0,a.createElement)("li",{key:e.key},(0,a.createElement)(w,{name:e.key}),(0,a.createElement)("button",{className:"oz-status__name",onClick:()=>L(e.key)},e.title),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,checked:!!S[e.mod]?.enabled,onChange:()=>K(e.mod,"enabled")}))),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-editor-code"}),(0,a.createElement)("button",{className:"oz-status__name",onClick:()=>L("files")},(0,i.__)("File Optimization","optimazz")),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,checked:!(!Ne.enabled&&!xe.enabled),onChange:()=>{const e=!(Ne.enabled||xe.enabled);V("js","enabled",e),V("css","enabled",e)}}))))));var We,Ie,qe;const Ue=(0,a.createElement)(b,{icon:"database",title:(0,i.__)("Page Cache","optimazz"),description:(0,i.__)("Serve static HTML instead of running PHP on every request.","optimazz"),enabled:!!Ce.enabled,onToggle:()=>K("cache","enabled")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Cache mobile devices separately","optimazz"),checked:!!Ce.cache_mobile,onChange:()=>K("cache","cache_mobile")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Cache logged-in users","optimazz"),help:(0,i.__)("Leave off unless you know what you are doing.","optimazz"),checked:!!Ce.cache_logged_in,onChange:()=>K("cache","cache_logged_in")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Browser cache headers","optimazz"),help:(0,i.__)("Add Expires / Cache-Control rules so repeat visits load CSS, JS, images and fonts from the browser cache. Apache and LiteSpeed only (written to .htaccess); ignored on Nginx.","optimazz"),checked:!!Ce.browser_cache,onChange:()=>K("cache","browser_cache")}),(0,a.createElement)(C,{label:(0,i.__)("Cache lifetime","optimazz"),help:(0,i.__)("How long a cached page is kept before it is refreshed. Pages are always purged immediately when their content changes.","optimazz"),value:null!==(g=Ce.lifespan_hours)&&void 0!==g?g:0,options:[{value:0,label:(0,i.__)("Never","optimazz")},{value:12,label:(0,i.__)("12 hours","optimazz")},{value:24,label:(0,i.__)("1 day","optimazz")},{value:168,label:(0,i.__)("1 week","optimazz")},{value:720,label:(0,i.__)("1 month","optimazz")}],onChange:e=>V("cache","lifespan_hours",e)}),(0,a.createElement)("div",{className:"oz-health"},(0,a.createElement)("div",{className:"oz-health__head"},(0,a.createElement)("strong",null,(0,i.__)("Cache health","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost",isBusy:O,onClick:oe},(0,i.__)("Check now","optimazz"))),(0,a.createElement)("p",{className:"oz-health__hint"},(0,i.__)("Confirms caching is actually serving pages. If it is not, the plugin rebuilds every page on each request — which is slower, not faster.","optimazz")),P&&!P.error&&(0,a.createElement)("ul",{className:"oz-health__list"},(0,a.createElement)(k,{ok:"hit"===P.live,warn:"miss"===P.live,label:"hit"===P.live?(0,i.__)("Pages ARE being served from cache (HIT)","optimazz"):"miss"===P.live?(0,i.__)("Pages are NOT being served from cache (MISS) — see below","optimazz"):(0,i.__)("Live check unavailable","optimazz")}),(0,a.createElement)(k,{ok:P.dropin_installed&&P.dropin_ours,warn:P.conflict,label:P.conflict?P.conflict_name?`${(0,i.__)("advanced-cache.php belongs to","optimazz")} ${P.conflict_name} — ${(0,i.__)("take it over below","optimazz")}`:(0,i.__)("advanced-cache.php belongs to ANOTHER plugin — take it over below","optimazz"):P.dropin_ours?(0,i.__)("advanced-cache.php drop-in installed","optimazz"):(0,i.__)("advanced-cache.php drop-in is missing","optimazz")}),(0,a.createElement)(k,{ok:P.wp_cache,label:P.wp_cache?(0,i.__)("WP_CACHE is enabled in wp-config.php","optimazz"):(0,i.__)("WP_CACHE is not enabled in wp-config.php","optimazz")}),!P.wpconfig_writable&&(0,a.createElement)(k,{warn:!0,label:(0,i.__)("wp-config.php is not writable — add the WP_CACHE line manually (below)","optimazz")}),(0,a.createElement)(k,{ok:P.warmed>0,label:`${P.warmed} ${(0,i.__)("pages warmed in cache","optimazz")}`})),P&&!P.error&&(P.conflict||!P.wp_cache&&P.wpconfig_writable||!P.dropin_ours)&&P.enabled&&(0,a.createElement)("div",{className:"oz-health__fix"},(0,a.createElement)(o.Button,{className:"oz-btn-primary",isBusy:O,onClick:le},(0,i.__)("Take over caching","optimazz")),(0,a.createElement)("span",null,(0,i.__)("Make Optimazz the caching plugin — installs its drop-in and enables WP_CACHE.","optimazz"))),P&&!P.error&&Array.isArray(P.active_conflicts)&&P.active_conflicts.length>0&&(0,a.createElement)("div",{className:"oz-health__conflict"},P.active_conflicts.map(e=>(0,a.createElement)("div",{className:"oz-health__conflict-row",key:e.file},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,`${e.name} ${(0,i.__)("is still active and may reclaim caching. Deactivate it so Optimazz keeps serving the cache.","optimazz")}`),(0,a.createElement)(o.Button,{className:"oz-btn-danger",isBusy:O,onClick:()=>ne(e.file,e.name)},`${(0,i.__)("Deactivate","optimazz")} ${e.name}`)))),P&&!P.error&&!P.wp_cache&&!P.wpconfig_writable&&(0,a.createElement)("div",{className:"oz-health__manual"},(0,a.createElement)("p",null,(0,i.__)("wp-config.php is not writable. Add this line just after the opening <?php tag:","optimazz")),(0,a.createElement)("code",null,"define( 'WP_CACHE', true );")),P&&P.error&&(0,a.createElement)("p",{className:"oz-health__err"},(0,i.__)("Could not run the check. Try reloading the page.","optimazz")))),Je=(0,a.createElement)(a.Fragment,null,(0,a.createElement)(b,{icon:"editor-code",title:(0,i.__)("JavaScript","optimazz"),description:(0,i.__)("Defer and delay scripts to cut Total Blocking Time.","optimazz"),enabled:!!Ne.enabled,onToggle:()=>K("js","enabled")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Minify JavaScript","optimazz"),help:(0,i.__)("Strip comments and whitespace from inline scripts. Line breaks are kept for safety; test your site after enabling.","optimazz"),checked:!!Ne.minify,onChange:()=>K("js","minify")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Defer JavaScript","optimazz"),help:(0,i.__)("Load scripts without blocking page render.","optimazz"),checked:!!Ne.defer,onChange:()=>K("js","defer")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Delay JS until interaction","optimazz"),help:(0,i.__)("Run scripts only after the first user interaction.","optimazz"),checked:!!Ne.delay,onChange:()=>K("js","delay")}),Ne.delay&&(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Delay all scripts","optimazz"),help:(0,i.__)("Delay every script until interaction. jQuery, your theme, and Elementor are always kept safe. Still, on sites where content is rendered by JavaScript this can delay the visible page — test before keeping it on.","optimazz"),checked:!!Ne.delay_all,onChange:()=>K("js","delay_all")}),Ne.delay&&Ne.delay_all&&(0,a.createElement)("div",{className:"oz-dbwarn"},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,(0,i.__)('Delaying all scripts can slow the first paint on JavaScript-rendered sites (Elementor, sliders). If your PageSpeed FCP/LCP get worse, switch to "Delay selected" and delay only tracking scripts.',"optimazz"))),Ne.delay&&!Ne.delay_all&&(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Delay these scripts","optimazz"),help:(0,i.__)("Only scripts matching these keywords are delayed. Match by filename, handle or URL — e.g. gtag, analytics, facebook.","optimazz"),placeholder:(0,i.__)("e.g. gtag, analytics","optimazz"),value:Ne.delay_list||[],onChange:e=>V("js","delay_list",e)}),(Ne.defer||Ne.delay)&&(0,a.createElement)("div",{className:"oz-fieldgroup"},(0,a.createElement)("h4",{className:"oz-fieldgroup__title"},(0,i.__)("Exclude specific scripts","optimazz")),(0,a.createElement)("p",{className:"oz-fieldgroup__desc"},(0,i.__)("If a script breaks, exclude it from just one optimization instead of both. Match by filename, script handle, or part of the URL.","optimazz")),Ne.defer&&(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never defer","optimazz"),help:(0,i.__)("Keep loading these render-blocking, but still allow delay. Use for scripts that must run during page load.","optimazz"),placeholder:(0,i.__)("e.g. jquery.min.js","optimazz"),value:Ne.no_defer||[],onChange:e=>V("js","no_defer",e)}),Ne.delay&&(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never delay","optimazz"),help:(0,i.__)("Run these before any interaction, but still allow defer. Use for sliders, above-the-fold, or consent scripts.","optimazz"),placeholder:(0,i.__)("e.g. slider, swiper","optimazz"),value:Ne.no_delay||[],onChange:e=>V("js","no_delay",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never optimize","optimazz"),help:(0,i.__)("Skip both defer and delay — leave these scripts exactly as they are.","optimazz"),placeholder:(0,i.__)("e.g. recaptcha","optimazz"),value:Ne.exclusions||[],onChange:e=>V("js","exclusions",e)}))),(0,a.createElement)(b,{icon:"admin-appearance",title:(0,i.__)("CSS","optimazz"),description:(0,i.__)("Minify, combine, and remove render-blocking stylesheets.","optimazz"),enabled:!!xe.enabled,onToggle:()=>K("css","enabled")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Minify CSS","optimazz"),checked:!!xe.minify,onChange:()=>K("css","minify")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Combine CSS files","optimazz"),help:(0,i.__)("Merge stylesheets to reduce requests.","optimazz"),checked:!!xe.combine,onChange:()=>K("css","combine")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Load CSS asynchronously","optimazz"),help:(0,i.__)("Removes render-blocking CSS. Paste your above-the-fold (critical) CSS below, or the page may briefly flash unstyled.","optimazz"),checked:!!xe.async_load,onChange:()=>K("css","async_load")}),xe.async_load&&(0,a.createElement)(a.Fragment,null,!(xe.critical_css||"").trim()&&(0,a.createElement)("div",{className:"oz-dbwarn"},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,(0,i.__)("No critical CSS provided. Async loading is on, so visitors may see a flash of unstyled content until you paste critical CSS below.","optimazz"))),(0,a.createElement)(o.TextareaControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Critical CSS (above-the-fold)","optimazz"),help:(0,i.__)("Paste the above-the-fold CSS to inline. Generate it with a free tool such as a critical-CSS generator. Leave empty to disable inlining.","optimazz"),value:xe.critical_css||"",onChange:e=>V("css","critical_css",e),rows:6})),(0,a.createElement)("div",{className:"oz-subhead"},(0,i.__)("Remove unused CSS","optimazz")),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Remove block-editor CSS on the front end","optimazz"),help:(0,i.__)("Dequeues WordPress block-library and global styles. Turn on only if your front end does NOT render Gutenberg blocks (e.g. a page-builder or classic theme). Big byte saving when it applies.","optimazz"),checked:!!xe.remove_block_css,onChange:()=>K("css","remove_block_css")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Load WooCommerce CSS only on shop pages","optimazz"),help:(0,i.__)("Dequeues WooCommerce styles on non-shop pages (kept on cart, checkout, account, product and shop). Turn off if you show Woo products or widgets on other pages.","optimazz"),checked:!!xe.remove_woo_css,onChange:()=>K("css","remove_woo_css")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Load Easy Digital Downloads CSS only on EDD pages","optimazz"),help:(0,i.__)("Dequeues EDD styles on non-EDD pages (kept on checkout, purchase-confirmation, purchase history and download archives). Turn off if you show download or purchase links elsewhere.","optimazz"),checked:!!xe.remove_edd_css,onChange:()=>K("css","remove_edd_css")}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Excluded CSS files (keywords)","optimazz"),value:xe.exclusions||[],onChange:e=>V("css","exclusions",e)}))),Ge=(0,a.createElement)(b,{icon:"format-image",title:(0,i.__)("Image Optimization","optimazz"),description:(0,i.__)("Lazy-load, prevent layout shift, serve next-gen formats, and lighten media.","optimazz"),enabled:!!Se.enabled,onToggle:()=>K("media","enabled")},pe&&!pe.can_process&&(0,a.createElement)("div",{className:"oz-notice oz-notice--error"},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,(0,i.__)('This server has no GD or ImageMagick extension, so image compression and WebP/AVIF cannot run — every image will be skipped. Ask your host to enable "php-gd" (or "php-imagick"). See Tools → Server Requirements.',"optimazz"))),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Lazy-load images","optimazz"),help:(0,i.__)("Load off-screen images only when scrolled into view.","optimazz"),checked:!!Se.lazy_load,onChange:()=>K("media","lazy_load")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Lazy-load iframes","optimazz"),checked:!!Se.lazy_iframes,onChange:()=>K("media","lazy_iframes")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Lazy-load CSS background images","optimazz"),help:(0,i.__)("Defer inline background-image elements (test with page builders).","optimazz"),checked:!!Se.lazy_bg,onChange:()=>K("media","lazy_bg")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Replace YouTube embeds with a preview","optimazz"),help:(0,i.__)("Load the video iframe only when the visitor clicks play.","optimazz"),checked:!!Se.youtube_preview,onChange:()=>K("media","youtube_preview")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Add missing image dimensions","optimazz"),help:(0,i.__)("Set width/height to prevent layout shift (CLS).","optimazz"),checked:!!Se.add_dimensions,onChange:()=>K("media","add_dimensions")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Preload the LCP image","optimazz"),help:(0,i.__)("Preload the first/hero image with high priority.","optimazz"),checked:!!Se.preload_lcp,onChange:()=>K("media","preload_lcp")}),Se.lazy_load&&(0,a.createElement)(C,{label:(0,i.__)("Keep first images eager (LCP)","optimazz"),help:(0,i.__)("Do not lazy-load the first N images, to protect LCP.","optimazz"),value:null!==(E=Se.skip_first)&&void 0!==E?E:1,onChange:e=>V("media","skip_first",e),options:[0,1,2,3,4,5].map(e=>({label:String(e),value:e}))}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Serve WebP via <picture>","optimazz"),help:(0,i.__)("Deliver WebP to supporting browsers, original as fallback.","optimazz"),checked:!!Se.webp,onChange:()=>K("media","webp")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Generate WebP/AVIF on upload","optimazz"),help:pe&&!pe.can_webp?(0,i.__)("WebP generation is not available on this server.","optimazz"):(0,i.__)("Auto-create next-gen copies for newly uploaded images.","optimazz"),checked:!!Se.webp_generate,onChange:()=>K("media","webp_generate")}),pe?.can_avif&&(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Also generate/serve AVIF","optimazz"),help:(0,i.__)("Smaller than WebP; served first when supported.","optimazz"),checked:!!Se.avif,onChange:()=>K("media","avif")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Compress & resize on upload","optimazz"),help:(0,i.__)("Recompress, cap dimensions, and strip metadata from new uploads.","optimazz"),checked:!!Se.compress,onChange:()=>K("media","compress")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Convert opaque PNG to JPEG","optimazz"),help:(0,i.__)("Re-save non-transparent PNGs as much smaller JPEGs during bulk optimization.","optimazz"),checked:!!Se.png_to_jpg,onChange:()=>K("media","png_to_jpg")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Keep a backup of originals","optimazz"),help:(0,i.__)("Copy originals to uploads/optimazz-backup before optimizing.","optimazz"),checked:!!Se.backup_originals,onChange:()=>K("media","backup_originals")}),(0,a.createElement)(o.RangeControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Image quality","optimazz"),value:null!==(y=Se.quality)&&void 0!==y?y:82,onChange:e=>V("media","quality",e),min:40,max:100,help:(0,i.__)("Used for compression and WebP/AVIF generation.","optimazz")}),(0,a.createElement)(o.RangeControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Max image width (px)","optimazz"),value:null!==(x=Se.max_width)&&void 0!==x?x:2560,onChange:e=>V("media","max_width",e),min:800,max:4e3,step:80}),(0,a.createElement)("div",{className:"oz-convert"},(0,a.createElement)("div",{className:"oz-convert__info"},(0,a.createElement)("strong",null,(0,i.__)("WebP coverage","optimazz")),pe?(0,a.createElement)("span",null,pe.converted," / ",pe.images," ",(0,i.__)("images converted","optimazz")):(0,a.createElement)("span",null,(0,i.__)("Loading…","optimazz"))),(0,a.createElement)(o.Button,{className:"oz-btn-primary",icon:"images-alt2",isBusy:ze?.running,disabled:pe&&!pe.can_webp||ze?.running,onClick:()=>ve("convert")},(0,i.__)("Convert to WebP","optimazz"))),(0,a.createElement)("div",{className:"oz-convert"},(0,a.createElement)("div",{className:"oz-convert__info"},(0,a.createElement)("strong",null,(0,i.__)("Bulk compression","optimazz")),pe?(0,a.createElement)("span",null,pe.optimized," ",(0,i.__)("optimized","optimazz")," · ",we(pe.saved)," ",(0,i.__)("saved","optimazz")):(0,a.createElement)("span",null,(0,i.__)("Loading…","optimazz"))),(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"filter",isBusy:ze?.running,disabled:ze?.running,onClick:()=>ve("compress")},(0,i.__)("Compress existing images","optimazz")))),Ve=(0,a.createElement)(N,{title:(0,i.__)("Preload Cache","optimazz"),description:(0,i.__)("Warm the cache automatically so visitors never hit a cold page.","optimazz")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Activate preloading","optimazz"),help:(0,i.__)("Warms the cache on a schedule and right after every full purge (for example after changing a setting). Warming follows the sitemap — home, pages, posts, custom post types, then the busiest category and tag archives — most-important first, for both mobile and desktop.","optimazz"),checked:!!Ce.preload,onChange:()=>K("cache","preload")}),(0,a.createElement)(o.SelectControl,{__nextHasNoMarginBottom:!0,__next40pxDefaultSize:!0,label:(0,i.__)("Automatic preload","optimazz"),help:(0,i.__)("How often to re-warm the whole site. Choose “Every hour” if your content changes often or you use a short cache lifespan.","optimazz"),value:Ce.preload_interval||"twicedaily",disabled:!Ce.preload,options:[{label:(0,i.__)("Every hour","optimazz"),value:"hourly"},{label:(0,i.__)("Twice a day","optimazz"),value:"twicedaily"},{label:(0,i.__)("Once a day","optimazz"),value:"daily"}],onChange:e=>V("cache","preload_interval",e)}),B&&B.wp_cron_disabled&&(0,a.createElement)("div",{className:"oz-notice oz-notice--warn",style:{marginTop:12}},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,(0,i.__)("WP-Cron is disabled on this site (DISABLE_WP_CRON). Scheduled preloads only run if a server cron job calls wp-cron.php — ask your host to set one up (e.g. every 5 minutes).","optimazz"))),(0,a.createElement)("div",{className:"oz-inline-actions"},(0,a.createElement)(o.Button,{className:"oz-btn-primary",icon:"update",isBusy:!!ze?.running,onClick:ce},(0,i.__)("Preload now","optimazz")))),Ke=(0,a.createElement)(N,{title:(0,i.__)("Advanced Rules","optimazz"),description:(0,i.__)("Fine-tune which requests are cached.","optimazz")},(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never cache these URLs (contains)","optimazz"),value:Ce.excluded_uris||[],onChange:e=>V("cache","excluded_uris",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never cache when these cookies are set","optimazz"),value:Ce.excluded_cookies||[],onChange:e=>V("cache","excluded_cookies",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never cache for these user agents","optimazz"),value:Ce.excluded_user_agents||[],onChange:e=>V("cache","excluded_user_agents",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Ignore these query arguments","optimazz"),value:Ce.ignored_query_args||[],onChange:e=>V("cache","ignored_query_args",e)})),Ye=!(!S.tools||!S.tools.safe_mode),Ze=(0,a.createElement)(a.Fragment,null,(0,a.createElement)(N,{title:(0,i.__)("Safe Mode","optimazz"),description:(0,i.__)("If a theme or page builder looks broken after optimizing, turn this on. It pauses the riskiest optimizations — combine CSS, async CSS, remove block CSS and JavaScript delay — while keeping caching, minify, defer and lazy-load. Your saved settings are not changed; they simply pause until you turn Safe Mode off.","optimazz")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Pause risky optimizations (Safe Mode)","optimazz"),checked:Ye,onChange:e=>{const a={...S,tools:{...S.tools||{},safe_mode:e}};M(a),se(a)}}),Ye&&(0,a.createElement)("div",{className:"oz-notice oz-notice--warn",style:{marginTop:12}},(0,a.createElement)("span",{className:"dashicons dashicons-shield"}),(0,a.createElement)("span",null,(0,i.__)("Safe Mode is ON — combine/async CSS, remove-block-CSS and JavaScript delay are paused site-wide. Turn it off to resume full optimization.","optimazz"))),(0,a.createElement)("div",{className:"oz-inline-actions",style:{marginTop:14}},(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"undo",disabled:!q,onClick:async()=>{try{const e=await l()({url:`${r}/settings/revert`,method:"POST"});if("nothing"===e.message)return U(!1),void ie((0,i.__)("Nothing to undo.","optimazz"),"error");$(e.settings),M(e.settings),U(!!e.hasBackup),ae(null),ie((0,i.__)("Reverted to the previous settings.","optimazz")),Y()}catch(e){ie(e.message||(0,i.__)("Undo failed.","optimazz"),"error")}}},(0,i.__)("Undo last change","optimazz")),(0,a.createElement)("span",{className:"oz-muted",style:{fontSize:12}},q?(0,i.__)("Restores the settings from just before your last save, reset or Safe Mode toggle.","optimazz"):(0,i.__)("Nothing to undo yet — this becomes available after your next change.","optimazz")))),(0,a.createElement)(N,{title:(0,i.__)("Server Requirements","optimazz"),description:(0,i.__)("What your host supports. Warnings here explain why a feature (like WebP/AVIF) may be unavailable or slow.","optimazz")},!Z&&(0,a.createElement)("p",{className:"oz-muted"},(0,i.__)("Checking your server…","optimazz")),Z&&Z.error&&(0,a.createElement)("p",{className:"oz-muted"},(0,i.__)("Could not read the server report.","optimazz")),Z&&!Z.error&&(0,a.createElement)("ul",{className:"oz-reqs"},(Z.checks||[]).map((e,t)=>(0,a.createElement)("li",{key:t,className:`oz-req oz-req--${e.status}`},(0,a.createElement)("span",{className:"oz-req__dot dashicons "+("ok"===e.status?"dashicons-yes-alt":"warn"===e.status?"dashicons-warning":"info"===e.status?"dashicons-minus":"dashicons-dismiss")}),(0,a.createElement)("span",{className:"oz-req__label"},e.label),(0,a.createElement)("span",{className:"oz-req__value"},e.value),e.note&&(0,a.createElement)("span",{className:"oz-req__note"},e.note)))),(0,a.createElement)("div",{className:"oz-inline-actions",style:{marginTop:14}},(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"update",isBusy:!Z,onClick:()=>{Q(null),X()}},(0,i.__)("Re-check","optimazz")))),(0,a.createElement)(N,{title:(0,i.__)("Import / Export Settings","optimazz"),description:(0,i.__)("Back up your configuration or copy it to another site.","optimazz")},(0,a.createElement)("div",{className:"oz-inline-actions"},(0,a.createElement)(o.Button,{className:"oz-btn-primary",icon:"download",onClick:()=>{const e=new window.Blob([JSON.stringify(S,null,2)],{type:"application/json"}),a=document.createElement("a");a.href=URL.createObjectURL(e),a.download="optimazz-settings.json",a.click(),URL.revokeObjectURL(a.href)}},(0,i.__)("Export settings","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"upload",onClick:()=>J.current?.click()},(0,i.__)("Import settings","optimazz")),(0,a.createElement)("input",{ref:J,type:"file",accept:"application/json",style:{display:"none"},onChange:e=>{const a=e.target.files?.[0];if(!a)return;const t=new window.FileReader;t.onload=()=>{try{const e=JSON.parse(t.result);se(e)}catch(e){ie((0,i.__)("Invalid settings file.","optimazz"),"error")}},t.readAsText(a),e.target.value=""}}))),(0,a.createElement)(N,{title:(0,i.__)("Reset","optimazz"),description:(0,i.__)("Restore every module to its default configuration. This cannot be undone.","optimazz")},(0,a.createElement)("div",{className:"oz-inline-actions"},(0,a.createElement)(o.Button,{className:"oz-btn-danger",icon:"image-rotate",onClick:async()=>{if(window.confirm((0,i.__)('Reset all Optimazz settings to defaults? You can undo this with "Undo last change".',"optimazz")))try{const e=await l()({url:`${r}/settings/reset`,method:"POST"});$(e.settings),M(e.settings),U(!!e.hasBackup),ie((0,i.__)("Settings reset to defaults.","optimazz")),Y()}catch(e){ie(e.message||(0,i.__)("Reset failed.","optimazz"),"error")}}},(0,i.__)("Reset all settings to defaults","optimazz"))))),Qe=(0,a.createElement)(b,{icon:"editor-textcolor",title:(0,i.__)("Fonts","optimazz"),description:(0,i.__)("Swap, preconnect, and preload fonts to remove render-blocking requests.","optimazz"),enabled:!!Me.enabled,onToggle:()=>K("fonts","enabled")},(0,a.createElement)(o.SelectControl,{__nextHasNoMarginBottom:!0,__next40pxDefaultSize:!0,label:(0,i.__)("Font loading (font-display)","optimazz"),help:(0,i.__)("Optional = no layout shift (best for CLS): the web font is skipped on the first uncached load and used once cached. Swap = text stays visible but the web font swaps in and can shift layout (higher CLS). Block = brief invisible text, then the web font.","optimazz"),value:Me.font_display||(Me.display_swap,"swap"),options:[{label:(0,i.__)("Optional — no layout shift (recommended for CLS)","optimazz"),value:"optional"},{label:(0,i.__)("Swap — always show text (may shift layout)","optimazz"),value:"swap"},{label:(0,i.__)("Block — brief hidden text, then web font","optimazz"),value:"block"}],onChange:e=>V("fonts","font_display",e)}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Combine Google Fonts into one request","optimazz"),help:(0,i.__)("Page builders often load each Google Font family separately. This merges them into a single request.","optimazz"),checked:!!Me.combine_google,onChange:e=>V("fonts","combine_google",e)}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Load only the font weights your pages use","optimazz"),help:(0,i.__)("Drops unused weights (e.g. 100, 200, 900) and italics your pages never use — often cutting font CSS by more than half. Weights 400 and 700 are always kept. Turn off if some text looks thinner or bolder than intended.","optimazz"),checked:!!Me.trim_weights,disabled:!Me.combine_google,onChange:e=>V("fonts","trim_weights",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Preload these font files (URLs)","optimazz"),value:Me.preload||[],onChange:e=>V("fonts","preload",e)})),Xe=e=>{var a;if(!me||void 0===me[e.key])return e.help;const t=null!==(a=me[e.key])&&void 0!==a?a:0; 6 /* translators: %d: number of items found in the database. */return`${(0,i.sprintf)((0,i._n)("%d item in your database.","%d items in your database.",t,"optimazz"),t)} ${e.help}`},ea=(0,a.createElement)(N,{title:(0,i.__)("Database Optimization","optimazz"),description:(0,i.__)("Reduce bloat and reclaim space. Once a cleanup runs it cannot be undone, so back up your database first.","optimazz")},h.map(e=>(0,a.createElement)("div",{key:e.title,className:"oz-dbgroup"},(0,a.createElement)("h3",{className:"oz-dbgroup__title"},e.title),(0,a.createElement)("p",{className:"oz-dbgroup__desc"},e.desc),e.items.map(e=>{var t;return(0,a.createElement)(o.ToggleControl,{key:e.key,__nextHasNoMarginBottom:!0,label:me?`${e.label} (${null!==(t=me[e.key])&&void 0!==t?t:0})`:e.label,help:Xe(e),checked:!!Te[e.key],onChange:()=>K("database",e.key)})}))),(0,a.createElement)("div",{className:"oz-dbgroup"},(0,a.createElement)("h3",{className:"oz-dbgroup__title"},(0,i.__)("Set it and forget it","optimazz")),(0,a.createElement)("p",{className:"oz-dbgroup__desc"},(0,i.__)("Let Optimazz repeat the cleanups you picked above on a schedule, so your database stays lean without you lifting a finger.","optimazz")),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Schedule automatic cleanup","optimazz"),help:(0,i.__)("Uses WP-Cron to repeat the cleanups you enabled above.","optimazz"),checked:!!Te.enabled,onChange:()=>K("database","enabled")}),Te.enabled&&(0,a.createElement)(C,{label:(0,i.__)("Cleanup frequency","optimazz"),value:Te.schedule||"off",onChange:e=>V("database","schedule",e),options:[{label:(0,i.__)("Off","optimazz"),value:"off"},{label:(0,i.__)("Daily","optimazz"),value:"daily"},{label:(0,i.__)("Weekly","optimazz"),value:"weekly"},{label:(0,i.__)("Monthly","optimazz"),value:"monthly"}]})),(0,a.createElement)("div",{className:"oz-dbwarn"},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,(0,i.__)("Back up your database before you run a cleanup — a database optimization cannot be undone.","optimazz"))),(0,a.createElement)("div",{className:"oz-inline-actions"},(0,a.createElement)(o.Button,{className:"oz-btn-primary",icon:"trash",isBusy:!!ze?.running,onClick:async()=>{if(ze?.running)return;const e=(0,i.__)("Cleaning and optimizing database…","optimazz");he({title:(0,i.__)("Optimizing database","optimazz"),subtitle:(0,i.__)("Cleanup & table optimization","optimazz"),pct:100,indeterminate:!0,label:e,step:"",logs:[{level:"INFO",text:e}],running:!0,status:"running",minimized:!1});try{const e=await l()({url:`${r}/database/optimize`,method:"POST",data:S.database||{}});de(e.counts);const a=Object.values(e.removed||{}).reduce((e,a)=>e+a,0),t=`${(0,i.__)("Cleaned","optimazz")} ${a} ${(0,i.__)("rows,","optimazz")} ${e.tables} ${(0,i.__)("tables optimized.","optimazz")}`;he(e=>e&&{...e,running:!1,status:"done",indeterminate:!1,pct:100,label:t,logs:[...e.logs,{level:"INFO",text:t},{level:"DONE",text:(0,i.__)("Completed.","optimazz")}]}),ie(t)}catch(e){he(a=>a&&{...a,running:!1,status:"error",indeterminate:!1,logs:[...a.logs,{level:"ERROR",text:e.message||"Error"}]}),ie(e.message||(0,i.__)("Optimize failed.","optimazz"),"error")}}},(0,i.__)("Optimize now","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"update",onClick:ye},(0,i.__)("Refresh counts","optimazz")))),aa={dashboard:je,cache:Ue,files:Je,media:Ge,fonts:Qe,preload:Ve,advanced:Ke,database:ea,heartbeat:(0,a.createElement)(N,{title:(0,i.__)("Heartbeat Control","optimazz"),description:(0,i.__)("Reduce or disable the WordPress Heartbeat API to lower server load.","optimazz")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Enable Heartbeat control","optimazz"),checked:!!$e.enabled,onChange:()=>K("heartbeat","enabled")}),$e.enabled&&(0,a.createElement)(a.Fragment,null,(0,a.createElement)(C,{label:(0,i.__)("Behavior in admin dashboard","optimazz"),value:$e.backend||"default",options:u,onChange:e=>V("heartbeat","backend",e)}),(0,a.createElement)(C,{label:(0,i.__)("Behavior in post editor","optimazz"),value:$e.editor||"default",options:u,onChange:e=>V("heartbeat","editor",e)}),(0,a.createElement)(C,{label:(0,i.__)("Behavior on front-end","optimazz"),value:$e.frontend||"default",options:u,onChange:e=>V("heartbeat","frontend",e)}))),tweaks:(0,a.createElement)(b,{icon:"admin-generic",title:(0,i.__)("Tweaks","optimazz"),description:(0,i.__)("Strip unnecessary WordPress front-end weight.","optimazz"),enabled:!!Be.enabled,onToggle:()=>K("tweaks","enabled")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Disable emojis","optimazz"),help:(0,i.__)("Remove the emoji detection script and styles.","optimazz"),checked:!!Be.disable_emojis,onChange:()=>K("tweaks","disable_emojis")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Disable embeds (oEmbed)","optimazz"),checked:!!Be.disable_embeds,onChange:()=>K("tweaks","disable_embeds")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Disable dashicons on front-end","optimazz"),help:(0,i.__)("Only for anonymous visitors.","optimazz"),checked:!!Be.disable_dashicons,onChange:()=>K("tweaks","disable_dashicons")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Remove query strings from assets","optimazz"),help:(0,i.__)("Strip ?ver= for better proxy caching.","optimazz"),checked:!!Be.remove_query_strings,onChange:()=>K("tweaks","remove_query_strings")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Disable XML-RPC","optimazz"),checked:!!Be.disable_xmlrpc,onChange:()=>K("tweaks","disable_xmlrpc")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Disable self-pingbacks","optimazz"),checked:!!Be.disable_pingback,onChange:()=>K("tweaks","disable_pingback")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Remove head meta (generator, RSD, shortlink)","optimazz"),checked:!!Be.remove_meta,onChange:()=>K("tweaks","remove_meta")}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("DNS-prefetch domains","optimazz"),value:Be.dns_prefetch||[],onChange:e=>V("tweaks","dns_prefetch",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Preconnect origins","optimazz"),value:Be.preconnect||[],onChange:e=>V("tweaks","preconnect",e)})),cdn:(0,a.createElement)(b,{icon:"cloud",title:(0,i.__)("Content Delivery Network","optimazz"),description:(0,i.__)("Rewrite static asset URLs to your CDN hostname.","optimazz"),enabled:!!Re.enabled,onToggle:()=>K("cdn","enabled")},(0,a.createElement)(o.TextControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("CDN URL (CNAME)","optimazz"),placeholder:"https://cdn.example.com",value:Re.url||"",onChange:e=>V("cdn","url",e),help:(0,i.__)("CSS, JS, image, and font URLs will be served from here.","optimazz")}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Exclude paths containing","optimazz"),value:Re.exclusions||[],onChange:e=>V("cdn","exclusions",e)})),tools:Ze};return p.forEach(e=>{aa[e.key]="function"==typeof e.render?e.render():e.render}),(0,a.createElement)("div",{className:"oz-shell"},(0,a.createElement)("aside",{className:"oz-sidebar"},(0,a.createElement)("div",{className:"oz-sidebar__brand"},window.optimazzData?.logoUrl?(0,a.createElement)("img",{className:"oz-logo-img",src:window.optimazzData.logoUrl,alt:"Optimazz"}):(0,a.createElement)("div",{className:"oz-sidebar__brand-fallback"},(0,a.createElement)("span",{className:"oz-logo dashicons dashicons-performance"}),(0,a.createElement)("strong",null,"Optimazz"))),(0,a.createElement)("nav",null,_.map(e=>(0,a.createElement)("button",{key:e.key,className:"oz-navitem "+(H===e.key?"is-active":""),onClick:()=>L(e.key)},(0,a.createElement)(w,{name:e.key,paths:e.iconSvg}),(0,a.createElement)("span",{className:"oz-navitem__text"},(0,a.createElement)("strong",null,e.title),(0,a.createElement)("small",null,e.sub)),e.mod&&S[e.mod]?.enabled&&(0,a.createElement)("i",{className:"oz-navitem__dot"})))),(0,a.createElement)("div",{className:"oz-sidebar__foot"},"v",window.optimazzData?.version)),(0,a.createElement)("div",{className:"oz-content"},(0,a.createElement)("header",{className:"oz-topbar"},(0,a.createElement)("span",{className:`oz-topbar__icon dashicons dashicons-${Pe.icon}`}),(0,a.createElement)("div",null,(0,a.createElement)("h1",null,Pe.title),(0,a.createElement)("p",null,Pe.sub))),(0,a.createElement)("main",{className:"oz-main"},aa[H])),(0,a.createElement)("div",{className:"oz-savebar "+(G?"is-dirty":"")},(0,a.createElement)("span",{className:"oz-savebar__hint"},G?(0,i.__)("You have unsaved changes","optimazz"):(0,i.__)("All changes saved","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-primary",isBusy:A,disabled:!G,onClick:()=>se(S)},(0,i.__)("Save changes","optimazz"))),W&&(0,a.createElement)("div",{className:"oz-toast"},(0,a.createElement)(o.Snackbar,{status:W.status,onRemove:()=>I(null)},W.text)),(0,a.createElement)(f,{job:ze,onStop:()=>{ue.current=!0},onClose:()=>{ue.current=!0,he(null)},onToggleMin:()=>he(e=>e&&{...e,minimized:!e.minimized})}))},null))})(); 1 (()=>{"use strict";const e={n:a=>{const t=a&&a.__esModule?()=>a.default:()=>a;return e.d(t,{a:t}),t},d:(a,t)=>{for(var o in t)e.o(t,o)&&!e.o(a,o)&&Object.defineProperty(a,o,{enumerable:!0,get:t[o]})},o:(e,a)=>Object.hasOwn(e,a)},a=window.React,t=window.wp.element,o=window.wp.components,n=window.wp.apiFetch;var l=e.n(n);const i=window.wp.i18n,s=window.wp.hooks,{restUrl:r,nonce:c,settings:m}=window.optimazzData||{};l().use(l().createNonceMiddleware(c));const d=[{key:"dashboard",title:(0,i.__)("Dashboard","optimazz"),sub:(0,i.__)("Overview & quick actions","optimazz"),icon:"admin-home"},{key:"cache",title:(0,i.__)("Page Cache","optimazz"),sub:(0,i.__)("Static HTML caching","optimazz"),icon:"database",mod:"cache"},{key:"files",title:(0,i.__)("File Optimization","optimazz"),sub:(0,i.__)("Optimize CSS & JS","optimazz"),icon:"editor-code"},{key:"media",title:(0,i.__)("Media","optimazz"),sub:(0,i.__)("LazyLoad, dimensions, WebP","optimazz"),icon:"format-image",mod:"media"},{key:"fonts",title:(0,i.__)("Fonts","optimazz"),sub:(0,i.__)("Preload, swap, preconnect","optimazz"),icon:"editor-textcolor",mod:"fonts"},{key:"preload",title:(0,i.__)("Preload","optimazz"),sub:(0,i.__)("Generate cache files","optimazz"),icon:"update"},{key:"advanced",title:(0,i.__)("Advanced Rules","optimazz"),sub:(0,i.__)("Fine-tune cache rules","optimazz"),icon:"list-view"},{key:"database",title:(0,i.__)("Database","optimazz"),sub:(0,i.__)("Optimize, reduce bloat","optimazz"),icon:"database-view",mod:"database"},{key:"heartbeat",title:(0,i.__)("Heartbeat","optimazz"),sub:(0,i.__)("Control Heartbeat API","optimazz"),icon:"heart",mod:"heartbeat"},{key:"tweaks",title:(0,i.__)("Tweaks","optimazz"),sub:(0,i.__)("Remove WordPress bloat","optimazz"),icon:"admin-generic",mod:"tweaks"},{key:"cdn",title:(0,i.__)("CDN","optimazz"),sub:(0,i.__)("Rewrite asset URLs","optimazz"),icon:"cloud",mod:"cdn"},{key:"tools",title:(0,i.__)("Tools","optimazz"),sub:(0,i.__)("Import, export","optimazz"),icon:"admin-tools"}],p=(0,s.applyFilters)("optimazz.adminSections",[]),_=[...d,...p],z=e=>![!1,"","0",0,void 0,null].includes(e),h=![!1,"","0",0].includes(window.optimazzData?.serverFeatures),u=window.optimazzData?.networkCache||null,g=!h&&!!u&&z(u.enabled),b=h||g,f=z(window.optimazzData?.isSuperAdmin),E=window.optimazzData?.networkAdminUrl||"",v=e=>{if(!e)return"0 KB";const a=Math.floor(Math.log(e)/Math.log(1024));return`${(e/Math.pow(1024,a)).toFixed(a?1:0)} ${["B","KB","MB","GB"][a]}`},y=e=>({hourly:(0,i.__)("every hour","optimazz"),twicedaily:(0,i.__)("twice a day","optimazz"),daily:(0,i.__)("once a day","optimazz")}[e]||(0,i.__)("twice a day","optimazz"));function w(){return f&&E?(0,a.createElement)("a",{className:"oz-cachewarn__link",href:E},(0,i.__)("Open Network Admin → Optimazz","optimazz")):null}function k(){return(0,a.createElement)("div",{className:"oz-cachewarn",role:"status"},(0,a.createElement)("span",{className:"dashicons dashicons-info-outline"}),(0,a.createElement)("div",{className:"oz-cachewarn__text"},(0,a.createElement)("strong",null,(0,i.__)("Page caching is off for this network","optimazz")),(0,a.createElement)("p",null,f?(0,i.__)("Turn it on for every site in Network Admin → Optimazz. Each site gets its own cache, and keeps its own exclusions here under Advanced Rules.","optimazz"):(0,i.__)("Your network administrator can turn it on for every site in Network Admin → Optimazz. All other optimizations — CSS, JavaScript, images, fonts and more — work normally on this site.","optimazz")),(0,a.createElement)(w,null)))}function C(){const e=u||{};return(0,a.createElement)("div",{className:"oz-cachewarn oz-cachewarn--ok",role:"status"},(0,a.createElement)("span",{className:"dashicons dashicons-yes-alt"}),(0,a.createElement)("div",{className:"oz-cachewarn__text"},(0,a.createElement)("strong",null,(0,i.__)("Page caching is on — managed for the whole network","optimazz")),(0,a.createElement)("p",null,(0,i.sprintf)(/* translators: 1: yes/no, 2: cache lifetime, 3: preload frequency. */ /* translators: 1: yes/no, 2: cache lifetime, 3: preload frequency. */ 2 (0,i.__)("Separate mobile cache: %1$s · Cache lifetime: %2$s · Automatic preload: %3$s.","optimazz"),z(e.cache_mobile)?(0,i.__)("yes","optimazz"):(0,i.__)("no","optimazz"),(t=e.lifespan_hours,null!==(o={0:(0,i.__)("never expires","optimazz"),12:(0,i.__)("12 hours","optimazz"),24:(0,i.__)("1 day","optimazz"),168:(0,i.__)("1 week","optimazz"),720:(0,i.__)("1 month","optimazz")}[Number(t)])&&void 0!==o?o:(0,i.sprintf)((0,i._n)("%d hour","%d hours",Number(t),"optimazz"),Number(t))),z(e.preload)?y(e.preload_interval):(0,i.__)("off","optimazz"))),(0,a.createElement)("p",null,(0,i.__)("This site has its own cache. Purge or preload it from the Dashboard or the Preload screen, and set its own exclusions under Advanced Rules.","optimazz")),(0,a.createElement)(w,null)));var t,o}const N=["cache","js","css","media","fonts","database","heartbeat","tweaks","cdn"],x=[{title:(0,i.__)("Content leftovers","optimazz"),desc:(0,i.__)("Old post data that piles up over time. Removing it is permanent, so keep anything you still need.","optimazz"),items:[{key:"revisions",label:(0,i.__)("Post revisions","optimazz"),help:(0,i.__)("Old copies of posts saved automatically. Deleting them keeps only your current content.","optimazz")},{key:"auto_drafts",label:(0,i.__)("Auto drafts","optimazz"),help:(0,i.__)("Empty drafts WordPress creates while you write. Safe to remove.","optimazz")},{key:"trashed_posts",label:(0,i.__)("Trashed posts","optimazz"),help:(0,i.__)("Posts in the trash that have not been permanently deleted yet.","optimazz")}]},{title:(0,i.__)("Comment clutter","optimazz"),desc:(0,i.__)("Spam and trashed comments you are never going to approve. Clearing them keeps the comments table light.","optimazz"),items:[{key:"spam_comments",label:(0,i.__)("Spam comments","optimazz"),help:(0,i.__)("Comments marked as spam. Removing them frees up the comments table.","optimazz")},{key:"trashed_comments",label:(0,i.__)("Trashed comments","optimazz"),help:(0,i.__)("Comments in the trash awaiting permanent deletion.","optimazz")}]},{title:(0,i.__)("Temporary data","optimazz"),desc:(0,i.__)("Short-lived cached values. They are safe to clear and are rebuilt automatically whenever a plugin needs them.","optimazz"),items:[{key:"expired_transients",label:(0,i.__)("Expired transients","optimazz"),help:(0,i.__)("Cached values that have already passed their expiry date.","optimazz")}]},{title:(0,i.__)("Table maintenance","optimazz"),desc:(0,i.__)("Defragment your tables to reclaim wasted space and trim database overhead.","optimazz"),items:[{key:"optimize_tables",label:(0,i.__)("Optimize database tables","optimazz"),help:(0,i.__)("Runs OPTIMIZE on your tables to defragment them and free wasted space.","optimazz")}]}],S=[{label:(0,i.__)("Do not limit","optimazz"),value:"default"},{label:(0,i.__)("Reduce activity","optimazz"),value:"reduce"},{label:(0,i.__)("Disable","optimazz"),value:"disable"}];function $({on:e}){return(0,a.createElement)("span",{className:"oz-pill "+(e?"is-on":"is-off")},(0,a.createElement)("i",null),e?(0,i.__)("Active","optimazz"):(0,i.__)("Off","optimazz"))}function T({icon:e,title:t,description:n,enabled:l,onToggle:i,children:s}){return(0,a.createElement)("section",{className:"oz-card "+(l?"is-enabled":"")},(0,a.createElement)("header",{className:"oz-card__head"},(0,a.createElement)("span",{className:`oz-card__icon dashicons dashicons-${e}`}),(0,a.createElement)("div",{className:"oz-card__title"},(0,a.createElement)("h2",null,t," ",(0,a.createElement)($,{on:l})),(0,a.createElement)("p",null,n)),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,checked:l,onChange:i})),l&&(0,a.createElement)("div",{className:"oz-card__body"},s))}function M({job:e,onStop:n,onClose:l,onToggleMin:s}){const r=(0,t.useRef)(null);if((0,t.useEffect)(()=>{r.current&&(r.current.scrollTop=r.current.scrollHeight)},[e?.logs?.length]),!e)return null;const c=e.status||(e.running?"running":"done"),m={stopped:(0,i.__)("Cancelled","optimazz"),error:(0,i.__)("Failed","optimazz")},d="running"===c||"done"===c?e.title:`${e.title} — ${m[c]}`;return(0,a.createElement)("div",{className:`oz-job is-${c} ${e.minimized?"is-min":""}`},(0,a.createElement)("div",{className:"oz-job__head"},"running"===c?(0,a.createElement)(o.Spinner,null):(0,a.createElement)("span",{className:`oz-job__icon-status oz-job__icon-status--${c} dashicons dashicons-${{done:"yes-alt",stopped:"dismiss",error:"warning"}[c]||"yes-alt"}`}),(0,a.createElement)("div",{className:"oz-job__title"},(0,a.createElement)("strong",null,d),(0,a.createElement)("span",null,e.minimized?`${e.pct}%${e.step?` · ${e.step}`:""}`:e.subtitle)),(0,a.createElement)("button",{className:"oz-job__icon","aria-label":e.minimized?(0,i.__)("Expand","optimazz"):(0,i.__)("Minimize","optimazz"),onClick:s},(0,a.createElement)("span",{className:"dashicons dashicons-"+(e.minimized?"editor-expand":"minus")})),(0,a.createElement)("button",{className:"oz-job__icon","aria-label":(0,i.__)("Close","optimazz"),onClick:l},(0,a.createElement)("span",{className:"dashicons dashicons-no-alt"}))),e.minimized?(0,a.createElement)("div",{className:"oz-job__bar "+(e.indeterminate&&e.running?"is-indeterminate":"")},(0,a.createElement)("div",{style:{width:`${e.pct}%`}})):(0,a.createElement)("div",{className:"oz-job__body"},(0,a.createElement)("div",{className:"oz-job__row"},(0,a.createElement)("span",null,e.label),(0,a.createElement)("strong",null,e.pct,"%")),(0,a.createElement)("div",{className:"oz-job__bar "+(e.indeterminate&&e.running?"is-indeterminate":"")},(0,a.createElement)("div",{style:{width:`${e.pct}%`}})),e.step&&(0,a.createElement)("div",{className:"oz-job__step"},(0,i.__)("Step","optimazz")," ",e.step),e.running&&(0,a.createElement)("button",{className:"oz-job__stop",onClick:n},(0,a.createElement)("span",{className:"dashicons dashicons-controls-pause"}),(0,i.__)("Stop","optimazz")),(0,a.createElement)("div",{className:"oz-job__log",ref:r},e.logs.map((e,t)=>{const o=e.level||"INFO";return(0,a.createElement)("div",{key:t,className:`oz-logline oz-logline--${o.toLowerCase()}`},(0,a.createElement)("span",{className:"oz-logline__tag"},o),(0,a.createElement)("span",{className:"oz-logline__text"},e.text))}))))}let B=0;function P({label:e,value:o,options:n,onChange:l,compact:i=!1,disabled:s=!1}){const[r,c]=(0,t.useState)(!1),m=Math.max(0,n.findIndex(e=>e.value===o)),[d,p]=(0,t.useState)(m),_=(0,t.useRef)(null),z=(0,t.useRef)("oz-sel-"+ ++B).current;(0,t.useEffect)(()=>{const e=e=>{_.current&&!_.current.contains(e.target)&&c(!1)};return document.addEventListener("mousedown",e),()=>document.removeEventListener("mousedown",e)},[]);const h=n.find(e=>e.value===o)||n[0],u=e=>{l(n[e].value),c(!1)};return(0,a.createElement)("div",{className:"components-base-control oz-select"+(i?" oz-select--compact":""),ref:_},(0,a.createElement)("span",{className:i?"screen-reader-text":"components-base-control__label",id:`${z}-label`},e),(0,a.createElement)("button",{type:"button",className:"oz-select__btn",disabled:s,role:"combobox","aria-haspopup":"listbox","aria-expanded":r,"aria-labelledby":`${z}-label`,"aria-activedescendant":r?`${z}-opt-${d}`:void 0,onClick:()=>c(e=>!e),onKeyDown:e=>{r?"Escape"===e.key||"Tab"===e.key?c(!1):"ArrowDown"===e.key?(e.preventDefault(),p(e=>Math.min(n.length-1,e+1))):"ArrowUp"===e.key?(e.preventDefault(),p(e=>Math.max(0,e-1))):"Home"===e.key?(e.preventDefault(),p(0)):"End"===e.key?(e.preventDefault(),p(n.length-1)):"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),u(d)):["ArrowDown","ArrowUp","Enter"," "].includes(e.key)&&(e.preventDefault(),p(m),c(!0))}},(0,a.createElement)("span",null,h?.label),(0,a.createElement)("span",{className:"dashicons dashicons-arrow-down-alt2"})),r&&(0,a.createElement)("ul",{className:"oz-select__menu",role:"listbox","aria-labelledby":`${z}-label`},n.map((e,t)=>(0,a.createElement)("li",{key:e.value,id:`${z}-opt-${t}`,role:"option","aria-selected":e.value===o,className:`oz-select__item ${e.value===o?"is-selected":""} ${t===d?"is-active":""}`,onMouseEnter:()=>p(t),onClick:()=>u(t)},(0,a.createElement)("span",null,e.label),e.value===o&&(0,a.createElement)("span",{className:"dashicons dashicons-yes"})))))}const O={dashboard:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"M3 9.6 12 3l9 6.6"}),(0,a.createElement)("path",{d:"M5 9.1V20h5v-5h4v5h5V9.1"})),cache:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"M12 3 2.5 8 12 13l9.5-5L12 3Z"}),(0,a.createElement)("path",{d:"M2.5 12 12 17l9.5-5"})),files:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"m9 8-4 4 4 4"}),(0,a.createElement)("path",{d:"m15 8 4 4-4 4"})),media:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("rect",{x:"3",y:"4",width:"18",height:"16",rx:"2"}),(0,a.createElement)("circle",{cx:"8.5",cy:"9.5",r:"1.5"}),(0,a.createElement)("path",{d:"m21 16-5-5-6 6"})),fonts:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"M5 6V4h14v2"}),(0,a.createElement)("path",{d:"M12 4v16"}),(0,a.createElement)("path",{d:"M9 20h6"})),preload:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"M21 12a9 9 0 1 1-2.6-6.4"}),(0,a.createElement)("path",{d:"M21 4v5h-5"})),advanced:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"M4 8h9"}),(0,a.createElement)("circle",{cx:"16",cy:"8",r:"2.1"}),(0,a.createElement)("path",{d:"M19 8h1"}),(0,a.createElement)("path",{d:"M4 16h1"}),(0,a.createElement)("circle",{cx:"8",cy:"16",r:"2.1"}),(0,a.createElement)("path",{d:"M11 16h9"})),database:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("ellipse",{cx:"12",cy:"5",rx:"8",ry:"3"}),(0,a.createElement)("path",{d:"M4 5v14c0 1.6 3.6 3 8 3s8-1.4 8-3V5"}),(0,a.createElement)("path",{d:"M4 12c0 1.6 3.6 3 8 3s8-1.4 8-3"})),heartbeat:(0,a.createElement)("path",{d:"M3 12h4l2 6 4-14 2 8h6"}),tweaks:(0,a.createElement)(a.Fragment,null,(0,a.createElement)("path",{d:"m12 4 1.5 4.5L18 10l-4.5 1.5L12 16l-1.5-4.5L6 10l4.5-1.5Z"}),(0,a.createElement)("path",{d:"M18.5 15v3"}),(0,a.createElement)("path",{d:"M17 16.5h3"})),cdn:(0,a.createElement)("path",{d:"M17 18H7a4 4 0 0 1-.3-8A6 6 0 0 1 18.3 9 3.5 3.5 0 0 1 17 18Z"}),tools:(0,a.createElement)("path",{d:"M14.5 5.5a4 4 0 0 0-5 5L4 16v4h4l5.5-5.5a4 4 0 0 0 5-5l-2.6 2.6-2-.4-.4-2 2.5-2.6Z"})};function R({name:e,paths:t}){return(0,a.createElement)("svg",{className:"oz-ico",viewBox:"0 0 24 24",width:"20",height:"20",fill:"none",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",focusable:"false"},t||O[e]||O.dashboard)}function D({ok:e,warn:t,label:o}){const n=e?"ok":t?"warn":"bad",l=e?"yes-alt":t?"warning":"dismiss";return(0,a.createElement)("li",{className:`oz-health__row is-${n}`},(0,a.createElement)("span",{className:`dashicons dashicons-${l}`}),(0,a.createElement)("span",null,o))}function F({label:e,help:t,value:o,options:n,onChange:l}){return(0,a.createElement)("div",{className:"components-base-control oz-pillgroup"},e&&(0,a.createElement)("span",{className:"components-base-control__label"},e),(0,a.createElement)("div",{className:"oz-pillgroup__row",role:"radiogroup","aria-label":e,onKeyDown:e=>{const a=n.findIndex(e=>e.value===o);"ArrowRight"===e.key||"ArrowDown"===e.key?(e.preventDefault(),l(n[Math.min(n.length-1,a+1)].value)):"ArrowLeft"!==e.key&&"ArrowUp"!==e.key||(e.preventDefault(),l(n[Math.max(0,a-1)].value))}},n.map(e=>{const t=e.value===o;return(0,a.createElement)("button",{key:String(e.value),type:"button",role:"radio","aria-checked":t,tabIndex:t?0:-1,className:"oz-pillbtn "+(t?"is-on":""),onClick:()=>l(e.value)},e.label)})),t&&(0,a.createElement)("span",{className:"components-base-control__help"},t))}function H({title:e,description:t,children:o}){return(0,a.createElement)("section",{className:"oz-panel"},(0,a.createElement)("header",{className:"oz-panel__head"},(0,a.createElement)("h2",null,e),t&&(0,a.createElement)("p",null,t)),(0,a.createElement)("div",{className:"oz-panel__body"},o))}function A({className:e="",items:t,nav:n,onNav:l,foot:s,topIcon:r,title:c,sub:m,dirty:d,saving:p,onSave:_,toast:z,onToastRemove:h,children:u,after:g}){return(0,a.createElement)("div",{className:`oz-shell ${e}`},(0,a.createElement)("aside",{className:"oz-sidebar"},(0,a.createElement)("div",{className:"oz-sidebar__brand"},window.optimazzData?.logoUrl?(0,a.createElement)("img",{className:"oz-logo-img",src:window.optimazzData.logoUrl,alt:"Optimazz"}):(0,a.createElement)("div",{className:"oz-sidebar__brand-fallback"},(0,a.createElement)("span",{className:"oz-logo dashicons dashicons-performance"}),(0,a.createElement)("strong",null,"Optimazz"))),(0,a.createElement)("nav",null,t.map(e=>(0,a.createElement)("button",{key:e.key,className:"oz-navitem "+(n===e.key?"is-active":""),onClick:()=>l(e.key)},(0,a.createElement)(R,{name:e.svg||e.key,paths:e.iconSvg}),(0,a.createElement)("span",{className:"oz-navitem__text"},(0,a.createElement)("strong",null,e.title),(0,a.createElement)("small",null,e.sub)),e.dot&&(0,a.createElement)("i",{className:"oz-navitem__dot"})))),(0,a.createElement)("div",{className:"oz-sidebar__foot"},s)),(0,a.createElement)("div",{className:"oz-content"},(0,a.createElement)("header",{className:"oz-topbar"},(0,a.createElement)("span",{className:`oz-topbar__icon dashicons dashicons-${r}`}),(0,a.createElement)("div",null,(0,a.createElement)("h1",null,c),(0,a.createElement)("p",null,m))),(0,a.createElement)("main",{className:"oz-main"},u)),(0,a.createElement)("div",{className:"oz-savebar "+(d?"is-dirty":"")},(0,a.createElement)("span",{className:"oz-savebar__hint"},d?(0,i.__)("You have unsaved changes","optimazz"):(0,i.__)("All changes saved","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-primary",isBusy:p,disabled:!d||p,onClick:_},(0,i.__)("Save changes","optimazz"))),z&&(0,a.createElement)("div",{className:"oz-toast"},(0,a.createElement)(o.Snackbar,{status:z.status,onRemove:h},z.text)),g)}const L=document.getElementById("optimazz-app");L&&(0,t.createRoot)(L).render((0,a.createElement)(function(){var e,n,s,c,f,E,w,$;const[B,O]=(0,t.useState)(m||{}),[L,W]=(0,t.useState)(m||{}),[j,I]=(0,t.useState)(null),[q,U]=(0,t.useState)(null),[J,G]=(0,t.useState)(!1),[V,K]=(0,t.useState)("dashboard"),[Y,Z]=(0,t.useState)(!1),[Q,X]=(0,t.useState)(null),[ee,ae]=(0,t.useState)(!(!window.optimazzData||!window.optimazzData.hasBackup)),te=(0,t.useRef)(null);(0,t.useEffect)(()=>{const e=e=>{const a=e&&e.detail&&e.detail.key;a&&K(a)};return window.addEventListener("optimazz:navigate",e),()=>window.removeEventListener("optimazz:navigate",e)},[]);const oe=JSON.stringify(B)!==JSON.stringify(L),ne=(0,t.useCallback)((e,a,t)=>O(o=>({...o,[e]:{...o[e],[a]:t}})),[]),le=(e,a)=>ne(e,a,!B[e]?.[a]),ie=(0,t.useCallback)(async()=>{try{I(await l()({url:`${r}/cache/stats`}))}catch(e){}},[]),[se,re]=(0,t.useState)(null),ce=(0,t.useCallback)(async()=>{try{re(await l()({url:`${r}/system?_=${Date.now()}`}))}catch(e){re({error:!0})}},[]);(0,t.useEffect)(()=>{"tools"!==V||se||ce()},[V,se,ce]);const[me,de]=(0,t.useState)(null),pe=(0,t.useCallback)(async()=>{try{de(await l()({url:`${r}/recommendations?_=${Date.now()}`}))}catch(e){de({error:!0})}},[]);(0,t.useEffect)(()=>{"dashboard"!==V||me||pe()},[V,me,pe]);const _e=(0,t.useCallback)(async()=>{G(!0);try{U(await l()({url:`${r}/cache/health`}))}catch(e){U({error:!0})}G(!1)},[]),ze=(0,t.useCallback)(async(e,a)=>{if(window.confirm(`${(0,i.__)("Deactivate","optimazz")} ${a}? ${(0,i.__)("Optimazz will keep handling caching.","optimazz")}`)){G(!0);try{const t=await l()({url:`${r}/cache/deactivate-conflict`,method:"POST",data:{plugin:e}});U(t),ue(`${a} ${(0,i.__)("deactivated — Optimazz is now the only cache.","optimazz")}`,"success")}catch(e){ue((0,i.__)("Could not deactivate the plugin.","optimazz"),"error")}G(!1)}},[]),he=(0,t.useCallback)(async()=>{if(window.confirm((0,i.__)("Make Optimazz the caching plugin? This replaces the current advanced-cache.php (from another caching plugin) and turns on WP_CACHE. Deactivate the other caching plugin afterwards to avoid conflicts.","optimazz"))){G(!0);try{const e=await l()({url:`${r}/cache/takeover`,method:"POST"});U(e),ue("hit"===e.live?(0,i.__)("Optimazz is now serving pages from cache.","optimazz"):e.dropin_ours?(0,i.__)("Drop-in installed. Preload the cache, then check again.","optimazz"):(0,i.__)("Could not fully take over — see the cache health details.","optimazz"),e.dropin_ours?"success":"error")}catch(e){ue((0,i.__)("Take-over failed. Check file permissions.","optimazz"),"error")}G(!1)}},[]);(0,t.useEffect)(()=>{ie()},[ie]);const ue=(e,a="success")=>{X({text:e,status:a}),window.clearTimeout(ue._t),ue._t=window.setTimeout(()=>X(null),3e3)};(0,t.useEffect)(()=>{try{const e=new URLSearchParams(window.location.search),a=e.get("optimazz_notice"),t={purged:(0,i.__)("Cache purged.","optimazz"),preloaded:(0,i.__)("Cache preload started in the background.","optimazz"),cleared:(0,i.__)("Cache cleared; preload started in the background.","optimazz")};if(a&&t[a]){ue(t[a]),e.delete("optimazz_notice");const o=e.toString();window.history.replaceState({},"",window.location.pathname+(o?`?${o}`:""))}window.optimazzData?.wpconfigUnwritable&&ue((0,i.__)("wp-config.php is not writable — add WP_CACHE manually to enable page caching.","optimazz"),"error")}catch(e){}},[]);const ge=async e=>{Z(!0);try{const a=await l()({url:`${r}/settings`,method:"POST",data:e});W(a.settings),O(a.settings),ae(!!a.hasBackup),de(null),ue((0,i.__)("Settings saved.","optimazz")),ie()}catch(e){ue(e.message||(0,i.__)("Save failed.","optimazz"),"error")}Z(!1)},be=async()=>{await l()({url:`${r}/cache/purge`,method:"POST"}),ue((0,i.__)("Cache purged.","optimazz")),ie()},fe=()=>Me("preload"),[Ee,ve]=(0,t.useState)(null),[ye,we]=(0,t.useState)(null),[ke,Ce]=(0,t.useState)(null),Ne=(0,t.useRef)(!1),xe=(0,t.useCallback)(async()=>{try{we(await l()({url:`${r}/media/stats`}))}catch(e){}},[]);(0,t.useEffect)(()=>{"media"!==V||ye||xe()},[V,ye,xe]);const Se={convert:{title:(0,i.__)("Converting images to WebP/AVIF","optimazz"),subtitle:(0,i.__)("Next-gen format generation","optimazz"),endpoint:`${r}/media/convert`,refresh:xe,summarize:e=>`${(0,i.__)("Generated","optimazz")} ${e.generated||0} ${(0,i.__)("files in this batch","optimazz")}${e.skipped?` · ${e.skipped} ${(0,i.__)("skipped","optimazz")}`:""}`},compress:{title:(0,i.__)("Compressing images","optimazz"),subtitle:(0,i.__)("Recompress, resize & strip metadata","optimazz"),endpoint:`${r}/media/optimize`,refresh:xe,summarize:e=>{const a=Math.max(0,(e.before||0)-(e.after||0)),t=e.skipped?` · ${e.skipped} ${(0,i.__)("skipped","optimazz")}`:"";return`${(0,i.__)("Processed","optimazz")} ${e.processed||0} — ${Pe(a)} ${(0,i.__)("saved","optimazz")}${t}`}},preload:{title:(0,i.__)("Preloading cache","optimazz"),subtitle:(0,i.__)("Warming pages","optimazz"),endpoint:`${r}/cache/preload`,refresh:ie,onBatch:ie,summarize:(e,a)=>`${(0,i.__)("Warmed","optimazz")} ${Math.min(e.next_offset||0,e.total||0)} / ${e.total||0} ${(0,i.__)("pages","optimazz")} · ${a.cached} ${(0,i.__)("cached","optimazz")}${a.missed?` · ${a.missed} ${(0,i.__)("not cached","optimazz")}`:""}`}},$e=e=>!e||e<1?"":e>=3600?`~${Math.round(e/3600)}h`:e>=60?`~${Math.round(e/60)}m`:`~${Math.round(e)}s`,Te=()=>l()({url:`${r}/media/job`,method:"DELETE"}).catch(()=>{}),Me=async(e,{startOffset:a=0,reconnect:t=!1}={})=>{const o=Se[e];if(!o||ke?.running)return;Ne.current=!1;const n=t?(0,i.__)("Resuming…","optimazz"):`${o.title}…`;Ce({title:o.title,subtitle:o.subtitle,pct:0,label:n,step:"",logs:[{level:"INFO",text:n}],running:!0,status:"running",minimized:!1});let s=a,r=0,c=0;const m={cached:0,missed:0,diagnosis:""},d=Date.now();try{for(;;){var p;if(Ne.current){await Te(),Ce(e=>e&&{...e,running:!1,status:"stopped",logs:[...e.logs,{level:"WARN",text:(0,i.__)("Stopped by user.","optimazz")}]});break}const a=s;let t;try{t=await l()({url:o.endpoint,method:"POST",data:{offset:s}})}catch(e){if(c++,c>20)throw e;if(s=a+1,Ce(e=>e&&{...e,logs:[...e.logs.slice(-60),{level:"WARN",text:(0,i.__)("A batch failed (likely one slow/large image); skipped it and continued.","optimazz")}]}),r&&s>=r){Ce(e=>e&&{...e,running:!1,status:"done",pct:100,logs:[...e.logs,{level:"DONE",text:(0,i.__)("Completed (some items skipped).","optimazz")}]});break}await new Promise(e=>setTimeout(e,600));continue}if(c=0,s=null!==(p=t.next_offset)&&void 0!==p?p:s+(t.processed||0),r=t.total||r,!t.done&&s<=a){await Te(),Ce(e=>e&&{...e,running:!1,status:"error",logs:[...e.logs,{level:"ERROR",text:(0,i.__)("Job stalled — no progress.","optimazz")}]});break}m.cached+=t.cached||0,m.missed+=t.missed||0,m.diagnosis=t.diagnosis||m.diagnosis;const n=r?Math.min(100,Math.round(s/r*100)):100,_=o.summarize(t,m),z=(Date.now()-d)/1e3,h=s>0?s/Math.max(z,.001):0,u=h>0&&r&&s<r?(r-s)/h:0,g=u?` · ${$e(u)} ${(0,i.__)("left","optimazz")}`:"";if(Ce(e=>e&&{...e,pct:n,label:_,step:r?`${Math.min(s,r)} / ${r}${g}`:"",logs:[...e.logs.slice(-60),...t.breakdown?[{level:"INFO",text:`${(0,i.sprintf)(/* translators: %d: number of pages to warm. */ /* translators: %d: number of pages to warm. */ 3 (0,i.__)("Found %d pages to warm:","optimazz"),t.total||0)} ${t.breakdown}.`}]:[],{level:"INFO",text:_}]}),o.onBatch&&o.onBatch(),t.unsupported){await Te(),Ce(e=>e&&{...e,running:!1,status:"error",logs:[...e.logs,{level:"ERROR",text:t.message||(0,i.__)("Image processing is unavailable on this server (no GD or ImageMagick). See Tools → Server Requirements.","optimazz")}]});break}if(t.blocked){await Te(),Ce(e=>e&&{...e,running:!1,status:"error",logs:[...e.logs,{level:"ERROR",text:(0,i.__)('Warming stopped: the server returned 403/429, so a firewall or security plugin is blocking the preload requests. Cached what we could; allow-list the "Optimazz-CacheWarmer" user-agent in your firewall or security plugin (e.g. All-In-One Security, Wordfence, Cloudflare), or slow warming with the optimazz_preload_delay_ms filter.',"optimazz")}]});break}if("preload"===e&&!t.done&&0===m.cached&&m.missed>=3){await Te(),Ce(e=>e&&{...e,running:!1,status:"error",label:(0,i.__)("Pages are not being cached","optimazz"),logs:[...e.logs,{level:"ERROR",text:`${(0,i.__)("Stopped: pages are loading but none are being saved to the cache.","optimazz")} ${m.diagnosis}`}]});break}if(t.done){const e=[{level:"DONE",text:(0,i.__)("Completed.","optimazz")}];m.missed&&e.unshift({level:"WARN",text:`${(0,i.sprintf)(/* translators: %d: number of pages. */ /* translators: %d: number of pages. */ 4 (0,i._n)("%d page was not cached.","%d pages were not cached.",m.missed,"optimazz"),m.missed)} ${m.diagnosis}`}),Ce(a=>a&&{...a,running:!1,status:"done",pct:100,logs:[...a.logs,...e]});break}}}catch(e){Ce(a=>a&&{...a,running:!1,status:"error",logs:[...a.logs,{level:"ERROR",text:e.message||"Error"}]})}(o.refresh||xe)()};(0,t.useEffect)(()=>{let e=!1;return(async()=>{try{const a=await l()({url:`${r}/media/job`});!e&&a&&a.running&&Se[a.type]&&Me(a.type,{startOffset:a.offset||0,reconnect:!0})}catch(e){}})(),()=>{e=!0}},[]);const Be=(0,t.useCallback)(async()=>{try{ve(await l()({url:`${r}/database/counts`}))}catch(e){}},[]);(0,t.useEffect)(()=>{"database"!==V||Ee||Be()},[V,Ee,Be]);const Pe=v,Oe=h?N:N.filter(e=>"cache"!==e),Re=Oe.filter(e=>B[e]?.enabled).length,De=B.cache||{},Fe=B.js||{},He=B.css||{},Ae=B.media||{},Le=B.fonts||{},We=B.database||{},je=B.heartbeat||{},Ie=B.tweaks||{},qe=B.cdn||{},Ue=_.find(e=>e.key===V),Je=[{on:!!De.enabled,w:30,label:(0,i.__)("Page caching","optimazz"),server:!0},{on:!(!Fe.enabled||!Fe.delay),w:18,label:(0,i.__)("Delay JavaScript","optimazz")},{on:!(!Fe.enabled||!Fe.defer),w:8,label:(0,i.__)("Defer JavaScript","optimazz")},{on:!(!He.enabled||!He.minify),w:10,label:(0,i.__)("Minify CSS","optimazz")},{on:!(!Ae.enabled||!Ae.lazy_load),w:12,label:(0,i.__)("Lazy-load images","optimazz")},{on:!(!Ae.enabled||!Ae.webp&&!Ae.webp_generate),w:8,label:(0,i.__)("Next-gen images","optimazz")},{on:!(!Le.enabled||!Le.display_swap),w:6,label:(0,i.__)("Font optimization","optimazz")},{on:!!De.preload,w:5,label:(0,i.__)("Cache preloading","optimazz"),server:!0},{on:!!je.enabled,w:3,label:(0,i.__)("Heartbeat control","optimazz")}],Ge=b?Je:Je.filter(e=>!0!==e.server),Ve=Ge.reduce((e,a)=>e+a.w,0)||1,Ke=Math.round(Ge.reduce((e,a)=>e+(a.on?a.w:0),0)/Ve*100),Ye=Ge.filter(e=>!e.on).sort((e,a)=>a.w-e.w)[0],Ze=Ke>=90?"A":Ke>=75?"B":Ke>=55?"C":Ke>=35?"D":"E",Qe=2*Math.PI*52,Xe=h&&(Fe.enabled||He.enabled||Ae.enabled)&&!De.enabled,ea=(0,a.createElement)(a.Fragment,null,Xe&&(0,a.createElement)("div",{className:"oz-cachewarn",role:"status"},(0,a.createElement)("span",{className:"dashicons dashicons-info-outline"}),(0,a.createElement)("div",{className:"oz-cachewarn__text"},(0,a.createElement)("strong",null,(0,i.__)("Turn on Page Cache for the real speed gain","optimazz")),(0,a.createElement)("p",null,(0,i.__)("File and media optimizations are on, but Page Cache is off — so the server rebuilds every page on every request. Without caching this can make a site feel slower, not faster, especially on mobile. Enable Page Cache to serve ready-made HTML.","optimazz"))),(0,a.createElement)(o.Button,{className:"oz-btn-primary",onClick:()=>K("cache")},(0,i.__)("Enable Page Cache","optimazz"))),(0,a.createElement)("div",{className:"oz-score"},(0,a.createElement)("div",{className:"oz-score__gauge"},(0,a.createElement)("svg",{viewBox:"0 0 120 120",width:"132",height:"132"},(0,a.createElement)("circle",{cx:"60",cy:"60",r:"52",fill:"none",stroke:"rgba(255,255,255,.25)",strokeWidth:"10"}),(0,a.createElement)("circle",{cx:"60",cy:"60",r:"52",fill:"none",stroke:"#fff",strokeWidth:"10",strokeLinecap:"round",strokeDasharray:Qe,strokeDashoffset:Qe-Qe*Ke/100,transform:"rotate(-90 60 60)",style:{transition:"stroke-dashoffset .6s ease"}}),(0,a.createElement)("text",{x:"60",y:"56",textAnchor:"middle",fontSize:"30",fontWeight:"800",fill:"#fff"},Ke),(0,a.createElement)("text",{x:"60",y:"78",textAnchor:"middle",fontSize:"12",fill:"rgba(255,255,255,.85)"},(0,i.__)("SCORE","optimazz"))),(0,a.createElement)("span",{className:"oz-score__grade"},Ze)),(0,a.createElement)("div",{className:"oz-score__body"},(0,a.createElement)("h2",null,(0,i.__)("Performance score","optimazz")),(0,a.createElement)("p",null,Ye?`${(0,i.__)("Biggest remaining win:","optimazz")} ${Ye.label} (+${Ye.w})`:(0,i.__)("Every optimization is enabled. Nicely done!","optimazz")),(0,a.createElement)("div",{className:"oz-score__chips"},Ge.map(e=>(0,a.createElement)("span",{key:e.label,className:"oz-chip "+(e.on?"is-on":"")},(0,a.createElement)("span",{className:"dashicons dashicons-"+(e.on?"yes-alt":"marker")}),e.label))))),(0,a.createElement)("div",{className:"oz-kpis"+(b?"":" oz-kpis--single")},b&&(0,a.createElement)("div",{className:"oz-kpi"},(0,a.createElement)("span",{className:"dashicons dashicons-database"}),(0,a.createElement)("strong",null,j?null!==(e=j.pages)&&void 0!==e?e:j.files:(0,a.createElement)(o.Spinner,null)),(0,a.createElement)("span",null,(0,i.__)("Cached pages","optimazz"))),b&&(0,a.createElement)("div",{className:"oz-kpi"},(0,a.createElement)("span",{className:"dashicons dashicons-chart-area"}),(0,a.createElement)("strong",null,j?Pe(j.bytes):(0,a.createElement)(o.Spinner,null)),(0,a.createElement)("span",null,(0,i.__)("Cache size","optimazz"))),(0,a.createElement)("div",{className:"oz-kpi"},(0,a.createElement)("span",{className:"dashicons dashicons-yes-alt"}),(0,a.createElement)("strong",null,Re,"/",Oe.length),(0,a.createElement)("span",null,(0,i.__)("Modules active","optimazz")))),me&&!me.error&&(me.items||[]).length>0&&(0,a.createElement)(H,{title:(0,i.__)("Recommendations","optimazz"),description:(0,i.__)("What to fix next to speed up your site — one click each.","optimazz")},(0,a.createElement)("ul",{className:"oz-recs"},(me.items||[]).map(e=>(0,a.createElement)("li",{key:e.id,className:`oz-rec oz-rec--${e.severity}`},(0,a.createElement)("span",{className:"oz-rec__dot dashicons "+("high"===e.severity?"dashicons-warning":"medium"===e.severity?"dashicons-info":"dashicons-marker")}),(0,a.createElement)("span",{className:"oz-rec__text"},(0,a.createElement)("strong",null,e.title),(0,a.createElement)("small",null,e.desc)),e.fix?(0,a.createElement)(o.Button,{className:"oz-btn-primary oz-rec__btn",isBusy:Y,onClick:()=>(async e=>{if(!e||!e.module||!e.key)return;const a={...B,[e.module]:{...B[e.module],[e.key]:e.value}};await ge(a),de(null)})(e.fix)},(0,i.__)("Fix it","optimazz")):e.link?(0,a.createElement)(o.Button,{className:"oz-btn-ghost oz-rec__btn",onClick:()=>K(e.link)},(0,i.__)("Open","optimazz")):null)))),me&&!me.error&&0===(me.items||[]).length&&(0,a.createElement)(H,{title:(0,i.__)("Recommendations","optimazz")},(0,a.createElement)("p",{className:"oz-recs-clear"},(0,a.createElement)("span",{className:"dashicons dashicons-yes-alt"}),(0,i.__)("Nice — everything we recommend is already on. Your site is well optimized.","optimazz"))),(0,a.createElement)("div",{className:"oz-quick"},b&&(0,a.createElement)(H,{title:(0,i.__)("Cache Files","optimazz"),description:(0,i.__)("Clear and preload all cache files.","optimazz")},(0,a.createElement)("div",{className:"oz-quick__actions"},(0,a.createElement)(o.Button,{className:"oz-btn-primary",icon:"trash",isBusy:!!ke?.running,onClick:async()=>{await be(),fe()}},(0,i.__)("Clear & Preload","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"update",onClick:fe},(0,i.__)("Preload","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost oz-btn--x",icon:"dismiss",onClick:be},(0,i.__)("Purge cache","optimazz"))),(0,a.createElement)("ul",{className:"oz-cachelist"},(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-media-text"}),(0,i.__)("Cached pages","optimazz"),(0,a.createElement)("strong",null,j?null!==(n=j.pages)&&void 0!==n?n:j.files:"—")),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-database"}),(0,i.__)("Page cache size","optimazz"),(0,a.createElement)("strong",null,j?Pe(j.bytes):"—")),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-admin-appearance"}),(0,i.__)("Optimized CSS","optimazz"),(0,a.createElement)("strong",null,j?`${j.css_files} (${Pe(j.css_bytes)})`:"—")),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-clock"}),(0,i.__)("Cache lifespan","optimazz"),(0,a.createElement)(P,{compact:!0,disabled:Y,label:(0,i.__)("Cache lifespan","optimazz"),value:Number(null!==(s=De.lifespan_hours)&&void 0!==s?s:0),options:(aa=[{value:0,label:(0,i.__)("Never expire","optimazz")},{value:12,label:(0,i.__)("12 hours","optimazz")},{value:24,label:(0,i.__)("1 day","optimazz")},{value:168,label:(0,i.__)("1 week","optimazz")},{value:720,label:(0,i.__)("1 month","optimazz")}],ta=Number(null!==(c=De.lifespan_hours)&&void 0!==c?c:0),oa=e=>(0,i.sprintf)(/* translators: %d: number of hours. */ /* translators: %d: number of hours. */ 5 (0,i._n)("%d hour","%d hours",e,"optimazz"),e),aa.some(e=>e.value===ta)?aa:[...aa,{value:ta,label:oa(ta)}].sort((e,a)=>e.value-a.value)),onChange:e=>ge({...B,cache:{...B.cache,lifespan_hours:e}})})),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-update"}),(0,a.createElement)("span",{className:"oz-row-label"},(0,i.__)("Auto-preload","optimazz"),(0,a.createElement)("small",null,j?j.preload&&j.next_preload?j.next_preload_overdue?(0,i.__)("next run: due now","optimazz"):`${(0,i.__)("next run in","optimazz")} ${j.next_preload}`:(0,i.__)("off","optimazz"):"")),(0,a.createElement)(P,{compact:!0,disabled:Y,label:(0,i.__)("Auto-preload frequency","optimazz"),value:De.preload?De.preload_interval||"twicedaily":"off",options:[{value:"off",label:(0,i.__)("Off","optimazz")},{value:"hourly",label:(0,i.__)("Every hour","optimazz")},{value:"twicedaily",label:(0,i.__)("Twice a day","optimazz")},{value:"daily",label:(0,i.__)("Once a day","optimazz")}],onChange:e=>{ge({...B,cache:{...B.cache,preload:"off"!==e,preload_interval:"off"===e?De.preload_interval||"twicedaily":e}})}})),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-backup"}),(0,i.__)("Last auto-preload","optimazz"),(0,a.createElement)("strong",null,j?j.last_preload?(0,i.sprintf)(/* translators: 1: time ago, e.g. "2 hours", 2: number of pages */ /* translators: 1: time ago, e.g. "2 hours", 2: number of pages */ 6 (0,i.__)("%1$s ago · %2$d pages","optimazz"),j.last_preload.ago,j.last_preload.warmed)+(j.last_preload.blocked?` · ${(0,i.__)("blocked by firewall","optimazz")}`:""):(0,i.__)("not yet","optimazz"):"—")))),(0,a.createElement)(H,{title:(0,i.__)("Module Status","optimazz"),description:(0,i.__)("Enable or disable any module right here.","optimazz")},(0,a.createElement)("ul",{className:"oz-status"},d.filter(e=>e.mod&&(h||"cache"!==e.mod)).map(e=>(0,a.createElement)("li",{key:e.key},(0,a.createElement)(R,{name:e.key}),(0,a.createElement)("button",{className:"oz-status__name",onClick:()=>K(e.key)},e.title),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,checked:!!B[e.mod]?.enabled,onChange:()=>le(e.mod,"enabled")}))),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-editor-code"}),(0,a.createElement)("button",{className:"oz-status__name",onClick:()=>K("files")},(0,i.__)("File Optimization","optimazz")),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,checked:!(!Fe.enabled&&!He.enabled),onChange:()=>{const e=!(Fe.enabled||He.enabled);ne("js","enabled",e),ne("css","enabled",e)}}))))));var aa,ta,oa;const na=h?(0,a.createElement)(T,{icon:"database",title:(0,i.__)("Page Cache","optimazz"),description:(0,i.__)("Serve static HTML instead of running PHP on every request.","optimazz"),enabled:!!De.enabled,onToggle:()=>le("cache","enabled")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Cache mobile devices separately","optimazz"),checked:!!De.cache_mobile,onChange:()=>le("cache","cache_mobile")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Cache logged-in users","optimazz"),help:(0,i.__)("Leave off unless you know what you are doing.","optimazz"),checked:!!De.cache_logged_in,onChange:()=>le("cache","cache_logged_in")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Browser cache headers","optimazz"),help:(0,i.__)("Add Expires / Cache-Control rules so repeat visits load CSS, JS, images and fonts from the browser cache. Apache and LiteSpeed only (written to .htaccess); ignored on Nginx.","optimazz"),checked:!!De.browser_cache,onChange:()=>le("cache","browser_cache")}),(0,a.createElement)(F,{label:(0,i.__)("Cache lifetime","optimazz"),help:(0,i.__)("How long a cached page is kept before it is refreshed. Pages are always purged immediately when their content changes.","optimazz"),value:null!==(f=De.lifespan_hours)&&void 0!==f?f:0,options:[{value:0,label:(0,i.__)("Never","optimazz")},{value:12,label:(0,i.__)("12 hours","optimazz")},{value:24,label:(0,i.__)("1 day","optimazz")},{value:168,label:(0,i.__)("1 week","optimazz")},{value:720,label:(0,i.__)("1 month","optimazz")}],onChange:e=>ne("cache","lifespan_hours",e)}),(0,a.createElement)("div",{className:"oz-health"},(0,a.createElement)("div",{className:"oz-health__head"},(0,a.createElement)("strong",null,(0,i.__)("Cache health","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost",isBusy:J,onClick:_e},(0,i.__)("Check now","optimazz"))),(0,a.createElement)("p",{className:"oz-health__hint"},(0,i.__)("Confirms caching is actually serving pages. If it is not, the plugin rebuilds every page on each request — which is slower, not faster.","optimazz")),q&&!q.error&&(0,a.createElement)("ul",{className:"oz-health__list"},(0,a.createElement)(D,{ok:"hit"===q.live,warn:"miss"===q.live,label:"hit"===q.live?(0,i.__)("Pages ARE being served from cache (HIT)","optimazz"):"miss"===q.live?(0,i.__)("Pages are NOT being served from cache (MISS) — see below","optimazz"):(0,i.__)("Live check unavailable","optimazz")}),(0,a.createElement)(D,{ok:q.dropin_installed&&q.dropin_ours,warn:q.conflict,label:q.conflict?q.conflict_name?`${(0,i.__)("advanced-cache.php belongs to","optimazz")} ${q.conflict_name} — ${(0,i.__)("take it over below","optimazz")}`:(0,i.__)("advanced-cache.php belongs to ANOTHER plugin — take it over below","optimazz"):q.dropin_ours?(0,i.__)("advanced-cache.php drop-in installed","optimazz"):(0,i.__)("advanced-cache.php drop-in is missing","optimazz")}),(0,a.createElement)(D,{ok:q.wp_cache,label:q.wp_cache?(0,i.__)("WP_CACHE is enabled in wp-config.php","optimazz"):(0,i.__)("WP_CACHE is not enabled in wp-config.php","optimazz")}),!q.wpconfig_writable&&(0,a.createElement)(D,{warn:!0,label:(0,i.__)("wp-config.php is not writable — add the WP_CACHE line manually (below)","optimazz")}),(0,a.createElement)(D,{ok:q.warmed>0,label:`${q.warmed} ${(0,i.__)("pages warmed in cache","optimazz")}`})),q&&!q.error&&(q.conflict||!q.wp_cache&&q.wpconfig_writable||!q.dropin_ours)&&q.enabled&&(0,a.createElement)("div",{className:"oz-health__fix"},(0,a.createElement)(o.Button,{className:"oz-btn-primary",isBusy:J,onClick:he},(0,i.__)("Take over caching","optimazz")),(0,a.createElement)("span",null,(0,i.__)("Make Optimazz the caching plugin — installs its drop-in and enables WP_CACHE.","optimazz"))),q&&!q.error&&Array.isArray(q.active_conflicts)&&q.active_conflicts.length>0&&(0,a.createElement)("div",{className:"oz-health__conflict"},q.active_conflicts.map(e=>(0,a.createElement)("div",{className:"oz-health__conflict-row",key:e.file},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,`${e.name} ${(0,i.__)("is still active and may reclaim caching. Deactivate it so Optimazz keeps serving the cache.","optimazz")}`),(0,a.createElement)(o.Button,{className:"oz-btn-danger",isBusy:J,onClick:()=>ze(e.file,e.name)},`${(0,i.__)("Deactivate","optimazz")} ${e.name}`)))),q&&!q.error&&!q.wp_cache&&!q.wpconfig_writable&&(0,a.createElement)("div",{className:"oz-health__manual"},(0,a.createElement)("p",null,(0,i.__)("wp-config.php is not writable. Add this line just after the opening <?php tag:","optimazz")),(0,a.createElement)("code",null,"define( 'WP_CACHE', true );")),q&&q.error&&(0,a.createElement)("p",{className:"oz-health__err"},(0,i.__)("Could not run the check. Try reloading the page.","optimazz")))):g?(0,a.createElement)(C,null):(0,a.createElement)(k,null),la=(0,a.createElement)(a.Fragment,null,(0,a.createElement)(T,{icon:"editor-code",title:(0,i.__)("JavaScript","optimazz"),description:(0,i.__)("Defer and delay scripts to cut Total Blocking Time.","optimazz"),enabled:!!Fe.enabled,onToggle:()=>le("js","enabled")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Minify JavaScript","optimazz"),help:(0,i.__)("Strip comments and whitespace from inline scripts. Line breaks are kept for safety; test your site after enabling.","optimazz"),checked:!!Fe.minify,onChange:()=>le("js","minify")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Defer JavaScript","optimazz"),help:(0,i.__)("Load scripts without blocking page render.","optimazz"),checked:!!Fe.defer,onChange:()=>le("js","defer")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Delay JS until interaction","optimazz"),help:(0,i.__)("Run scripts only after the first user interaction.","optimazz"),checked:!!Fe.delay,onChange:()=>le("js","delay")}),Fe.delay&&(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Delay all scripts","optimazz"),help:(0,i.__)("Delay every script until interaction. jQuery, your theme, and Elementor are always kept safe. Still, on sites where content is rendered by JavaScript this can delay the visible page — test before keeping it on.","optimazz"),checked:!!Fe.delay_all,onChange:()=>le("js","delay_all")}),Fe.delay&&Fe.delay_all&&(0,a.createElement)("div",{className:"oz-dbwarn"},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,(0,i.__)('Delaying all scripts can slow the first paint on JavaScript-rendered sites (Elementor, sliders). If your PageSpeed FCP/LCP get worse, switch to "Delay selected" and delay only tracking scripts.',"optimazz"))),Fe.delay&&!Fe.delay_all&&(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Delay these scripts","optimazz"),help:(0,i.__)("Only scripts matching these keywords are delayed. Match by filename, handle or URL — e.g. gtag, analytics, facebook.","optimazz"),placeholder:(0,i.__)("e.g. gtag, analytics","optimazz"),value:Fe.delay_list||[],onChange:e=>ne("js","delay_list",e)}),(Fe.defer||Fe.delay)&&(0,a.createElement)("div",{className:"oz-fieldgroup"},(0,a.createElement)("h4",{className:"oz-fieldgroup__title"},(0,i.__)("Exclude specific scripts","optimazz")),(0,a.createElement)("p",{className:"oz-fieldgroup__desc"},(0,i.__)("If a script breaks, exclude it from just one optimization instead of both. Match by filename, script handle, or part of the URL.","optimazz")),Fe.defer&&(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never defer","optimazz"),help:(0,i.__)("Keep loading these render-blocking, but still allow delay. Use for scripts that must run during page load.","optimazz"),placeholder:(0,i.__)("e.g. jquery.min.js","optimazz"),value:Fe.no_defer||[],onChange:e=>ne("js","no_defer",e)}),Fe.delay&&(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never delay","optimazz"),help:(0,i.__)("Run these before any interaction, but still allow defer. Use for sliders, above-the-fold, or consent scripts.","optimazz"),placeholder:(0,i.__)("e.g. slider, swiper","optimazz"),value:Fe.no_delay||[],onChange:e=>ne("js","no_delay",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never optimize","optimazz"),help:(0,i.__)("Skip both defer and delay — leave these scripts exactly as they are.","optimazz"),placeholder:(0,i.__)("e.g. recaptcha","optimazz"),value:Fe.exclusions||[],onChange:e=>ne("js","exclusions",e)}))),(0,a.createElement)(T,{icon:"admin-appearance",title:(0,i.__)("CSS","optimazz"),description:(0,i.__)("Minify, combine, and remove render-blocking stylesheets.","optimazz"),enabled:!!He.enabled,onToggle:()=>le("css","enabled")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Minify CSS","optimazz"),checked:!!He.minify,onChange:()=>le("css","minify")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Combine CSS files","optimazz"),help:(0,i.__)("Merge stylesheets to reduce requests.","optimazz"),checked:!!He.combine,onChange:()=>le("css","combine")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Load CSS asynchronously","optimazz"),help:(0,i.__)("Removes render-blocking CSS. Paste your above-the-fold (critical) CSS below, or the page may briefly flash unstyled.","optimazz"),checked:!!He.async_load,onChange:()=>le("css","async_load")}),He.async_load&&(0,a.createElement)(a.Fragment,null,!(He.critical_css||"").trim()&&(0,a.createElement)("div",{className:"oz-dbwarn"},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,(0,i.__)("No critical CSS provided. Async loading is on, so visitors may see a flash of unstyled content until you paste critical CSS below.","optimazz"))),(0,a.createElement)(o.TextareaControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Critical CSS (above-the-fold)","optimazz"),help:(0,i.__)("Paste the above-the-fold CSS to inline. Generate it with a free tool such as a critical-CSS generator. Leave empty to disable inlining.","optimazz"),value:He.critical_css||"",onChange:e=>ne("css","critical_css",e),rows:6})),(0,a.createElement)("div",{className:"oz-subhead"},(0,i.__)("Remove unused CSS","optimazz")),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Remove block-editor CSS on the front end","optimazz"),help:(0,i.__)("Dequeues WordPress block-library and global styles. Turn on only if your front end does NOT render Gutenberg blocks (e.g. a page-builder or classic theme). Big byte saving when it applies.","optimazz"),checked:!!He.remove_block_css,onChange:()=>le("css","remove_block_css")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Load WooCommerce CSS only on shop pages","optimazz"),help:(0,i.__)("Dequeues WooCommerce styles on non-shop pages (kept on cart, checkout, account, product and shop). Turn off if you show Woo products or widgets on other pages.","optimazz"),checked:!!He.remove_woo_css,onChange:()=>le("css","remove_woo_css")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Load Easy Digital Downloads CSS only on EDD pages","optimazz"),help:(0,i.__)("Dequeues EDD styles on non-EDD pages (kept on checkout, purchase-confirmation, purchase history and download archives). Turn off if you show download or purchase links elsewhere.","optimazz"),checked:!!He.remove_edd_css,onChange:()=>le("css","remove_edd_css")}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Excluded CSS files (keywords)","optimazz"),value:He.exclusions||[],onChange:e=>ne("css","exclusions",e)}))),ia=(0,a.createElement)(T,{icon:"format-image",title:(0,i.__)("Image Optimization","optimazz"),description:(0,i.__)("Lazy-load, prevent layout shift, serve next-gen formats, and lighten media.","optimazz"),enabled:!!Ae.enabled,onToggle:()=>le("media","enabled")},ye&&!ye.can_process&&(0,a.createElement)("div",{className:"oz-notice oz-notice--error"},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,(0,i.__)('This server has no GD or ImageMagick extension, so image compression and WebP/AVIF cannot run — every image will be skipped. Ask your host to enable "php-gd" (or "php-imagick"). See Tools → Server Requirements.',"optimazz"))),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Lazy-load images","optimazz"),help:(0,i.__)("Load off-screen images only when scrolled into view.","optimazz"),checked:!!Ae.lazy_load,onChange:()=>le("media","lazy_load")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Lazy-load iframes","optimazz"),checked:!!Ae.lazy_iframes,onChange:()=>le("media","lazy_iframes")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Lazy-load CSS background images","optimazz"),help:(0,i.__)("Defer inline background-image elements (test with page builders).","optimazz"),checked:!!Ae.lazy_bg,onChange:()=>le("media","lazy_bg")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Replace YouTube embeds with a preview","optimazz"),help:(0,i.__)("Load the video iframe only when the visitor clicks play.","optimazz"),checked:!!Ae.youtube_preview,onChange:()=>le("media","youtube_preview")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Add missing image dimensions","optimazz"),help:(0,i.__)("Set width/height to prevent layout shift (CLS).","optimazz"),checked:!!Ae.add_dimensions,onChange:()=>le("media","add_dimensions")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Preload the LCP image","optimazz"),help:(0,i.__)("Preload the first/hero image with high priority.","optimazz"),checked:!!Ae.preload_lcp,onChange:()=>le("media","preload_lcp")}),Ae.lazy_load&&(0,a.createElement)(F,{label:(0,i.__)("Keep first images eager (LCP)","optimazz"),help:(0,i.__)("Do not lazy-load the first N images, to protect LCP.","optimazz"),value:null!==(E=Ae.skip_first)&&void 0!==E?E:1,onChange:e=>ne("media","skip_first",e),options:[0,1,2,3,4,5].map(e=>({label:String(e),value:e}))}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Serve WebP via <picture>","optimazz"),help:(0,i.__)("Deliver WebP to supporting browsers, original as fallback.","optimazz"),checked:!!Ae.webp,onChange:()=>le("media","webp")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Generate WebP/AVIF on upload","optimazz"),help:ye&&!ye.can_webp?(0,i.__)("WebP generation is not available on this server.","optimazz"):(0,i.__)("Auto-create next-gen copies for newly uploaded images.","optimazz"),checked:!!Ae.webp_generate,onChange:()=>le("media","webp_generate")}),ye?.can_avif&&(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Also generate/serve AVIF","optimazz"),help:(0,i.__)("Smaller than WebP; served first when supported.","optimazz"),checked:!!Ae.avif,onChange:()=>le("media","avif")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Compress & resize on upload","optimazz"),help:(0,i.__)("Recompress, cap dimensions, and strip metadata from new uploads.","optimazz"),checked:!!Ae.compress,onChange:()=>le("media","compress")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Convert opaque PNG to JPEG","optimazz"),help:(0,i.__)("Re-save non-transparent PNGs as much smaller JPEGs during bulk optimization.","optimazz"),checked:!!Ae.png_to_jpg,onChange:()=>le("media","png_to_jpg")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Keep a backup of originals","optimazz"),help:(0,i.__)("Copy originals to uploads/optimazz-backup before optimizing.","optimazz"),checked:!!Ae.backup_originals,onChange:()=>le("media","backup_originals")}),(0,a.createElement)(o.RangeControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Image quality","optimazz"),value:null!==(w=Ae.quality)&&void 0!==w?w:82,onChange:e=>ne("media","quality",e),min:40,max:100,help:(0,i.__)("Used for compression and WebP/AVIF generation.","optimazz")}),(0,a.createElement)(o.RangeControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Max image width (px)","optimazz"),value:null!==($=Ae.max_width)&&void 0!==$?$:2560,onChange:e=>ne("media","max_width",e),min:800,max:4e3,step:80}),(0,a.createElement)("div",{className:"oz-convert"},(0,a.createElement)("div",{className:"oz-convert__info"},(0,a.createElement)("strong",null,(0,i.__)("WebP coverage","optimazz")),ye?(0,a.createElement)("span",null,ye.converted," / ",ye.images," ",(0,i.__)("images converted","optimazz")):(0,a.createElement)("span",null,(0,i.__)("Loading…","optimazz"))),(0,a.createElement)(o.Button,{className:"oz-btn-primary",icon:"images-alt2",isBusy:ke?.running,disabled:ye&&!ye.can_webp||ke?.running,onClick:()=>Me("convert")},(0,i.__)("Convert to WebP","optimazz"))),(0,a.createElement)("div",{className:"oz-convert"},(0,a.createElement)("div",{className:"oz-convert__info"},(0,a.createElement)("strong",null,(0,i.__)("Bulk compression","optimazz")),ye?(0,a.createElement)("span",null,ye.optimized," ",(0,i.__)("optimized","optimazz")," · ",Pe(ye.saved)," ",(0,i.__)("saved","optimazz")):(0,a.createElement)("span",null,(0,i.__)("Loading…","optimazz"))),(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"filter",isBusy:ke?.running,disabled:ke?.running,onClick:()=>Me("compress")},(0,i.__)("Compress existing images","optimazz")))),sa=b?(0,a.createElement)(H,{title:(0,i.__)("Preload Cache","optimazz"),description:(0,i.__)("Warm the cache automatically so visitors never hit a cold page.","optimazz")},h?(0,a.createElement)(a.Fragment,null,(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Activate preloading","optimazz"),help:(0,i.__)("Warms the cache on a schedule and right after every full purge (for example after changing a setting). Warming follows the sitemap — home, pages, posts, custom post types, then the busiest category and tag archives — most-important first, for both mobile and desktop.","optimazz"),checked:!!De.preload,onChange:()=>le("cache","preload")}),(0,a.createElement)(o.SelectControl,{__nextHasNoMarginBottom:!0,__next40pxDefaultSize:!0,label:(0,i.__)("Automatic preload","optimazz"),help:(0,i.__)("How often to re-warm the whole site. Choose “Every hour” if your content changes often or you use a short cache lifespan.","optimazz"),value:De.preload_interval||"twicedaily",disabled:!De.preload,options:[{label:(0,i.__)("Every hour","optimazz"),value:"hourly"},{label:(0,i.__)("Twice a day","optimazz"),value:"twicedaily"},{label:(0,i.__)("Once a day","optimazz"),value:"daily"}],onChange:e=>ne("cache","preload_interval",e)})):(0,a.createElement)("p",{className:"oz-muted"},z(u?.preload)?(0,i.sprintf)(/* translators: %s: preload frequency, e.g. "twice a day". */ /* translators: %s: preload frequency, e.g. "twice a day". */ 7 (0,i.__)("Automatic preload runs %s for this site (set for the whole network in Network Admin → Optimazz).","optimazz"),y(u?.preload_interval)):(0,i.__)("Automatic preload is off for the network. You can still preload this site now.","optimazz")),j&&j.wp_cron_disabled&&(0,a.createElement)("div",{className:"oz-notice oz-notice--warn",style:{marginTop:12}},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,(0,i.__)("WP-Cron is disabled on this site (DISABLE_WP_CRON). Scheduled preloads only run if a server cron job calls wp-cron.php — ask your host to set one up (e.g. every 5 minutes).","optimazz"))),(0,a.createElement)("div",{className:"oz-inline-actions"},(0,a.createElement)(o.Button,{className:"oz-btn-primary",icon:"update",isBusy:!!ke?.running,onClick:fe},(0,i.__)("Preload now","optimazz")))):(0,a.createElement)(k,null),ra=b?(0,a.createElement)(H,{title:(0,i.__)("Advanced Rules","optimazz"),description:(0,i.__)("Fine-tune which requests are cached.","optimazz")},(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never cache these URLs (contains)","optimazz"),value:De.excluded_uris||[],onChange:e=>ne("cache","excluded_uris",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never cache when these cookies are set","optimazz"),value:De.excluded_cookies||[],onChange:e=>ne("cache","excluded_cookies",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Never cache for these user agents","optimazz"),value:De.excluded_user_agents||[],onChange:e=>ne("cache","excluded_user_agents",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Ignore these query arguments","optimazz"),value:De.ignored_query_args||[],onChange:e=>ne("cache","ignored_query_args",e)})):(0,a.createElement)(k,null),ca=!(!B.tools||!B.tools.safe_mode),ma=(0,a.createElement)(a.Fragment,null,(0,a.createElement)(H,{title:(0,i.__)("Safe Mode","optimazz"),description:(0,i.__)("If a theme or page builder looks broken after optimizing, turn this on. It pauses the riskiest optimizations — combine CSS, async CSS, remove block CSS and JavaScript delay — while keeping caching, minify, defer and lazy-load. Your saved settings are not changed; they simply pause until you turn Safe Mode off.","optimazz")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Pause risky optimizations (Safe Mode)","optimazz"),checked:ca,onChange:e=>{const a={...B,tools:{...B.tools||{},safe_mode:e}};O(a),ge(a)}}),ca&&(0,a.createElement)("div",{className:"oz-notice oz-notice--warn",style:{marginTop:12}},(0,a.createElement)("span",{className:"dashicons dashicons-shield"}),(0,a.createElement)("span",null,(0,i.__)("Safe Mode is ON — combine/async CSS, remove-block-CSS and JavaScript delay are paused site-wide. Turn it off to resume full optimization.","optimazz"))),(0,a.createElement)("div",{className:"oz-inline-actions",style:{marginTop:14}},(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"undo",disabled:!ee,onClick:async()=>{try{const e=await l()({url:`${r}/settings/revert`,method:"POST"});if("nothing"===e.message)return ae(!1),void ue((0,i.__)("Nothing to undo.","optimazz"),"error");W(e.settings),O(e.settings),ae(!!e.hasBackup),de(null),ue((0,i.__)("Reverted to the previous settings.","optimazz")),ie()}catch(e){ue(e.message||(0,i.__)("Undo failed.","optimazz"),"error")}}},(0,i.__)("Undo last change","optimazz")),(0,a.createElement)("span",{className:"oz-muted",style:{fontSize:12}},ee?(0,i.__)("Restores the settings from just before your last save, reset or Safe Mode toggle.","optimazz"):(0,i.__)("Nothing to undo yet — this becomes available after your next change.","optimazz")))),(0,a.createElement)(H,{title:(0,i.__)("Server Requirements","optimazz"),description:(0,i.__)("What your host supports. Warnings here explain why a feature (like WebP/AVIF) may be unavailable or slow.","optimazz")},!se&&(0,a.createElement)("p",{className:"oz-muted"},(0,i.__)("Checking your server…","optimazz")),se&&se.error&&(0,a.createElement)("p",{className:"oz-muted"},(0,i.__)("Could not read the server report.","optimazz")),se&&!se.error&&(0,a.createElement)("ul",{className:"oz-reqs"},(se.checks||[]).map((e,t)=>(0,a.createElement)("li",{key:t,className:`oz-req oz-req--${e.status}`},(0,a.createElement)("span",{className:"oz-req__dot dashicons "+("ok"===e.status?"dashicons-yes-alt":"warn"===e.status?"dashicons-warning":"info"===e.status?"dashicons-minus":"dashicons-dismiss")}),(0,a.createElement)("span",{className:"oz-req__label"},e.label),(0,a.createElement)("span",{className:"oz-req__value"},e.value),e.note&&(0,a.createElement)("span",{className:"oz-req__note"},e.note)))),(0,a.createElement)("div",{className:"oz-inline-actions",style:{marginTop:14}},(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"update",isBusy:!se,onClick:()=>{re(null),ce()}},(0,i.__)("Re-check","optimazz")))),(0,a.createElement)(H,{title:(0,i.__)("Import / Export Settings","optimazz"),description:(0,i.__)("Back up your configuration or copy it to another site.","optimazz")},(0,a.createElement)("div",{className:"oz-inline-actions"},(0,a.createElement)(o.Button,{className:"oz-btn-primary",icon:"download",onClick:()=>{const e=new window.Blob([JSON.stringify(B,null,2)],{type:"application/json"}),a=document.createElement("a");a.href=URL.createObjectURL(e),a.download="optimazz-settings.json",a.click(),URL.revokeObjectURL(a.href)}},(0,i.__)("Export settings","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"upload",onClick:()=>te.current?.click()},(0,i.__)("Import settings","optimazz")),(0,a.createElement)("input",{ref:te,type:"file",accept:"application/json",style:{display:"none"},onChange:e=>{const a=e.target.files?.[0];if(!a)return;const t=new window.FileReader;t.onload=()=>{try{const e=JSON.parse(t.result);ge(e)}catch(e){ue((0,i.__)("Invalid settings file.","optimazz"),"error")}},t.readAsText(a),e.target.value=""}}))),(0,a.createElement)(H,{title:(0,i.__)("Reset","optimazz"),description:(0,i.__)("Restore every module to its default configuration. This cannot be undone.","optimazz")},(0,a.createElement)("div",{className:"oz-inline-actions"},(0,a.createElement)(o.Button,{className:"oz-btn-danger",icon:"image-rotate",onClick:async()=>{if(window.confirm((0,i.__)('Reset all Optimazz settings to defaults? You can undo this with "Undo last change".',"optimazz")))try{const e=await l()({url:`${r}/settings/reset`,method:"POST"});W(e.settings),O(e.settings),ae(!!e.hasBackup),ue((0,i.__)("Settings reset to defaults.","optimazz")),ie()}catch(e){ue(e.message||(0,i.__)("Reset failed.","optimazz"),"error")}}},(0,i.__)("Reset all settings to defaults","optimazz"))))),da=(0,a.createElement)(T,{icon:"editor-textcolor",title:(0,i.__)("Fonts","optimazz"),description:(0,i.__)("Swap, preconnect, and preload fonts to remove render-blocking requests.","optimazz"),enabled:!!Le.enabled,onToggle:()=>le("fonts","enabled")},(0,a.createElement)(o.SelectControl,{__nextHasNoMarginBottom:!0,__next40pxDefaultSize:!0,label:(0,i.__)("Font loading (font-display)","optimazz"),help:(0,i.__)("Optional = no layout shift (best for CLS): the web font is skipped on the first uncached load and used once cached. Swap = text stays visible but the web font swaps in and can shift layout (higher CLS). Block = brief invisible text, then the web font.","optimazz"),value:Le.font_display||(Le.display_swap,"swap"),options:[{label:(0,i.__)("Optional — no layout shift (recommended for CLS)","optimazz"),value:"optional"},{label:(0,i.__)("Swap — always show text (may shift layout)","optimazz"),value:"swap"},{label:(0,i.__)("Block — brief hidden text, then web font","optimazz"),value:"block"}],onChange:e=>ne("fonts","font_display",e)}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Combine Google Fonts into one request","optimazz"),help:(0,i.__)("Page builders often load each Google Font family separately. This merges them into a single request.","optimazz"),checked:!!Le.combine_google,onChange:e=>ne("fonts","combine_google",e)}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Load only the font weights your pages use","optimazz"),help:(0,i.__)("Drops unused weights (e.g. 100, 200, 900) and italics your pages never use — often cutting font CSS by more than half. Weights 400 and 700 are always kept. Turn off if some text looks thinner or bolder than intended.","optimazz"),checked:!!Le.trim_weights,disabled:!Le.combine_google,onChange:e=>ne("fonts","trim_weights",e)}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Load Google Fonts without blocking the page","optimazz"),help:(0,i.__)("The page appears right away in a fallback font and switches to your Google Font as soon as it arrives, instead of waiting for Google first. A big win for the first paint on mobile. Applies when font display is Swap or Fallback; with Optional the stylesheet keeps loading first so text never shifts.","optimazz"),checked:!1!==Le.async_google,disabled:!Le.combine_google,onChange:e=>ne("fonts","async_google",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Preload these font files (URLs)","optimazz"),value:Le.preload||[],onChange:e=>ne("fonts","preload",e)})),pa=e=>{var a;if(!Ee||void 0===Ee[e.key])return e.help;const t=null!==(a=Ee[e.key])&&void 0!==a?a:0; 8 /* translators: %d: number of items found in the database. */return`${(0,i.sprintf)((0,i._n)("%d item in your database.","%d items in your database.",t,"optimazz"),t)} ${e.help}`},_a=(0,a.createElement)(H,{title:(0,i.__)("Database Optimization","optimazz"),description:(0,i.__)("Reduce bloat and reclaim space. Once a cleanup runs it cannot be undone, so back up your database first.","optimazz")},x.map(e=>(0,a.createElement)("div",{key:e.title,className:"oz-dbgroup"},(0,a.createElement)("h3",{className:"oz-dbgroup__title"},e.title),(0,a.createElement)("p",{className:"oz-dbgroup__desc"},e.desc),e.items.map(e=>{var t;return(0,a.createElement)(o.ToggleControl,{key:e.key,__nextHasNoMarginBottom:!0,label:Ee?`${e.label} (${null!==(t=Ee[e.key])&&void 0!==t?t:0})`:e.label,help:pa(e),checked:!!We[e.key],onChange:()=>le("database",e.key)})}))),(0,a.createElement)("div",{className:"oz-dbgroup"},(0,a.createElement)("h3",{className:"oz-dbgroup__title"},(0,i.__)("Set it and forget it","optimazz")),(0,a.createElement)("p",{className:"oz-dbgroup__desc"},(0,i.__)("Let Optimazz repeat the cleanups you picked above on a schedule, so your database stays lean without you lifting a finger.","optimazz")),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Schedule automatic cleanup","optimazz"),help:(0,i.__)("Uses WP-Cron to repeat the cleanups you enabled above.","optimazz"),checked:!!We.enabled,onChange:()=>le("database","enabled")}),We.enabled&&(0,a.createElement)(F,{label:(0,i.__)("Cleanup frequency","optimazz"),value:We.schedule||"off",onChange:e=>ne("database","schedule",e),options:[{label:(0,i.__)("Off","optimazz"),value:"off"},{label:(0,i.__)("Daily","optimazz"),value:"daily"},{label:(0,i.__)("Weekly","optimazz"),value:"weekly"},{label:(0,i.__)("Monthly","optimazz"),value:"monthly"}]})),(0,a.createElement)("div",{className:"oz-dbwarn"},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("span",null,(0,i.__)("Back up your database before you run a cleanup — a database optimization cannot be undone.","optimazz"))),(0,a.createElement)("div",{className:"oz-inline-actions"},(0,a.createElement)(o.Button,{className:"oz-btn-primary",icon:"trash",isBusy:!!ke?.running,onClick:async()=>{if(ke?.running)return;const e=(0,i.__)("Cleaning and optimizing database…","optimazz");Ce({title:(0,i.__)("Optimizing database","optimazz"),subtitle:(0,i.__)("Cleanup & table optimization","optimazz"),pct:100,indeterminate:!0,label:e,step:"",logs:[{level:"INFO",text:e}],running:!0,status:"running",minimized:!1});try{const e=await l()({url:`${r}/database/optimize`,method:"POST",data:B.database||{}});ve(e.counts);const a=Object.values(e.removed||{}).reduce((e,a)=>e+a,0),t=`${(0,i.__)("Cleaned","optimazz")} ${a} ${(0,i.__)("rows,","optimazz")} ${e.tables} ${(0,i.__)("tables optimized.","optimazz")}`;Ce(e=>e&&{...e,running:!1,status:"done",indeterminate:!1,pct:100,label:t,logs:[...e.logs,{level:"INFO",text:t},{level:"DONE",text:(0,i.__)("Completed.","optimazz")}]}),ue(t)}catch(e){Ce(a=>a&&{...a,running:!1,status:"error",indeterminate:!1,logs:[...a.logs,{level:"ERROR",text:e.message||"Error"}]}),ue(e.message||(0,i.__)("Optimize failed.","optimazz"),"error")}}},(0,i.__)("Optimize now","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"update",onClick:Be},(0,i.__)("Refresh counts","optimazz")))),za={dashboard:ea,cache:na,files:la,media:ia,fonts:da,preload:sa,advanced:ra,database:_a,heartbeat:(0,a.createElement)(H,{title:(0,i.__)("Heartbeat Control","optimazz"),description:(0,i.__)("Reduce or disable the WordPress Heartbeat API to lower server load.","optimazz")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Enable Heartbeat control","optimazz"),checked:!!je.enabled,onChange:()=>le("heartbeat","enabled")}),je.enabled&&(0,a.createElement)(a.Fragment,null,(0,a.createElement)(F,{label:(0,i.__)("Behavior in admin dashboard","optimazz"),value:je.backend||"default",options:S,onChange:e=>ne("heartbeat","backend",e)}),(0,a.createElement)(F,{label:(0,i.__)("Behavior in post editor","optimazz"),value:je.editor||"default",options:S,onChange:e=>ne("heartbeat","editor",e)}),(0,a.createElement)(F,{label:(0,i.__)("Behavior on front-end","optimazz"),value:je.frontend||"default",options:S,onChange:e=>ne("heartbeat","frontend",e)}))),tweaks:(0,a.createElement)(T,{icon:"admin-generic",title:(0,i.__)("Tweaks","optimazz"),description:(0,i.__)("Strip unnecessary WordPress front-end weight.","optimazz"),enabled:!!Ie.enabled,onToggle:()=>le("tweaks","enabled")},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Disable emojis","optimazz"),help:(0,i.__)("Remove the emoji detection script and styles.","optimazz"),checked:!!Ie.disable_emojis,onChange:()=>le("tweaks","disable_emojis")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Disable embeds (oEmbed)","optimazz"),checked:!!Ie.disable_embeds,onChange:()=>le("tweaks","disable_embeds")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Disable dashicons on front-end","optimazz"),help:(0,i.__)("Only for anonymous visitors.","optimazz"),checked:!!Ie.disable_dashicons,onChange:()=>le("tweaks","disable_dashicons")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Remove query strings from assets","optimazz"),help:(0,i.__)("Strip ?ver= for better proxy caching.","optimazz"),checked:!!Ie.remove_query_strings,onChange:()=>le("tweaks","remove_query_strings")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Disable XML-RPC","optimazz"),checked:!!Ie.disable_xmlrpc,onChange:()=>le("tweaks","disable_xmlrpc")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Disable self-pingbacks","optimazz"),checked:!!Ie.disable_pingback,onChange:()=>le("tweaks","disable_pingback")}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Remove head meta (generator, RSD, shortlink)","optimazz"),checked:!!Ie.remove_meta,onChange:()=>le("tweaks","remove_meta")}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("DNS-prefetch domains","optimazz"),value:Ie.dns_prefetch||[],onChange:e=>ne("tweaks","dns_prefetch",e)}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Preconnect origins","optimazz"),value:Ie.preconnect||[],onChange:e=>ne("tweaks","preconnect",e)})),cdn:(0,a.createElement)(T,{icon:"cloud",title:(0,i.__)("Content Delivery Network","optimazz"),description:(0,i.__)("Rewrite static asset URLs to your CDN hostname.","optimazz"),enabled:!!qe.enabled,onToggle:()=>le("cdn","enabled")},(0,a.createElement)(o.TextControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("CDN URL (CNAME)","optimazz"),placeholder:"https://cdn.example.com",value:qe.url||"",onChange:e=>ne("cdn","url",e),help:(0,i.__)("CSS, JS, image, and font URLs will be served from here.","optimazz")}),(0,a.createElement)(o.FormTokenField,{label:(0,i.__)("Exclude paths containing","optimazz"),value:qe.exclusions||[],onChange:e=>ne("cdn","exclusions",e)})),tools:ma};return p.forEach(e=>{za[e.key]="function"==typeof e.render?e.render():e.render}),(0,a.createElement)(A,{items:_.map(e=>({...e,dot:!(!e.mod||!B[e.mod]?.enabled)})),nav:V,onNav:K,foot:`v${window.optimazzData?.version}`,topIcon:Ue.icon,title:Ue.title,sub:Ue.sub,dirty:oe,saving:Y,onSave:()=>ge(B),toast:Q,onToastRemove:()=>X(null),after:(0,a.createElement)(M,{job:ke,onStop:()=>{Ne.current=!0},onClose:()=>{Ne.current=!0,Ce(null)},onToggleMin:()=>Ce(e=>e&&{...e,minimized:!e.minimized})})},za[V])},null));const W={enabled:!1,cache_mobile:!0,cache_logged_in:!1,lifespan_hours:0,preload:!0,preload_interval:"twicedaily",browser_cache:!1},j=document.getElementById("optimazz-network-app");j&&(0,t.createRoot)(j).render((0,a.createElement)(function(){const[e,n]=(0,t.useState)(null),[s,c]=(0,t.useState)(W),[m,d]=(0,t.useState)(!1),[p,_]=(0,t.useState)(!1),[z,h]=(0,t.useState)(""),[u,g]=(0,t.useState)(null),[b,f]=(0,t.useState)("cache"),[E,y]=(0,t.useState)(null),w=(0,t.useRef)(!1),k=async e=>{if(E?.running)return;w.current=!1;const a=(0,i.__)("Preloading cache…","optimazz");y({title:(0,i.__)("Preloading cache","optimazz"),subtitle:e.name||e.url,pct:0,label:a,step:"",logs:[{level:"INFO",text:a}],running:!0,status:"running",minimized:!1});const t={cached:0,missed:0,diagnosis:""},o=Date.now();let s=0,c=0;const m=(e,a,t,o={})=>y(n=>n&&{...n,running:!1,status:e,...o,logs:[...n.logs,{level:a,text:t}]});try{for(;;){var d;if(w.current){m("stopped","WARN",(0,i.__)("Stopped by user.","optimazz"));break}const a=await l()({url:`${r}/network/preload-batch`,method:"POST",data:{site_id:e.id,offset:s}}),n=s;s=null!==(d=a.next_offset)&&void 0!==d?d:s+(a.processed||0),c=a.total||c,t.cached+=a.cached||0,t.missed+=a.missed||0,t.diagnosis=a.diagnosis||t.diagnosis;const p=c?Math.min(100,Math.round(s/c*100)):100,_=`${(0,i.__)("Warmed","optimazz")} ${Math.min(s,c)} / ${c} ${(0,i.__)("pages","optimazz")} · ${t.cached} ${(0,i.__)("cached","optimazz")}${t.missed?` · ${t.missed} ${(0,i.__)("not cached","optimazz")}`:""}`,z=s/Math.max((Date.now()-o)/1e3,.001),h=z>0&&c&&s<c?(c-s)/z:0,u=h>=60?` · ~${Math.round(h/60)}m ${(0,i.__)("left","optimazz")}`:h>=1?` · ~${Math.round(h)}s ${(0,i.__)("left","optimazz")}`:"";if(y(e=>e&&{...e,pct:p,label:_,step:c?`${Math.min(s,c)} / ${c}${u}`:"",logs:[...e.logs.slice(-60),...a.breakdown?[{level:"INFO",text:`${(0,i.sprintf)(/* translators: %d: number of pages to warm. */ /* translators: %d: number of pages to warm. */ 9 (0,i.__)("Found %d pages to warm:","optimazz"),a.total||0)} ${a.breakdown}.`}]:[],{level:"INFO",text:_}]}),a.blocked){m("error","ERROR",(0,i.__)('Warming stopped: the server returned 403/429, so a firewall or security plugin is blocking the preload requests. Cached what we could; allow-list the "Optimazz-CacheWarmer" user-agent in your firewall or security plugin (e.g. All-In-One Security, Wordfence, Cloudflare), or slow warming with the optimazz_preload_delay_ms filter.',"optimazz"));break}if(!a.done&&0===t.cached&&t.missed>=3){m("error","ERROR",`${(0,i.__)("Stopped: pages are loading but none are being saved to the cache.","optimazz")} ${t.diagnosis}`,{label:(0,i.__)("Pages are not being cached","optimazz")});break}if(a.done||s<=n){t.missed&&y(e=>e&&{...e,logs:[...e.logs,{level:"WARN",text:`${(0,i.sprintf)(/* translators: %d: number of pages. */ /* translators: %d: number of pages. */ 10 (0,i._n)("%d page was not cached.","%d pages were not cached.",t.missed,"optimazz"),t.missed)} ${t.diagnosis}`}]}),m("done","DONE",(0,i.__)("Completed.","optimazz"),{pct:100});break}}}catch(e){m("error","ERROR",e?.message||(0,i.__)("Something went wrong.","optimazz"))}l()({url:`${r}/network`}).then(e=>{n(e)}).catch(()=>{})},C=e=>{n(e),c({...W,...e.settings||{}}),d(!1)},N=e=>g({status:"error",text:e?.message||(0,i.__)("Something went wrong.","optimazz")}),x=async(e,a,t,o)=>{h(t);try{const t=await l()({url:`${r}${e}`,method:"POST",data:a});return n(t),m||c({...W,...t.settings||{}}),o&&g({status:"success",text:o}),h(""),!0}catch(e){N(e)}return h(""),!1};(0,t.useEffect)(()=>{l()({url:`${r}/network`}).then(C).catch(N)},[]);const S=(e,a)=>{c(t=>({...t,[e]:a})),d(!0)};if(!e)return(0,a.createElement)("div",{className:"oz-shell oz-shell--network-app"},(0,a.createElement)("div",{className:"oz-main"},(0,a.createElement)(o.Spinner,null)));const $=e.health||{},B=e.sites||[],P=B.filter(e=>e.active),O=!!e.settings?.enabled,R=[{key:"cache",svg:"cache",icon:"database",title:(0,i.__)("Page Cache","optimazz"),sub:(0,i.__)("Caching for every site","optimazz"),dot:O},{key:"sites",svg:"dashboard",icon:"admin-multisite",title:(0,i.__)("Sites","optimazz"),sub:(0,i.__)("Cache per site","optimazz")}],L=R.find(e=>e.key===b)||R[0],j=(0,a.createElement)(a.Fragment,null,!e.network_active&&(0,a.createElement)("div",{className:"oz-cachewarn",role:"status"},(0,a.createElement)("span",{className:"dashicons dashicons-info-outline"}),(0,a.createElement)("div",{className:"oz-cachewarn__text"},(0,a.createElement)("strong",null,(0,i.__)("Optimazz is not network-activated","optimazz")),(0,a.createElement)("p",null,(0,i.__)("Only the sites where Optimazz is activated are cached. Network-activate it on Network Admin → Plugins to cache every site.","optimazz")))),(0,a.createElement)(T,{icon:"database",title:(0,i.__)("Page Cache","optimazz"),description:(0,i.__)("Serve ready-made HTML for every site instead of running PHP on each request. Each site keeps its own cache and its own exclusions.","optimazz"),enabled:!!s.enabled,onToggle:()=>S("enabled",!s.enabled)},(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Cache mobile devices separately","optimazz"),checked:!!s.cache_mobile,onChange:()=>S("cache_mobile",!s.cache_mobile)}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Cache logged-in users","optimazz"),help:(0,i.__)("Leave off unless you know what you are doing.","optimazz"),checked:!!s.cache_logged_in,onChange:()=>S("cache_logged_in",!s.cache_logged_in)}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Browser cache headers","optimazz"),help:(0,i.__)("Add Expires / Cache-Control rules to the shared .htaccess so repeat visits load CSS, JS, images and fonts from the browser cache. Apache and LiteSpeed only.","optimazz"),checked:!!s.browser_cache,onChange:()=>S("browser_cache",!s.browser_cache)}),(0,a.createElement)(F,{label:(0,i.__)("Cache lifetime","optimazz"),help:(0,i.__)("How long a cached page is kept. Pages are always purged as soon as their content changes.","optimazz"),value:Number(s.lifespan_hours)||0,options:[{value:0,label:(0,i.__)("Never","optimazz")},{value:12,label:(0,i.__)("12 hours","optimazz")},{value:24,label:(0,i.__)("1 day","optimazz")},{value:168,label:(0,i.__)("1 week","optimazz")},{value:720,label:(0,i.__)("1 month","optimazz")}],onChange:e=>S("lifespan_hours",e)}),(0,a.createElement)(o.ToggleControl,{__nextHasNoMarginBottom:!0,label:(0,i.__)("Preload every site automatically","optimazz"),help:(0,i.__)("Each site warms its own pages on a schedule and right after its cache is cleared.","optimazz"),checked:!!s.preload,onChange:()=>S("preload",!s.preload)}),(0,a.createElement)(o.SelectControl,{__nextHasNoMarginBottom:!0,__next40pxDefaultSize:!0,label:(0,i.__)("Automatic preload","optimazz"),value:s.preload_interval||"twicedaily",disabled:!s.preload,options:[{label:(0,i.__)("Every hour","optimazz"),value:"hourly"},{label:(0,i.__)("Twice a day","optimazz"),value:"twicedaily"},{label:(0,i.__)("Once a day","optimazz"),value:"daily"}],onChange:e=>S("preload_interval",e)})),(O||$.conflict)&&(0,a.createElement)(H,{title:(0,i.__)("Cache health","optimazz")},$.conflict?(0,a.createElement)("div",{className:"oz-cachewarn",role:"status"},(0,a.createElement)("span",{className:"dashicons dashicons-warning"}),(0,a.createElement)("div",{className:"oz-cachewarn__text"},(0,a.createElement)("strong",null,(0,i.sprintf)(/* translators: %s: name of another caching plugin. */ /* translators: %s: name of another caching plugin. */ 11 (0,i.__)("%s controls page caching (advanced-cache.php)","optimazz"),$.conflict_name||(0,i.__)("Another plugin","optimazz"))),(0,a.createElement)("p",null,(0,i.__)("Optimazz cannot cache while another plugin owns the cache file. Deactivate that plugin, or let Optimazz take over.","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-primary",isBusy:"takeover"===z,onClick:()=>x("/network/takeover",{},"takeover",(0,i.__)("Optimazz now handles page caching.","optimazz"))},(0,i.__)("Use Optimazz for caching","optimazz")))):(0,a.createElement)(a.Fragment,null,(0,a.createElement)(D,{ok:$.dropin_ours,label:(0,i.__)("Cache engine installed (advanced-cache.php)","optimazz")}),(0,a.createElement)(D,{ok:$.wp_cache,label:(0,i.__)("WP_CACHE is on in wp-config.php","optimazz")}),!$.wpconfig_writable&&!$.wp_cache&&(0,a.createElement)(D,{ok:!1,label:(0,i.__)("wp-config.php is not writable — add define( 'WP_CACHE', true ); yourself","optimazz")}),!$.content_writable&&(0,a.createElement)(D,{ok:!1,label:(0,i.__)("The wp-content folder is not writable","optimazz")})))),I=(0,a.createElement)(a.Fragment,null,(0,a.createElement)(H,{title:(0,i.__)("All sites","optimazz"),description:(0,i.sprintf)(/* translators: 1: sites with Optimazz, 2: all sites. */ /* translators: 1: sites with Optimazz, 2: all sites. */ 12 (0,i.__)("Optimazz runs on %1$d of %2$d sites. Each site has its own cache.","optimazz"),P.length,B.length)},!O&&(0,a.createElement)("p",{className:"oz-muted"},(0,i.__)("Page caching is off for the network — turn it on under Page Cache to purge or preload sites.","optimazz")),(0,a.createElement)("div",{className:"oz-quick__actions"},(0,a.createElement)(o.Button,{className:"oz-btn-ghost oz-btn--x",icon:"dismiss",disabled:!O,isBusy:"purge-all"===z,onClick:()=>x("/network/purge",{},"purge-all",(0,i.__)("Every site’s cache was purged.","optimazz"))},(0,i.__)("Purge all sites","optimazz")))),(0,a.createElement)("div",{className:"oz-quick oz-network__sites"},B.map(e=>(0,a.createElement)(H,{key:e.id,title:e.name||e.url,description:e.url},e.active?(0,a.createElement)(a.Fragment,null,(0,a.createElement)("div",{className:"oz-quick__actions"},(0,a.createElement)(o.Button,{className:"oz-btn-primary",icon:"trash",disabled:!O||!!E?.running,isBusy:`clear-${e.id}`===z,onClick:()=>(async e=>{await x("/network/purge",{site_id:e.id},`clear-${e.id}`)&&k(e)})(e)},(0,i.__)("Clear & Preload","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost",icon:"update",disabled:!O||!!E?.running,onClick:()=>k(e)},(0,i.__)("Preload","optimazz")),(0,a.createElement)(o.Button,{className:"oz-btn-ghost oz-btn--x",icon:"dismiss",disabled:!O,isBusy:`purge-${e.id}`===z,onClick:()=>x("/network/purge",{site_id:e.id},`purge-${e.id}`,(0,i.__)("Site cache purged.","optimazz"))},(0,i.__)("Purge cache","optimazz"))),(0,a.createElement)("ul",{className:"oz-cachelist"},(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-media-text"}),(0,i.__)("Cached pages","optimazz"),(0,a.createElement)("strong",null,e.pages)),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-database"}),(0,i.__)("Page cache size","optimazz"),(0,a.createElement)("strong",null,v(e.bytes))),(0,a.createElement)("li",null,(0,a.createElement)("span",{className:"dashicons dashicons-admin-generic"}),(0,i.__)("Optimazz settings","optimazz"),(0,a.createElement)("a",{className:"oz-cachelist__link",href:e.admin_url},(0,i.__)("Open","optimazz")," →")))):(0,a.createElement)("p",{className:"oz-muted"},(0,i.__)("Optimazz is not active on this site, so its pages are not cached.","optimazz"))))));return(0,a.createElement)(A,{className:"oz-shell--network-app",items:R,nav:b,onNav:f,foot:`${(0,i.__)("Network","optimazz")} · v${window.optimazzData?.version}`,topIcon:L.icon,title:L.title,sub:L.sub,dirty:m,saving:p,onSave:async()=>{_(!0);try{C(await l()({url:`${r}/network/settings`,method:"POST",data:{settings:s}})),g({status:"success",text:(0,i.__)("Network settings saved.","optimazz")})}catch(e){N(e)}_(!1)},toast:u,onToastRemove:()=>g(null),after:(0,a.createElement)(M,{job:E,onStop:()=>{w.current=!0},onClose:()=>{w.current=!0,y(null)},onToggleMin:()=>y(e=>e&&{...e,minimized:!e.minimized})})},"sites"===b?I:j)},null))})(); -
optimazz/trunk/changelog.txt
r3723776 r3743505 1 1 Optimazz – Website Optimization and Speedup — full changelog 2 3 = 1.1.0 = 4 * Fixed: Google Fonts never loaded on sites whose theme or plugin requests them in the old weight format through the new Google Fonts address (for example "Open Sans:400,700" or "w400,700"). Google rejects those requests, so the font was missing while the broken stylesheet still held back the first paint on mobile by most of a second. Optimazz now repairs them into a valid request. 5 * New: Google Fonts load without blocking the page (on by default, Fonts settings). With font display Swap or Fallback the page appears right away in a fallback font and switches when the web font arrives, instead of waiting for Google first. With Optional (zero layout shift) the stylesheet still loads first, so text never shifts. 6 * Improved: sites using Jetpack's Site Accelerator load WordPress core stylesheets from c0.wp.com, which Optimazz used to skip as external, so they stayed render-blocking. When the version matches your WordPress, they are now minified, combined and loaded like local files. 7 * Improved: more chat widgets are delayed until the visitor interacts — OpenWidget, Zendesk, Drift, Freshchat / Freshworks, JivoChat, Smartsupp and Small Chat — cutting mobile Total Blocking Time. 8 * New: full multisite support. Network-activate Optimazz and manage page caching for the whole network in Network Admin → Optimazz: switch it on once, choose mobile and logged-in caching, cache lifetime, browser-cache rules and automatic preload, and see every site with its cached pages and its own Purge and Preload buttons. Each site gets its own cache — on subdirectory and subdomain networks — keeps its own exclusions (Advanced Rules) and all its other Optimazz settings, and can purge and preload its own pages. 9 * Improved: on multisite only the network administrator controls the files every site shares (wp-config.php, advanced-cache.php, .htaccess); a site's own settings never change them, so one site can't switch caching on or off for the others. 10 * Fixed: before this version, on multisite one site's settings controlled caching for every site, only one site of a subdomain network was actually cached, any site administrator could rewrite wp-config.php and .htaccess, and deactivating Optimazz on one site removed the cache for the whole network. 11 * Fixed: deleting Optimazz from a multisite network only cleaned up the main site; settings, scheduled tasks, image data and generated CSS of the other sites were left behind. Uninstall now cleans every site. 12 * Fixed: on sites using plain permalinks (addresses like ?page_id=5) the "Cached pages" count showed 1 no matter how many pages were cached. 13 * Improved: the preload summary now names the home page and counts each address once, so its parts add up to the total. 14 * Improved: changing a cache rule (for example a new exclusion) now also clears pages that were cached under the old rules. 15 * Improved: the Changelog section of this readme now lists recent versions only; the complete history is in changelog.txt. 2 16 3 17 = 1.0.24 = -
optimazz/trunk/includes/Admin/Admin.php
r3715895 r3743505 18 18 19 19 /** 20 * Network Admin page slug (multisite). 21 */ 22 const NETWORK_SLUG = 'optimazz-network'; 23 24 /** 20 25 * @var Settings 21 26 */ … … 40 45 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) ); 41 46 add_action( 'admin_notices', array( $this, 'render_notices' ) ); 47 add_action( 'network_admin_menu', array( $this, 'add_network_menu' ) ); 42 48 // Hide third-party admin notices on the Optimazz screen to keep it clean. 43 49 add_action( 'in_admin_header', array( $this, 'suppress_foreign_notices' ), 1000 ); … … 120 126 } 121 127 $screen = get_current_screen(); 122 return $screen && 'toplevel_page_' . self::MENU_SLUG === $screen->id;128 return $screen && in_array( $screen->id, array( 'toplevel_page_' . self::MENU_SLUG, 'toplevel_page_' . self::NETWORK_SLUG . '-network' ), true ); 123 129 } 124 130 … … 276 282 277 283 /** 284 * Network Admin → Optimazz (super admins): network page caching and sites. 285 * 286 * @return void 287 */ 288 public function add_network_menu() { 289 add_menu_page( 290 __( 'Optimazz', 'optimazz' ), 291 __( 'Optimazz', 'optimazz' ), 292 'manage_network_options', 293 self::NETWORK_SLUG, 294 array( $this, 'render_network' ), 295 'dashicons-performance', 296 81 297 ); 298 } 299 300 /** 301 * Render the Network Admin mount node. 302 * 303 * @return void 304 */ 305 public function render_network() { 306 echo '<div class="wrap"><div id="optimazz-network-app"></div></div>'; 307 } 308 309 /** 278 310 * Enqueue the built React app on our page only. 279 311 * … … 282 314 */ 283 315 public function enqueue( $hook ) { 284 if ( 'toplevel_page_' . self::MENU_SLUG !== $hook ) { 316 $network = 'toplevel_page_' . self::NETWORK_SLUG === $hook; 317 if ( 'toplevel_page_' . self::MENU_SLUG !== $hook && ! $network ) { 285 318 return; 286 319 } … … 356 389 'wpconfigUnwritable' => (bool) get_transient( 'optimazz_wpconfig_unwritable' ), 357 390 'hasBackup' => $this->settings->has_backup(), 391 'serverFeatures' => \Optimazz\Core\Settings::server_features_allowed(), 392 'multisite' => is_multisite(), 393 // 'network' screen = Network Admin → Optimazz; 'site' = a site's own screen. 394 'context' => $network ? 'network' : 'site', 395 'networkCache' => is_multisite() ? \Optimazz\Core\Network::settings() : null, 396 'networkAdminUrl' => is_multisite() ? network_admin_url( 'admin.php?page=' . self::NETWORK_SLUG ) : '', 397 'isSuperAdmin' => is_super_admin(), 358 398 ) 359 399 ); -
optimazz/trunk/includes/Admin/AdminBar.php
r3617648 r3743505 50 50 51 51 $items = array( 52 'optimazz-settings' => array( __( 'Settings', 'optimazz' ), $settings ), 53 'optimazz-clear-preload' => array( __( 'Clear & Preload Cache', 'optimazz' ), $this->action_url( 'optimazz_clear_preload' ) ), 54 'optimazz-preload' => array( __( 'Preload Cache', 'optimazz' ), $this->action_url( 'optimazz_preload' ) ), 55 'optimazz-purge' => array( __( 'Purge Cache', 'optimazz' ), $this->action_url( 'optimazz_purge' ) ), 52 'optimazz-settings' => array( __( 'Settings', 'optimazz' ), $settings ), 56 53 ); 54 // Cache actions only where this site has a page cache (on multisite: when 55 // the network admin has switched network caching on). 56 if ( ! is_multisite() || \Optimazz\Core\Network::cache_enabled() ) { 57 $items['optimazz-clear-preload'] = array( __( 'Clear & Preload Cache', 'optimazz' ), $this->action_url( 'optimazz_clear_preload' ) ); 58 $items['optimazz-preload'] = array( __( 'Preload Cache', 'optimazz' ), $this->action_url( 'optimazz_preload' ) ); 59 $items['optimazz-purge'] = array( __( 'Purge Cache', 'optimazz' ), $this->action_url( 'optimazz_purge' ) ); 60 } 57 61 foreach ( $items as $id => $item ) { 58 62 $bar->add_node( … … 95 99 */ 96 100 private function guard( $action ) { 97 if ( ! current_user_can( 'manage_options' ) || ! check_admin_referer( $action ) ) {101 if ( ( is_multisite() && ! \Optimazz\Core\Network::cache_enabled() ) || ! current_user_can( 'manage_options' ) || ! check_admin_referer( $action ) ) { 98 102 wp_die( esc_html__( 'Not allowed.', 'optimazz' ) ); 99 103 } -
optimazz/trunk/includes/Admin/CacheConflictNotice.php
r3723128 r3743505 59 59 */ 60 60 public function render() { 61 if ( ! current_user_can( 'manage_options' ) ) {61 if ( is_multisite() || ! current_user_can( 'manage_options' ) ) { 62 62 return; 63 63 } … … 121 121 */ 122 122 private function guard( $action ) { 123 if ( ! current_user_can( 'manage_options' ) || ! check_admin_referer( $action ) ) { 123 // Multisite: never take over the shared drop-in or (network-)deactivate 124 // another plugin from a site — the same rule as the REST endpoints. 125 if ( ! \Optimazz\Core\Settings::server_features_allowed() || ! current_user_can( 'manage_options' ) || ! check_admin_referer( $action ) ) { 124 126 wp_die( esc_html__( 'Not allowed.', 'optimazz' ) ); 125 127 } -
optimazz/trunk/includes/Admin/RestController.php
r3723128 r3743505 278 278 // modules emit into the page, so stale cached HTML must be cleared for the 279 279 // change to take effect immediately (the whole point of Safe Mode). 280 // 'cache' is included so a new exclusion (or lifetime, mobile split …) 281 // also drops pages that were cached under the old rules. 280 282 $purged = false; 281 foreach ( array( 'c ss', 'js', 'media', 'fonts', 'cdn', 'tweaks', 'tools' ) as $mod ) {283 foreach ( array( 'cache', 'css', 'js', 'media', 'fonts', 'cdn', 'tweaks', 'tools' ) as $mod ) { 282 284 $old = isset( $before[ $mod ] ) ? $before[ $mod ] : array(); 283 285 $new = isset( $saved[ $mod ] ) ? $saved[ $mod ] : array(); 286 if ( 'cache' === $mod ) { 287 // Preload schedule and browser-cache headers don't change the 288 // cached HTML — changing them must not throw the cache away. 289 foreach ( array( 'preload', 'preload_interval', 'browser_cache' ) as $key ) { 290 unset( $old[ $key ], $new[ $key ] ); 291 } 292 } 284 293 if ( wp_json_encode( $old ) !== wp_json_encode( $new ) ) { 285 294 $purged = true; … … 373 382 */ 374 383 public function purge_cache() { 384 if ( is_multisite() && ! \Optimazz\Core\Network::cache_enabled() ) { 385 return new WP_REST_Response( array( 'message' => __( 'Page caching is not available on multisite networks.', 'optimazz' ) ), 400 ); 386 } 375 387 Manager::storage()->purge_all(); 376 388 return new WP_REST_Response( array( 'message' => 'purged' ), 200 ); … … 573 585 */ 574 586 public function cache_takeover( WP_REST_Request $request ) { 587 if ( ! \Optimazz\Core\Settings::server_features_allowed() ) { 588 return new WP_REST_Response( array( 'message' => __( 'Page caching is not available on multisite networks.', 'optimazz' ) ), 400 ); 589 } 575 590 // Make sure the page-cache module is on, then force our drop-in in. 576 591 $cache = $this->settings->all(); … … 596 611 */ 597 612 public function cache_deactivate_conflict( WP_REST_Request $request ) { 613 if ( ! \Optimazz\Core\Settings::server_features_allowed() ) { 614 return new WP_REST_Response( array( 'message' => __( 'Page caching is not available on multisite networks.', 'optimazz' ) ), 400 ); 615 } 598 616 $plugin = (string) $request->get_param( 'plugin' ); 599 617 if ( '' === $plugin || '' === Dropin::known_cache_plugin_name( $plugin ) ) { -
optimazz/trunk/includes/Core/Lifecycle.php
r3723128 r3743505 19 19 * actually enables page caching (handled by the Cache module). 20 20 * 21 * @param bool $network_wide Whether this is a network-wide activation. 21 22 * @return void 22 23 */ 23 public static function activate() { 24 public static function activate( $network_wide = false ) { 25 // Multisite: add this site (or every site) to the cache site map. The 26 // activated_plugin listener isn't registered yet in the activation 27 // request, so queue the sync here. 28 if ( is_multisite() ) { 29 Network::queue_sync(); 30 } 31 // Network activation: every site starts from the defaults the first time 32 // its settings are read, and the shared cache files follow the network 33 // settings (Network::on_plugin_change() re-syncs the site map). 34 24 35 if ( false === get_option( Settings::OPTION_KEY, false ) ) { 25 36 add_option( Settings::OPTION_KEY, Settings::defaults() ); … … 68 79 update_option( self::VERSION_KEY, OPTIMAZZ_VERSION, false ); 69 80 81 // Multisite: the shared files follow the network settings. Sync them once 82 // per network per version (this also removes a drop-in that a pre-1.1.0 83 // site installed, unless the network turned caching on); then bring this 84 // site's own rules and schedule up to date and rebuild its cache. 85 if ( is_multisite() ) { 86 if ( OPTIMAZZ_VERSION !== get_site_option( Network::VERSION_KEY ) ) { 87 Network::sync(); 88 \Optimazz_Cache_Engine::remove_mixed_case_hosts(); // Orphans of pre-1.1.0 mixed-case Host folders. 89 } 90 if ( class_exists( '\Optimazz\Modules\Cache\Manager' ) ) { 91 \Optimazz\Modules\Cache\Manager::sync( $settings->for_module( 'cache' ) ); 92 \Optimazz\Modules\Cache\Manager::purge_and_rewarm(); 93 } 94 if ( Network::cache_enabled() && class_exists( '\Optimazz\Core\Guard' ) ) { 95 Guard::schedule(); // Same post-update self-check as on a single site. 96 } 97 return; 98 } 99 70 100 // Only touch caching when the user actually has it enabled. 71 101 $cache = $settings->for_module( 'cache' ); … … 91 121 * so a disabled plugin never serves stale cached pages. 92 122 * 123 * @param bool $network_wide Whether this is a network-wide deactivation. 93 124 * @return void 94 125 */ 95 public static function deactivate() { 96 if ( class_exists( '\Optimazz\Modules\Cache\Dropin' ) ) { 97 Dropin::uninstall(); 126 public static function deactivate( $network_wide = false ) { 127 // Multisite, one site: the shared drop-in still serves the other sites. 128 // Clear this site's cache and jobs; Network::on_plugin_change() drops it 129 // from the site map so the engine stops caching it. 130 if ( is_multisite() && ! $network_wide ) { 131 if ( class_exists( '\Optimazz\Modules\Cache\Manager' ) ) { 132 \Optimazz\Modules\Cache\Manager::storage()->purge_all(); 133 } 134 self::clear_events(); 135 return; 98 136 } 99 if ( class_exists( '\Optimazz\Modules\Cache\BrowserCache' ) ) { 100 \Optimazz\Modules\Cache\BrowserCache::apply( false ); 137 Network::with_shared_writes( 138 static function () { 139 if ( class_exists( '\Optimazz\Modules\Cache\Dropin' ) ) { 140 Dropin::uninstall(); 141 } 142 if ( class_exists( '\Optimazz\Modules\Cache\BrowserCache' ) ) { 143 \Optimazz\Modules\Cache\BrowserCache::apply( false ); 144 } 145 } 146 ); 147 // Scheduled events live per site. A network deactivation runs once, so 148 // clear every site. 149 if ( is_multisite() && $network_wide ) { 150 foreach ( get_sites( array( 'fields' => 'ids', 'number' => 0 ) ) as $site_id ) { 151 switch_to_blog( $site_id ); 152 self::clear_events(); 153 restore_current_blog(); 154 } 155 } else { 156 self::clear_events(); 101 157 } 102 wp_clear_scheduled_hook( 'optimazz_preload_cache' ); 103 wp_clear_scheduled_hook( 'optimazz_preload_cache_once' ); 104 wp_clear_scheduled_hook( 'optimazz_purge_expired' ); 105 wp_clear_scheduled_hook( 'optimazz_db_cleanup' ); 106 wp_clear_scheduled_hook( 'optimazz_health_check' ); 158 } 159 160 /** 161 * Remove this site's Optimazz scheduled events. 162 * 163 * @return void 164 */ 165 private static function clear_events() { 166 foreach ( array( 'optimazz_preload_cache', 'optimazz_preload_cache_once', 'optimazz_purge_expired', 'optimazz_db_cleanup', 'optimazz_health_check' ) as $hook ) { 167 wp_clear_scheduled_hook( $hook ); 168 } 107 169 } 108 170 } -
optimazz/trunk/includes/Core/Paths.php
r3617648 r3743505 25 25 public static function url_to_path( $url ) { 26 26 $host = wp_parse_url( $url, PHP_URL_HOST ); 27 if ( 'c0.wp.com' === $host ) { 28 return self::jetpack_core_path( $url ); 29 } 27 30 if ( $host && $host !== wp_parse_url( home_url(), PHP_URL_HOST ) ) { 28 31 return ''; … … 48 51 return ABSPATH . ltrim( $path, '/' ); 49 52 } 53 54 /** 55 * Jetpack's Site Accelerator serves WordPress core files from 56 * c0.wp.com/c/<version>/wp-includes/… . When that version is the one 57 * installed here, the file is identical to our own copy, so it can be 58 * minified, combined and loaded without blocking like any local file — 59 * otherwise these core stylesheets stay render-blocking on every page. 60 * 61 * @param string $url c0.wp.com URL. 62 * @return string Local path, or '' when it is not a matching core file. 63 */ 64 private static function jetpack_core_path( $url ) { 65 $path = (string) wp_parse_url( strtok( (string) $url, '?' ), PHP_URL_PATH ); 66 if ( false !== strpos( $path, '..' ) || ! preg_match( '#^/c/([^/]+)/((?:wp-includes|wp-admin)/.+)$#', $path, $m ) ) { 67 return ''; 68 } 69 if ( $m[1] !== get_bloginfo( 'version' ) ) { 70 return ''; // A different WordPress version: the files may differ. 71 } 72 return ABSPATH . $m[2]; 73 } 50 74 } -
optimazz/trunk/includes/Core/Plugin.php
r3723128 r3743505 11 11 use Optimazz\Admin\AdminBar; 12 12 use Optimazz\Admin\CacheConflictNotice; 13 use Optimazz\Admin\NetworkRestController; 13 14 use Optimazz\Admin\RestController; 14 15 use Optimazz\Modules\Cache\CacheModule; … … 95 96 // Mode if a post-update front page renders unstyled. 96 97 Guard::register(); 98 Network::register(); // Multisite: keep the site map in step with the network. 97 99 98 100 // One-time reconciliation after a version change (update). Clears cached … … 109 111 110 112 ( new RestController( $this->settings, $this->loader ) )->register(); 113 ( new NetworkRestController() )->register(); // Multisite: Network Admin → Optimazz. 111 114 112 115 // Translations are auto-loaded by WordPress for .org-hosted plugins -
optimazz/trunk/includes/Core/Recommendations.php
r3700289 r3743505 56 56 } 57 57 58 // ---- Page cache (biggest single win) ---- 59 if ( ! $on( $cache, 'enabled' ) ) { 58 // ---- Page cache (biggest single win; not available on multisite) ---- 59 if ( ! \Optimazz\Core\Settings::server_features_allowed() ) { 60 // Nothing to recommend — see Settings::server_features_allowed(). 61 } elseif ( ! $on( $cache, 'enabled' ) ) { 60 62 $recs[] = self::rec( 'page_cache', 'high', 61 63 __( 'Enable Page Cache', 'optimazz' ), … … 63 65 array( 'cache', 'enabled', true ) 64 66 ); 65 } elseif ( ! $on( $cache, 'preload' ) ) {67 } elseif ( ! $on( $cache, 'preload' ) && ! is_multisite() ) { 66 68 $recs[] = self::rec( 'preload', 'medium', 67 69 __( 'Turn on cache preloading', 'optimazz' ), -
optimazz/trunk/includes/Core/Settings.php
r3723128 r3743505 114 114 'combine_google' => true, // Merge all Google Fonts requests into one. 115 115 'trim_weights' => true, // Request only the font weights the page uses. 116 'async_google' => true, // Load the Google Fonts stylesheet without blocking rendering. 116 117 'preload' => array(), 117 118 ), … … 153 154 if ( null === $this->cache ) { 154 155 $stored = get_option( self::OPTION_KEY, array() ); 155 $this->cache = $this->merge_deep( self::defaults(), is_array( $stored ) ? $stored : array() );156 $this->cache = self::enforce_platform( $this->merge_deep( self::defaults(), is_array( $stored ) ? $stored : array() ) ); 156 157 } 157 158 return $this->cache; 159 } 160 161 /** 162 * Whether the current code may write SHARED server files. 163 * 164 * Page cache (wp-config.php WP_CACHE, wp-content/advanced-cache.php and the 165 * cache folder) and browser-cache rules (.htaccess) are one-per-install. On 166 * a single site its own settings control them. On multisite only a network 167 * action (Network::sync() and friends, run for the super admin) may write 168 * them — never a site's own settings save, so one site can't change caching 169 * for the others and a site admin can't rewrite wp-config.php / .htaccess. 170 * 171 * @return bool 172 */ 173 public static function server_features_allowed() { 174 return ! is_multisite() || Network::is_writing(); 175 } 176 177 /** 178 * On multisite the page-cache switches come from the network settings 179 * (Network Admin → Optimazz); each site keeps its own exclusions. 180 * 181 * @param array $settings Settings. 182 * @return array 183 */ 184 private static function enforce_platform( array $settings ) { 185 if ( is_multisite() && isset( $settings['cache'] ) && is_array( $settings['cache'] ) ) { 186 $settings['cache'] = array_merge( $settings['cache'], Network::settings() ); 187 } 188 return $settings; 158 189 } 159 190 … … 214 245 $defaults = self::defaults(); 215 246 update_option( self::OPTION_KEY, $defaults ); 216 $this->cache = $defaults;217 return $ defaults;247 $this->cache = self::enforce_platform( $defaults ); // On multisite: network cache switches stay. 248 return $this->cache; 218 249 } 219 250 … … 285 316 } 286 317 } 287 return $clean;318 return self::enforce_platform( $clean ); 288 319 } 289 320 -
optimazz/trunk/includes/Modules/Cache/BrowserCache.php
r3617648 r3743505 25 25 */ 26 26 public static function apply( $enabled ) { 27 if ( $enabled && ! \Optimazz\Core\Settings::server_features_allowed() ) { 28 $enabled = false; // One .htaccess serves the whole network — never written from a subsite. 29 } 27 30 $htaccess = ABSPATH . '.htaccess'; 28 31 -
optimazz/trunk/includes/Modules/Cache/Dropin.php
r3723128 r3743505 53 53 */ 54 54 public static function install() { 55 if ( ! \Optimazz\Core\Settings::server_features_allowed() ) { 56 return false; // advanced-cache.php is shared by every site of a network. 57 } 55 58 $fs = self::filesystem(); 56 59 if ( ! $fs || ! $fs->is_readable( self::template() ) ) { … … 65 68 66 69 $ok = $fs->put_contents( self::DROPIN_PATH, $contents ); 70 self::forget_compiled( self::DROPIN_PATH ); 67 71 if ( $ok ) { 68 72 self::set_wp_cache( true ); … … 322 326 323 327 /** 328 * Make PHP's opcode cache drop its compiled copy of a file we rewrote 329 * (wp-config.php, advanced-cache.php), so the change applies on the very 330 * next request instead of after the next timestamp check. 331 * 332 * @param string $file Absolute path. 333 * @return void 334 */ 335 private static function forget_compiled( $file ) { 336 if ( function_exists( 'opcache_invalidate' ) ) { 337 @opcache_invalidate( $file, true ); // phpcs:ignore WordPress.PHP.NoSilencedErrors -- may be restricted by opcache.restrict_api. 338 } 339 } 340 341 /** 324 342 * Forcefully make Optimazz the caching drop-in — overwrite whatever 325 343 * advanced-cache.php is present and enable WP_CACHE. Used by the admin … … 371 389 */ 372 390 private static function set_wp_cache( $enabled ) { 391 // Multisite: wp-config.php is shared by the whole network, so a subsite 392 // never edits it — not even to turn WP_CACHE off. Removing our drop-in is 393 // enough to stop caching (WordPress only loads advanced-cache.php if the 394 // file exists). 395 if ( ! \Optimazz\Core\Settings::server_features_allowed() ) { 396 return false; 397 } 373 398 $config = ABSPATH . 'wp-config.php'; 374 399 if ( ! is_writable( $config ) ) { // phpcs:ignore WordPress.WP.AlternativeFunctions … … 405 430 // core caching plugins — we toggle that single line here via 406 431 // WP_Filesystem. Nothing else in the file is touched. 407 return $fs->put_contents( $config, $contents ); 432 $ok = $fs->put_contents( $config, $contents ); 433 self::forget_compiled( $config ); // WP_CACHE must take effect on the next request. 434 return $ok; 408 435 } 409 436 } -
optimazz/trunk/includes/Modules/Cache/Manager.php
r3723128 r3743505 54 54 } 55 55 56 // Multisite: the drop-in, WP_CACHE, the engine config and .htaccess are 57 // shared by the whole network and only Network::sync() (a network admin 58 // action) writes them. A site writes just its own rules file and keeps 59 // its own preload schedule in step with the network settings. 60 if ( is_multisite() && ! \Optimazz\Core\Network::is_writing() ) { 61 \Optimazz\Core\Network::write_current_site_rules(); 62 $on = ! empty( $cache_settings['enabled'] ); 63 Preloader::schedule( $on && ! empty( $cache_settings['preload'] ), isset( $cache_settings['preload_interval'] ) ? (string) $cache_settings['preload_interval'] : 'twicedaily' ); 64 if ( ! $on ) { 65 wp_clear_scheduled_hook( Preloader::HOOK . '_once' ); 66 wp_clear_scheduled_hook( 'optimazz_purge_expired' ); 67 } 68 Preloader::sync_due(); 69 return; 70 } 71 56 72 // The engine config is a trimmed, disk-readable copy of the settings. 57 73 \Optimazz_Cache_Engine::write_config( … … 107 123 */ 108 124 public static function purge_and_rewarm() { 109 self::storage()->purge_all(); 125 if ( is_multisite() && ! \Optimazz\Core\Network::cache_enabled() ) { 126 return; // Page caching is off for this network — nothing to rebuild. 127 } 128 self::storage()->purge_all(); // On multisite: this site's pages only. 110 129 111 130 $cache = ( new \Optimazz\Core\Settings() )->for_module( 'cache' ); … … 128 147 } 129 148 149 if ( is_multisite() ) { 150 // A new site has no rules file yet; write its own. 151 if ( ! is_readable( \Optimazz_Cache_Engine::site_rules_file( get_current_blog_id() ) ) ) { 152 \Optimazz\Core\Network::write_current_site_rules(); 153 } 154 // Network caching is on but the shared drop-in or engine config is 155 // missing/stale (plugin folder moved, a host tool emptied the cache 156 // folder): heal it for the whole network — at most every 10 minutes, 157 // so a drop-in that can't be installed (another plugin owns it, the 158 // folder isn't writable) never makes every request run a full sync. 159 if ( ! empty( $cache_settings['enabled'] ) 160 && null === Dropin::conflict_owner() // Another plugin's drop-in: nothing to heal (Network screen offers take-over). 161 && ( ! Dropin::is_current() || ! is_readable( \Optimazz_Cache_Engine::config_file() ) ) 162 && ! get_site_transient( 'optimazz_heal' ) ) { 163 set_site_transient( 'optimazz_heal', 1, 10 * MINUTE_IN_SECONDS ); 164 \Optimazz\Core\Network::queue_sync(); 165 } 166 return; 167 } 168 130 169 if ( ! is_readable( \Optimazz_Cache_Engine::config_file() ) ) { 131 170 self::sync( $cache_settings ); -
optimazz/trunk/includes/Modules/Cache/Preloader.php
r3723128 r3743505 113 113 } 114 114 $parts = array(); 115 if ( ! empty( $counts['__home'] ) ) { 116 $parts[] = __( 'the home page', 'optimazz' ); 117 } 115 118 foreach ( $counts as $group => $n ) { 116 if ( ! $n || '__terms' === $group ) {119 if ( ! $n || '__terms' === $group || '__home' === $group ) { 117 120 continue; 118 121 } … … 140 143 $max = max( 1, (int) apply_filters( 'optimazz_preload_max', 300 ) ); 141 144 $urls = array( home_url( '/' ) ); 142 $groups = array( ); // Source of each URL, for breakdown().145 $groups = array( '__home' => array( home_url( '/' ) ) ); // Source of each URL, for breakdown(). 143 146 144 147 // A static front page and/or posts page first, when configured. … … 214 217 215 218 // How many of the FINAL list came from each source (for breakdown()). 219 // Each URL counts once, under the first source that listed it (the home 220 // page may also be the static front Page). 221 $owner = array(); 222 foreach ( $groups as $group => $links ) { 223 foreach ( $links as $link ) { 224 if ( ! isset( $owner[ $link ] ) ) { 225 $owner[ $link ] = $group; 226 } 227 } 228 } 216 229 $counts = array(); 217 foreach ( $groups as $group => $links ) { 218 $counts[ $group ] = count( array_intersect( array_unique( $links ), $urls ) ); 230 foreach ( $urls as $url ) { 231 if ( isset( $owner[ $url ] ) ) { 232 $counts[ $owner[ $url ] ] = ( isset( $counts[ $owner[ $url ] ] ) ? $counts[ $owner[ $url ] ] : 0 ) + 1; 233 } 219 234 } 220 235 set_transient( self::URLS_CACHE . '_breakdown', $counts, HOUR_IN_SECONDS ); … … 275 290 require_once OPTIMAZZ_DIR . 'includes/Modules/Cache/engine.php'; 276 291 } 277 $config = \Optimazz_Cache_Engine::load_config();292 $config = self::engine_config(); 278 293 // A new manual run (offset 0) rebuilds the list; later steps reuse it. 279 294 $urls = self::collect_all_urls( 0 === (int) $offset ); … … 341 356 342 357 /** 358 * Engine config for this site: on a network, the network settings merged 359 * with this site's own rules (ignored query args decide the cache key). 360 * 361 * @return array 362 */ 363 private static function engine_config() { 364 $home = (array) wp_parse_url( home_url( '/' ) ); 365 return \Optimazz_Cache_Engine::config_for( \Optimazz_Cache_Engine::url_host( $home ), isset( $home['path'] ) ? $home['path'] : '/' ); 366 } 367 368 /** 343 369 * Device copies to warm: desktop, plus mobile when a separate mobile cache is on. 344 370 * … … 383 409 require_once OPTIMAZZ_DIR . 'includes/Modules/Cache/engine.php'; 384 410 } 385 $config = \Optimazz_Cache_Engine::load_config();411 $config = self::engine_config(); 386 412 if ( empty( $config['enabled'] ) ) { 387 413 delete_option( self::CURSOR ); … … 472 498 $parts = wp_parse_url( $url ); 473 499 $scheme = isset( $parts['scheme'] ) ? $parts['scheme'] : 'https'; 474 $host = isset( $parts['host'] ) ? $parts['host'] : '';500 $host = \Optimazz_Cache_Engine::url_host( (array) $parts ); 475 501 $uri = ( isset( $parts['path'] ) ? $parts['path'] : '/' ) . ( isset( $parts['query'] ) ? '?' . $parts['query'] : '' ); 476 502 $file = \Optimazz_Cache_Engine::path_for( $scheme, $host, $uri, $device, $config ); … … 848 874 require_once OPTIMAZZ_DIR . 'includes/Modules/Cache/engine.php'; 849 875 } 850 \Optimazz_Cache_Engine::write_cron_due( self::next_due(), site_url( 'wp-cron.php' ) );876 \Optimazz_Cache_Engine::write_cron_due( self::next_due(), site_url( 'wp-cron.php' ), is_multisite() ? get_current_blog_id() : 0 ); 851 877 } 852 878 -
optimazz/trunk/includes/Modules/Cache/Storage/DiskStorage.php
r3723128 r3743505 29 29 30 30 public function purge_all() { 31 if ( is_multisite() ) { 32 \Optimazz_Cache_Engine::purge_site_dir( self::site_dir() ); // This site only. 33 return; 34 } 31 35 \Optimazz_Cache_Engine::purge_all(); 36 } 37 38 /** 39 * Cache folder of the current network site. 40 * 41 * @return string 42 */ 43 private static function site_dir() { 44 $site = get_site(); 45 return \Optimazz_Cache_Engine::site_dir( $site ? $site->domain : (string) wp_parse_url( home_url(), PHP_URL_HOST ), $site ? $site->path : '/' ); 32 46 } 33 47 … … 39 53 public function stats() { 40 54 $root = \Optimazz_Cache_Engine::cache_root(); 55 $skip = array(); 56 if ( is_multisite() ) { 57 // Count this site's folder only, not other sites nested inside it. 58 $root = self::site_dir(); 59 $skip = array_values( array_diff( \Optimazz_Cache_Engine::site_roots(), array( $root ) ) ); 60 } 41 61 if ( ! is_dir( $root ) ) { 42 62 return array( … … 49 69 $files = 0; 50 70 $bytes = 0; 51 // A page (one URL) is stored as several variants in its own folder —52 // desktop/mobile, http/https, query strings. "Cached pages" counts the53 // pages, so it matches what the preloader reports; 'files' is kept for54 // the rawvariant count.71 // A page (one URL) is stored as several variants — desktop/mobile, 72 // http/https, logged-in buckets. "Cached pages" counts the pages, so it 73 // matches what the preloader reports; 'files' is kept for the raw 74 // variant count. 55 75 $dirs = array(); 56 76 $it = new \RecursiveIteratorIterator( 57 new \RecursiveDirectoryIterator( $root, \FilesystemIterator::SKIP_DOTS ) 77 new \RecursiveCallbackFilterIterator( 78 new \RecursiveDirectoryIterator( $root, \FilesystemIterator::SKIP_DOTS ), 79 static function ( $file ) use ( $skip ) { 80 // Skip other network sites' folders (empty on a single site). 81 return ! ( $file->isDir() && in_array( rtrim( $file->getPathname(), '/' ) . '/', $skip, true ) ); 82 } 83 ) 58 84 ); 59 85 foreach ( $it as $file ) { … … 66 92 ++$files; 67 93 $bytes += (int) $file->getSize(); 68 $dirs[ $file->getPath() ] = true; 94 // File name: {scheme}-{device}-{user}[-{query}].html. One page = its 95 // folder. At the site root the query part counts too, because plain 96 // permalinks (?page_id=5) keep every page there; elsewhere query 97 // variants (e.g. /shop/?orderby=price) are the same page. 98 $parts = explode( '-', $file->getBasename( '.html' ), 4 ); 99 $at_root = false === strpos( trim( substr( $file->getPath(), strlen( $root ) ), '/' ), '/' ); 100 $dirs[ $file->getPath() . '|' . ( $at_root && isset( $parts[3] ) ? $parts[3] : '' ) ] = true; 69 101 } 70 102 } -
optimazz/trunk/includes/Modules/Cache/engine.php
r3723128 r3743505 38 38 'site_host' => '', 39 39 'lifespan_hours' => 0, 40 // Multisite: map of "host/path/" => site ID for every site Optimazz 41 // caches, and the site the current request belongs to. 42 'multisite' => false, 43 'sites' => array(), 44 'site_id' => 0, 40 45 ); 41 46 } … … 77 82 78 83 /** 84 * Config for a request to $host + $uri. On a network this resolves the 85 * site (by host and the longest matching path) and merges that site's own 86 * rules — exclusions and ignored query args — over the network settings. 87 * A request for a host/path that no cached site owns gets enabled=false. 88 * 89 * @param string $host Host header. 90 * @param string $uri Request URI. 91 * @return array 92 */ 93 public static function config_for( $host, $uri ) { 94 $config = self::load_config(); 95 if ( empty( $config['multisite'] ) ) { 96 return $config; 97 } 98 $id = self::resolve_site( $config, $host, $uri ); 99 if ( ! $id ) { 100 $config['enabled'] = false; 101 return $config; 102 } 103 $config['site_id'] = $id; 104 $file = self::site_rules_file( $id ); 105 if ( is_readable( $file ) ) { 106 $rules = include $file; 107 if ( is_array( $rules ) ) { 108 foreach ( array( 'ignored_query_args', 'excluded_uris', 'excluded_cookies', 'excluded_user_agents' ) as $key ) { 109 if ( isset( $rules[ $key ] ) && is_array( $rules[ $key ] ) ) { 110 $config[ $key ] = $rules[ $key ]; 111 } 112 } 113 } 114 } 115 return $config; 116 } 117 118 /** 119 * Which network site a host + URI belongs to: the site whose "host/path/" 120 * key is the longest prefix of the request (so /shop/… goes to the /shop/ 121 * site, everything else on that host to the main site). 122 * 123 * @param array $config Config with a 'sites' map. 124 * @param string $host Host header. 125 * @param string $uri Request URI. 126 * @return int Site ID, or 0 when no cached site matches. 127 */ 128 public static function resolve_site( array $config, $host, $uri ) { 129 // Host as sent (a port is kept, matching how the cache folder is named 130 // and how WordPress stores a site's domain). 131 $host = strtolower( (string) $host ); 132 $path = (string) strtok( (string) $uri, '?' ); 133 $path = '/' . ltrim( $path, '/' ); 134 $key = $host . rtrim( $path, '/' ) . '/'; 135 $best = 0; 136 $len = 0; 137 foreach ( (array) $config['sites'] as $prefix => $id ) { 138 $prefix = (string) $prefix; 139 if ( strlen( $prefix ) > $len && 0 === strpos( $key, $prefix ) ) { 140 $best = (int) $id; 141 $len = strlen( $prefix ); 142 } 143 } 144 return $best; 145 } 146 147 /** 148 * Host of a parsed URL as a browser sends it in the Host header — with the 149 * port when there is one. Cache folders and the network site map are keyed 150 * on that, so URL-based lookups (purge, preload) must use it too. 151 * 152 * @param array $parts parse_url() result. 153 * @return string 154 */ 155 public static function url_host( array $parts ) { 156 $host = isset( $parts['host'] ) ? strtolower( (string) $parts['host'] ) : ''; 157 return ( '' !== $host && ! empty( $parts['port'] ) ) ? $host . ':' . (int) $parts['port'] : $host; 158 } 159 160 /** 161 * File holding one network site's own cache rules. 162 * 163 * @param int $id Site ID. 164 * @return string 165 */ 166 public static function site_rules_file( $id ) { 167 return self::cache_root() . 'sites/' . (int) $id . '.php'; 168 } 169 170 /** 171 * Write one network site's own cache rules (only changes when they differ). 172 * 173 * @param int $id Site ID. 174 * @param array $rules Rules: ignored_query_args, excluded_uris, excluded_cookies, excluded_user_agents. 175 * @return bool 176 */ 177 public static function write_site_rules( $id, array $rules ) { 178 $file = self::site_rules_file( $id ); 179 // var_export serialises to a fast include file, not for debugging. 180 $code = "<?php\n// Generated by Optimazz. Do not edit.\nreturn " . var_export( $rules, true ) . ";\n"; // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export 181 if ( is_readable( $file ) && (string) @file_get_contents( $file ) === $code ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors,WordPress.WP.AlternativeFunctions 182 return true; 183 } 184 self::mkdir( dirname( $file ) ); 185 self::protect_dir( self::cache_root() ); 186 return self::atomic_write( $file, $code ); 187 } 188 189 /** 79 190 * Persist config to disk as a returnable PHP array (fast to include). 80 191 * … … 87 198 // var_export is used to serialize the config to a fast include file, not for debugging. 88 199 $code = "<?php\n// Generated by Optimazz. Do not edit.\nreturn " . var_export( $config, true ) . ";\n"; // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export 200 if ( is_readable( self::config_file() ) && (string) @file_get_contents( self::config_file() ) === $code ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors,WordPress.WP.AlternativeFunctions 201 return true; // Unchanged: keep the compiled copy (no opcode-cache churn). 202 } 89 203 return self::atomic_write( self::config_file(), $code ); 90 204 } … … 144 258 */ 145 259 public static function serve() { 146 $config = self::load_config(); 260 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- pre-WP drop-in; only used to pick a site and build a sanitized cache key. 261 $config = self::config_for( isset( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : '', isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '/' ); 147 262 148 263 if ( empty( $config['enabled'] ) ) { … … 167 282 header( 'Content-Type: text/html; charset=UTF-8' ); 168 283 readfile( $file ); // phpcs:ignore WordPress.WP.AlternativeFunctions 169 self::maybe_kick_cron( );284 self::maybe_kick_cron( (int) $config['site_id'] ); 170 285 exit; 171 286 } … … 181 296 * A .php file, so the cache directory's access rules keep it private. 182 297 * 298 * @param int $site_id Network site ID (0 on a single site). 183 299 * @return string 184 300 */ 185 private static function cron_due_file( ) {186 return self::cache_root() . 'cron-due.php';301 private static function cron_due_file( $site_id = 0 ) { 302 return self::cache_root() . ( $site_id ? 'cron-due-' . (int) $site_id . '.php' : 'cron-due.php' ); 187 303 } 188 304 … … 193 309 * @param int $timestamp Unix time of the next run (0 = none pending). 194 310 * @param string $cron_url Absolute URL of wp-cron.php. 195 * @return void 196 */ 197 public static function write_cron_due( $timestamp, $cron_url ) { 311 * @param int $site_id Network site ID (0 on a single site). 312 * @return void 313 */ 314 public static function write_cron_due( $timestamp, $cron_url, $site_id = 0 ) { 198 315 $data = array( 199 316 'due' => (int) $timestamp, 200 317 'url' => (string) $cron_url, 201 318 ); 202 $file = self::cron_due_file( );319 $file = self::cron_due_file( $site_id ); 203 320 if ( is_readable( $file ) ) { 204 321 $current = include $file; … … 224 341 * after the page has been handed to the visitor where the server allows. 225 342 * 226 * @return void 227 */ 228 private static function maybe_kick_cron() { 229 $file = self::cron_due_file(); 343 * @param int $site_id Network site ID of the request (0 on a single site). 344 * @return void 345 */ 346 private static function maybe_kick_cron( $site_id = 0 ) { 347 $file = self::cron_due_file( $site_id ); 230 348 if ( ! is_readable( $file ) ) { 231 349 return; … … 235 353 return; 236 354 } 237 $lock = self::cache_root() . 'cron.lock';355 $lock = self::cache_root() . ( $site_id ? 'cron-' . (int) $site_id . '.lock' : 'cron.lock' ); 238 356 if ( file_exists( $lock ) && ( time() - (int) @filemtime( $lock ) ) < 60 ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors 239 357 return; … … 464 582 public static function path_for_request( array $config ) { 465 583 // phpcs:disable WordPress.Security.ValidatedSanitizedInput -- pre-WP drop-in; values feed a sanitized cache key only. 466 $host = isset( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : 'localhost';584 $host = isset( $_SERVER['HTTP_HOST'] ) ? strtolower( $_SERVER['HTTP_HOST'] ) : 'localhost'; // Hosts are case-insensitive; one folder per host. 467 585 $uri = isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '/'; 468 586 $scheme = ( ! empty( $_SERVER['HTTPS'] ) && 'off' !== $_SERVER['HTTPS'] ) ? 'https' : 'http'; … … 588 706 } 589 707 $parts = function_exists( 'wp_parse_url' ) ? wp_parse_url( $url ) : parse_url( $url ); // phpcs:ignore WordPress.WP.AlternativeFunctions 590 $host = isset( $parts['host'] ) ? $parts['host'] : '';708 $host = self::url_host( $parts ); 591 709 $path = isset( $parts['path'] ) ? $parts['path'] : '/'; 592 710 $path = str_replace( array( '..', "\0" ), '', $path ); … … 595 713 $dir = self::cache_root() . self::sanitize_segment( $host ) . rtrim( $path, '/' ) . '/'; 596 714 597 // The homepage's variants live directly in the host dir alongside every 598 // other page's SUBdirectory. Recursively deleting the host dir would wipe 599 // the whole site cache — so for the homepage, remove only its own .html 600 // variant files and leave the per-page subdirectories intact. 601 if ( $dir === $host_dir ) { 602 foreach ( (array) glob( $host_dir . '*.html' ) as $file ) { 715 // A site's front page has its variants directly in the site's root 716 // folder, alongside every other page's SUBdirectory. Recursively deleting 717 // that folder would wipe the whole site — so for a front page (the host 718 // root, or a network site's own root like /shop/) remove only its own 719 // .html variant files and leave the per-page subdirectories intact. 720 if ( $dir === $host_dir || in_array( $dir, self::site_roots(), true ) ) { 721 foreach ( (array) glob( $dir . '*.html' ) as $file ) { 603 722 wp_delete_file( $file ); // phpcs:ignore WordPress.PHP.NoSilencedErrors 604 723 } … … 628 747 // HTML while keeping the hashed assets keeps both sides consistent. 629 748 // (Stale assets are cleared deliberately when CSS/JS settings change.) 630 $keep = array( '.', '..', 'config.php', 'index.html', '.htaccess', 'css', 'js', 'cron-due.php', 'cron.lock' );749 $keep = array( '.', '..', 'config.php', 'index.html', '.htaccess', 'css', 'js', 'cron-due.php', 'cron.lock', 'sites' ); 631 750 foreach ( (array) scandir( $root ) as $item ) { 632 if ( in_array( $item, $keep, true ) ) {751 if ( in_array( $item, $keep, true ) || preg_match( '/^cron(?:-due)?-\d+\.(?:php|lock)$/', (string) $item ) ) { 633 752 continue; 634 753 } … … 642 761 } 643 762 763 /** 764 * Remove cache folders named after a mixed-case Host header. Since 1.1.0 765 * hosts are lowercased, so such folders are never served or purged again. 766 * 767 * @return void 768 */ 769 public static function remove_mixed_case_hosts() { 770 $root = self::cache_root(); 771 if ( ! is_dir( $root ) ) { 772 return; 773 } 774 foreach ( (array) scandir( $root ) as $item ) { 775 if ( '.' !== $item && '..' !== $item && is_dir( $root . $item ) && strtolower( $item ) !== $item ) { 776 self::rrmdir( $root . $item . '/' ); 777 } 778 } 779 } 780 781 /** 782 * Cache folders of every network site's root ("<root>/<host>/<path>/"). 783 * 784 * @return string[] 785 */ 786 public static function site_roots() { 787 $config = self::load_config(); 788 $roots = array(); 789 foreach ( array_keys( (array) $config['sites'] ) as $key ) { 790 $parts = explode( '/', (string) $key, 2 ); 791 $path = isset( $parts[1] ) ? trim( $parts[1], '/' ) : ''; 792 $roots[] = self::cache_root() . self::sanitize_segment( $parts[0] ) . ( '' !== $path ? '/' . $path : '' ) . '/'; 793 } 794 return $roots; 795 } 796 797 /** 798 * Cache folder of a site (host + path), e.g. "<root>/example.com/shop/". 799 * 800 * @param string $host Site host. 801 * @param string $path Site path ('/' or '/shop/'). 802 * @return string 803 */ 804 public static function site_dir( $host, $path ) { 805 $path = trim( str_replace( array( '..', "\0" ), '', (string) $path ), '/' ); 806 $path = preg_replace( '#[^a-zA-Z0-9/_\-.]#', '-', $path ); 807 return self::cache_root() . self::sanitize_segment( strtolower( (string) $host ) ) . ( '' !== $path ? '/' . $path : '' ) . '/'; 808 } 809 810 /** 811 * Purge one network site's pages without touching sites nested inside its 812 * folder (the main site "/" contains the folder of a subdirectory site). 813 * 814 * @param string $dir Site cache folder (from site_dir()). 815 * @return void 816 */ 817 public static function purge_site_dir( $dir ) { 818 $dir = rtrim( (string) $dir, '/' ) . '/'; 819 if ( 0 !== strpos( $dir, self::cache_root() ) || ! is_dir( $dir ) ) { 820 return; 821 } 822 $others = array_values( array_diff( self::site_roots(), array( $dir ) ) ); 823 self::purge_tree( $dir, $others ); 824 } 825 826 /** 827 * Delete cached pages under $dir, skipping (and descending towards) the 828 * folders of other sites. 829 * 830 * @param string $dir Folder with trailing slash. 831 * @param string[] $others Other sites' root folders. 832 * @return void 833 */ 834 private static function purge_tree( $dir, array $others ) { 835 foreach ( (array) scandir( $dir ) as $item ) { 836 if ( '.' === $item || '..' === $item ) { 837 continue; 838 } 839 $path = $dir . $item; 840 if ( ! is_dir( $path ) ) { 841 if ( '.html' === substr( $item, -5 ) && 'index.html' !== $item ) { 842 wp_delete_file( $path ); // phpcs:ignore WordPress.PHP.NoSilencedErrors 843 } 844 continue; 845 } 846 $sub = $path . '/'; 847 if ( in_array( $sub, $others, true ) ) { 848 continue; // Another site's folder. 849 } 850 $contains_other = false; 851 foreach ( $others as $other ) { 852 if ( 0 === strpos( $other, $sub ) ) { 853 $contains_other = true; 854 break; 855 } 856 } 857 if ( $contains_other ) { 858 self::purge_tree( $sub, $others ); 859 } else { 860 self::rrmdir( $sub ); 861 } 862 } 863 } 864 644 865 /* --------------------------------------------------------------------- * 645 866 * Filesystem helpers … … 684 905 return false; 685 906 } 907 self::forget_compiled( $file ); 686 908 return true; 909 } 910 911 /** 912 * Make PHP's opcode cache drop its compiled copy of a file we just 913 * rewrote. Config and rules files are PHP includes: without this, a server 914 * that only re-checks file times every few seconds (or never, with 915 * opcache.validate_timestamps=0) keeps running the OLD settings — caching 916 * stays off after being switched on, or a new exclusion is ignored. 917 * 918 * @param string $file Absolute path. 919 * @return void 920 */ 921 public static function forget_compiled( $file ) { 922 if ( function_exists( 'opcache_invalidate' ) ) { 923 @opcache_invalidate( $file, true ); // phpcs:ignore WordPress.PHP.NoSilencedErrors -- may be restricted by opcache.restrict_api. 924 } 687 925 } 688 926 -
optimazz/trunk/includes/Modules/Fonts/FontsModule.php
r3715895 r3743505 21 21 22 22 class FontsModule extends AbstractModule { 23 24 /** 25 * Whether parse_css2() repaired an invalid (CSS1-style) family request on 26 * the current page, so the rebuilt URL must be output even when nothing 27 * else changes. 28 * 29 * @var bool 30 */ 31 private static $repaired = false; 23 32 24 33 public function id() { … … 157 166 return $html; 158 167 } 168 self::$repaired = false; 159 169 160 170 $links = array(); // Tags we will merge (in document order). … … 173 183 } 174 184 $query = (string) wp_parse_url( html_entity_decode( $m[2], ENT_QUOTES ), PHP_URL_QUERY ); // decode first: "&" contains a "#" that would truncate the query. 185 $was = self::$repaired; 175 186 $parsed = 'css2' === strtolower( $m[3] ) ? self::parse_css2( $query ) : self::parse_css1( $query ); 176 187 if ( null === $parsed ) { 188 self::$repaired = $was; // This link is left as-is, so nothing was repaired. 177 189 continue; // unsupported form — keep as-is. 178 190 } … … 197 209 } 198 210 $trim = (bool) $this->option( 'trim_weights', true ); 199 // One link and nothing to trim — nothing to gain. 200 if ( 1 === count( $links ) && ! $trim ) { 211 $disp = '' !== $display ? $display : $orig_display; 212 // Non-blocking load only with swap / fallback. Those swap the font in 213 // anyway; 'optional' is chosen for zero layout shift, and loading its 214 // stylesheet late would make a cached font swap in after first paint. 215 $async = in_array( $disp, array( 'swap', 'fallback' ), true ) && $this->option( 'async_google', true ); 216 // One link, nothing to trim, nothing repaired and no async loading — 217 // rebuilding the URL would gain nothing (and would drop its other 218 // attributes and parameters). 219 if ( 1 === count( $links ) && ! $trim && ! $async && ! self::$repaired ) { 201 220 return $html; 202 221 } … … 208 227 $params[] = 'family=' . str_replace( '%20', '+', rawurlencode( $name ) ) . self::css2_axis( $spec, $used ); 209 228 } 210 $disp = '' !== $display ? $display : $orig_display;211 229 if ( '' !== $disp ) { 212 230 $params[] = 'display=' . $disp; 213 231 } 214 $url = 'https://fonts.googleapis.com/css2?' . implode( '&', $params ); 215 // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet -- rewriting existing output-buffer markup, not enqueueing. 216 $new = '<link rel="stylesheet" id="optimazz-google-fonts" href="' . esc_url( $url ) . '" media="all">'; 232 $url = esc_url( 'https://fonts.googleapis.com/css2?' . implode( '&', $params ) ); 233 // phpcs:disable WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet -- rewriting existing output-buffer markup, not enqueueing. 234 if ( $async ) { 235 // With swap/fallback the text renders in a fallback font first 236 // anyway, so the font stylesheet need not block the page: on 237 // mobile it otherwise holds back the first paint by the full round 238 // trip to Google. Preload it, apply it when loaded, and keep a 239 // <noscript> copy for visitors without JavaScript. 240 $new = '<link rel="preload" as="style" href="' . $url . '">' 241 . '<link rel="stylesheet" id="optimazz-google-fonts" href="' . $url . '" media="print" onload="this.media=\'all\'">' 242 . '<noscript><link rel="stylesheet" href="' . $url . '"></noscript>'; 243 } else { 244 $new = '<link rel="stylesheet" id="optimazz-google-fonts" href="' . $url . '" media="all">'; 245 } 246 // phpcs:enable WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet 217 247 218 248 // Swap the first link for the merged one; remove the rest (each once). … … 252 282 continue; 253 283 } 254 $spec = array( 'normal' => array(), 'italic' => array(), 'raw' => null ); 255 $vars = isset( $parts[1] ) ? explode( ',', $parts[1] ) : array( '400' ); 256 foreach ( $vars as $v ) { 257 $v = strtolower( trim( $v ) ); 258 if ( '' === $v ) { 259 continue; 260 } 261 $v = strtr( $v, array( 'regular' => '400', 'bold' => '700' ) ); 262 $ital = (bool) preg_match( '/(italic|i)$/', $v ); 263 $w = (int) preg_replace( '/\D/', '', $v ); 264 $w = $w ? $w : 400; // bare "italic". 265 if ( $w < 100 || $w > 900 ) { 266 continue; 267 } 268 $spec[ $ital ? 'italic' : 'normal' ][ $w ] = 1; 269 } 270 if ( empty( $spec['normal'] ) && empty( $spec['italic'] ) ) { 271 $spec['normal'][400] = 1; 272 } 273 $out[ $name ] = $spec; 284 $out[ $name ] = self::parse_variants( isset( $parts[1] ) ? $parts[1] : '400' ); 274 285 } 275 286 } 276 287 return $out ? $out : null; 288 } 289 290 /** 291 * Parse a CSS1-style variant list ("400,700i,bold,italic") into a spec. 292 * 293 * @param string $list Comma-separated variants. 294 * @return array{normal:array,italic:array,raw:null} 295 */ 296 private static function parse_variants( $list ) { 297 $spec = array( 'normal' => array(), 'italic' => array(), 'raw' => null ); 298 foreach ( explode( ',', $list ) as $v ) { 299 $v = strtolower( trim( $v ) ); 300 if ( '' === $v ) { 301 continue; 302 } 303 $v = strtr( $v, array( 'regular' => '400', 'bold' => '700' ) ); 304 $ital = (bool) preg_match( '/(italic|i)$/', $v ); 305 $w = (int) preg_replace( '/\D/', '', $v ); 306 $w = $w ? $w : 400; // bare "italic". 307 if ( $w < 100 || $w > 900 ) { 308 continue; 309 } 310 $spec[ $ital ? 'italic' : 'normal' ][ $w ] = 1; 311 } 312 if ( empty( $spec['normal'] ) && empty( $spec['italic'] ) ) { 313 $spec['normal'][400] = 1; 314 } 315 return $spec; 277 316 } 278 317 … … 314 353 $spec['normal'][ (int) $w ] = 1; 315 354 } 355 } elseif ( preg_match( '/^(?:w?\d{3}i?|regular|bold|italic|bolditalic|\d{3}italic)(?:,(?:w?\d{3}i?|regular|bold|italic|bolditalic|\d{3}italic))*$/i', trim( $parts[1] ) ) ) { 356 // Old CSS1-style weights ("400,700", or the "w400,700" some themes 357 // print) sent to the CSS2 API. Google rejects these with HTTP 400, so 358 // the font never loads while the broken stylesheet still blocks 359 // rendering. Read them the CSS1 way so a valid request is built. 360 $spec = self::parse_variants( str_ireplace( 'w', '', $parts[1] ) ); 361 self::$repaired = true; 316 362 } else { 317 363 $spec['raw'] = $parts[1]; // variable ranges / other axes — keep verbatim. -
optimazz/trunk/includes/Modules/Js/JsModule.php
r3692106 r3743505 239 239 'tawk.to', 'crisp.chat', 'widget.intercom.io', 'js.intercomcdn.com', 240 240 'code.tidio.co', 'cdn.livechatinc.com', 'embed.tawk.to', 'messenger/customerchat', 241 'cdn.openwidget.com', 'static.zdassets.com', 'js.driftt.com', 'wchat.freshchat.com', 242 'widget.freshworks.com', 'code.jivosite.com', 'smartsuppchat.com', 'embed.small.chat', 241 243 // Marketing / CRM. 242 244 'js.hs-scripts.com', 'js.hsforms.net', 'cdn.onesignal.com', -
optimazz/trunk/languages/optimazz.pot
r3723128 r3743505 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: Optimazz – Website Optimization and Speedup 1. 0.24\n"5 "Project-Id-Version: Optimazz – Website Optimization and Speedup 1.1.0\n" 6 6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/optimazz\n" 7 7 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" … … 10 10 "Content-Type: text/plain; charset=UTF-8\n" 11 11 "Content-Transfer-Encoding: 8bit\n" 12 "POT-Creation-Date: 2026-10- 01T12:01:49+00:00\n"12 "POT-Creation-Date: 2026-10-11T19:05:58+00:00\n" 13 13 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 14 "X-Generator: WP-CLI 2.12.0\n" … … 40 40 msgstr "" 41 41 42 #: includes/Admin/Admin.php:149 43 #: includes/Admin/Admin.php:150 42 #: includes/Admin/Admin.php:155 43 #: includes/Admin/Admin.php:156 44 #: includes/Admin/Admin.php:290 45 #: includes/Admin/Admin.php:291 44 46 msgid "Optimazz" 45 47 msgstr "" 46 48 47 #: includes/Admin/Admin.php:1 6649 #: includes/Admin/Admin.php:172 48 50 #: src/index.js:36 49 51 msgid "Dashboard" 50 52 msgstr "" 51 53 52 #: includes/Admin/Admin.php:1 6754 #: includes/Admin/Admin.php:173 53 55 #: includes/Modules/Cache/CacheModule.php:24 54 56 #: src/index.js:37 55 #: src/index.js:1171 57 #: src/index.js:1317 58 #: src/index.js:2075 59 #: src/index.js:2091 56 60 msgid "Page Cache" 57 61 msgstr "" 58 62 59 #: includes/Admin/Admin.php:1 6863 #: includes/Admin/Admin.php:174 60 64 #: src/index.js:38 61 #: src/index.js:1 15065 #: src/index.js:1296 62 66 msgid "File Optimization" 63 67 msgstr "" 64 68 65 #: includes/Admin/Admin.php:1 6969 #: includes/Admin/Admin.php:175 66 70 #: src/index.js:39 67 71 msgid "Media" 68 72 msgstr "" 69 73 70 #: includes/Admin/Admin.php:17 074 #: includes/Admin/Admin.php:176 71 75 #: includes/Modules/Database/DatabaseModule.php:26 72 76 #: src/index.js:43 … … 74 78 msgstr "" 75 79 76 #: includes/Admin/Admin.php:17 177 #: includes/Admin/Admin.php:2 2978 #: includes/Admin/Admin.php:23 380 #: includes/Admin/Admin.php:177 81 #: includes/Admin/Admin.php:235 82 #: includes/Admin/Admin.php:239 79 83 #: src/index.js:47 80 84 msgid "Tools" 81 85 msgstr "" 82 86 83 #: includes/Admin/Admin.php:19 387 #: includes/Admin/Admin.php:199 84 88 #: includes/Admin/AdminBar.php:52 89 #: src/index.js:2198 85 90 msgid "Settings" 86 91 msgstr "" 87 92 88 #: includes/Admin/Admin.php:2 2493 #: includes/Admin/Admin.php:230 89 94 msgid "Optimazz Safe Mode is on." 90 95 msgstr "" 91 96 92 #: includes/Admin/Admin.php:2 2597 #: includes/Admin/Admin.php:231 93 98 msgid "Some pages were loading without styling — their optimized CSS could not be found, usually because a server or CDN cache was serving old pages. Optimazz paused advanced CSS/JS optimization to keep your site working." 94 99 msgstr "" 95 100 96 #: includes/Admin/Admin.php:2 27101 #: includes/Admin/Admin.php:233 97 102 msgid "It broke again shortly after full optimization was restored, so Safe Mode will stay on until you switch it off in" 98 103 msgstr "" 99 104 100 #: includes/Admin/Admin.php:2 28105 #: includes/Admin/Admin.php:234 101 106 msgid "It re-checks automatically and restores full optimization as soon as pages load correctly — clearing your server/CDN cache speeds that up. You can also manage it in" 102 107 msgstr "" 103 108 104 #: includes/Admin/Admin.php:23 2109 #: includes/Admin/Admin.php:238 105 110 msgid "Your pages are loading correctly again — you can switch Safe Mode off in" 106 111 msgstr "" 107 112 108 #: includes/Admin/Admin.php:2 36113 #: includes/Admin/Admin.php:242 109 114 msgid "Pages are loading correctly again, so Safe Mode was switched off automatically and full optimization is back on." 110 115 msgstr "" 111 116 112 #: includes/Admin/Admin.php:25 0117 #: includes/Admin/Admin.php:256 113 118 msgid "wp-config.php is not writable, so page caching could not be enabled. Add" 114 119 msgstr "" 115 120 116 #: includes/Admin/Admin.php:25 2121 #: includes/Admin/Admin.php:258 117 122 msgid "manually, or make wp-config.php writable." 118 123 msgstr "" 119 124 120 #: includes/Admin/Admin.php:2 59125 #: includes/Admin/Admin.php:265 121 126 msgid "Optimazz cache purged." 122 127 msgstr "" 123 128 124 #: includes/Admin/Admin.php:26 0129 #: includes/Admin/Admin.php:266 125 130 msgid "Optimazz cache preload started in the background." 126 131 msgstr "" 127 132 128 #: includes/Admin/Admin.php:26 1133 #: includes/Admin/Admin.php:267 129 134 msgid "Optimazz cache cleared; preload started in the background." 130 135 msgstr "" 131 136 132 #: includes/Admin/AdminBar.php:5 3137 #: includes/Admin/AdminBar.php:57 133 138 msgid "Clear & Preload Cache" 134 139 msgstr "" 135 140 136 #: includes/Admin/AdminBar.php:5 4137 #: src/index.js:1 451141 #: includes/Admin/AdminBar.php:58 142 #: src/index.js:1597 138 143 msgid "Preload Cache" 139 144 msgstr "" 140 145 141 #: includes/Admin/AdminBar.php:5 5146 #: includes/Admin/AdminBar.php:59 142 147 msgid "Purge Cache" 143 148 msgstr "" 144 149 145 #: includes/Admin/AdminBar.php:7 3150 #: includes/Admin/AdminBar.php:77 146 151 msgid "Documentation" 147 152 msgstr "" 148 153 149 #: includes/Admin/AdminBar.php: 98150 #: includes/Admin/CacheConflictNotice.php:12 4151 #: includes/Admin/CacheConflictNotice.php:15 4154 #: includes/Admin/AdminBar.php:102 155 #: includes/Admin/CacheConflictNotice.php:126 156 #: includes/Admin/CacheConflictNotice.php:156 152 157 msgid "Not allowed." 153 158 msgstr "" … … 162 167 163 168 #: includes/Admin/CacheConflictNotice.php:88 164 #: includes/Modules/Cache/Preloader.php:714 169 #: includes/Modules/Cache/Preloader.php:740 170 #: src/index.js:2136 165 171 msgid "Another plugin" 166 172 msgstr "" … … 180 186 #: includes/Admin/CacheConflictNotice.php:108 181 187 #: includes/Admin/CacheConflictNotice.php:110 188 #: src/index.js:2140 182 189 msgid "Use Optimazz for caching" 183 190 msgstr "" … … 187 194 msgstr "" 188 195 189 #: includes/Admin/RestController.php:467 196 #: includes/Admin/NetworkRestController.php:115 197 #: includes/Admin/NetworkRestController.php:134 198 msgid "Page caching is off for this site." 199 msgstr "" 200 201 #: includes/Admin/NetworkRestController.php:153 202 msgid "Optimazz is not active on any site yet. Network-activate it (or activate it on a site) first." 203 msgstr "" 204 205 #: includes/Admin/RestController.php:385 206 #: includes/Admin/RestController.php:588 207 #: includes/Admin/RestController.php:614 208 msgid "Page caching is not available on multisite networks." 209 msgstr "" 210 211 #: includes/Admin/RestController.php:479 190 212 msgid "PHP version" 191 213 msgstr "" 192 214 193 #: includes/Admin/RestController.php:4 70215 #: includes/Admin/RestController.php:482 194 216 msgid "Optimazz requires PHP 7.4 or newer." 195 217 msgstr "" 196 218 197 #: includes/Admin/RestController.php:4 73219 #: includes/Admin/RestController.php:485 198 220 msgid "WordPress version" 199 221 msgstr "" 200 222 201 #: includes/Admin/RestController.php:4 79223 #: includes/Admin/RestController.php:491 202 224 msgid "GD (image library)" 203 225 msgstr "" 204 226 205 #: includes/Admin/RestController.php:4 80206 #: includes/Admin/RestController.php: 489207 #: includes/Admin/RestController.php:5 25227 #: includes/Admin/RestController.php:492 228 #: includes/Admin/RestController.php:501 229 #: includes/Admin/RestController.php:537 208 230 msgid "Enabled" 209 231 msgstr "" 210 232 211 #: includes/Admin/RestController.php:4 80212 #: includes/Admin/RestController.php: 489233 #: includes/Admin/RestController.php:492 234 #: includes/Admin/RestController.php:501 213 235 msgid "Not installed (optional)" 214 236 msgstr "" 215 237 216 #: includes/Admin/RestController.php:4 84238 #: includes/Admin/RestController.php:496 217 239 msgid "GD is enabled but without WebP support — ask your host to enable WebP in GD." 218 240 msgstr "" 219 241 220 #: includes/Admin/RestController.php:4 85242 #: includes/Admin/RestController.php:497 221 243 msgid "The PHP GD extension is not enabled. GD is the most common engine for image compression and WebP. Ask your host to enable the \"php-gd\" extension." 222 244 msgstr "" 223 245 224 #: includes/Admin/RestController.php: 488246 #: includes/Admin/RestController.php:500 225 247 msgid "ImageMagick (Imagick)" 226 248 msgstr "" 227 249 228 #: includes/Admin/RestController.php: 489250 #: includes/Admin/RestController.php:501 229 251 msgid "Not enabled" 230 252 msgstr "" 231 253 232 #: includes/Admin/RestController.php: 496254 #: includes/Admin/RestController.php:508 233 255 msgid "Optional — GD is already handling images. Imagick can add AVIF and higher-quality output if your host enables the \"php-imagick\" extension." 234 256 msgstr "" 235 257 236 #: includes/Admin/RestController.php: 497258 #: includes/Admin/RestController.php:509 237 259 msgid "The PHP Imagick extension is not enabled either. With neither GD nor Imagick, image compression and WebP/AVIF cannot run. Ask your host to enable \"php-gd\" or \"php-imagick\"." 238 260 msgstr "" 239 261 240 #: includes/Admin/RestController.php:5 00262 #: includes/Admin/RestController.php:512 241 263 msgid "WebP support" 242 264 msgstr "" 243 265 244 #: includes/Admin/RestController.php:5 01245 #: includes/Admin/RestController.php:5 07266 #: includes/Admin/RestController.php:513 267 #: includes/Admin/RestController.php:519 246 268 msgid "Available" 247 269 msgstr "" 248 270 249 #: includes/Admin/RestController.php:5 01250 #: includes/Admin/RestController.php:5 07271 #: includes/Admin/RestController.php:513 272 #: includes/Admin/RestController.php:519 251 273 msgid "Not available" 252 274 msgstr "" 253 275 254 #: includes/Admin/RestController.php:5 03276 #: includes/Admin/RestController.php:515 255 277 msgid "This server cannot generate WebP images. Update GD/Imagick or ask your host to enable WebP." 256 278 msgstr "" 257 279 258 #: includes/Admin/RestController.php:5 06280 #: includes/Admin/RestController.php:518 259 281 msgid "AVIF support" 260 282 msgstr "" 261 283 262 #: includes/Admin/RestController.php:5 09284 #: includes/Admin/RestController.php:521 263 285 msgid "This server cannot generate AVIF images (needs PHP 8.1+ GD with AVIF, or ImageMagick with the AVIF delegate). WebP still works." 264 286 msgstr "" 265 287 266 #: includes/Admin/RestController.php:5 12288 #: includes/Admin/RestController.php:524 267 289 msgid "PHP memory limit" 268 290 msgstr "" 269 291 270 #: includes/Admin/RestController.php:5 15292 #: includes/Admin/RestController.php:527 271 293 msgid "Image optimization works best with 256 MB or more. On lower limits Optimazz processes fewer images per request to stay safe." 272 294 msgstr "" 273 295 274 #: includes/Admin/RestController.php:5 18296 #: includes/Admin/RestController.php:530 275 297 msgid "Max execution time" 276 298 msgstr "" 277 299 278 #: includes/Admin/RestController.php:5 19300 #: includes/Admin/RestController.php:531 279 301 msgid "Unlimited" 280 302 msgstr "" 281 303 282 #: includes/Admin/RestController.php:5 21304 #: includes/Admin/RestController.php:533 283 305 msgid "Under 30 seconds can interrupt bulk AVIF conversion of large images." 284 306 msgstr "" 285 307 286 #: includes/Admin/RestController.php:5 24308 #: includes/Admin/RestController.php:536 287 309 msgid "WP-Cron" 288 310 msgstr "" 289 311 290 #: includes/Admin/RestController.php:5 25312 #: includes/Admin/RestController.php:537 291 313 msgid "Disabled" 292 314 msgstr "" 293 315 294 #: includes/Admin/RestController.php:5 27316 #: includes/Admin/RestController.php:539 295 317 msgid "WP-Cron is disabled, so scheduled cache preloading relies on a real system cron job being configured." 296 318 msgstr "" 297 319 298 #: includes/Admin/RestController.php:5 41320 #: includes/Admin/RestController.php:553 299 321 msgid "GD and Imagick are" 300 322 msgstr "" 301 323 302 #: includes/Admin/RestController.php:5 42324 #: includes/Admin/RestController.php:554 303 325 msgid "GD is" 304 326 msgstr "" 305 327 306 #: includes/Admin/RestController.php:5 42328 #: includes/Admin/RestController.php:554 307 329 msgid "Imagick is" 308 330 msgstr "" 309 331 310 #: includes/Admin/RestController.php:5 44332 #: includes/Admin/RestController.php:556 311 333 msgid "PHP that runs this site" 312 334 msgstr "" 313 335 314 336 #. translators: 1: which engine is missing, 2: php.ini path, 3: PHP version 315 #: includes/Admin/RestController.php:5 50337 #: includes/Admin/RestController.php:562 316 338 #, php-format 317 339 msgid "This is the exact PHP handling your site — it loads its settings from %2$s. If you already enabled an image library in your hosting panel but %1$s still shown as not enabled, you almost certainly enabled it for a different PHP version or handler. It must be enabled for THIS PHP (%3$s). After enabling, restart PHP (or PHP-FPM) and click Re-check." … … 319 341 320 342 #. translators: 1: which engine is missing, 2: PHP version 321 #: includes/Admin/RestController.php:5 57343 #: includes/Admin/RestController.php:569 322 344 #, php-format 323 345 msgid "This is the exact PHP handling your site. If you already enabled an image library in your hosting panel but %1$s still shown as not enabled, you enabled it for a different PHP version. It must be enabled for THIS PHP (%2$s). After enabling, restart PHP and click Re-check." 324 346 msgstr "" 325 347 326 #: includes/Admin/RestController.php:7 07348 #: includes/Admin/RestController.php:725 327 349 msgid "Image processing is unavailable: this server has no GD or ImageMagick extension. Ask your host to enable \"php-gd\" (or \"php-imagick\"), then try again. See Tools → Server Requirements." 328 350 msgstr "" … … 336 358 msgstr "" 337 359 338 #: includes/Core/Recommendations.php:6 1339 #: src/index.js: 971360 #: includes/Core/Recommendations.php:63 361 #: src/index.js:1117 340 362 msgid "Enable Page Cache" 341 363 msgstr "" 342 364 343 #: includes/Core/Recommendations.php:6 2365 #: includes/Core/Recommendations.php:64 344 366 msgid "Serve static HTML instead of running PHP on every request — the single biggest speed win." 345 367 msgstr "" 346 368 347 #: includes/Core/Recommendations.php:6 7369 #: includes/Core/Recommendations.php:69 348 370 msgid "Turn on cache preloading" 349 371 msgstr "" 350 372 351 #: includes/Core/Recommendations.php: 68373 #: includes/Core/Recommendations.php:70 352 374 msgid "Warm the cache automatically so visitors never hit a cold, uncached page." 353 375 msgstr "" 354 376 355 #: includes/Core/Recommendations.php:7 6377 #: includes/Core/Recommendations.php:78 356 378 msgid "Enable JavaScript optimization" 357 379 msgstr "" 358 380 359 #: includes/Core/Recommendations.php:7 7381 #: includes/Core/Recommendations.php:79 360 382 msgid "Defer and delay scripts to cut Total Blocking Time — the heaviest-weighted mobile metric." 361 383 msgstr "" 362 384 363 #: includes/Core/Recommendations.php:8 4385 #: includes/Core/Recommendations.php:86 364 386 msgid "Delay JavaScript until interaction" 365 387 msgstr "" 366 388 367 #: includes/Core/Recommendations.php:8 5389 #: includes/Core/Recommendations.php:87 368 390 msgid "Delay non-critical scripts (analytics, chat, pixels) until the first interaction to reduce blocking time." 369 391 msgstr "" 370 392 371 #: includes/Core/Recommendations.php:9 3393 #: includes/Core/Recommendations.php:95 372 394 msgid "Enable CSS optimization" 373 395 msgstr "" 374 396 375 #: includes/Core/Recommendations.php:9 4397 #: includes/Core/Recommendations.php:96 376 398 msgid "Minify CSS to remove render-blocking bytes and speed up First Contentful Paint." 377 399 msgstr "" 378 400 379 #: includes/Core/Recommendations.php:10 2401 #: includes/Core/Recommendations.php:104 380 402 msgid "Enable Image optimization" 381 403 msgstr "" 382 404 383 #: includes/Core/Recommendations.php:10 3405 #: includes/Core/Recommendations.php:105 384 406 msgid "Lazy-load images, reserve their space (to prevent layout shift) and serve next-gen formats." 385 407 msgstr "" 386 408 387 #: includes/Core/Recommendations.php:1 09409 #: includes/Core/Recommendations.php:111 388 410 msgid "Reserve image space (fix layout shift)" 389 411 msgstr "" 390 412 391 #: includes/Core/Recommendations.php:11 0413 #: includes/Core/Recommendations.php:112 392 414 msgid "Add missing image dimensions and aspect-ratio so images do not push content down as they load (lowers CLS)." 393 415 msgstr "" 394 416 395 #: includes/Core/Recommendations.php:11 6396 #: src/index.js: 948397 #: src/index.js:1 369417 #: includes/Core/Recommendations.php:118 418 #: src/index.js:1092 419 #: src/index.js:1515 398 420 msgid "Lazy-load images" 399 421 msgstr "" 400 422 401 #: includes/Core/Recommendations.php:11 7423 #: includes/Core/Recommendations.php:119 402 424 msgid "Load off-screen images only when scrolled into view to speed up the initial load." 403 425 msgstr "" 404 426 405 #: includes/Core/Recommendations.php:12 4427 #: includes/Core/Recommendations.php:126 406 428 msgid "Serve WebP images" 407 429 msgstr "" 408 430 409 #: includes/Core/Recommendations.php:12 5431 #: includes/Core/Recommendations.php:127 410 432 msgid "Generate and serve WebP copies — typically 25–35% smaller than JPEG/PNG at the same quality." 411 433 msgstr "" 412 434 413 #: includes/Core/Recommendations.php:13 6435 #: includes/Core/Recommendations.php:138 414 436 msgid "Set font loading to \"Optional\" (reduce CLS)" 415 437 msgstr "" 416 438 417 #: includes/Core/Recommendations.php:13 7439 #: includes/Core/Recommendations.php:139 418 440 msgid "The \"swap\" mode lets web fonts reflow the page as they load, raising Cumulative Layout Shift. \"Optional\" removes that shift." 419 441 msgstr "" 420 442 421 #: includes/Core/Recommendations.php:14 3443 #: includes/Core/Recommendations.php:145 422 444 msgid "Enable Font optimization" 423 445 msgstr "" 424 446 425 #: includes/Core/Recommendations.php:14 4447 #: includes/Core/Recommendations.php:146 426 448 msgid "Preconnect to font hosts and control font-display to reduce render-blocking and layout shift." 427 449 msgstr "" 428 450 429 #: includes/Core/Recommendations.php:15 2451 #: includes/Core/Recommendations.php:154 430 452 msgid "Enable Database cleanup" 431 453 msgstr "" 432 454 433 #: includes/Core/Recommendations.php:15 3455 #: includes/Core/Recommendations.php:155 434 456 msgid "Remove revisions, auto-drafts, spam and expired transients, and optimize tables to keep the database lean." 435 457 msgstr "" 436 458 437 #: includes/Core/Recommendations.php:16 1459 #: includes/Core/Recommendations.php:163 438 460 msgid "Enable an image library on your server" 439 461 msgstr "" 440 462 441 #: includes/Core/Recommendations.php:16 2463 #: includes/Core/Recommendations.php:164 442 464 msgid "Your server has neither GD nor ImageMagick, so image compression and WebP/AVIF cannot run. Ask your host to enable the \"php-gd\" (or \"php-imagick\") extension. See Tools → Server Requirements." 443 465 msgstr "" 444 466 467 #: includes/Modules/Cache/Preloader.php:116 468 msgid "the home page" 469 msgstr "" 470 445 471 #. translators: %d: number of category / tag archive pages. 446 #: includes/Modules/Cache/Preloader.php:12 4472 #: includes/Modules/Cache/Preloader.php:127 447 473 #, php-format 448 474 msgid "%d category or tag page" … … 451 477 msgstr[1] "" 452 478 453 #: includes/Modules/Cache/Preloader.php: 681479 #: includes/Modules/Cache/Preloader.php:707 454 480 msgid "your host's server cache" 455 481 msgstr "" 456 482 457 #: includes/Modules/Cache/Preloader.php: 685483 #: includes/Modules/Cache/Preloader.php:711 458 484 msgid "a server or CDN cache" 459 485 msgstr "" 460 486 461 #: includes/Modules/Cache/Preloader.php:7 04487 #: includes/Modules/Cache/Preloader.php:730 462 488 msgid "The site could not load its own pages (the loopback request failed or timed out). Some hosts block a server from requesting its own website — ask your host to allow it." 463 489 msgstr "" 464 490 465 491 #. translators: %s: name of another caching plugin. 466 #: includes/Modules/Cache/Preloader.php:7 13492 #: includes/Modules/Cache/Preloader.php:739 467 493 #, php-format 468 494 msgid "%s controls page caching (advanced-cache.php), so Optimazz cannot save pages. Deactivate it, or use \"Take over caching\" in Cache health." 469 495 msgstr "" 470 496 471 #: includes/Modules/Cache/Preloader.php:7 18497 #: includes/Modules/Cache/Preloader.php:744 472 498 msgid "The cache file advanced-cache.php is missing. Turn Page Cache off and on again to reinstall it, and make sure the wp-content folder is writable." 473 499 msgstr "" 474 500 475 #: includes/Modules/Cache/Preloader.php:7 21501 #: includes/Modules/Cache/Preloader.php:747 476 502 msgid "WP_CACHE is not enabled in wp-config.php, so WordPress never loads the cache. Make wp-config.php writable and save the Cache settings, or add define( 'WP_CACHE', true ); yourself." 477 503 msgstr "" 478 504 479 505 #. translators: %s: name of a server or CDN cache, e.g. "Cloudflare". 480 #: includes/Modules/Cache/Preloader.php:7 26506 #: includes/Modules/Cache/Preloader.php:752 481 507 #, php-format 482 508 msgid "%s answered from its own cache before WordPress ran, so Optimazz never saw these requests. Purge that cache, or turn it off for HTML pages if you want Optimazz to cache them." 483 509 msgstr "" 484 510 485 #: includes/Modules/Cache/Preloader.php:7 30511 #: includes/Modules/Cache/Preloader.php:756 486 512 msgid "The Optimazz cache engine did not run for these requests. Usually a server-level cache or proxy (for example LiteSpeed, Varnish or a host cache) answers before WordPress loads. Check Cache health, or ask your host." 487 513 msgstr "" 488 514 489 515 #. translators: %s: HTTP status code, e.g. 404. 490 #: includes/Modules/Cache/Preloader.php:7 37516 #: includes/Modules/Cache/Preloader.php:763 491 517 #, php-format 492 518 msgid "Pages answered with HTTP %s instead of 200, and only normal pages are cached. Check that these URLs open correctly." 493 519 msgstr "" 494 520 495 #: includes/Modules/Cache/Preloader.php:7 42521 #: includes/Modules/Cache/Preloader.php:768 496 522 msgid "Page Cache is switched off." 497 523 msgstr "" 498 524 499 #: includes/Modules/Cache/Preloader.php:7 43525 #: includes/Modules/Cache/Preloader.php:769 500 526 msgid "Another plugin or the theme marks these pages \"do not cache\" (DONOTCACHEPAGE), so they are skipped. Membership, security or form plugins often do this." 501 527 msgstr "" 502 528 503 #: includes/Modules/Cache/Preloader.php:7 44529 #: includes/Modules/Cache/Preloader.php:770 504 530 msgid "The web address used to reach the site does not match the WordPress Site Address (for example www vs non-www). Check Settings → General." 505 531 msgstr "" 506 532 507 #: includes/Modules/Cache/Preloader.php:7 45533 #: includes/Modules/Cache/Preloader.php:771 508 534 msgid "Cache files could not be written. Make the wp-content/cache folder writable (permissions 755)." 509 535 msgstr "" 510 536 511 #: includes/Modules/Cache/Preloader.php:7 46537 #: includes/Modules/Cache/Preloader.php:772 512 538 msgid "These pages match your \"Never cache these URLs\" setting." 513 539 msgstr "" 514 540 515 #: includes/Modules/Cache/Preloader.php:7 47541 #: includes/Modules/Cache/Preloader.php:773 516 542 msgid "A cookie on the request matches your \"Never cache when these cookies are set\" setting." 517 543 msgstr "" 518 544 519 #: includes/Modules/Cache/Preloader.php:7 48545 #: includes/Modules/Cache/Preloader.php:774 520 546 msgid "The preloader's user-agent matches your \"Never cache for these user agents\" setting." 521 547 msgstr "" 522 548 523 #: includes/Modules/Cache/Preloader.php:7 49549 #: includes/Modules/Cache/Preloader.php:775 524 550 msgid "The pages came back empty or almost empty, so there was nothing to cache." 525 551 msgstr "" 526 552 527 #: includes/Modules/Cache/Preloader.php:7 50553 #: includes/Modules/Cache/Preloader.php:776 528 554 msgid "WordPress treated the request as logged in, so it was not cached." 529 555 msgstr "" 530 556 531 #: includes/Modules/Cache/Preloader.php:7 51557 #: includes/Modules/Cache/Preloader.php:777 532 558 msgid "These URLs show a \"not found\" (404) page, which is never cached." 533 559 msgstr "" 534 560 535 561 #. translators: %s: internal reason code. 536 #: includes/Modules/Cache/Preloader.php:7 57562 #: includes/Modules/Cache/Preloader.php:783 537 563 #, php-format 538 564 msgid "The cache skipped these pages (reason: %s)." … … 552 578 msgstr "" 553 579 554 #: includes/Modules/Fonts/FontsModule.php: 29580 #: includes/Modules/Fonts/FontsModule.php:38 555 581 #: src/index.js:40 556 #: src/index.js:1 570582 #: src/index.js:1726 557 583 msgid "Fonts" 558 584 msgstr "" … … 568 594 569 595 #: includes/Modules/Media/MediaModule.php:75 570 #: src/index.js:1 359596 #: src/index.js:1505 571 597 msgid "Image Optimization" 572 598 msgstr "" … … 578 604 #: includes/Modules/Tweaks/TweaksModule.php:25 579 605 #: src/index.js:45 580 #: src/index.js:1 679606 #: src/index.js:1841 581 607 msgid "Tweaks" 582 608 msgstr "" … … 603 629 604 630 #: src/index.js:41 605 #: src/index.js:1055 631 #: src/index.js:1201 632 #: src/index.js:2191 606 633 msgid "Preload" 607 634 msgstr "" … … 612 639 613 640 #: src/index.js:42 614 #: src/index.js:1 482641 #: src/index.js:1638 615 642 msgid "Advanced Rules" 616 643 msgstr "" … … 640 667 msgstr "" 641 668 642 #: src/index.js:64 669 #: src/index.js:81 670 msgid "every hour" 671 msgstr "" 672 673 #: src/index.js:82 674 #: src/index.js:84 675 msgid "twice a day" 676 msgstr "" 677 678 #: src/index.js:83 679 msgid "once a day" 680 msgstr "" 681 682 #: src/index.js:88 683 msgid "never expires" 684 msgstr "" 685 686 #: src/index.js:89 687 #: src/index.js:1231 688 #: src/index.js:1333 689 #: src/index.js:2107 690 msgid "12 hours" 691 msgstr "" 692 693 #: src/index.js:90 694 #: src/index.js:1232 695 #: src/index.js:1334 696 #: src/index.js:2108 697 msgid "1 day" 698 msgstr "" 699 700 #: src/index.js:91 701 #: src/index.js:1233 702 #: src/index.js:1335 703 #: src/index.js:2109 704 msgid "1 week" 705 msgstr "" 706 707 #: src/index.js:92 708 #: src/index.js:1234 709 #: src/index.js:1336 710 #: src/index.js:2110 711 msgid "1 month" 712 msgstr "" 713 714 #. translators: %d: number of hours. 715 #: src/index.js:95 716 #: src/index.js:1237 717 #, js-format 718 msgid "%d hour" 719 msgid_plural "%d hours" 720 msgstr[0] "" 721 msgstr[1] "" 722 723 #: src/index.js:102 724 msgid "Open Network Admin → Optimazz" 725 msgstr "" 726 727 #: src/index.js:111 728 msgid "Page caching is off for this network" 729 msgstr "" 730 731 #: src/index.js:113 732 msgid "Turn it on for every site in Network Admin → Optimazz. Each site gets its own cache, and keeps its own exclusions here under Advanced Rules." 733 msgstr "" 734 735 #: src/index.js:114 736 msgid "Your network administrator can turn it on for every site in Network Admin → Optimazz. All other optimizations — CSS, JavaScript, images, fonts and more — work normally on this site." 737 msgstr "" 738 739 #: src/index.js:128 740 msgid "Page caching is on — managed for the whole network" 741 msgstr "" 742 743 #. translators: 1: yes/no, 2: cache lifetime, 3: preload frequency. 744 #: src/index.js:131 745 #, js-format 746 msgid "Separate mobile cache: %1$s · Cache lifetime: %2$s · Automatic preload: %3$s." 747 msgstr "" 748 749 #: src/index.js:132 750 msgid "yes" 751 msgstr "" 752 753 #: src/index.js:132 754 msgid "no" 755 msgstr "" 756 757 #: src/index.js:134 758 #: src/index.js:1247 759 msgid "off" 760 msgstr "" 761 762 #: src/index.js:136 763 msgid "This site has its own cache. Purge or preload it from the Dashboard or the Preload screen, and set its own exclusions under Advanced Rules." 764 msgstr "" 765 766 #: src/index.js:146 643 767 msgid "Content leftovers" 644 768 msgstr "" 645 769 646 #: src/index.js: 65770 #: src/index.js:147 647 771 msgid "Old post data that piles up over time. Removing it is permanent, so keep anything you still need." 648 772 msgstr "" 649 773 650 #: src/index.js: 67774 #: src/index.js:149 651 775 msgid "Post revisions" 652 776 msgstr "" 653 777 654 #: src/index.js: 67778 #: src/index.js:149 655 779 msgid "Old copies of posts saved automatically. Deleting them keeps only your current content." 656 780 msgstr "" 657 781 658 #: src/index.js: 68782 #: src/index.js:150 659 783 msgid "Auto drafts" 660 784 msgstr "" 661 785 662 #: src/index.js: 68786 #: src/index.js:150 663 787 msgid "Empty drafts WordPress creates while you write. Safe to remove." 664 788 msgstr "" 665 789 666 #: src/index.js: 69790 #: src/index.js:151 667 791 msgid "Trashed posts" 668 792 msgstr "" 669 793 670 #: src/index.js: 69794 #: src/index.js:151 671 795 msgid "Posts in the trash that have not been permanently deleted yet." 672 796 msgstr "" 673 797 674 #: src/index.js: 73798 #: src/index.js:155 675 799 msgid "Comment clutter" 676 800 msgstr "" 677 801 678 #: src/index.js: 74802 #: src/index.js:156 679 803 msgid "Spam and trashed comments you are never going to approve. Clearing them keeps the comments table light." 680 804 msgstr "" 681 805 682 #: src/index.js: 76806 #: src/index.js:158 683 807 msgid "Spam comments" 684 808 msgstr "" 685 809 686 #: src/index.js: 76810 #: src/index.js:158 687 811 msgid "Comments marked as spam. Removing them frees up the comments table." 688 812 msgstr "" 689 813 690 #: src/index.js: 77814 #: src/index.js:159 691 815 msgid "Trashed comments" 692 816 msgstr "" 693 817 694 #: src/index.js: 77818 #: src/index.js:159 695 819 msgid "Comments in the trash awaiting permanent deletion." 696 820 msgstr "" 697 821 698 #: src/index.js: 81822 #: src/index.js:163 699 823 msgid "Temporary data" 700 824 msgstr "" 701 825 702 #: src/index.js: 82826 #: src/index.js:164 703 827 msgid "Short-lived cached values. They are safe to clear and are rebuilt automatically whenever a plugin needs them." 704 828 msgstr "" 705 829 706 #: src/index.js: 84830 #: src/index.js:166 707 831 msgid "Expired transients" 708 832 msgstr "" 709 833 710 #: src/index.js: 84834 #: src/index.js:166 711 835 msgid "Cached values that have already passed their expiry date." 712 836 msgstr "" 713 837 714 #: src/index.js: 88838 #: src/index.js:170 715 839 msgid "Table maintenance" 716 840 msgstr "" 717 841 718 #: src/index.js: 89842 #: src/index.js:171 719 843 msgid "Defragment your tables to reclaim wasted space and trim database overhead." 720 844 msgstr "" 721 845 722 #: src/index.js: 91846 #: src/index.js:173 723 847 msgid "Optimize database tables" 724 848 msgstr "" 725 849 726 #: src/index.js: 91850 #: src/index.js:173 727 851 msgid "Runs OPTIMIZE on your tables to defragment them and free wasted space." 728 852 msgstr "" 729 853 730 #: src/index.js: 97854 #: src/index.js:179 731 855 msgid "Do not limit" 732 856 msgstr "" 733 857 734 #: src/index.js: 98858 #: src/index.js:180 735 859 msgid "Reduce activity" 736 860 msgstr "" 737 861 738 #: src/index.js: 99862 #: src/index.js:181 739 863 msgid "Disable" 740 864 msgstr "" 741 865 742 #: src/index.js:1 08866 #: src/index.js:190 743 867 msgid "Active" 744 868 msgstr "" 745 869 746 #: src/index.js:1 08747 #: src/index.js:1 110748 #: src/index.js:1 636870 #: src/index.js:190 871 #: src/index.js:1256 872 #: src/index.js:1798 749 873 msgid "Off" 750 874 msgstr "" 751 875 752 #: src/index.js: 150876 #: src/index.js:232 753 877 msgid "Cancelled" 754 878 msgstr "" 755 879 756 #: src/index.js: 151880 #: src/index.js:233 757 881 msgid "Failed" 758 882 msgstr "" 759 883 760 #: src/index.js: 167884 #: src/index.js:249 761 885 msgid "Expand" 762 886 msgstr "" 763 887 764 #: src/index.js: 167888 #: src/index.js:249 765 889 msgid "Minimize" 766 890 msgstr "" 767 891 768 #: src/index.js: 170892 #: src/index.js:252 769 893 msgid "Close" 770 894 msgstr "" 771 895 772 #: src/index.js: 184896 #: src/index.js:266 773 897 msgid "Step" 774 898 msgstr "" 775 899 776 #: src/index.js: 189900 #: src/index.js:271 777 901 msgid "Stop" 778 902 msgstr "" 779 903 780 #: src/index.js:492 781 #: src/index.js:1239 904 #: src/index.js:538 905 msgid "You have unsaved changes" 906 msgstr "" 907 908 #: src/index.js:538 909 msgid "All changes saved" 910 msgstr "" 911 912 #: src/index.js:541 913 msgid "Save changes" 914 msgstr "" 915 916 #: src/index.js:639 917 #: src/index.js:1385 782 918 msgid "Deactivate" 783 919 msgstr "" 784 920 785 #: src/index.js: 492921 #: src/index.js:639 786 922 msgid "Optimazz will keep handling caching." 787 923 msgstr "" 788 924 789 #: src/index.js: 499925 #: src/index.js:646 790 926 msgid "deactivated — Optimazz is now the only cache." 791 927 msgstr "" 792 928 793 #: src/index.js: 501929 #: src/index.js:648 794 930 msgid "Could not deactivate the plugin." 795 931 msgstr "" 796 932 797 #: src/index.js: 508933 #: src/index.js:655 798 934 msgid "Make Optimazz the caching plugin? This replaces the current advanced-cache.php (from another caching plugin) and turns on WP_CACHE. Deactivate the other caching plugin afterwards to avoid conflicts." 799 935 msgstr "" 800 936 801 #: src/index.js: 515937 #: src/index.js:662 802 938 msgid "Optimazz is now serving pages from cache." 803 939 msgstr "" 804 940 805 #: src/index.js: 516941 #: src/index.js:663 806 942 msgid "Drop-in installed. Preload the cache, then check again." 807 943 msgstr "" 808 944 809 #: src/index.js: 517945 #: src/index.js:664 810 946 msgid "Could not fully take over — see the cache health details." 811 947 msgstr "" 812 948 813 #: src/index.js: 520949 #: src/index.js:667 814 950 msgid "Take-over failed. Check file permissions." 815 951 msgstr "" 816 952 817 #: src/index.js: 542818 #: src/index.js: 588953 #: src/index.js:689 954 #: src/index.js:735 819 955 msgid "Cache purged." 820 956 msgstr "" 821 957 822 #: src/index.js: 543958 #: src/index.js:690 823 959 msgid "Cache preload started in the background." 824 960 msgstr "" 825 961 826 #: src/index.js: 544962 #: src/index.js:691 827 963 msgid "Cache cleared; preload started in the background." 828 964 msgstr "" 829 965 830 #: src/index.js: 553966 #: src/index.js:700 831 967 msgid "wp-config.php is not writable — add WP_CACHE manually to enable page caching." 832 968 msgstr "" 833 969 834 #: src/index.js: 567970 #: src/index.js:714 835 971 msgid "Settings saved." 836 972 msgstr "" 837 973 838 #: src/index.js: 570974 #: src/index.js:717 839 975 msgid "Save failed." 840 976 msgstr "" 841 977 842 #: src/index.js: 620978 #: src/index.js:767 843 979 msgid "Converting images to WebP/AVIF" 844 980 msgstr "" 845 981 846 #: src/index.js: 621982 #: src/index.js:768 847 983 msgid "Next-gen format generation" 848 984 msgstr "" 849 985 850 #: src/index.js: 624986 #: src/index.js:771 851 987 msgid "Generated" 852 988 msgstr "" 853 989 854 #: src/index.js: 624990 #: src/index.js:771 855 991 msgid "files in this batch" 856 992 msgstr "" 857 993 858 #: src/index.js: 624859 #: src/index.js: 633994 #: src/index.js:771 995 #: src/index.js:780 860 996 msgid "skipped" 861 997 msgstr "" 862 998 863 #: src/index.js: 627999 #: src/index.js:774 864 1000 msgid "Compressing images" 865 1001 msgstr "" 866 1002 867 #: src/index.js: 6281003 #: src/index.js:775 868 1004 msgid "Recompress, resize & strip metadata" 869 1005 msgstr "" 870 1006 871 #: src/index.js: 6341007 #: src/index.js:781 872 1008 msgid "Processed" 873 1009 msgstr "" 874 1010 875 #: src/index.js: 634876 #: src/index.js:1 4401011 #: src/index.js:781 1012 #: src/index.js:1586 877 1013 msgid "saved" 878 1014 msgstr "" 879 1015 880 #: src/index.js:638 1016 #: src/index.js:785 1017 #: src/index.js:1953 881 1018 msgid "Preloading cache" 882 1019 msgstr "" 883 1020 884 #: src/index.js: 6391021 #: src/index.js:786 885 1022 msgid "Warming pages" 886 1023 msgstr "" 887 1024 888 #: src/index.js:643 1025 #: src/index.js:790 1026 #: src/index.js:1974 889 1027 msgid "Warmed" 890 1028 msgstr "" 891 1029 892 #: src/index.js:643 1030 #: src/index.js:790 1031 #: src/index.js:1974 893 1032 msgid "pages" 894 1033 msgstr "" 895 1034 896 #: src/index.js:643 1035 #: src/index.js:790 1036 #: src/index.js:1974 897 1037 msgid "cached" 898 1038 msgstr "" 899 1039 900 #: src/index.js:643 1040 #: src/index.js:790 1041 #: src/index.js:1974 901 1042 msgid "not cached" 902 1043 msgstr "" 903 1044 904 #: src/index.js: 6641045 #: src/index.js:811 905 1046 msgid "Resuming…" 906 1047 msgstr "" 907 1048 908 #: src/index.js:678 1049 #: src/index.js:825 1050 #: src/index.js:1963 909 1051 msgid "Stopped by user." 910 1052 msgstr "" 911 1053 912 #: src/index.js: 6971054 #: src/index.js:844 913 1055 msgid "A batch failed (likely one slow/large image); skipped it and continued." 914 1056 msgstr "" 915 1057 916 #: src/index.js: 6991058 #: src/index.js:846 917 1059 msgid "Completed (some items skipped)." 918 1060 msgstr "" 919 1061 920 #: src/index.js: 7111062 #: src/index.js:858 921 1063 msgid "Job stalled — no progress." 922 1064 msgstr "" 923 1065 924 #: src/index.js:722 1066 #: src/index.js:869 1067 #: src/index.js:1977 925 1068 msgid "left" 926 1069 msgstr "" 927 1070 928 1071 #. translators: %d: number of pages to warm. 929 #: src/index.js:734 1072 #: src/index.js:881 1073 #: src/index.js:1987 930 1074 #, js-format 931 1075 msgid "Found %d pages to warm:" 932 1076 msgstr "" 933 1077 934 #: src/index.js: 7441078 #: src/index.js:891 935 1079 msgid "Image processing is unavailable on this server (no GD or ImageMagick). See Tools → Server Requirements." 936 1080 msgstr "" 937 1081 938 #: src/index.js:749 1082 #: src/index.js:896 1083 #: src/index.js:1993 939 1084 msgid "Warming stopped: the server returned 403/429, so a firewall or security plugin is blocking the preload requests. Cached what we could; allow-list the \"Optimazz-CacheWarmer\" user-agent in your firewall or security plugin (e.g. All-In-One Security, Wordfence, Cloudflare), or slow warming with the optimazz_preload_delay_ms filter." 940 1085 msgstr "" 941 1086 942 #: src/index.js:756 1087 #: src/index.js:903 1088 #: src/index.js:1997 943 1089 msgid "Pages are not being cached" 944 1090 msgstr "" 945 1091 946 #: src/index.js:756 1092 #: src/index.js:903 1093 #: src/index.js:1997 947 1094 msgid "Stopped: pages are loading but none are being saved to the cache." 948 1095 msgstr "" 949 1096 950 #: src/index.js:760 951 #: src/index.js:841 1097 #: src/index.js:907 1098 #: src/index.js:988 1099 #: src/index.js:2007 952 1100 msgid "Completed." 953 1101 msgstr "" 954 1102 955 1103 #. translators: %d: number of pages. 956 #: src/index.js:764 1104 #: src/index.js:911 1105 #: src/index.js:2004 957 1106 #, js-format 958 1107 msgid "%d page was not cached." … … 961 1110 msgstr[1] "" 962 1111 963 #: src/index.js: 8231112 #: src/index.js:970 964 1113 msgid "Cleaning and optimizing database…" 965 1114 msgstr "" 966 1115 967 #: src/index.js: 8271116 #: src/index.js:974 968 1117 msgid "Optimizing database" 969 1118 msgstr "" 970 1119 971 #: src/index.js: 8281120 #: src/index.js:975 972 1121 msgid "Cleanup & table optimization" 973 1122 msgstr "" 974 1123 975 #: src/index.js: 8401124 #: src/index.js:987 976 1125 msgid "Cleaned" 977 1126 msgstr "" 978 1127 979 #: src/index.js: 8401128 #: src/index.js:987 980 1129 msgid "rows," 981 1130 msgstr "" 982 1131 983 #: src/index.js: 8401132 #: src/index.js:987 984 1133 msgid "tables optimized." 985 1134 msgstr "" 986 1135 987 #: src/index.js: 8451136 #: src/index.js:992 988 1137 msgid "Optimize failed." 989 1138 msgstr "" 990 1139 991 #: src/index.js: 8511140 #: src/index.js:998 992 1141 msgid "Reset all Optimazz settings to defaults? You can undo this with \"Undo last change\"." 993 1142 msgstr "" 994 1143 995 #: src/index.js: 8591144 #: src/index.js:1006 996 1145 msgid "Settings reset to defaults." 997 1146 msgstr "" 998 1147 999 #: src/index.js: 8621148 #: src/index.js:1009 1000 1149 msgid "Reset failed." 1001 1150 msgstr "" 1002 1151 1003 #: src/index.js: 8721152 #: src/index.js:1019 1004 1153 msgid "Nothing to undo." 1005 1154 msgstr "" 1006 1155 1007 #: src/index.js: 8791156 #: src/index.js:1026 1008 1157 msgid "Reverted to the previous settings." 1009 1158 msgstr "" 1010 1159 1011 #: src/index.js: 8821160 #: src/index.js:1029 1012 1161 msgid "Undo failed." 1013 1162 msgstr "" 1014 1163 1015 #: src/index.js: 9141164 #: src/index.js:1061 1016 1165 msgid "Invalid settings file." 1017 1166 msgstr "" 1018 1167 1019 #: src/index.js: 9441168 #: src/index.js:1088 1020 1169 msgid "Page caching" 1021 1170 msgstr "" 1022 1171 1023 #: src/index.js: 9451172 #: src/index.js:1089 1024 1173 msgid "Delay JavaScript" 1025 1174 msgstr "" 1026 1175 1027 #: src/index.js: 9461028 #: src/index.js:1 2661176 #: src/index.js:1090 1177 #: src/index.js:1412 1029 1178 msgid "Defer JavaScript" 1030 1179 msgstr "" 1031 1180 1032 #: src/index.js: 9471033 #: src/index.js:1 3191181 #: src/index.js:1091 1182 #: src/index.js:1465 1034 1183 msgid "Minify CSS" 1035 1184 msgstr "" 1036 1185 1037 #: src/index.js: 9491186 #: src/index.js:1093 1038 1187 msgid "Next-gen images" 1039 1188 msgstr "" 1040 1189 1041 #: src/index.js: 9501190 #: src/index.js:1094 1042 1191 msgid "Font optimization" 1043 1192 msgstr "" 1044 1193 1045 #: src/index.js: 9511194 #: src/index.js:1095 1046 1195 msgid "Cache preloading" 1047 1196 msgstr "" 1048 1197 1049 #: src/index.js: 9521198 #: src/index.js:1096 1050 1199 msgid "Heartbeat control" 1051 1200 msgstr "" 1052 1201 1053 #: src/index.js: 9671202 #: src/index.js:1113 1054 1203 msgid "Turn on Page Cache for the real speed gain" 1055 1204 msgstr "" 1056 1205 1057 #: src/index.js: 9681206 #: src/index.js:1114 1058 1207 msgid "File and media optimizations are on, but Page Cache is off — so the server rebuilds every page on every request. Without caching this can make a site feel slower, not faster, especially on mobile. Enable Page Cache to serve ready-made HTML." 1059 1208 msgstr "" 1060 1209 1061 #: src/index.js: 9871210 #: src/index.js:1133 1062 1211 msgid "SCORE" 1063 1212 msgstr "" 1064 1213 1065 #: src/index.js: 9921214 #: src/index.js:1138 1066 1215 msgid "Performance score" 1067 1216 msgstr "" 1068 1217 1069 #: src/index.js: 9951218 #: src/index.js:1141 1070 1219 msgid "Biggest remaining win:" 1071 1220 msgstr "" 1072 1221 1073 #: src/index.js: 9961222 #: src/index.js:1142 1074 1223 msgid "Every optimization is enabled. Nicely done!" 1075 1224 msgstr "" 1076 1225 1077 #: src/index.js:1013 1078 #: src/index.js:1065 1226 #: src/index.js:1159 1227 #: src/index.js:1211 1228 #: src/index.js:2204 1079 1229 msgid "Cached pages" 1080 1230 msgstr "" 1081 1231 1082 #: src/index.js:1 0181232 #: src/index.js:1164 1083 1233 msgid "Cache size" 1084 1234 msgstr "" 1085 1235 1086 #: src/index.js:1 0231236 #: src/index.js:1169 1087 1237 msgid "Modules active" 1088 1238 msgstr "" 1089 1239 1090 #: src/index.js:1 0281091 #: src/index.js:1 0431240 #: src/index.js:1174 1241 #: src/index.js:1189 1092 1242 msgid "Recommendations" 1093 1243 msgstr "" 1094 1244 1095 #: src/index.js:1 0281245 #: src/index.js:1174 1096 1246 msgid "What to fix next to speed up your site — one click each." 1097 1247 msgstr "" 1098 1248 1099 #: src/index.js:1 0351249 #: src/index.js:1181 1100 1250 msgid "Fix it" 1101 1251 msgstr "" 1102 1252 1103 #: src/index.js:1 0361253 #: src/index.js:1182 1104 1254 msgid "Open" 1105 1255 msgstr "" 1106 1256 1107 #: src/index.js:1 0441257 #: src/index.js:1190 1108 1258 msgid "Nice — everything we recommend is already on. Your site is well optimized." 1109 1259 msgstr "" 1110 1260 1111 #: src/index.js:1 0491261 #: src/index.js:1195 1112 1262 msgid "Cache Files" 1113 1263 msgstr "" 1114 1264 1115 #: src/index.js:1 0491265 #: src/index.js:1195 1116 1266 msgid "Clear and preload all cache files." 1117 1267 msgstr "" 1118 1268 1119 #: src/index.js:1052 1269 #: src/index.js:1198 1270 #: src/index.js:2187 1120 1271 msgid "Clear & Preload" 1121 1272 msgstr "" 1122 1273 1123 #: src/index.js:1058 1274 #: src/index.js:1204 1275 #: src/index.js:2195 1124 1276 msgid "Purge cache" 1125 1277 msgstr "" 1126 1278 1127 #: src/index.js:1070 1279 #: src/index.js:1216 1280 #: src/index.js:2209 1128 1281 msgid "Page cache size" 1129 1282 msgstr "" 1130 1283 1131 #: src/index.js:1 0751284 #: src/index.js:1221 1132 1285 msgid "Optimized CSS" 1133 1286 msgstr "" 1134 1287 1135 #: src/index.js:1 0801136 #: src/index.js:1 0811288 #: src/index.js:1226 1289 #: src/index.js:1227 1137 1290 msgid "Cache lifespan" 1138 1291 msgstr "" 1139 1292 1140 #: src/index.js:1 0841293 #: src/index.js:1230 1141 1294 msgid "Never expire" 1142 1295 msgstr "" 1143 1296 1144 #: src/index.js:1085 1145 #: src/index.js:1187 1146 msgid "12 hours" 1147 msgstr "" 1148 1149 #: src/index.js:1086 1150 #: src/index.js:1188 1151 msgid "1 day" 1152 msgstr "" 1153 1154 #: src/index.js:1087 1155 #: src/index.js:1189 1156 msgid "1 week" 1157 msgstr "" 1158 1159 #: src/index.js:1088 1160 #: src/index.js:1190 1161 msgid "1 month" 1162 msgstr "" 1163 1164 #. translators: %d: number of hours. 1165 #: src/index.js:1091 1166 #, js-format 1167 msgid "%d hour" 1168 msgid_plural "%d hours" 1169 msgstr[0] "" 1170 msgstr[1] "" 1171 1172 #: src/index.js:1098 1297 #: src/index.js:1244 1173 1298 msgid "Auto-preload" 1174 1299 msgstr "" 1175 1300 1176 #: src/index.js:1101 1177 msgid "off" 1178 msgstr "" 1179 1180 #: src/index.js:1103 1301 #: src/index.js:1249 1181 1302 msgid "next run: due now" 1182 1303 msgstr "" 1183 1304 1184 #: src/index.js:1 1041305 #: src/index.js:1250 1185 1306 msgid "next run in" 1186 1307 msgstr "" 1187 1308 1188 #: src/index.js:1 1071309 #: src/index.js:1253 1189 1310 msgid "Auto-preload frequency" 1190 1311 msgstr "" 1191 1312 1192 #: src/index.js:1111 1193 #: src/index.js:1462 1313 #: src/index.js:1257 1314 #: src/index.js:1609 1315 #: src/index.js:2121 1194 1316 msgid "Every hour" 1195 1317 msgstr "" 1196 1318 1197 #: src/index.js:1112 1198 #: src/index.js:1463 1319 #: src/index.js:1258 1320 #: src/index.js:1610 1321 #: src/index.js:2122 1199 1322 msgid "Twice a day" 1200 1323 msgstr "" 1201 1324 1202 #: src/index.js:1113 1203 #: src/index.js:1464 1325 #: src/index.js:1259 1326 #: src/index.js:1611 1327 #: src/index.js:2123 1204 1328 msgid "Once a day" 1205 1329 msgstr "" 1206 1330 1207 #: src/index.js:1 1211331 #: src/index.js:1267 1208 1332 msgid "Last auto-preload" 1209 1333 msgstr "" 1210 1334 1211 #: src/index.js:1 1241335 #: src/index.js:1270 1212 1336 msgid "not yet" 1213 1337 msgstr "" 1214 1338 1215 1339 #. translators: 1: time ago, e.g. "2 hours", 2: number of pages 1216 #: src/index.js:1 1271340 #: src/index.js:1273 1217 1341 #, js-format 1218 1342 msgid "%1$s ago · %2$d pages" 1219 1343 msgstr "" 1220 1344 1221 #: src/index.js:1 1301345 #: src/index.js:1276 1222 1346 msgid "blocked by firewall" 1223 1347 msgstr "" 1224 1348 1225 #: src/index.js:1 1351349 #: src/index.js:1281 1226 1350 msgid "Module Status" 1227 1351 msgstr "" 1228 1352 1229 #: src/index.js:1 1351353 #: src/index.js:1281 1230 1354 msgid "Enable or disable any module right here." 1231 1355 msgstr "" 1232 1356 1233 #: src/index.js:1 1721357 #: src/index.js:1318 1234 1358 msgid "Serve static HTML instead of running PHP on every request." 1235 1359 msgstr "" 1236 1360 1237 #: src/index.js:1174 1361 #: src/index.js:1320 1362 #: src/index.js:2094 1238 1363 msgid "Cache mobile devices separately" 1239 1364 msgstr "" 1240 1365 1241 #: src/index.js:1176 1366 #: src/index.js:1322 1367 #: src/index.js:2096 1242 1368 msgid "Cache logged-in users" 1243 1369 msgstr "" 1244 1370 1245 #: src/index.js:1177 1371 #: src/index.js:1323 1372 #: src/index.js:2097 1246 1373 msgid "Leave off unless you know what you are doing." 1247 1374 msgstr "" 1248 1375 1249 #: src/index.js:1179 1376 #: src/index.js:1325 1377 #: src/index.js:2099 1250 1378 msgid "Browser cache headers" 1251 1379 msgstr "" 1252 1380 1253 #: src/index.js:1 1801381 #: src/index.js:1326 1254 1382 msgid "Add Expires / Cache-Control rules so repeat visits load CSS, JS, images and fonts from the browser cache. Apache and LiteSpeed only (written to .htaccess); ignored on Nginx." 1255 1383 msgstr "" 1256 1384 1257 #: src/index.js:1182 1385 #: src/index.js:1328 1386 #: src/index.js:2102 1258 1387 msgid "Cache lifetime" 1259 1388 msgstr "" 1260 1389 1261 #: src/index.js:1 1831390 #: src/index.js:1329 1262 1391 msgid "How long a cached page is kept before it is refreshed. Pages are always purged immediately when their content changes." 1263 1392 msgstr "" 1264 1393 1265 #: src/index.js:1186 1394 #: src/index.js:1332 1395 #: src/index.js:2106 1266 1396 msgid "Never" 1267 1397 msgstr "" 1268 1398 1269 #: src/index.js:1196 1399 #: src/index.js:1342 1400 #: src/index.js:2128 1270 1401 msgid "Cache health" 1271 1402 msgstr "" 1272 1403 1273 #: src/index.js:1 1981404 #: src/index.js:1344 1274 1405 msgid "Check now" 1275 1406 msgstr "" 1276 1407 1277 #: src/index.js:1 2011408 #: src/index.js:1347 1278 1409 msgid "Confirms caching is actually serving pages. If it is not, the plugin rebuilds every page on each request — which is slower, not faster." 1279 1410 msgstr "" 1280 1411 1281 #: src/index.js:1 2051412 #: src/index.js:1351 1282 1413 msgid "Pages ARE being served from cache (HIT)" 1283 1414 msgstr "" 1284 1415 1285 #: src/index.js:1 2061416 #: src/index.js:1352 1286 1417 msgid "Pages are NOT being served from cache (MISS) — see below" 1287 1418 msgstr "" 1288 1419 1289 #: src/index.js:1 2071420 #: src/index.js:1353 1290 1421 msgid "Live check unavailable" 1291 1422 msgstr "" 1292 1423 1293 #: src/index.js:1 2111424 #: src/index.js:1357 1294 1425 msgid "advanced-cache.php belongs to" 1295 1426 msgstr "" 1296 1427 1297 #: src/index.js:1 2111428 #: src/index.js:1357 1298 1429 msgid "take it over below" 1299 1430 msgstr "" 1300 1431 1301 #: src/index.js:1 2121432 #: src/index.js:1358 1302 1433 msgid "advanced-cache.php belongs to ANOTHER plugin — take it over below" 1303 1434 msgstr "" 1304 1435 1305 #: src/index.js:1 2131436 #: src/index.js:1359 1306 1437 msgid "advanced-cache.php drop-in installed" 1307 1438 msgstr "" 1308 1439 1309 #: src/index.js:1 2141440 #: src/index.js:1360 1310 1441 msgid "advanced-cache.php drop-in is missing" 1311 1442 msgstr "" 1312 1443 1313 #: src/index.js:1 2161444 #: src/index.js:1362 1314 1445 msgid "WP_CACHE is enabled in wp-config.php" 1315 1446 msgstr "" 1316 1447 1317 #: src/index.js:1 2161448 #: src/index.js:1362 1318 1449 msgid "WP_CACHE is not enabled in wp-config.php" 1319 1450 msgstr "" 1320 1451 1321 #: src/index.js:1 2181452 #: src/index.js:1364 1322 1453 msgid "wp-config.php is not writable — add the WP_CACHE line manually (below)" 1323 1454 msgstr "" 1324 1455 1325 #: src/index.js:1 2211456 #: src/index.js:1367 1326 1457 msgid "pages warmed in cache" 1327 1458 msgstr "" 1328 1459 1329 #: src/index.js:1 2271460 #: src/index.js:1373 1330 1461 msgid "Take over caching" 1331 1462 msgstr "" 1332 1463 1333 #: src/index.js:1 2291464 #: src/index.js:1375 1334 1465 msgid "Make Optimazz the caching plugin — installs its drop-in and enables WP_CACHE." 1335 1466 msgstr "" 1336 1467 1337 #: src/index.js:1 2371468 #: src/index.js:1383 1338 1469 msgid "is still active and may reclaim caching. Deactivate it so Optimazz keeps serving the cache." 1339 1470 msgstr "" 1340 1471 1341 #: src/index.js:1 2471472 #: src/index.js:1393 1342 1473 msgid "wp-config.php is not writable. Add this line just after the opening <?php tag:" 1343 1474 msgstr "" 1344 1475 1345 #: src/index.js:1 2521476 #: src/index.js:1398 1346 1477 msgid "Could not run the check. Try reloading the page." 1347 1478 msgstr "" 1348 1479 1349 #: src/index.js:1 2601480 #: src/index.js:1406 1350 1481 msgid "JavaScript" 1351 1482 msgstr "" 1352 1483 1353 #: src/index.js:1 2611484 #: src/index.js:1407 1354 1485 msgid "Defer and delay scripts to cut Total Blocking Time." 1355 1486 msgstr "" 1356 1487 1357 #: src/index.js:1 2631488 #: src/index.js:1409 1358 1489 msgid "Minify JavaScript" 1359 1490 msgstr "" 1360 1491 1361 #: src/index.js:1 2641492 #: src/index.js:1410 1362 1493 msgid "Strip comments and whitespace from inline scripts. Line breaks are kept for safety; test your site after enabling." 1363 1494 msgstr "" 1364 1495 1365 #: src/index.js:1 2671496 #: src/index.js:1413 1366 1497 msgid "Load scripts without blocking page render." 1367 1498 msgstr "" 1368 1499 1369 #: src/index.js:1 2691500 #: src/index.js:1415 1370 1501 msgid "Delay JS until interaction" 1371 1502 msgstr "" 1372 1503 1373 #: src/index.js:1 2701504 #: src/index.js:1416 1374 1505 msgid "Run scripts only after the first user interaction." 1375 1506 msgstr "" 1376 1507 1377 #: src/index.js:1 2731508 #: src/index.js:1419 1378 1509 msgid "Delay all scripts" 1379 1510 msgstr "" 1380 1511 1381 #: src/index.js:1 2741512 #: src/index.js:1420 1382 1513 msgid "Delay every script until interaction. jQuery, your theme, and Elementor are always kept safe. Still, on sites where content is rendered by JavaScript this can delay the visible page — test before keeping it on." 1383 1514 msgstr "" 1384 1515 1385 #: src/index.js:1 2801516 #: src/index.js:1426 1386 1517 msgid "Delaying all scripts can slow the first paint on JavaScript-rendered sites (Elementor, sliders). If your PageSpeed FCP/LCP get worse, switch to \"Delay selected\" and delay only tracking scripts." 1387 1518 msgstr "" 1388 1519 1389 #: src/index.js:1 2841520 #: src/index.js:1430 1390 1521 msgid "Delay these scripts" 1391 1522 msgstr "" 1392 1523 1393 #: src/index.js:1 2851524 #: src/index.js:1431 1394 1525 msgid "Only scripts matching these keywords are delayed. Match by filename, handle or URL — e.g. gtag, analytics, facebook." 1395 1526 msgstr "" 1396 1527 1397 #: src/index.js:1 2861528 #: src/index.js:1432 1398 1529 msgid "e.g. gtag, analytics" 1399 1530 msgstr "" 1400 1531 1401 #: src/index.js:1 2921532 #: src/index.js:1438 1402 1533 msgid "Exclude specific scripts" 1403 1534 msgstr "" 1404 1535 1405 #: src/index.js:1 2941536 #: src/index.js:1440 1406 1537 msgid "If a script breaks, exclude it from just one optimization instead of both. Match by filename, script handle, or part of the URL." 1407 1538 msgstr "" 1408 1539 1409 #: src/index.js:1 2981540 #: src/index.js:1444 1410 1541 msgid "Never defer" 1411 1542 msgstr "" 1412 1543 1413 #: src/index.js:1 2991544 #: src/index.js:1445 1414 1545 msgid "Keep loading these render-blocking, but still allow delay. Use for scripts that must run during page load." 1415 1546 msgstr "" 1416 1547 1417 #: src/index.js:1 3001548 #: src/index.js:1446 1418 1549 msgid "e.g. jquery.min.js" 1419 1550 msgstr "" 1420 1551 1421 #: src/index.js:1 3041552 #: src/index.js:1450 1422 1553 msgid "Never delay" 1423 1554 msgstr "" 1424 1555 1425 #: src/index.js:1 3051556 #: src/index.js:1451 1426 1557 msgid "Run these before any interaction, but still allow defer. Use for sliders, above-the-fold, or consent scripts." 1427 1558 msgstr "" 1428 1559 1429 #: src/index.js:1 3061560 #: src/index.js:1452 1430 1561 msgid "e.g. slider, swiper" 1431 1562 msgstr "" 1432 1563 1433 #: src/index.js:1 3091564 #: src/index.js:1455 1434 1565 msgid "Never optimize" 1435 1566 msgstr "" 1436 1567 1437 #: src/index.js:1 3101568 #: src/index.js:1456 1438 1569 msgid "Skip both defer and delay — leave these scripts exactly as they are." 1439 1570 msgstr "" 1440 1571 1441 #: src/index.js:1 3111572 #: src/index.js:1457 1442 1573 msgid "e.g. recaptcha" 1443 1574 msgstr "" 1444 1575 1445 #: src/index.js:1 3161576 #: src/index.js:1462 1446 1577 msgid "CSS" 1447 1578 msgstr "" 1448 1579 1449 #: src/index.js:1 3171580 #: src/index.js:1463 1450 1581 msgid "Minify, combine, and remove render-blocking stylesheets." 1451 1582 msgstr "" 1452 1583 1453 #: src/index.js:1 3211584 #: src/index.js:1467 1454 1585 msgid "Combine CSS files" 1455 1586 msgstr "" 1456 1587 1457 #: src/index.js:1 3221588 #: src/index.js:1468 1458 1589 msgid "Merge stylesheets to reduce requests." 1459 1590 msgstr "" 1460 1591 1461 #: src/index.js:1 3241592 #: src/index.js:1470 1462 1593 msgid "Load CSS asynchronously" 1463 1594 msgstr "" 1464 1595 1465 #: src/index.js:1 3251596 #: src/index.js:1471 1466 1597 msgid "Removes render-blocking CSS. Paste your above-the-fold (critical) CSS below, or the page may briefly flash unstyled." 1467 1598 msgstr "" 1468 1599 1469 #: src/index.js:1 3321600 #: src/index.js:1478 1470 1601 msgid "No critical CSS provided. Async loading is on, so visitors may see a flash of unstyled content until you paste critical CSS below." 1471 1602 msgstr "" 1472 1603 1473 #: src/index.js:1 3361604 #: src/index.js:1482 1474 1605 msgid "Critical CSS (above-the-fold)" 1475 1606 msgstr "" 1476 1607 1477 #: src/index.js:1 3371608 #: src/index.js:1483 1478 1609 msgid "Paste the above-the-fold CSS to inline. Generate it with a free tool such as a critical-CSS generator. Leave empty to disable inlining." 1479 1610 msgstr "" 1480 1611 1481 #: src/index.js:1 3421612 #: src/index.js:1488 1482 1613 msgid "Remove unused CSS" 1483 1614 msgstr "" 1484 1615 1485 #: src/index.js:1 3431616 #: src/index.js:1489 1486 1617 msgid "Remove block-editor CSS on the front end" 1487 1618 msgstr "" 1488 1619 1489 #: src/index.js:1 3441620 #: src/index.js:1490 1490 1621 msgid "Dequeues WordPress block-library and global styles. Turn on only if your front end does NOT render Gutenberg blocks (e.g. a page-builder or classic theme). Big byte saving when it applies." 1491 1622 msgstr "" 1492 1623 1493 #: src/index.js:1 3461624 #: src/index.js:1492 1494 1625 msgid "Load WooCommerce CSS only on shop pages" 1495 1626 msgstr "" 1496 1627 1497 #: src/index.js:1 3471628 #: src/index.js:1493 1498 1629 msgid "Dequeues WooCommerce styles on non-shop pages (kept on cart, checkout, account, product and shop). Turn off if you show Woo products or widgets on other pages." 1499 1630 msgstr "" 1500 1631 1501 #: src/index.js:1 3491632 #: src/index.js:1495 1502 1633 msgid "Load Easy Digital Downloads CSS only on EDD pages" 1503 1634 msgstr "" 1504 1635 1505 #: src/index.js:1 3501636 #: src/index.js:1496 1506 1637 msgid "Dequeues EDD styles on non-EDD pages (kept on checkout, purchase-confirmation, purchase history and download archives). Turn off if you show download or purchase links elsewhere." 1507 1638 msgstr "" 1508 1639 1509 #: src/index.js:1 3521640 #: src/index.js:1498 1510 1641 msgid "Excluded CSS files (keywords)" 1511 1642 msgstr "" 1512 1643 1513 #: src/index.js:1 3601644 #: src/index.js:1506 1514 1645 msgid "Lazy-load, prevent layout shift, serve next-gen formats, and lighten media." 1515 1646 msgstr "" 1516 1647 1517 #: src/index.js:1 3661648 #: src/index.js:1512 1518 1649 msgid "This server has no GD or ImageMagick extension, so image compression and WebP/AVIF cannot run — every image will be skipped. Ask your host to enable \"php-gd\" (or \"php-imagick\"). See Tools → Server Requirements." 1519 1650 msgstr "" 1520 1651 1521 #: src/index.js:1 3701652 #: src/index.js:1516 1522 1653 msgid "Load off-screen images only when scrolled into view." 1523 1654 msgstr "" 1524 1655 1525 #: src/index.js:1 3721656 #: src/index.js:1518 1526 1657 msgid "Lazy-load iframes" 1527 1658 msgstr "" 1528 1659 1529 #: src/index.js:1 3741660 #: src/index.js:1520 1530 1661 msgid "Lazy-load CSS background images" 1531 1662 msgstr "" 1532 1663 1533 #: src/index.js:1 3751664 #: src/index.js:1521 1534 1665 msgid "Defer inline background-image elements (test with page builders)." 1535 1666 msgstr "" 1536 1667 1537 #: src/index.js:1 3771668 #: src/index.js:1523 1538 1669 msgid "Replace YouTube embeds with a preview" 1539 1670 msgstr "" 1540 1671 1541 #: src/index.js:1 3781672 #: src/index.js:1524 1542 1673 msgid "Load the video iframe only when the visitor clicks play." 1543 1674 msgstr "" 1544 1675 1545 #: src/index.js:1 3801676 #: src/index.js:1526 1546 1677 msgid "Add missing image dimensions" 1547 1678 msgstr "" 1548 1679 1549 #: src/index.js:1 3811680 #: src/index.js:1527 1550 1681 msgid "Set width/height to prevent layout shift (CLS)." 1551 1682 msgstr "" 1552 1683 1553 #: src/index.js:1 3831684 #: src/index.js:1529 1554 1685 msgid "Preload the LCP image" 1555 1686 msgstr "" 1556 1687 1557 #: src/index.js:1 3841688 #: src/index.js:1530 1558 1689 msgid "Preload the first/hero image with high priority." 1559 1690 msgstr "" 1560 1691 1561 #: src/index.js:1 3871692 #: src/index.js:1533 1562 1693 msgid "Keep first images eager (LCP)" 1563 1694 msgstr "" 1564 1695 1565 #: src/index.js:1 3881696 #: src/index.js:1534 1566 1697 msgid "Do not lazy-load the first N images, to protect LCP." 1567 1698 msgstr "" 1568 1699 1569 #: src/index.js:1 3931700 #: src/index.js:1539 1570 1701 msgid "Serve WebP via <picture>" 1571 1702 msgstr "" 1572 1703 1573 #: src/index.js:1 3941704 #: src/index.js:1540 1574 1705 msgid "Deliver WebP to supporting browsers, original as fallback." 1575 1706 msgstr "" 1576 1707 1577 #: src/index.js:1 3961708 #: src/index.js:1542 1578 1709 msgid "Generate WebP/AVIF on upload" 1579 1710 msgstr "" 1580 1711 1581 #: src/index.js:1 3981712 #: src/index.js:1544 1582 1713 msgid "WebP generation is not available on this server." 1583 1714 msgstr "" 1584 1715 1585 #: src/index.js:1 3991716 #: src/index.js:1545 1586 1717 msgid "Auto-create next-gen copies for newly uploaded images." 1587 1718 msgstr "" 1588 1719 1589 #: src/index.js:1 4021720 #: src/index.js:1548 1590 1721 msgid "Also generate/serve AVIF" 1591 1722 msgstr "" 1592 1723 1593 #: src/index.js:1 4031724 #: src/index.js:1549 1594 1725 msgid "Smaller than WebP; served first when supported." 1595 1726 msgstr "" 1596 1727 1597 #: src/index.js:1 4071728 #: src/index.js:1553 1598 1729 msgid "Compress & resize on upload" 1599 1730 msgstr "" 1600 1731 1601 #: src/index.js:1 4081732 #: src/index.js:1554 1602 1733 msgid "Recompress, cap dimensions, and strip metadata from new uploads." 1603 1734 msgstr "" 1604 1735 1605 #: src/index.js:1 4101736 #: src/index.js:1556 1606 1737 msgid "Convert opaque PNG to JPEG" 1607 1738 msgstr "" 1608 1739 1609 #: src/index.js:1 4111740 #: src/index.js:1557 1610 1741 msgid "Re-save non-transparent PNGs as much smaller JPEGs during bulk optimization." 1611 1742 msgstr "" 1612 1743 1613 #: src/index.js:1 4131744 #: src/index.js:1559 1614 1745 msgid "Keep a backup of originals" 1615 1746 msgstr "" 1616 1747 1617 #: src/index.js:1 4141748 #: src/index.js:1560 1618 1749 msgid "Copy originals to uploads/optimazz-backup before optimizing." 1619 1750 msgstr "" 1620 1751 1621 #: src/index.js:1 4161752 #: src/index.js:1562 1622 1753 msgid "Image quality" 1623 1754 msgstr "" 1624 1755 1625 #: src/index.js:1 4181756 #: src/index.js:1564 1626 1757 msgid "Used for compression and WebP/AVIF generation." 1627 1758 msgstr "" 1628 1759 1629 #: src/index.js:1 4191760 #: src/index.js:1565 1630 1761 msgid "Max image width (px)" 1631 1762 msgstr "" 1632 1763 1633 #: src/index.js:1 4241764 #: src/index.js:1570 1634 1765 msgid "WebP coverage" 1635 1766 msgstr "" 1636 1767 1637 #: src/index.js:1 4261768 #: src/index.js:1572 1638 1769 msgid "images converted" 1639 1770 msgstr "" 1640 1771 1641 #: src/index.js:1 4271642 #: src/index.js:1 4411772 #: src/index.js:1573 1773 #: src/index.js:1587 1643 1774 msgid "Loading…" 1644 1775 msgstr "" 1645 1776 1646 #: src/index.js:1 4321777 #: src/index.js:1578 1647 1778 msgid "Convert to WebP" 1648 1779 msgstr "" 1649 1780 1650 #: src/index.js:1 4381781 #: src/index.js:1584 1651 1782 msgid "Bulk compression" 1652 1783 msgstr "" 1653 1784 1654 #: src/index.js:1 4401785 #: src/index.js:1586 1655 1786 msgid "optimized" 1656 1787 msgstr "" 1657 1788 1658 #: src/index.js:1 4441789 #: src/index.js:1590 1659 1790 msgid "Compress existing images" 1660 1791 msgstr "" 1661 1792 1662 #: src/index.js:1 4521793 #: src/index.js:1598 1663 1794 msgid "Warm the cache automatically so visitors never hit a cold page." 1664 1795 msgstr "" 1665 1796 1666 #: src/index.js:1 4531797 #: src/index.js:1600 1667 1798 msgid "Activate preloading" 1668 1799 msgstr "" 1669 1800 1670 #: src/index.js:1 4541801 #: src/index.js:1601 1671 1802 msgid "Warms the cache on a schedule and right after every full purge (for example after changing a setting). Warming follows the sitemap — home, pages, posts, custom post types, then the busiest category and tag archives — most-important first, for both mobile and desktop." 1672 1803 msgstr "" 1673 1804 1674 #: src/index.js:1457 1805 #: src/index.js:1604 1806 #: src/index.js:2117 1675 1807 msgid "Automatic preload" 1676 1808 msgstr "" 1677 1809 1678 #: src/index.js:1 4581810 #: src/index.js:1605 1679 1811 msgid "How often to re-warm the whole site. Choose “Every hour” if your content changes often or you use a short cache lifespan." 1680 1812 msgstr "" 1681 1813 1682 #: src/index.js:1470 1814 #. translators: %s: preload frequency, e.g. "twice a day". 1815 #: src/index.js:1618 1816 #, js-format 1817 msgid "Automatic preload runs %s for this site (set for the whole network in Network Admin → Optimazz)." 1818 msgstr "" 1819 1820 #: src/index.js:1621 1821 msgid "Automatic preload is off for the network. You can still preload this site now." 1822 msgstr "" 1823 1824 #: src/index.js:1626 1683 1825 msgid "WP-Cron is disabled on this site (DISABLE_WP_CRON). Scheduled preloads only run if a server cron job calls wp-cron.php — ask your host to set one up (e.g. every 5 minutes)." 1684 1826 msgstr "" 1685 1827 1686 #: src/index.js:1 4751828 #: src/index.js:1631 1687 1829 msgid "Preload now" 1688 1830 msgstr "" 1689 1831 1690 #: src/index.js:1 4831832 #: src/index.js:1639 1691 1833 msgid "Fine-tune which requests are cached." 1692 1834 msgstr "" 1693 1835 1694 #: src/index.js:1 4841836 #: src/index.js:1640 1695 1837 msgid "Never cache these URLs (contains)" 1696 1838 msgstr "" 1697 1839 1698 #: src/index.js:1 4861840 #: src/index.js:1642 1699 1841 msgid "Never cache when these cookies are set" 1700 1842 msgstr "" 1701 1843 1702 #: src/index.js:1 4881844 #: src/index.js:1644 1703 1845 msgid "Never cache for these user agents" 1704 1846 msgstr "" 1705 1847 1706 #: src/index.js:1 4901848 #: src/index.js:1646 1707 1849 msgid "Ignore these query arguments" 1708 1850 msgstr "" 1709 1851 1710 #: src/index.js:1 4981852 #: src/index.js:1654 1711 1853 msgid "Safe Mode" 1712 1854 msgstr "" 1713 1855 1714 #: src/index.js:1 4991856 #: src/index.js:1655 1715 1857 msgid "If a theme or page builder looks broken after optimizing, turn this on. It pauses the riskiest optimizations — combine CSS, async CSS, remove block CSS and JavaScript delay — while keeping caching, minify, defer and lazy-load. Your saved settings are not changed; they simply pause until you turn Safe Mode off." 1716 1858 msgstr "" 1717 1859 1718 #: src/index.js:1 5021860 #: src/index.js:1658 1719 1861 msgid "Pause risky optimizations (Safe Mode)" 1720 1862 msgstr "" 1721 1863 1722 #: src/index.js:1 5091864 #: src/index.js:1665 1723 1865 msgid "Safe Mode is ON — combine/async CSS, remove-block-CSS and JavaScript delay are paused site-wide. Turn it off to resume full optimization." 1724 1866 msgstr "" 1725 1867 1726 #: src/index.js:1 5141868 #: src/index.js:1670 1727 1869 msgid "Undo last change" 1728 1870 msgstr "" 1729 1871 1730 #: src/index.js:1 5181872 #: src/index.js:1674 1731 1873 msgid "Restores the settings from just before your last save, reset or Safe Mode toggle." 1732 1874 msgstr "" 1733 1875 1734 #: src/index.js:1 5191876 #: src/index.js:1675 1735 1877 msgid "Nothing to undo yet — this becomes available after your next change." 1736 1878 msgstr "" 1737 1879 1738 #: src/index.js:1 5231880 #: src/index.js:1679 1739 1881 msgid "Server Requirements" 1740 1882 msgstr "" 1741 1883 1742 #: src/index.js:1 5241884 #: src/index.js:1680 1743 1885 msgid "What your host supports. Warnings here explain why a feature (like WebP/AVIF) may be unavailable or slow." 1744 1886 msgstr "" 1745 1887 1746 #: src/index.js:1 5251888 #: src/index.js:1681 1747 1889 msgid "Checking your server…" 1748 1890 msgstr "" 1749 1891 1750 #: src/index.js:1 5261892 #: src/index.js:1682 1751 1893 msgid "Could not read the server report." 1752 1894 msgstr "" 1753 1895 1754 #: src/index.js:1 5421896 #: src/index.js:1698 1755 1897 msgid "Re-check" 1756 1898 msgstr "" 1757 1899 1758 #: src/index.js:1 5461900 #: src/index.js:1702 1759 1901 msgid "Import / Export Settings" 1760 1902 msgstr "" 1761 1903 1762 #: src/index.js:1 5471904 #: src/index.js:1703 1763 1905 msgid "Back up your configuration or copy it to another site." 1764 1906 msgstr "" 1765 1907 1766 #: src/index.js:1 5501908 #: src/index.js:1706 1767 1909 msgid "Export settings" 1768 1910 msgstr "" 1769 1911 1770 #: src/index.js:1 5531912 #: src/index.js:1709 1771 1913 msgid "Import settings" 1772 1914 msgstr "" 1773 1915 1774 #: src/index.js:1 5581916 #: src/index.js:1714 1775 1917 msgid "Reset" 1776 1918 msgstr "" 1777 1919 1778 #: src/index.js:1 5591920 #: src/index.js:1715 1779 1921 msgid "Restore every module to its default configuration. This cannot be undone." 1780 1922 msgstr "" 1781 1923 1782 #: src/index.js:1 5621924 #: src/index.js:1718 1783 1925 msgid "Reset all settings to defaults" 1784 1926 msgstr "" 1785 1927 1786 #: src/index.js:1 5711928 #: src/index.js:1727 1787 1929 msgid "Swap, preconnect, and preload fonts to remove render-blocking requests." 1788 1930 msgstr "" 1789 1931 1790 #: src/index.js:1 5741932 #: src/index.js:1730 1791 1933 msgid "Font loading (font-display)" 1792 1934 msgstr "" 1793 1935 1794 #: src/index.js:1 5751936 #: src/index.js:1731 1795 1937 msgid "Optional = no layout shift (best for CLS): the web font is skipped on the first uncached load and used once cached. Swap = text stays visible but the web font swaps in and can shift layout (higher CLS). Block = brief invisible text, then the web font." 1796 1938 msgstr "" 1797 1939 1798 #: src/index.js:1 5781940 #: src/index.js:1734 1799 1941 msgid "Optional — no layout shift (recommended for CLS)" 1800 1942 msgstr "" 1801 1943 1802 #: src/index.js:1 5791944 #: src/index.js:1735 1803 1945 msgid "Swap — always show text (may shift layout)" 1804 1946 msgstr "" 1805 1947 1806 #: src/index.js:1 5801948 #: src/index.js:1736 1807 1949 msgid "Block — brief hidden text, then web font" 1808 1950 msgstr "" 1809 1951 1810 #: src/index.js:1 5841952 #: src/index.js:1740 1811 1953 msgid "Combine Google Fonts into one request" 1812 1954 msgstr "" 1813 1955 1814 #: src/index.js:1 5851956 #: src/index.js:1741 1815 1957 msgid "Page builders often load each Google Font family separately. This merges them into a single request." 1816 1958 msgstr "" 1817 1959 1818 #: src/index.js:1 5891960 #: src/index.js:1745 1819 1961 msgid "Load only the font weights your pages use" 1820 1962 msgstr "" 1821 1963 1822 #: src/index.js:1 5901964 #: src/index.js:1746 1823 1965 msgid "Drops unused weights (e.g. 100, 200, 900) and italics your pages never use — often cutting font CSS by more than half. Weights 400 and 700 are always kept. Turn off if some text looks thinner or bolder than intended." 1824 1966 msgstr "" 1825 1967 1826 #: src/index.js:1594 1968 #: src/index.js:1751 1969 msgid "Load Google Fonts without blocking the page" 1970 msgstr "" 1971 1972 #: src/index.js:1752 1973 msgid "The page appears right away in a fallback font and switches to your Google Font as soon as it arrives, instead of waiting for Google first. A big win for the first paint on mobile. Applies when font display is Swap or Fallback; with Optional the stylesheet keeps loading first so text never shifts." 1974 msgstr "" 1975 1976 #: src/index.js:1756 1827 1977 msgid "Preload these font files (URLs)" 1828 1978 msgstr "" 1829 1979 1830 1980 #. translators: %d: number of items found in the database. 1831 #: src/index.js:1 6051981 #: src/index.js:1767 1832 1982 #, js-format 1833 1983 msgid "%d item in your database." … … 1836 1986 msgstr[1] "" 1837 1987 1838 #: src/index.js:1 6101988 #: src/index.js:1772 1839 1989 msgid "Database Optimization" 1840 1990 msgstr "" 1841 1991 1842 #: src/index.js:1 6111992 #: src/index.js:1773 1843 1993 msgid "Reduce bloat and reclaim space. Once a cleanup runs it cannot be undone, so back up your database first." 1844 1994 msgstr "" 1845 1995 1846 #: src/index.js:1 6271996 #: src/index.js:1789 1847 1997 msgid "Set it and forget it" 1848 1998 msgstr "" 1849 1999 1850 #: src/index.js:1 6282000 #: src/index.js:1790 1851 2001 msgid "Let Optimazz repeat the cleanups you picked above on a schedule, so your database stays lean without you lifting a finger." 1852 2002 msgstr "" 1853 2003 1854 #: src/index.js:1 6292004 #: src/index.js:1791 1855 2005 msgid "Schedule automatic cleanup" 1856 2006 msgstr "" 1857 2007 1858 #: src/index.js:1 6302008 #: src/index.js:1792 1859 2009 msgid "Uses WP-Cron to repeat the cleanups you enabled above." 1860 2010 msgstr "" 1861 2011 1862 #: src/index.js:1 6332012 #: src/index.js:1795 1863 2013 msgid "Cleanup frequency" 1864 2014 msgstr "" 1865 2015 1866 #: src/index.js:1 6372016 #: src/index.js:1799 1867 2017 msgid "Daily" 1868 2018 msgstr "" 1869 2019 1870 #: src/index.js:1 6382020 #: src/index.js:1800 1871 2021 msgid "Weekly" 1872 2022 msgstr "" 1873 2023 1874 #: src/index.js:1 6392024 #: src/index.js:1801 1875 2025 msgid "Monthly" 1876 2026 msgstr "" 1877 2027 1878 #: src/index.js:1 6462028 #: src/index.js:1808 1879 2029 msgid "Back up your database before you run a cleanup — a database optimization cannot be undone." 1880 2030 msgstr "" 1881 2031 1882 #: src/index.js:1 6512032 #: src/index.js:1813 1883 2033 msgid "Optimize now" 1884 2034 msgstr "" 1885 2035 1886 #: src/index.js:1 6542036 #: src/index.js:1816 1887 2037 msgid "Refresh counts" 1888 2038 msgstr "" 1889 2039 1890 #: src/index.js:1 6612040 #: src/index.js:1823 1891 2041 msgid "Heartbeat Control" 1892 2042 msgstr "" 1893 2043 1894 #: src/index.js:1 6622044 #: src/index.js:1824 1895 2045 msgid "Reduce or disable the WordPress Heartbeat API to lower server load." 1896 2046 msgstr "" 1897 2047 1898 #: src/index.js:1 6632048 #: src/index.js:1825 1899 2049 msgid "Enable Heartbeat control" 1900 2050 msgstr "" 1901 2051 1902 #: src/index.js:1 6672052 #: src/index.js:1829 1903 2053 msgid "Behavior in admin dashboard" 1904 2054 msgstr "" 1905 2055 1906 #: src/index.js:1 6692056 #: src/index.js:1831 1907 2057 msgid "Behavior in post editor" 1908 2058 msgstr "" 1909 2059 1910 #: src/index.js:1 6712060 #: src/index.js:1833 1911 2061 msgid "Behavior on front-end" 1912 2062 msgstr "" 1913 2063 1914 #: src/index.js:1 6802064 #: src/index.js:1842 1915 2065 msgid "Strip unnecessary WordPress front-end weight." 1916 2066 msgstr "" 1917 2067 1918 #: src/index.js:1 6822068 #: src/index.js:1844 1919 2069 msgid "Disable emojis" 1920 2070 msgstr "" 1921 2071 1922 #: src/index.js:1 6832072 #: src/index.js:1845 1923 2073 msgid "Remove the emoji detection script and styles." 1924 2074 msgstr "" 1925 2075 1926 #: src/index.js:1 6852076 #: src/index.js:1847 1927 2077 msgid "Disable embeds (oEmbed)" 1928 2078 msgstr "" 1929 2079 1930 #: src/index.js:1 6872080 #: src/index.js:1849 1931 2081 msgid "Disable dashicons on front-end" 1932 2082 msgstr "" 1933 2083 1934 #: src/index.js:1 6882084 #: src/index.js:1850 1935 2085 msgid "Only for anonymous visitors." 1936 2086 msgstr "" 1937 2087 1938 #: src/index.js:1 6902088 #: src/index.js:1852 1939 2089 msgid "Remove query strings from assets" 1940 2090 msgstr "" 1941 2091 1942 #: src/index.js:1 6912092 #: src/index.js:1853 1943 2093 msgid "Strip ?ver= for better proxy caching." 1944 2094 msgstr "" 1945 2095 1946 #: src/index.js:1 6932096 #: src/index.js:1855 1947 2097 msgid "Disable XML-RPC" 1948 2098 msgstr "" 1949 2099 1950 #: src/index.js:1 6952100 #: src/index.js:1857 1951 2101 msgid "Disable self-pingbacks" 1952 2102 msgstr "" 1953 2103 1954 #: src/index.js:1 6972104 #: src/index.js:1859 1955 2105 msgid "Remove head meta (generator, RSD, shortlink)" 1956 2106 msgstr "" 1957 2107 1958 #: src/index.js:1 6992108 #: src/index.js:1861 1959 2109 msgid "DNS-prefetch domains" 1960 2110 msgstr "" 1961 2111 1962 #: src/index.js:1 7012112 #: src/index.js:1863 1963 2113 msgid "Preconnect origins" 1964 2114 msgstr "" 1965 2115 1966 #: src/index.js:1 7072116 #: src/index.js:1869 1967 2117 msgid "Content Delivery Network" 1968 2118 msgstr "" 1969 2119 1970 #: src/index.js:1 7082120 #: src/index.js:1870 1971 2121 msgid "Rewrite static asset URLs to your CDN hostname." 1972 2122 msgstr "" 1973 2123 1974 #: src/index.js:1 7102124 #: src/index.js:1872 1975 2125 msgid "CDN URL (CNAME)" 1976 2126 msgstr "" 1977 2127 1978 #: src/index.js:1 7132128 #: src/index.js:1875 1979 2129 msgid "CSS, JS, image, and font URLs will be served from here." 1980 2130 msgstr "" 1981 2131 1982 #: src/index.js:1 7142132 #: src/index.js:1876 1983 2133 msgid "Exclude paths containing" 1984 2134 msgstr "" 1985 2135 1986 #: src/index.js:1781 1987 msgid "You have unsaved changes" 1988 msgstr "" 1989 1990 #: src/index.js:1781 1991 msgid "All changes saved" 1992 msgstr "" 1993 1994 #: src/index.js:1784 1995 msgid "Save changes" 1996 msgstr "" 2136 #: src/index.js:1952 2137 msgid "Preloading cache…" 2138 msgstr "" 2139 2140 #: src/index.js:2012 2141 #: src/index.js:2025 2142 msgid "Something went wrong." 2143 msgstr "" 2144 2145 #: src/index.js:2059 2146 msgid "Network settings saved." 2147 msgstr "" 2148 2149 #: src/index.js:2075 2150 msgid "Caching for every site" 2151 msgstr "" 2152 2153 #: src/index.js:2076 2154 msgid "Sites" 2155 msgstr "" 2156 2157 #: src/index.js:2076 2158 msgid "Cache per site" 2159 msgstr "" 2160 2161 #: src/index.js:2086 2162 msgid "Optimazz is not network-activated" 2163 msgstr "" 2164 2165 #: src/index.js:2087 2166 msgid "Only the sites where Optimazz is activated are cached. Network-activate it on Network Admin → Plugins to cache every site." 2167 msgstr "" 2168 2169 #: src/index.js:2092 2170 msgid "Serve ready-made HTML for every site instead of running PHP on each request. Each site keeps its own cache and its own exclusions." 2171 msgstr "" 2172 2173 #: src/index.js:2100 2174 msgid "Add Expires / Cache-Control rules to the shared .htaccess so repeat visits load CSS, JS, images and fonts from the browser cache. Apache and LiteSpeed only." 2175 msgstr "" 2176 2177 #: src/index.js:2103 2178 msgid "How long a cached page is kept. Pages are always purged as soon as their content changes." 2179 msgstr "" 2180 2181 #: src/index.js:2113 2182 msgid "Preload every site automatically" 2183 msgstr "" 2184 2185 #: src/index.js:2114 2186 msgid "Each site warms its own pages on a schedule and right after its cache is cleared." 2187 msgstr "" 2188 2189 #. translators: %s: name of another caching plugin. 2190 #: src/index.js:2135 2191 #, js-format 2192 msgid "%s controls page caching (advanced-cache.php)" 2193 msgstr "" 2194 2195 #: src/index.js:2138 2196 msgid "Optimazz cannot cache while another plugin owns the cache file. Deactivate that plugin, or let Optimazz take over." 2197 msgstr "" 2198 2199 #: src/index.js:2139 2200 msgid "Optimazz now handles page caching." 2201 msgstr "" 2202 2203 #: src/index.js:2146 2204 msgid "Cache engine installed (advanced-cache.php)" 2205 msgstr "" 2206 2207 #: src/index.js:2147 2208 msgid "WP_CACHE is on in wp-config.php" 2209 msgstr "" 2210 2211 #: src/index.js:2148 2212 msgid "wp-config.php is not writable — add define( 'WP_CACHE', true ); yourself" 2213 msgstr "" 2214 2215 #: src/index.js:2149 2216 msgid "The wp-content folder is not writable" 2217 msgstr "" 2218 2219 #: src/index.js:2167 2220 msgid "All sites" 2221 msgstr "" 2222 2223 #. translators: 1: sites with Optimazz, 2: all sites. 2224 #: src/index.js:2170 2225 #, js-format 2226 msgid "Optimazz runs on %1$d of %2$d sites. Each site has its own cache." 2227 msgstr "" 2228 2229 #: src/index.js:2172 2230 msgid "Page caching is off for the network — turn it on under Page Cache to purge or preload sites." 2231 msgstr "" 2232 2233 #: src/index.js:2175 2234 msgid "Every site’s cache was purged." 2235 msgstr "" 2236 2237 #: src/index.js:2176 2238 msgid "Purge all sites" 2239 msgstr "" 2240 2241 #: src/index.js:2194 2242 msgid "Site cache purged." 2243 msgstr "" 2244 2245 #: src/index.js:2215 2246 msgid "Optimazz is not active on this site, so its pages are not cached." 2247 msgstr "" 2248 2249 #: src/index.js:2225 2250 msgid "Network" 2251 msgstr "" -
optimazz/trunk/optimazz.php
r3723128 r3743505 4 4 * Plugin URI: https://wordpress.org/plugins/optimazz/ 5 5 * Description: All-in-one WordPress performance optimization — page caching, JS delay, and CSS optimization. 6 * Version: 1. 0.246 * Version: 1.1.0 7 7 * Requires at least: 6.0 8 8 * Requires PHP: 7.4 … … 26 26 } 27 27 28 define( 'OPTIMAZZ_VERSION', '1. 0.24' );28 define( 'OPTIMAZZ_VERSION', '1.1.0' ); 29 29 define( 'OPTIMAZZ_FILE', __FILE__ ); 30 30 define( 'OPTIMAZZ_DIR', plugin_dir_path( __FILE__ ) ); -
optimazz/trunk/optimazz.pot
r3723128 r3743505 3 3 msgid "" 4 4 msgstr "" 5 "Project-Id-Version: Optimazz – Website Optimization and Speedup 1. 0.24\n"5 "Project-Id-Version: Optimazz – Website Optimization and Speedup 1.1.0\n" 6 6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/optimazz\n" 7 7 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" … … 10 10 "Content-Type: text/plain; charset=UTF-8\n" 11 11 "Content-Transfer-Encoding: 8bit\n" 12 "POT-Creation-Date: 2026-10- 01T12:01:49+00:00\n"12 "POT-Creation-Date: 2026-10-11T19:05:58+00:00\n" 13 13 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 14 "X-Generator: WP-CLI 2.12.0\n" … … 40 40 msgstr "" 41 41 42 #: includes/Admin/Admin.php:149 43 #: includes/Admin/Admin.php:150 42 #: includes/Admin/Admin.php:155 43 #: includes/Admin/Admin.php:156 44 #: includes/Admin/Admin.php:290 45 #: includes/Admin/Admin.php:291 44 46 msgid "Optimazz" 45 47 msgstr "" 46 48 47 #: includes/Admin/Admin.php:1 6649 #: includes/Admin/Admin.php:172 48 50 #: src/index.js:36 49 51 msgid "Dashboard" 50 52 msgstr "" 51 53 52 #: includes/Admin/Admin.php:1 6754 #: includes/Admin/Admin.php:173 53 55 #: includes/Modules/Cache/CacheModule.php:24 54 56 #: src/index.js:37 55 #: src/index.js:1171 57 #: src/index.js:1317 58 #: src/index.js:2075 59 #: src/index.js:2091 56 60 msgid "Page Cache" 57 61 msgstr "" 58 62 59 #: includes/Admin/Admin.php:1 6863 #: includes/Admin/Admin.php:174 60 64 #: src/index.js:38 61 #: src/index.js:1 15065 #: src/index.js:1296 62 66 msgid "File Optimization" 63 67 msgstr "" 64 68 65 #: includes/Admin/Admin.php:1 6969 #: includes/Admin/Admin.php:175 66 70 #: src/index.js:39 67 71 msgid "Media" 68 72 msgstr "" 69 73 70 #: includes/Admin/Admin.php:17 074 #: includes/Admin/Admin.php:176 71 75 #: includes/Modules/Database/DatabaseModule.php:26 72 76 #: src/index.js:43 … … 74 78 msgstr "" 75 79 76 #: includes/Admin/Admin.php:17 177 #: includes/Admin/Admin.php:2 2978 #: includes/Admin/Admin.php:23 380 #: includes/Admin/Admin.php:177 81 #: includes/Admin/Admin.php:235 82 #: includes/Admin/Admin.php:239 79 83 #: src/index.js:47 80 84 msgid "Tools" 81 85 msgstr "" 82 86 83 #: includes/Admin/Admin.php:19 387 #: includes/Admin/Admin.php:199 84 88 #: includes/Admin/AdminBar.php:52 89 #: src/index.js:2198 85 90 msgid "Settings" 86 91 msgstr "" 87 92 88 #: includes/Admin/Admin.php:2 2493 #: includes/Admin/Admin.php:230 89 94 msgid "Optimazz Safe Mode is on." 90 95 msgstr "" 91 96 92 #: includes/Admin/Admin.php:2 2597 #: includes/Admin/Admin.php:231 93 98 msgid "Some pages were loading without styling — their optimized CSS could not be found, usually because a server or CDN cache was serving old pages. Optimazz paused advanced CSS/JS optimization to keep your site working." 94 99 msgstr "" 95 100 96 #: includes/Admin/Admin.php:2 27101 #: includes/Admin/Admin.php:233 97 102 msgid "It broke again shortly after full optimization was restored, so Safe Mode will stay on until you switch it off in" 98 103 msgstr "" 99 104 100 #: includes/Admin/Admin.php:2 28105 #: includes/Admin/Admin.php:234 101 106 msgid "It re-checks automatically and restores full optimization as soon as pages load correctly — clearing your server/CDN cache speeds that up. You can also manage it in" 102 107 msgstr "" 103 108 104 #: includes/Admin/Admin.php:23 2109 #: includes/Admin/Admin.php:238 105 110 msgid "Your pages are loading correctly again — you can switch Safe Mode off in" 106 111 msgstr "" 107 112 108 #: includes/Admin/Admin.php:2 36113 #: includes/Admin/Admin.php:242 109 114 msgid "Pages are loading correctly again, so Safe Mode was switched off automatically and full optimization is back on." 110 115 msgstr "" 111 116 112 #: includes/Admin/Admin.php:25 0117 #: includes/Admin/Admin.php:256 113 118 msgid "wp-config.php is not writable, so page caching could not be enabled. Add" 114 119 msgstr "" 115 120 116 #: includes/Admin/Admin.php:25 2121 #: includes/Admin/Admin.php:258 117 122 msgid "manually, or make wp-config.php writable." 118 123 msgstr "" 119 124 120 #: includes/Admin/Admin.php:2 59125 #: includes/Admin/Admin.php:265 121 126 msgid "Optimazz cache purged." 122 127 msgstr "" 123 128 124 #: includes/Admin/Admin.php:26 0129 #: includes/Admin/Admin.php:266 125 130 msgid "Optimazz cache preload started in the background." 126 131 msgstr "" 127 132 128 #: includes/Admin/Admin.php:26 1133 #: includes/Admin/Admin.php:267 129 134 msgid "Optimazz cache cleared; preload started in the background." 130 135 msgstr "" 131 136 132 #: includes/Admin/AdminBar.php:5 3137 #: includes/Admin/AdminBar.php:57 133 138 msgid "Clear & Preload Cache" 134 139 msgstr "" 135 140 136 #: includes/Admin/AdminBar.php:5 4137 #: src/index.js:1 451141 #: includes/Admin/AdminBar.php:58 142 #: src/index.js:1597 138 143 msgid "Preload Cache" 139 144 msgstr "" 140 145 141 #: includes/Admin/AdminBar.php:5 5146 #: includes/Admin/AdminBar.php:59 142 147 msgid "Purge Cache" 143 148 msgstr "" 144 149 145 #: includes/Admin/AdminBar.php:7 3150 #: includes/Admin/AdminBar.php:77 146 151 msgid "Documentation" 147 152 msgstr "" 148 153 149 #: includes/Admin/AdminBar.php: 98150 #: includes/Admin/CacheConflictNotice.php:12 4151 #: includes/Admin/CacheConflictNotice.php:15 4154 #: includes/Admin/AdminBar.php:102 155 #: includes/Admin/CacheConflictNotice.php:126 156 #: includes/Admin/CacheConflictNotice.php:156 152 157 msgid "Not allowed." 153 158 msgstr "" … … 162 167 163 168 #: includes/Admin/CacheConflictNotice.php:88 164 #: includes/Modules/Cache/Preloader.php:714 169 #: includes/Modules/Cache/Preloader.php:740 170 #: src/index.js:2136 165 171 msgid "Another plugin" 166 172 msgstr "" … … 180 186 #: includes/Admin/CacheConflictNotice.php:108 181 187 #: includes/Admin/CacheConflictNotice.php:110 188 #: src/index.js:2140 182 189 msgid "Use Optimazz for caching" 183 190 msgstr "" … … 187 194 msgstr "" 188 195 189 #: includes/Admin/RestController.php:467 196 #: includes/Admin/NetworkRestController.php:115 197 #: includes/Admin/NetworkRestController.php:134 198 msgid "Page caching is off for this site." 199 msgstr "" 200 201 #: includes/Admin/NetworkRestController.php:153 202 msgid "Optimazz is not active on any site yet. Network-activate it (or activate it on a site) first." 203 msgstr "" 204 205 #: includes/Admin/RestController.php:385 206 #: includes/Admin/RestController.php:588 207 #: includes/Admin/RestController.php:614 208 msgid "Page caching is not available on multisite networks." 209 msgstr "" 210 211 #: includes/Admin/RestController.php:479 190 212 msgid "PHP version" 191 213 msgstr "" 192 214 193 #: includes/Admin/RestController.php:4 70215 #: includes/Admin/RestController.php:482 194 216 msgid "Optimazz requires PHP 7.4 or newer." 195 217 msgstr "" 196 218 197 #: includes/Admin/RestController.php:4 73219 #: includes/Admin/RestController.php:485 198 220 msgid "WordPress version" 199 221 msgstr "" 200 222 201 #: includes/Admin/RestController.php:4 79223 #: includes/Admin/RestController.php:491 202 224 msgid "GD (image library)" 203 225 msgstr "" 204 226 205 #: includes/Admin/RestController.php:4 80206 #: includes/Admin/RestController.php: 489207 #: includes/Admin/RestController.php:5 25227 #: includes/Admin/RestController.php:492 228 #: includes/Admin/RestController.php:501 229 #: includes/Admin/RestController.php:537 208 230 msgid "Enabled" 209 231 msgstr "" 210 232 211 #: includes/Admin/RestController.php:4 80212 #: includes/Admin/RestController.php: 489233 #: includes/Admin/RestController.php:492 234 #: includes/Admin/RestController.php:501 213 235 msgid "Not installed (optional)" 214 236 msgstr "" 215 237 216 #: includes/Admin/RestController.php:4 84238 #: includes/Admin/RestController.php:496 217 239 msgid "GD is enabled but without WebP support — ask your host to enable WebP in GD." 218 240 msgstr "" 219 241 220 #: includes/Admin/RestController.php:4 85242 #: includes/Admin/RestController.php:497 221 243 msgid "The PHP GD extension is not enabled. GD is the most common engine for image compression and WebP. Ask your host to enable the \"php-gd\" extension." 222 244 msgstr "" 223 245 224 #: includes/Admin/RestController.php: 488246 #: includes/Admin/RestController.php:500 225 247 msgid "ImageMagick (Imagick)" 226 248 msgstr "" 227 249 228 #: includes/Admin/RestController.php: 489250 #: includes/Admin/RestController.php:501 229 251 msgid "Not enabled" 230 252 msgstr "" 231 253 232 #: includes/Admin/RestController.php: 496254 #: includes/Admin/RestController.php:508 233 255 msgid "Optional — GD is already handling images. Imagick can add AVIF and higher-quality output if your host enables the \"php-imagick\" extension." 234 256 msgstr "" 235 257 236 #: includes/Admin/RestController.php: 497258 #: includes/Admin/RestController.php:509 237 259 msgid "The PHP Imagick extension is not enabled either. With neither GD nor Imagick, image compression and WebP/AVIF cannot run. Ask your host to enable \"php-gd\" or \"php-imagick\"." 238 260 msgstr "" 239 261 240 #: includes/Admin/RestController.php:5 00262 #: includes/Admin/RestController.php:512 241 263 msgid "WebP support" 242 264 msgstr "" 243 265 244 #: includes/Admin/RestController.php:5 01245 #: includes/Admin/RestController.php:5 07266 #: includes/Admin/RestController.php:513 267 #: includes/Admin/RestController.php:519 246 268 msgid "Available" 247 269 msgstr "" 248 270 249 #: includes/Admin/RestController.php:5 01250 #: includes/Admin/RestController.php:5 07271 #: includes/Admin/RestController.php:513 272 #: includes/Admin/RestController.php:519 251 273 msgid "Not available" 252 274 msgstr "" 253 275 254 #: includes/Admin/RestController.php:5 03276 #: includes/Admin/RestController.php:515 255 277 msgid "This server cannot generate WebP images. Update GD/Imagick or ask your host to enable WebP." 256 278 msgstr "" 257 279 258 #: includes/Admin/RestController.php:5 06280 #: includes/Admin/RestController.php:518 259 281 msgid "AVIF support" 260 282 msgstr "" 261 283 262 #: includes/Admin/RestController.php:5 09284 #: includes/Admin/RestController.php:521 263 285 msgid "This server cannot generate AVIF images (needs PHP 8.1+ GD with AVIF, or ImageMagick with the AVIF delegate). WebP still works." 264 286 msgstr "" 265 287 266 #: includes/Admin/RestController.php:5 12288 #: includes/Admin/RestController.php:524 267 289 msgid "PHP memory limit" 268 290 msgstr "" 269 291 270 #: includes/Admin/RestController.php:5 15292 #: includes/Admin/RestController.php:527 271 293 msgid "Image optimization works best with 256 MB or more. On lower limits Optimazz processes fewer images per request to stay safe." 272 294 msgstr "" 273 295 274 #: includes/Admin/RestController.php:5 18296 #: includes/Admin/RestController.php:530 275 297 msgid "Max execution time" 276 298 msgstr "" 277 299 278 #: includes/Admin/RestController.php:5 19300 #: includes/Admin/RestController.php:531 279 301 msgid "Unlimited" 280 302 msgstr "" 281 303 282 #: includes/Admin/RestController.php:5 21304 #: includes/Admin/RestController.php:533 283 305 msgid "Under 30 seconds can interrupt bulk AVIF conversion of large images." 284 306 msgstr "" 285 307 286 #: includes/Admin/RestController.php:5 24308 #: includes/Admin/RestController.php:536 287 309 msgid "WP-Cron" 288 310 msgstr "" 289 311 290 #: includes/Admin/RestController.php:5 25312 #: includes/Admin/RestController.php:537 291 313 msgid "Disabled" 292 314 msgstr "" 293 315 294 #: includes/Admin/RestController.php:5 27316 #: includes/Admin/RestController.php:539 295 317 msgid "WP-Cron is disabled, so scheduled cache preloading relies on a real system cron job being configured." 296 318 msgstr "" 297 319 298 #: includes/Admin/RestController.php:5 41320 #: includes/Admin/RestController.php:553 299 321 msgid "GD and Imagick are" 300 322 msgstr "" 301 323 302 #: includes/Admin/RestController.php:5 42324 #: includes/Admin/RestController.php:554 303 325 msgid "GD is" 304 326 msgstr "" 305 327 306 #: includes/Admin/RestController.php:5 42328 #: includes/Admin/RestController.php:554 307 329 msgid "Imagick is" 308 330 msgstr "" 309 331 310 #: includes/Admin/RestController.php:5 44332 #: includes/Admin/RestController.php:556 311 333 msgid "PHP that runs this site" 312 334 msgstr "" 313 335 314 336 #. translators: 1: which engine is missing, 2: php.ini path, 3: PHP version 315 #: includes/Admin/RestController.php:5 50337 #: includes/Admin/RestController.php:562 316 338 #, php-format 317 339 msgid "This is the exact PHP handling your site — it loads its settings from %2$s. If you already enabled an image library in your hosting panel but %1$s still shown as not enabled, you almost certainly enabled it for a different PHP version or handler. It must be enabled for THIS PHP (%3$s). After enabling, restart PHP (or PHP-FPM) and click Re-check." … … 319 341 320 342 #. translators: 1: which engine is missing, 2: PHP version 321 #: includes/Admin/RestController.php:5 57343 #: includes/Admin/RestController.php:569 322 344 #, php-format 323 345 msgid "This is the exact PHP handling your site. If you already enabled an image library in your hosting panel but %1$s still shown as not enabled, you enabled it for a different PHP version. It must be enabled for THIS PHP (%2$s). After enabling, restart PHP and click Re-check." 324 346 msgstr "" 325 347 326 #: includes/Admin/RestController.php:7 07348 #: includes/Admin/RestController.php:725 327 349 msgid "Image processing is unavailable: this server has no GD or ImageMagick extension. Ask your host to enable \"php-gd\" (or \"php-imagick\"), then try again. See Tools → Server Requirements." 328 350 msgstr "" … … 336 358 msgstr "" 337 359 338 #: includes/Core/Recommendations.php:6 1339 #: src/index.js: 971360 #: includes/Core/Recommendations.php:63 361 #: src/index.js:1117 340 362 msgid "Enable Page Cache" 341 363 msgstr "" 342 364 343 #: includes/Core/Recommendations.php:6 2365 #: includes/Core/Recommendations.php:64 344 366 msgid "Serve static HTML instead of running PHP on every request — the single biggest speed win." 345 367 msgstr "" 346 368 347 #: includes/Core/Recommendations.php:6 7369 #: includes/Core/Recommendations.php:69 348 370 msgid "Turn on cache preloading" 349 371 msgstr "" 350 372 351 #: includes/Core/Recommendations.php: 68373 #: includes/Core/Recommendations.php:70 352 374 msgid "Warm the cache automatically so visitors never hit a cold, uncached page." 353 375 msgstr "" 354 376 355 #: includes/Core/Recommendations.php:7 6377 #: includes/Core/Recommendations.php:78 356 378 msgid "Enable JavaScript optimization" 357 379 msgstr "" 358 380 359 #: includes/Core/Recommendations.php:7 7381 #: includes/Core/Recommendations.php:79 360 382 msgid "Defer and delay scripts to cut Total Blocking Time — the heaviest-weighted mobile metric." 361 383 msgstr "" 362 384 363 #: includes/Core/Recommendations.php:8 4385 #: includes/Core/Recommendations.php:86 364 386 msgid "Delay JavaScript until interaction" 365 387 msgstr "" 366 388 367 #: includes/Core/Recommendations.php:8 5389 #: includes/Core/Recommendations.php:87 368 390 msgid "Delay non-critical scripts (analytics, chat, pixels) until the first interaction to reduce blocking time." 369 391 msgstr "" 370 392 371 #: includes/Core/Recommendations.php:9 3393 #: includes/Core/Recommendations.php:95 372 394 msgid "Enable CSS optimization" 373 395 msgstr "" 374 396 375 #: includes/Core/Recommendations.php:9 4397 #: includes/Core/Recommendations.php:96 376 398 msgid "Minify CSS to remove render-blocking bytes and speed up First Contentful Paint." 377 399 msgstr "" 378 400 379 #: includes/Core/Recommendations.php:10 2401 #: includes/Core/Recommendations.php:104 380 402 msgid "Enable Image optimization" 381 403 msgstr "" 382 404 383 #: includes/Core/Recommendations.php:10 3405 #: includes/Core/Recommendations.php:105 384 406 msgid "Lazy-load images, reserve their space (to prevent layout shift) and serve next-gen formats." 385 407 msgstr "" 386 408 387 #: includes/Core/Recommendations.php:1 09409 #: includes/Core/Recommendations.php:111 388 410 msgid "Reserve image space (fix layout shift)" 389 411 msgstr "" 390 412 391 #: includes/Core/Recommendations.php:11 0413 #: includes/Core/Recommendations.php:112 392 414 msgid "Add missing image dimensions and aspect-ratio so images do not push content down as they load (lowers CLS)." 393 415 msgstr "" 394 416 395 #: includes/Core/Recommendations.php:11 6396 #: src/index.js: 948397 #: src/index.js:1 369417 #: includes/Core/Recommendations.php:118 418 #: src/index.js:1092 419 #: src/index.js:1515 398 420 msgid "Lazy-load images" 399 421 msgstr "" 400 422 401 #: includes/Core/Recommendations.php:11 7423 #: includes/Core/Recommendations.php:119 402 424 msgid "Load off-screen images only when scrolled into view to speed up the initial load." 403 425 msgstr "" 404 426 405 #: includes/Core/Recommendations.php:12 4427 #: includes/Core/Recommendations.php:126 406 428 msgid "Serve WebP images" 407 429 msgstr "" 408 430 409 #: includes/Core/Recommendations.php:12 5431 #: includes/Core/Recommendations.php:127 410 432 msgid "Generate and serve WebP copies — typically 25–35% smaller than JPEG/PNG at the same quality." 411 433 msgstr "" 412 434 413 #: includes/Core/Recommendations.php:13 6435 #: includes/Core/Recommendations.php:138 414 436 msgid "Set font loading to \"Optional\" (reduce CLS)" 415 437 msgstr "" 416 438 417 #: includes/Core/Recommendations.php:13 7439 #: includes/Core/Recommendations.php:139 418 440 msgid "The \"swap\" mode lets web fonts reflow the page as they load, raising Cumulative Layout Shift. \"Optional\" removes that shift." 419 441 msgstr "" 420 442 421 #: includes/Core/Recommendations.php:14 3443 #: includes/Core/Recommendations.php:145 422 444 msgid "Enable Font optimization" 423 445 msgstr "" 424 446 425 #: includes/Core/Recommendations.php:14 4447 #: includes/Core/Recommendations.php:146 426 448 msgid "Preconnect to font hosts and control font-display to reduce render-blocking and layout shift." 427 449 msgstr "" 428 450 429 #: includes/Core/Recommendations.php:15 2451 #: includes/Core/Recommendations.php:154 430 452 msgid "Enable Database cleanup" 431 453 msgstr "" 432 454 433 #: includes/Core/Recommendations.php:15 3455 #: includes/Core/Recommendations.php:155 434 456 msgid "Remove revisions, auto-drafts, spam and expired transients, and optimize tables to keep the database lean." 435 457 msgstr "" 436 458 437 #: includes/Core/Recommendations.php:16 1459 #: includes/Core/Recommendations.php:163 438 460 msgid "Enable an image library on your server" 439 461 msgstr "" 440 462 441 #: includes/Core/Recommendations.php:16 2463 #: includes/Core/Recommendations.php:164 442 464 msgid "Your server has neither GD nor ImageMagick, so image compression and WebP/AVIF cannot run. Ask your host to enable the \"php-gd\" (or \"php-imagick\") extension. See Tools → Server Requirements." 443 465 msgstr "" 444 466 467 #: includes/Modules/Cache/Preloader.php:116 468 msgid "the home page" 469 msgstr "" 470 445 471 #. translators: %d: number of category / tag archive pages. 446 #: includes/Modules/Cache/Preloader.php:12 4472 #: includes/Modules/Cache/Preloader.php:127 447 473 #, php-format 448 474 msgid "%d category or tag page" … … 451 477 msgstr[1] "" 452 478 453 #: includes/Modules/Cache/Preloader.php: 681479 #: includes/Modules/Cache/Preloader.php:707 454 480 msgid "your host's server cache" 455 481 msgstr "" 456 482 457 #: includes/Modules/Cache/Preloader.php: 685483 #: includes/Modules/Cache/Preloader.php:711 458 484 msgid "a server or CDN cache" 459 485 msgstr "" 460 486 461 #: includes/Modules/Cache/Preloader.php:7 04487 #: includes/Modules/Cache/Preloader.php:730 462 488 msgid "The site could not load its own pages (the loopback request failed or timed out). Some hosts block a server from requesting its own website — ask your host to allow it." 463 489 msgstr "" 464 490 465 491 #. translators: %s: name of another caching plugin. 466 #: includes/Modules/Cache/Preloader.php:7 13492 #: includes/Modules/Cache/Preloader.php:739 467 493 #, php-format 468 494 msgid "%s controls page caching (advanced-cache.php), so Optimazz cannot save pages. Deactivate it, or use \"Take over caching\" in Cache health." 469 495 msgstr "" 470 496 471 #: includes/Modules/Cache/Preloader.php:7 18497 #: includes/Modules/Cache/Preloader.php:744 472 498 msgid "The cache file advanced-cache.php is missing. Turn Page Cache off and on again to reinstall it, and make sure the wp-content folder is writable." 473 499 msgstr "" 474 500 475 #: includes/Modules/Cache/Preloader.php:7 21501 #: includes/Modules/Cache/Preloader.php:747 476 502 msgid "WP_CACHE is not enabled in wp-config.php, so WordPress never loads the cache. Make wp-config.php writable and save the Cache settings, or add define( 'WP_CACHE', true ); yourself." 477 503 msgstr "" 478 504 479 505 #. translators: %s: name of a server or CDN cache, e.g. "Cloudflare". 480 #: includes/Modules/Cache/Preloader.php:7 26506 #: includes/Modules/Cache/Preloader.php:752 481 507 #, php-format 482 508 msgid "%s answered from its own cache before WordPress ran, so Optimazz never saw these requests. Purge that cache, or turn it off for HTML pages if you want Optimazz to cache them." 483 509 msgstr "" 484 510 485 #: includes/Modules/Cache/Preloader.php:7 30511 #: includes/Modules/Cache/Preloader.php:756 486 512 msgid "The Optimazz cache engine did not run for these requests. Usually a server-level cache or proxy (for example LiteSpeed, Varnish or a host cache) answers before WordPress loads. Check Cache health, or ask your host." 487 513 msgstr "" 488 514 489 515 #. translators: %s: HTTP status code, e.g. 404. 490 #: includes/Modules/Cache/Preloader.php:7 37516 #: includes/Modules/Cache/Preloader.php:763 491 517 #, php-format 492 518 msgid "Pages answered with HTTP %s instead of 200, and only normal pages are cached. Check that these URLs open correctly." 493 519 msgstr "" 494 520 495 #: includes/Modules/Cache/Preloader.php:7 42521 #: includes/Modules/Cache/Preloader.php:768 496 522 msgid "Page Cache is switched off." 497 523 msgstr "" 498 524 499 #: includes/Modules/Cache/Preloader.php:7 43525 #: includes/Modules/Cache/Preloader.php:769 500 526 msgid "Another plugin or the theme marks these pages \"do not cache\" (DONOTCACHEPAGE), so they are skipped. Membership, security or form plugins often do this." 501 527 msgstr "" 502 528 503 #: includes/Modules/Cache/Preloader.php:7 44529 #: includes/Modules/Cache/Preloader.php:770 504 530 msgid "The web address used to reach the site does not match the WordPress Site Address (for example www vs non-www). Check Settings → General." 505 531 msgstr "" 506 532 507 #: includes/Modules/Cache/Preloader.php:7 45533 #: includes/Modules/Cache/Preloader.php:771 508 534 msgid "Cache files could not be written. Make the wp-content/cache folder writable (permissions 755)." 509 535 msgstr "" 510 536 511 #: includes/Modules/Cache/Preloader.php:7 46537 #: includes/Modules/Cache/Preloader.php:772 512 538 msgid "These pages match your \"Never cache these URLs\" setting." 513 539 msgstr "" 514 540 515 #: includes/Modules/Cache/Preloader.php:7 47541 #: includes/Modules/Cache/Preloader.php:773 516 542 msgid "A cookie on the request matches your \"Never cache when these cookies are set\" setting." 517 543 msgstr "" 518 544 519 #: includes/Modules/Cache/Preloader.php:7 48545 #: includes/Modules/Cache/Preloader.php:774 520 546 msgid "The preloader's user-agent matches your \"Never cache for these user agents\" setting." 521 547 msgstr "" 522 548 523 #: includes/Modules/Cache/Preloader.php:7 49549 #: includes/Modules/Cache/Preloader.php:775 524 550 msgid "The pages came back empty or almost empty, so there was nothing to cache." 525 551 msgstr "" 526 552 527 #: includes/Modules/Cache/Preloader.php:7 50553 #: includes/Modules/Cache/Preloader.php:776 528 554 msgid "WordPress treated the request as logged in, so it was not cached." 529 555 msgstr "" 530 556 531 #: includes/Modules/Cache/Preloader.php:7 51557 #: includes/Modules/Cache/Preloader.php:777 532 558 msgid "These URLs show a \"not found\" (404) page, which is never cached." 533 559 msgstr "" 534 560 535 561 #. translators: %s: internal reason code. 536 #: includes/Modules/Cache/Preloader.php:7 57562 #: includes/Modules/Cache/Preloader.php:783 537 563 #, php-format 538 564 msgid "The cache skipped these pages (reason: %s)." … … 552 578 msgstr "" 553 579 554 #: includes/Modules/Fonts/FontsModule.php: 29580 #: includes/Modules/Fonts/FontsModule.php:38 555 581 #: src/index.js:40 556 #: src/index.js:1 570582 #: src/index.js:1726 557 583 msgid "Fonts" 558 584 msgstr "" … … 568 594 569 595 #: includes/Modules/Media/MediaModule.php:75 570 #: src/index.js:1 359596 #: src/index.js:1505 571 597 msgid "Image Optimization" 572 598 msgstr "" … … 578 604 #: includes/Modules/Tweaks/TweaksModule.php:25 579 605 #: src/index.js:45 580 #: src/index.js:1 679606 #: src/index.js:1841 581 607 msgid "Tweaks" 582 608 msgstr "" … … 603 629 604 630 #: src/index.js:41 605 #: src/index.js:1055 631 #: src/index.js:1201 632 #: src/index.js:2191 606 633 msgid "Preload" 607 634 msgstr "" … … 612 639 613 640 #: src/index.js:42 614 #: src/index.js:1 482641 #: src/index.js:1638 615 642 msgid "Advanced Rules" 616 643 msgstr "" … … 640 667 msgstr "" 641 668 642 #: src/index.js:64 669 #: src/index.js:81 670 msgid "every hour" 671 msgstr "" 672 673 #: src/index.js:82 674 #: src/index.js:84 675 msgid "twice a day" 676 msgstr "" 677 678 #: src/index.js:83 679 msgid "once a day" 680 msgstr "" 681 682 #: src/index.js:88 683 msgid "never expires" 684 msgstr "" 685 686 #: src/index.js:89 687 #: src/index.js:1231 688 #: src/index.js:1333 689 #: src/index.js:2107 690 msgid "12 hours" 691 msgstr "" 692 693 #: src/index.js:90 694 #: src/index.js:1232 695 #: src/index.js:1334 696 #: src/index.js:2108 697 msgid "1 day" 698 msgstr "" 699 700 #: src/index.js:91 701 #: src/index.js:1233 702 #: src/index.js:1335 703 #: src/index.js:2109 704 msgid "1 week" 705 msgstr "" 706 707 #: src/index.js:92 708 #: src/index.js:1234 709 #: src/index.js:1336 710 #: src/index.js:2110 711 msgid "1 month" 712 msgstr "" 713 714 #. translators: %d: number of hours. 715 #: src/index.js:95 716 #: src/index.js:1237 717 #, js-format 718 msgid "%d hour" 719 msgid_plural "%d hours" 720 msgstr[0] "" 721 msgstr[1] "" 722 723 #: src/index.js:102 724 msgid "Open Network Admin → Optimazz" 725 msgstr "" 726 727 #: src/index.js:111 728 msgid "Page caching is off for this network" 729 msgstr "" 730 731 #: src/index.js:113 732 msgid "Turn it on for every site in Network Admin → Optimazz. Each site gets its own cache, and keeps its own exclusions here under Advanced Rules." 733 msgstr "" 734 735 #: src/index.js:114 736 msgid "Your network administrator can turn it on for every site in Network Admin → Optimazz. All other optimizations — CSS, JavaScript, images, fonts and more — work normally on this site." 737 msgstr "" 738 739 #: src/index.js:128 740 msgid "Page caching is on — managed for the whole network" 741 msgstr "" 742 743 #. translators: 1: yes/no, 2: cache lifetime, 3: preload frequency. 744 #: src/index.js:131 745 #, js-format 746 msgid "Separate mobile cache: %1$s · Cache lifetime: %2$s · Automatic preload: %3$s." 747 msgstr "" 748 749 #: src/index.js:132 750 msgid "yes" 751 msgstr "" 752 753 #: src/index.js:132 754 msgid "no" 755 msgstr "" 756 757 #: src/index.js:134 758 #: src/index.js:1247 759 msgid "off" 760 msgstr "" 761 762 #: src/index.js:136 763 msgid "This site has its own cache. Purge or preload it from the Dashboard or the Preload screen, and set its own exclusions under Advanced Rules." 764 msgstr "" 765 766 #: src/index.js:146 643 767 msgid "Content leftovers" 644 768 msgstr "" 645 769 646 #: src/index.js: 65770 #: src/index.js:147 647 771 msgid "Old post data that piles up over time. Removing it is permanent, so keep anything you still need." 648 772 msgstr "" 649 773 650 #: src/index.js: 67774 #: src/index.js:149 651 775 msgid "Post revisions" 652 776 msgstr "" 653 777 654 #: src/index.js: 67778 #: src/index.js:149 655 779 msgid "Old copies of posts saved automatically. Deleting them keeps only your current content." 656 780 msgstr "" 657 781 658 #: src/index.js: 68782 #: src/index.js:150 659 783 msgid "Auto drafts" 660 784 msgstr "" 661 785 662 #: src/index.js: 68786 #: src/index.js:150 663 787 msgid "Empty drafts WordPress creates while you write. Safe to remove." 664 788 msgstr "" 665 789 666 #: src/index.js: 69790 #: src/index.js:151 667 791 msgid "Trashed posts" 668 792 msgstr "" 669 793 670 #: src/index.js: 69794 #: src/index.js:151 671 795 msgid "Posts in the trash that have not been permanently deleted yet." 672 796 msgstr "" 673 797 674 #: src/index.js: 73798 #: src/index.js:155 675 799 msgid "Comment clutter" 676 800 msgstr "" 677 801 678 #: src/index.js: 74802 #: src/index.js:156 679 803 msgid "Spam and trashed comments you are never going to approve. Clearing them keeps the comments table light." 680 804 msgstr "" 681 805 682 #: src/index.js: 76806 #: src/index.js:158 683 807 msgid "Spam comments" 684 808 msgstr "" 685 809 686 #: src/index.js: 76810 #: src/index.js:158 687 811 msgid "Comments marked as spam. Removing them frees up the comments table." 688 812 msgstr "" 689 813 690 #: src/index.js: 77814 #: src/index.js:159 691 815 msgid "Trashed comments" 692 816 msgstr "" 693 817 694 #: src/index.js: 77818 #: src/index.js:159 695 819 msgid "Comments in the trash awaiting permanent deletion." 696 820 msgstr "" 697 821 698 #: src/index.js: 81822 #: src/index.js:163 699 823 msgid "Temporary data" 700 824 msgstr "" 701 825 702 #: src/index.js: 82826 #: src/index.js:164 703 827 msgid "Short-lived cached values. They are safe to clear and are rebuilt automatically whenever a plugin needs them." 704 828 msgstr "" 705 829 706 #: src/index.js: 84830 #: src/index.js:166 707 831 msgid "Expired transients" 708 832 msgstr "" 709 833 710 #: src/index.js: 84834 #: src/index.js:166 711 835 msgid "Cached values that have already passed their expiry date." 712 836 msgstr "" 713 837 714 #: src/index.js: 88838 #: src/index.js:170 715 839 msgid "Table maintenance" 716 840 msgstr "" 717 841 718 #: src/index.js: 89842 #: src/index.js:171 719 843 msgid "Defragment your tables to reclaim wasted space and trim database overhead." 720 844 msgstr "" 721 845 722 #: src/index.js: 91846 #: src/index.js:173 723 847 msgid "Optimize database tables" 724 848 msgstr "" 725 849 726 #: src/index.js: 91850 #: src/index.js:173 727 851 msgid "Runs OPTIMIZE on your tables to defragment them and free wasted space." 728 852 msgstr "" 729 853 730 #: src/index.js: 97854 #: src/index.js:179 731 855 msgid "Do not limit" 732 856 msgstr "" 733 857 734 #: src/index.js: 98858 #: src/index.js:180 735 859 msgid "Reduce activity" 736 860 msgstr "" 737 861 738 #: src/index.js: 99862 #: src/index.js:181 739 863 msgid "Disable" 740 864 msgstr "" 741 865 742 #: src/index.js:1 08866 #: src/index.js:190 743 867 msgid "Active" 744 868 msgstr "" 745 869 746 #: src/index.js:1 08747 #: src/index.js:1 110748 #: src/index.js:1 636870 #: src/index.js:190 871 #: src/index.js:1256 872 #: src/index.js:1798 749 873 msgid "Off" 750 874 msgstr "" 751 875 752 #: src/index.js: 150876 #: src/index.js:232 753 877 msgid "Cancelled" 754 878 msgstr "" 755 879 756 #: src/index.js: 151880 #: src/index.js:233 757 881 msgid "Failed" 758 882 msgstr "" 759 883 760 #: src/index.js: 167884 #: src/index.js:249 761 885 msgid "Expand" 762 886 msgstr "" 763 887 764 #: src/index.js: 167888 #: src/index.js:249 765 889 msgid "Minimize" 766 890 msgstr "" 767 891 768 #: src/index.js: 170892 #: src/index.js:252 769 893 msgid "Close" 770 894 msgstr "" 771 895 772 #: src/index.js: 184896 #: src/index.js:266 773 897 msgid "Step" 774 898 msgstr "" 775 899 776 #: src/index.js: 189900 #: src/index.js:271 777 901 msgid "Stop" 778 902 msgstr "" 779 903 780 #: src/index.js:492 781 #: src/index.js:1239 904 #: src/index.js:538 905 msgid "You have unsaved changes" 906 msgstr "" 907 908 #: src/index.js:538 909 msgid "All changes saved" 910 msgstr "" 911 912 #: src/index.js:541 913 msgid "Save changes" 914 msgstr "" 915 916 #: src/index.js:639 917 #: src/index.js:1385 782 918 msgid "Deactivate" 783 919 msgstr "" 784 920 785 #: src/index.js: 492921 #: src/index.js:639 786 922 msgid "Optimazz will keep handling caching." 787 923 msgstr "" 788 924 789 #: src/index.js: 499925 #: src/index.js:646 790 926 msgid "deactivated — Optimazz is now the only cache." 791 927 msgstr "" 792 928 793 #: src/index.js: 501929 #: src/index.js:648 794 930 msgid "Could not deactivate the plugin." 795 931 msgstr "" 796 932 797 #: src/index.js: 508933 #: src/index.js:655 798 934 msgid "Make Optimazz the caching plugin? This replaces the current advanced-cache.php (from another caching plugin) and turns on WP_CACHE. Deactivate the other caching plugin afterwards to avoid conflicts." 799 935 msgstr "" 800 936 801 #: src/index.js: 515937 #: src/index.js:662 802 938 msgid "Optimazz is now serving pages from cache." 803 939 msgstr "" 804 940 805 #: src/index.js: 516941 #: src/index.js:663 806 942 msgid "Drop-in installed. Preload the cache, then check again." 807 943 msgstr "" 808 944 809 #: src/index.js: 517945 #: src/index.js:664 810 946 msgid "Could not fully take over — see the cache health details." 811 947 msgstr "" 812 948 813 #: src/index.js: 520949 #: src/index.js:667 814 950 msgid "Take-over failed. Check file permissions." 815 951 msgstr "" 816 952 817 #: src/index.js: 542818 #: src/index.js: 588953 #: src/index.js:689 954 #: src/index.js:735 819 955 msgid "Cache purged." 820 956 msgstr "" 821 957 822 #: src/index.js: 543958 #: src/index.js:690 823 959 msgid "Cache preload started in the background." 824 960 msgstr "" 825 961 826 #: src/index.js: 544962 #: src/index.js:691 827 963 msgid "Cache cleared; preload started in the background." 828 964 msgstr "" 829 965 830 #: src/index.js: 553966 #: src/index.js:700 831 967 msgid "wp-config.php is not writable — add WP_CACHE manually to enable page caching." 832 968 msgstr "" 833 969 834 #: src/index.js: 567970 #: src/index.js:714 835 971 msgid "Settings saved." 836 972 msgstr "" 837 973 838 #: src/index.js: 570974 #: src/index.js:717 839 975 msgid "Save failed." 840 976 msgstr "" 841 977 842 #: src/index.js: 620978 #: src/index.js:767 843 979 msgid "Converting images to WebP/AVIF" 844 980 msgstr "" 845 981 846 #: src/index.js: 621982 #: src/index.js:768 847 983 msgid "Next-gen format generation" 848 984 msgstr "" 849 985 850 #: src/index.js: 624986 #: src/index.js:771 851 987 msgid "Generated" 852 988 msgstr "" 853 989 854 #: src/index.js: 624990 #: src/index.js:771 855 991 msgid "files in this batch" 856 992 msgstr "" 857 993 858 #: src/index.js: 624859 #: src/index.js: 633994 #: src/index.js:771 995 #: src/index.js:780 860 996 msgid "skipped" 861 997 msgstr "" 862 998 863 #: src/index.js: 627999 #: src/index.js:774 864 1000 msgid "Compressing images" 865 1001 msgstr "" 866 1002 867 #: src/index.js: 6281003 #: src/index.js:775 868 1004 msgid "Recompress, resize & strip metadata" 869 1005 msgstr "" 870 1006 871 #: src/index.js: 6341007 #: src/index.js:781 872 1008 msgid "Processed" 873 1009 msgstr "" 874 1010 875 #: src/index.js: 634876 #: src/index.js:1 4401011 #: src/index.js:781 1012 #: src/index.js:1586 877 1013 msgid "saved" 878 1014 msgstr "" 879 1015 880 #: src/index.js:638 1016 #: src/index.js:785 1017 #: src/index.js:1953 881 1018 msgid "Preloading cache" 882 1019 msgstr "" 883 1020 884 #: src/index.js: 6391021 #: src/index.js:786 885 1022 msgid "Warming pages" 886 1023 msgstr "" 887 1024 888 #: src/index.js:643 1025 #: src/index.js:790 1026 #: src/index.js:1974 889 1027 msgid "Warmed" 890 1028 msgstr "" 891 1029 892 #: src/index.js:643 1030 #: src/index.js:790 1031 #: src/index.js:1974 893 1032 msgid "pages" 894 1033 msgstr "" 895 1034 896 #: src/index.js:643 1035 #: src/index.js:790 1036 #: src/index.js:1974 897 1037 msgid "cached" 898 1038 msgstr "" 899 1039 900 #: src/index.js:643 1040 #: src/index.js:790 1041 #: src/index.js:1974 901 1042 msgid "not cached" 902 1043 msgstr "" 903 1044 904 #: src/index.js: 6641045 #: src/index.js:811 905 1046 msgid "Resuming…" 906 1047 msgstr "" 907 1048 908 #: src/index.js:678 1049 #: src/index.js:825 1050 #: src/index.js:1963 909 1051 msgid "Stopped by user." 910 1052 msgstr "" 911 1053 912 #: src/index.js: 6971054 #: src/index.js:844 913 1055 msgid "A batch failed (likely one slow/large image); skipped it and continued." 914 1056 msgstr "" 915 1057 916 #: src/index.js: 6991058 #: src/index.js:846 917 1059 msgid "Completed (some items skipped)." 918 1060 msgstr "" 919 1061 920 #: src/index.js: 7111062 #: src/index.js:858 921 1063 msgid "Job stalled — no progress." 922 1064 msgstr "" 923 1065 924 #: src/index.js:722 1066 #: src/index.js:869 1067 #: src/index.js:1977 925 1068 msgid "left" 926 1069 msgstr "" 927 1070 928 1071 #. translators: %d: number of pages to warm. 929 #: src/index.js:734 1072 #: src/index.js:881 1073 #: src/index.js:1987 930 1074 #, js-format 931 1075 msgid "Found %d pages to warm:" 932 1076 msgstr "" 933 1077 934 #: src/index.js: 7441078 #: src/index.js:891 935 1079 msgid "Image processing is unavailable on this server (no GD or ImageMagick). See Tools → Server Requirements." 936 1080 msgstr "" 937 1081 938 #: src/index.js:749 1082 #: src/index.js:896 1083 #: src/index.js:1993 939 1084 msgid "Warming stopped: the server returned 403/429, so a firewall or security plugin is blocking the preload requests. Cached what we could; allow-list the \"Optimazz-CacheWarmer\" user-agent in your firewall or security plugin (e.g. All-In-One Security, Wordfence, Cloudflare), or slow warming with the optimazz_preload_delay_ms filter." 940 1085 msgstr "" 941 1086 942 #: src/index.js:756 1087 #: src/index.js:903 1088 #: src/index.js:1997 943 1089 msgid "Pages are not being cached" 944 1090 msgstr "" 945 1091 946 #: src/index.js:756 1092 #: src/index.js:903 1093 #: src/index.js:1997 947 1094 msgid "Stopped: pages are loading but none are being saved to the cache." 948 1095 msgstr "" 949 1096 950 #: src/index.js:760 951 #: src/index.js:841 1097 #: src/index.js:907 1098 #: src/index.js:988 1099 #: src/index.js:2007 952 1100 msgid "Completed." 953 1101 msgstr "" 954 1102 955 1103 #. translators: %d: number of pages. 956 #: src/index.js:764 1104 #: src/index.js:911 1105 #: src/index.js:2004 957 1106 #, js-format 958 1107 msgid "%d page was not cached." … … 961 1110 msgstr[1] "" 962 1111 963 #: src/index.js: 8231112 #: src/index.js:970 964 1113 msgid "Cleaning and optimizing database…" 965 1114 msgstr "" 966 1115 967 #: src/index.js: 8271116 #: src/index.js:974 968 1117 msgid "Optimizing database" 969 1118 msgstr "" 970 1119 971 #: src/index.js: 8281120 #: src/index.js:975 972 1121 msgid "Cleanup & table optimization" 973 1122 msgstr "" 974 1123 975 #: src/index.js: 8401124 #: src/index.js:987 976 1125 msgid "Cleaned" 977 1126 msgstr "" 978 1127 979 #: src/index.js: 8401128 #: src/index.js:987 980 1129 msgid "rows," 981 1130 msgstr "" 982 1131 983 #: src/index.js: 8401132 #: src/index.js:987 984 1133 msgid "tables optimized." 985 1134 msgstr "" 986 1135 987 #: src/index.js: 8451136 #: src/index.js:992 988 1137 msgid "Optimize failed." 989 1138 msgstr "" 990 1139 991 #: src/index.js: 8511140 #: src/index.js:998 992 1141 msgid "Reset all Optimazz settings to defaults? You can undo this with \"Undo last change\"." 993 1142 msgstr "" 994 1143 995 #: src/index.js: 8591144 #: src/index.js:1006 996 1145 msgid "Settings reset to defaults." 997 1146 msgstr "" 998 1147 999 #: src/index.js: 8621148 #: src/index.js:1009 1000 1149 msgid "Reset failed." 1001 1150 msgstr "" 1002 1151 1003 #: src/index.js: 8721152 #: src/index.js:1019 1004 1153 msgid "Nothing to undo." 1005 1154 msgstr "" 1006 1155 1007 #: src/index.js: 8791156 #: src/index.js:1026 1008 1157 msgid "Reverted to the previous settings." 1009 1158 msgstr "" 1010 1159 1011 #: src/index.js: 8821160 #: src/index.js:1029 1012 1161 msgid "Undo failed." 1013 1162 msgstr "" 1014 1163 1015 #: src/index.js: 9141164 #: src/index.js:1061 1016 1165 msgid "Invalid settings file." 1017 1166 msgstr "" 1018 1167 1019 #: src/index.js: 9441168 #: src/index.js:1088 1020 1169 msgid "Page caching" 1021 1170 msgstr "" 1022 1171 1023 #: src/index.js: 9451172 #: src/index.js:1089 1024 1173 msgid "Delay JavaScript" 1025 1174 msgstr "" 1026 1175 1027 #: src/index.js: 9461028 #: src/index.js:1 2661176 #: src/index.js:1090 1177 #: src/index.js:1412 1029 1178 msgid "Defer JavaScript" 1030 1179 msgstr "" 1031 1180 1032 #: src/index.js: 9471033 #: src/index.js:1 3191181 #: src/index.js:1091 1182 #: src/index.js:1465 1034 1183 msgid "Minify CSS" 1035 1184 msgstr "" 1036 1185 1037 #: src/index.js: 9491186 #: src/index.js:1093 1038 1187 msgid "Next-gen images" 1039 1188 msgstr "" 1040 1189 1041 #: src/index.js: 9501190 #: src/index.js:1094 1042 1191 msgid "Font optimization" 1043 1192 msgstr "" 1044 1193 1045 #: src/index.js: 9511194 #: src/index.js:1095 1046 1195 msgid "Cache preloading" 1047 1196 msgstr "" 1048 1197 1049 #: src/index.js: 9521198 #: src/index.js:1096 1050 1199 msgid "Heartbeat control" 1051 1200 msgstr "" 1052 1201 1053 #: src/index.js: 9671202 #: src/index.js:1113 1054 1203 msgid "Turn on Page Cache for the real speed gain" 1055 1204 msgstr "" 1056 1205 1057 #: src/index.js: 9681206 #: src/index.js:1114 1058 1207 msgid "File and media optimizations are on, but Page Cache is off — so the server rebuilds every page on every request. Without caching this can make a site feel slower, not faster, especially on mobile. Enable Page Cache to serve ready-made HTML." 1059 1208 msgstr "" 1060 1209 1061 #: src/index.js: 9871210 #: src/index.js:1133 1062 1211 msgid "SCORE" 1063 1212 msgstr "" 1064 1213 1065 #: src/index.js: 9921214 #: src/index.js:1138 1066 1215 msgid "Performance score" 1067 1216 msgstr "" 1068 1217 1069 #: src/index.js: 9951218 #: src/index.js:1141 1070 1219 msgid "Biggest remaining win:" 1071 1220 msgstr "" 1072 1221 1073 #: src/index.js: 9961222 #: src/index.js:1142 1074 1223 msgid "Every optimization is enabled. Nicely done!" 1075 1224 msgstr "" 1076 1225 1077 #: src/index.js:1013 1078 #: src/index.js:1065 1226 #: src/index.js:1159 1227 #: src/index.js:1211 1228 #: src/index.js:2204 1079 1229 msgid "Cached pages" 1080 1230 msgstr "" 1081 1231 1082 #: src/index.js:1 0181232 #: src/index.js:1164 1083 1233 msgid "Cache size" 1084 1234 msgstr "" 1085 1235 1086 #: src/index.js:1 0231236 #: src/index.js:1169 1087 1237 msgid "Modules active" 1088 1238 msgstr "" 1089 1239 1090 #: src/index.js:1 0281091 #: src/index.js:1 0431240 #: src/index.js:1174 1241 #: src/index.js:1189 1092 1242 msgid "Recommendations" 1093 1243 msgstr "" 1094 1244 1095 #: src/index.js:1 0281245 #: src/index.js:1174 1096 1246 msgid "What to fix next to speed up your site — one click each." 1097 1247 msgstr "" 1098 1248 1099 #: src/index.js:1 0351249 #: src/index.js:1181 1100 1250 msgid "Fix it" 1101 1251 msgstr "" 1102 1252 1103 #: src/index.js:1 0361253 #: src/index.js:1182 1104 1254 msgid "Open" 1105 1255 msgstr "" 1106 1256 1107 #: src/index.js:1 0441257 #: src/index.js:1190 1108 1258 msgid "Nice — everything we recommend is already on. Your site is well optimized." 1109 1259 msgstr "" 1110 1260 1111 #: src/index.js:1 0491261 #: src/index.js:1195 1112 1262 msgid "Cache Files" 1113 1263 msgstr "" 1114 1264 1115 #: src/index.js:1 0491265 #: src/index.js:1195 1116 1266 msgid "Clear and preload all cache files." 1117 1267 msgstr "" 1118 1268 1119 #: src/index.js:1052 1269 #: src/index.js:1198 1270 #: src/index.js:2187 1120 1271 msgid "Clear & Preload" 1121 1272 msgstr "" 1122 1273 1123 #: src/index.js:1058 1274 #: src/index.js:1204 1275 #: src/index.js:2195 1124 1276 msgid "Purge cache" 1125 1277 msgstr "" 1126 1278 1127 #: src/index.js:1070 1279 #: src/index.js:1216 1280 #: src/index.js:2209 1128 1281 msgid "Page cache size" 1129 1282 msgstr "" 1130 1283 1131 #: src/index.js:1 0751284 #: src/index.js:1221 1132 1285 msgid "Optimized CSS" 1133 1286 msgstr "" 1134 1287 1135 #: src/index.js:1 0801136 #: src/index.js:1 0811288 #: src/index.js:1226 1289 #: src/index.js:1227 1137 1290 msgid "Cache lifespan" 1138 1291 msgstr "" 1139 1292 1140 #: src/index.js:1 0841293 #: src/index.js:1230 1141 1294 msgid "Never expire" 1142 1295 msgstr "" 1143 1296 1144 #: src/index.js:1085 1145 #: src/index.js:1187 1146 msgid "12 hours" 1147 msgstr "" 1148 1149 #: src/index.js:1086 1150 #: src/index.js:1188 1151 msgid "1 day" 1152 msgstr "" 1153 1154 #: src/index.js:1087 1155 #: src/index.js:1189 1156 msgid "1 week" 1157 msgstr "" 1158 1159 #: src/index.js:1088 1160 #: src/index.js:1190 1161 msgid "1 month" 1162 msgstr "" 1163 1164 #. translators: %d: number of hours. 1165 #: src/index.js:1091 1166 #, js-format 1167 msgid "%d hour" 1168 msgid_plural "%d hours" 1169 msgstr[0] "" 1170 msgstr[1] "" 1171 1172 #: src/index.js:1098 1297 #: src/index.js:1244 1173 1298 msgid "Auto-preload" 1174 1299 msgstr "" 1175 1300 1176 #: src/index.js:1101 1177 msgid "off" 1178 msgstr "" 1179 1180 #: src/index.js:1103 1301 #: src/index.js:1249 1181 1302 msgid "next run: due now" 1182 1303 msgstr "" 1183 1304 1184 #: src/index.js:1 1041305 #: src/index.js:1250 1185 1306 msgid "next run in" 1186 1307 msgstr "" 1187 1308 1188 #: src/index.js:1 1071309 #: src/index.js:1253 1189 1310 msgid "Auto-preload frequency" 1190 1311 msgstr "" 1191 1312 1192 #: src/index.js:1111 1193 #: src/index.js:1462 1313 #: src/index.js:1257 1314 #: src/index.js:1609 1315 #: src/index.js:2121 1194 1316 msgid "Every hour" 1195 1317 msgstr "" 1196 1318 1197 #: src/index.js:1112 1198 #: src/index.js:1463 1319 #: src/index.js:1258 1320 #: src/index.js:1610 1321 #: src/index.js:2122 1199 1322 msgid "Twice a day" 1200 1323 msgstr "" 1201 1324 1202 #: src/index.js:1113 1203 #: src/index.js:1464 1325 #: src/index.js:1259 1326 #: src/index.js:1611 1327 #: src/index.js:2123 1204 1328 msgid "Once a day" 1205 1329 msgstr "" 1206 1330 1207 #: src/index.js:1 1211331 #: src/index.js:1267 1208 1332 msgid "Last auto-preload" 1209 1333 msgstr "" 1210 1334 1211 #: src/index.js:1 1241335 #: src/index.js:1270 1212 1336 msgid "not yet" 1213 1337 msgstr "" 1214 1338 1215 1339 #. translators: 1: time ago, e.g. "2 hours", 2: number of pages 1216 #: src/index.js:1 1271340 #: src/index.js:1273 1217 1341 #, js-format 1218 1342 msgid "%1$s ago · %2$d pages" 1219 1343 msgstr "" 1220 1344 1221 #: src/index.js:1 1301345 #: src/index.js:1276 1222 1346 msgid "blocked by firewall" 1223 1347 msgstr "" 1224 1348 1225 #: src/index.js:1 1351349 #: src/index.js:1281 1226 1350 msgid "Module Status" 1227 1351 msgstr "" 1228 1352 1229 #: src/index.js:1 1351353 #: src/index.js:1281 1230 1354 msgid "Enable or disable any module right here." 1231 1355 msgstr "" 1232 1356 1233 #: src/index.js:1 1721357 #: src/index.js:1318 1234 1358 msgid "Serve static HTML instead of running PHP on every request." 1235 1359 msgstr "" 1236 1360 1237 #: src/index.js:1174 1361 #: src/index.js:1320 1362 #: src/index.js:2094 1238 1363 msgid "Cache mobile devices separately" 1239 1364 msgstr "" 1240 1365 1241 #: src/index.js:1176 1366 #: src/index.js:1322 1367 #: src/index.js:2096 1242 1368 msgid "Cache logged-in users" 1243 1369 msgstr "" 1244 1370 1245 #: src/index.js:1177 1371 #: src/index.js:1323 1372 #: src/index.js:2097 1246 1373 msgid "Leave off unless you know what you are doing." 1247 1374 msgstr "" 1248 1375 1249 #: src/index.js:1179 1376 #: src/index.js:1325 1377 #: src/index.js:2099 1250 1378 msgid "Browser cache headers" 1251 1379 msgstr "" 1252 1380 1253 #: src/index.js:1 1801381 #: src/index.js:1326 1254 1382 msgid "Add Expires / Cache-Control rules so repeat visits load CSS, JS, images and fonts from the browser cache. Apache and LiteSpeed only (written to .htaccess); ignored on Nginx." 1255 1383 msgstr "" 1256 1384 1257 #: src/index.js:1182 1385 #: src/index.js:1328 1386 #: src/index.js:2102 1258 1387 msgid "Cache lifetime" 1259 1388 msgstr "" 1260 1389 1261 #: src/index.js:1 1831390 #: src/index.js:1329 1262 1391 msgid "How long a cached page is kept before it is refreshed. Pages are always purged immediately when their content changes." 1263 1392 msgstr "" 1264 1393 1265 #: src/index.js:1186 1394 #: src/index.js:1332 1395 #: src/index.js:2106 1266 1396 msgid "Never" 1267 1397 msgstr "" 1268 1398 1269 #: src/index.js:1196 1399 #: src/index.js:1342 1400 #: src/index.js:2128 1270 1401 msgid "Cache health" 1271 1402 msgstr "" 1272 1403 1273 #: src/index.js:1 1981404 #: src/index.js:1344 1274 1405 msgid "Check now" 1275 1406 msgstr "" 1276 1407 1277 #: src/index.js:1 2011408 #: src/index.js:1347 1278 1409 msgid "Confirms caching is actually serving pages. If it is not, the plugin rebuilds every page on each request — which is slower, not faster." 1279 1410 msgstr "" 1280 1411 1281 #: src/index.js:1 2051412 #: src/index.js:1351 1282 1413 msgid "Pages ARE being served from cache (HIT)" 1283 1414 msgstr "" 1284 1415 1285 #: src/index.js:1 2061416 #: src/index.js:1352 1286 1417 msgid "Pages are NOT being served from cache (MISS) — see below" 1287 1418 msgstr "" 1288 1419 1289 #: src/index.js:1 2071420 #: src/index.js:1353 1290 1421 msgid "Live check unavailable" 1291 1422 msgstr "" 1292 1423 1293 #: src/index.js:1 2111424 #: src/index.js:1357 1294 1425 msgid "advanced-cache.php belongs to" 1295 1426 msgstr "" 1296 1427 1297 #: src/index.js:1 2111428 #: src/index.js:1357 1298 1429 msgid "take it over below" 1299 1430 msgstr "" 1300 1431 1301 #: src/index.js:1 2121432 #: src/index.js:1358 1302 1433 msgid "advanced-cache.php belongs to ANOTHER plugin — take it over below" 1303 1434 msgstr "" 1304 1435 1305 #: src/index.js:1 2131436 #: src/index.js:1359 1306 1437 msgid "advanced-cache.php drop-in installed" 1307 1438 msgstr "" 1308 1439 1309 #: src/index.js:1 2141440 #: src/index.js:1360 1310 1441 msgid "advanced-cache.php drop-in is missing" 1311 1442 msgstr "" 1312 1443 1313 #: src/index.js:1 2161444 #: src/index.js:1362 1314 1445 msgid "WP_CACHE is enabled in wp-config.php" 1315 1446 msgstr "" 1316 1447 1317 #: src/index.js:1 2161448 #: src/index.js:1362 1318 1449 msgid "WP_CACHE is not enabled in wp-config.php" 1319 1450 msgstr "" 1320 1451 1321 #: src/index.js:1 2181452 #: src/index.js:1364 1322 1453 msgid "wp-config.php is not writable — add the WP_CACHE line manually (below)" 1323 1454 msgstr "" 1324 1455 1325 #: src/index.js:1 2211456 #: src/index.js:1367 1326 1457 msgid "pages warmed in cache" 1327 1458 msgstr "" 1328 1459 1329 #: src/index.js:1 2271460 #: src/index.js:1373 1330 1461 msgid "Take over caching" 1331 1462 msgstr "" 1332 1463 1333 #: src/index.js:1 2291464 #: src/index.js:1375 1334 1465 msgid "Make Optimazz the caching plugin — installs its drop-in and enables WP_CACHE." 1335 1466 msgstr "" 1336 1467 1337 #: src/index.js:1 2371468 #: src/index.js:1383 1338 1469 msgid "is still active and may reclaim caching. Deactivate it so Optimazz keeps serving the cache." 1339 1470 msgstr "" 1340 1471 1341 #: src/index.js:1 2471472 #: src/index.js:1393 1342 1473 msgid "wp-config.php is not writable. Add this line just after the opening <?php tag:" 1343 1474 msgstr "" 1344 1475 1345 #: src/index.js:1 2521476 #: src/index.js:1398 1346 1477 msgid "Could not run the check. Try reloading the page." 1347 1478 msgstr "" 1348 1479 1349 #: src/index.js:1 2601480 #: src/index.js:1406 1350 1481 msgid "JavaScript" 1351 1482 msgstr "" 1352 1483 1353 #: src/index.js:1 2611484 #: src/index.js:1407 1354 1485 msgid "Defer and delay scripts to cut Total Blocking Time." 1355 1486 msgstr "" 1356 1487 1357 #: src/index.js:1 2631488 #: src/index.js:1409 1358 1489 msgid "Minify JavaScript" 1359 1490 msgstr "" 1360 1491 1361 #: src/index.js:1 2641492 #: src/index.js:1410 1362 1493 msgid "Strip comments and whitespace from inline scripts. Line breaks are kept for safety; test your site after enabling." 1363 1494 msgstr "" 1364 1495 1365 #: src/index.js:1 2671496 #: src/index.js:1413 1366 1497 msgid "Load scripts without blocking page render." 1367 1498 msgstr "" 1368 1499 1369 #: src/index.js:1 2691500 #: src/index.js:1415 1370 1501 msgid "Delay JS until interaction" 1371 1502 msgstr "" 1372 1503 1373 #: src/index.js:1 2701504 #: src/index.js:1416 1374 1505 msgid "Run scripts only after the first user interaction." 1375 1506 msgstr "" 1376 1507 1377 #: src/index.js:1 2731508 #: src/index.js:1419 1378 1509 msgid "Delay all scripts" 1379 1510 msgstr "" 1380 1511 1381 #: src/index.js:1 2741512 #: src/index.js:1420 1382 1513 msgid "Delay every script until interaction. jQuery, your theme, and Elementor are always kept safe. Still, on sites where content is rendered by JavaScript this can delay the visible page — test before keeping it on." 1383 1514 msgstr "" 1384 1515 1385 #: src/index.js:1 2801516 #: src/index.js:1426 1386 1517 msgid "Delaying all scripts can slow the first paint on JavaScript-rendered sites (Elementor, sliders). If your PageSpeed FCP/LCP get worse, switch to \"Delay selected\" and delay only tracking scripts." 1387 1518 msgstr "" 1388 1519 1389 #: src/index.js:1 2841520 #: src/index.js:1430 1390 1521 msgid "Delay these scripts" 1391 1522 msgstr "" 1392 1523 1393 #: src/index.js:1 2851524 #: src/index.js:1431 1394 1525 msgid "Only scripts matching these keywords are delayed. Match by filename, handle or URL — e.g. gtag, analytics, facebook." 1395 1526 msgstr "" 1396 1527 1397 #: src/index.js:1 2861528 #: src/index.js:1432 1398 1529 msgid "e.g. gtag, analytics" 1399 1530 msgstr "" 1400 1531 1401 #: src/index.js:1 2921532 #: src/index.js:1438 1402 1533 msgid "Exclude specific scripts" 1403 1534 msgstr "" 1404 1535 1405 #: src/index.js:1 2941536 #: src/index.js:1440 1406 1537 msgid "If a script breaks, exclude it from just one optimization instead of both. Match by filename, script handle, or part of the URL." 1407 1538 msgstr "" 1408 1539 1409 #: src/index.js:1 2981540 #: src/index.js:1444 1410 1541 msgid "Never defer" 1411 1542 msgstr "" 1412 1543 1413 #: src/index.js:1 2991544 #: src/index.js:1445 1414 1545 msgid "Keep loading these render-blocking, but still allow delay. Use for scripts that must run during page load." 1415 1546 msgstr "" 1416 1547 1417 #: src/index.js:1 3001548 #: src/index.js:1446 1418 1549 msgid "e.g. jquery.min.js" 1419 1550 msgstr "" 1420 1551 1421 #: src/index.js:1 3041552 #: src/index.js:1450 1422 1553 msgid "Never delay" 1423 1554 msgstr "" 1424 1555 1425 #: src/index.js:1 3051556 #: src/index.js:1451 1426 1557 msgid "Run these before any interaction, but still allow defer. Use for sliders, above-the-fold, or consent scripts." 1427 1558 msgstr "" 1428 1559 1429 #: src/index.js:1 3061560 #: src/index.js:1452 1430 1561 msgid "e.g. slider, swiper" 1431 1562 msgstr "" 1432 1563 1433 #: src/index.js:1 3091564 #: src/index.js:1455 1434 1565 msgid "Never optimize" 1435 1566 msgstr "" 1436 1567 1437 #: src/index.js:1 3101568 #: src/index.js:1456 1438 1569 msgid "Skip both defer and delay — leave these scripts exactly as they are." 1439 1570 msgstr "" 1440 1571 1441 #: src/index.js:1 3111572 #: src/index.js:1457 1442 1573 msgid "e.g. recaptcha" 1443 1574 msgstr "" 1444 1575 1445 #: src/index.js:1 3161576 #: src/index.js:1462 1446 1577 msgid "CSS" 1447 1578 msgstr "" 1448 1579 1449 #: src/index.js:1 3171580 #: src/index.js:1463 1450 1581 msgid "Minify, combine, and remove render-blocking stylesheets." 1451 1582 msgstr "" 1452 1583 1453 #: src/index.js:1 3211584 #: src/index.js:1467 1454 1585 msgid "Combine CSS files" 1455 1586 msgstr "" 1456 1587 1457 #: src/index.js:1 3221588 #: src/index.js:1468 1458 1589 msgid "Merge stylesheets to reduce requests." 1459 1590 msgstr "" 1460 1591 1461 #: src/index.js:1 3241592 #: src/index.js:1470 1462 1593 msgid "Load CSS asynchronously" 1463 1594 msgstr "" 1464 1595 1465 #: src/index.js:1 3251596 #: src/index.js:1471 1466 1597 msgid "Removes render-blocking CSS. Paste your above-the-fold (critical) CSS below, or the page may briefly flash unstyled." 1467 1598 msgstr "" 1468 1599 1469 #: src/index.js:1 3321600 #: src/index.js:1478 1470 1601 msgid "No critical CSS provided. Async loading is on, so visitors may see a flash of unstyled content until you paste critical CSS below." 1471 1602 msgstr "" 1472 1603 1473 #: src/index.js:1 3361604 #: src/index.js:1482 1474 1605 msgid "Critical CSS (above-the-fold)" 1475 1606 msgstr "" 1476 1607 1477 #: src/index.js:1 3371608 #: src/index.js:1483 1478 1609 msgid "Paste the above-the-fold CSS to inline. Generate it with a free tool such as a critical-CSS generator. Leave empty to disable inlining." 1479 1610 msgstr "" 1480 1611 1481 #: src/index.js:1 3421612 #: src/index.js:1488 1482 1613 msgid "Remove unused CSS" 1483 1614 msgstr "" 1484 1615 1485 #: src/index.js:1 3431616 #: src/index.js:1489 1486 1617 msgid "Remove block-editor CSS on the front end" 1487 1618 msgstr "" 1488 1619 1489 #: src/index.js:1 3441620 #: src/index.js:1490 1490 1621 msgid "Dequeues WordPress block-library and global styles. Turn on only if your front end does NOT render Gutenberg blocks (e.g. a page-builder or classic theme). Big byte saving when it applies." 1491 1622 msgstr "" 1492 1623 1493 #: src/index.js:1 3461624 #: src/index.js:1492 1494 1625 msgid "Load WooCommerce CSS only on shop pages" 1495 1626 msgstr "" 1496 1627 1497 #: src/index.js:1 3471628 #: src/index.js:1493 1498 1629 msgid "Dequeues WooCommerce styles on non-shop pages (kept on cart, checkout, account, product and shop). Turn off if you show Woo products or widgets on other pages." 1499 1630 msgstr "" 1500 1631 1501 #: src/index.js:1 3491632 #: src/index.js:1495 1502 1633 msgid "Load Easy Digital Downloads CSS only on EDD pages" 1503 1634 msgstr "" 1504 1635 1505 #: src/index.js:1 3501636 #: src/index.js:1496 1506 1637 msgid "Dequeues EDD styles on non-EDD pages (kept on checkout, purchase-confirmation, purchase history and download archives). Turn off if you show download or purchase links elsewhere." 1507 1638 msgstr "" 1508 1639 1509 #: src/index.js:1 3521640 #: src/index.js:1498 1510 1641 msgid "Excluded CSS files (keywords)" 1511 1642 msgstr "" 1512 1643 1513 #: src/index.js:1 3601644 #: src/index.js:1506 1514 1645 msgid "Lazy-load, prevent layout shift, serve next-gen formats, and lighten media." 1515 1646 msgstr "" 1516 1647 1517 #: src/index.js:1 3661648 #: src/index.js:1512 1518 1649 msgid "This server has no GD or ImageMagick extension, so image compression and WebP/AVIF cannot run — every image will be skipped. Ask your host to enable \"php-gd\" (or \"php-imagick\"). See Tools → Server Requirements." 1519 1650 msgstr "" 1520 1651 1521 #: src/index.js:1 3701652 #: src/index.js:1516 1522 1653 msgid "Load off-screen images only when scrolled into view." 1523 1654 msgstr "" 1524 1655 1525 #: src/index.js:1 3721656 #: src/index.js:1518 1526 1657 msgid "Lazy-load iframes" 1527 1658 msgstr "" 1528 1659 1529 #: src/index.js:1 3741660 #: src/index.js:1520 1530 1661 msgid "Lazy-load CSS background images" 1531 1662 msgstr "" 1532 1663 1533 #: src/index.js:1 3751664 #: src/index.js:1521 1534 1665 msgid "Defer inline background-image elements (test with page builders)." 1535 1666 msgstr "" 1536 1667 1537 #: src/index.js:1 3771668 #: src/index.js:1523 1538 1669 msgid "Replace YouTube embeds with a preview" 1539 1670 msgstr "" 1540 1671 1541 #: src/index.js:1 3781672 #: src/index.js:1524 1542 1673 msgid "Load the video iframe only when the visitor clicks play." 1543 1674 msgstr "" 1544 1675 1545 #: src/index.js:1 3801676 #: src/index.js:1526 1546 1677 msgid "Add missing image dimensions" 1547 1678 msgstr "" 1548 1679 1549 #: src/index.js:1 3811680 #: src/index.js:1527 1550 1681 msgid "Set width/height to prevent layout shift (CLS)." 1551 1682 msgstr "" 1552 1683 1553 #: src/index.js:1 3831684 #: src/index.js:1529 1554 1685 msgid "Preload the LCP image" 1555 1686 msgstr "" 1556 1687 1557 #: src/index.js:1 3841688 #: src/index.js:1530 1558 1689 msgid "Preload the first/hero image with high priority." 1559 1690 msgstr "" 1560 1691 1561 #: src/index.js:1 3871692 #: src/index.js:1533 1562 1693 msgid "Keep first images eager (LCP)" 1563 1694 msgstr "" 1564 1695 1565 #: src/index.js:1 3881696 #: src/index.js:1534 1566 1697 msgid "Do not lazy-load the first N images, to protect LCP." 1567 1698 msgstr "" 1568 1699 1569 #: src/index.js:1 3931700 #: src/index.js:1539 1570 1701 msgid "Serve WebP via <picture>" 1571 1702 msgstr "" 1572 1703 1573 #: src/index.js:1 3941704 #: src/index.js:1540 1574 1705 msgid "Deliver WebP to supporting browsers, original as fallback." 1575 1706 msgstr "" 1576 1707 1577 #: src/index.js:1 3961708 #: src/index.js:1542 1578 1709 msgid "Generate WebP/AVIF on upload" 1579 1710 msgstr "" 1580 1711 1581 #: src/index.js:1 3981712 #: src/index.js:1544 1582 1713 msgid "WebP generation is not available on this server." 1583 1714 msgstr "" 1584 1715 1585 #: src/index.js:1 3991716 #: src/index.js:1545 1586 1717 msgid "Auto-create next-gen copies for newly uploaded images." 1587 1718 msgstr "" 1588 1719 1589 #: src/index.js:1 4021720 #: src/index.js:1548 1590 1721 msgid "Also generate/serve AVIF" 1591 1722 msgstr "" 1592 1723 1593 #: src/index.js:1 4031724 #: src/index.js:1549 1594 1725 msgid "Smaller than WebP; served first when supported." 1595 1726 msgstr "" 1596 1727 1597 #: src/index.js:1 4071728 #: src/index.js:1553 1598 1729 msgid "Compress & resize on upload" 1599 1730 msgstr "" 1600 1731 1601 #: src/index.js:1 4081732 #: src/index.js:1554 1602 1733 msgid "Recompress, cap dimensions, and strip metadata from new uploads." 1603 1734 msgstr "" 1604 1735 1605 #: src/index.js:1 4101736 #: src/index.js:1556 1606 1737 msgid "Convert opaque PNG to JPEG" 1607 1738 msgstr "" 1608 1739 1609 #: src/index.js:1 4111740 #: src/index.js:1557 1610 1741 msgid "Re-save non-transparent PNGs as much smaller JPEGs during bulk optimization." 1611 1742 msgstr "" 1612 1743 1613 #: src/index.js:1 4131744 #: src/index.js:1559 1614 1745 msgid "Keep a backup of originals" 1615 1746 msgstr "" 1616 1747 1617 #: src/index.js:1 4141748 #: src/index.js:1560 1618 1749 msgid "Copy originals to uploads/optimazz-backup before optimizing." 1619 1750 msgstr "" 1620 1751 1621 #: src/index.js:1 4161752 #: src/index.js:1562 1622 1753 msgid "Image quality" 1623 1754 msgstr "" 1624 1755 1625 #: src/index.js:1 4181756 #: src/index.js:1564 1626 1757 msgid "Used for compression and WebP/AVIF generation." 1627 1758 msgstr "" 1628 1759 1629 #: src/index.js:1 4191760 #: src/index.js:1565 1630 1761 msgid "Max image width (px)" 1631 1762 msgstr "" 1632 1763 1633 #: src/index.js:1 4241764 #: src/index.js:1570 1634 1765 msgid "WebP coverage" 1635 1766 msgstr "" 1636 1767 1637 #: src/index.js:1 4261768 #: src/index.js:1572 1638 1769 msgid "images converted" 1639 1770 msgstr "" 1640 1771 1641 #: src/index.js:1 4271642 #: src/index.js:1 4411772 #: src/index.js:1573 1773 #: src/index.js:1587 1643 1774 msgid "Loading…" 1644 1775 msgstr "" 1645 1776 1646 #: src/index.js:1 4321777 #: src/index.js:1578 1647 1778 msgid "Convert to WebP" 1648 1779 msgstr "" 1649 1780 1650 #: src/index.js:1 4381781 #: src/index.js:1584 1651 1782 msgid "Bulk compression" 1652 1783 msgstr "" 1653 1784 1654 #: src/index.js:1 4401785 #: src/index.js:1586 1655 1786 msgid "optimized" 1656 1787 msgstr "" 1657 1788 1658 #: src/index.js:1 4441789 #: src/index.js:1590 1659 1790 msgid "Compress existing images" 1660 1791 msgstr "" 1661 1792 1662 #: src/index.js:1 4521793 #: src/index.js:1598 1663 1794 msgid "Warm the cache automatically so visitors never hit a cold page." 1664 1795 msgstr "" 1665 1796 1666 #: src/index.js:1 4531797 #: src/index.js:1600 1667 1798 msgid "Activate preloading" 1668 1799 msgstr "" 1669 1800 1670 #: src/index.js:1 4541801 #: src/index.js:1601 1671 1802 msgid "Warms the cache on a schedule and right after every full purge (for example after changing a setting). Warming follows the sitemap — home, pages, posts, custom post types, then the busiest category and tag archives — most-important first, for both mobile and desktop." 1672 1803 msgstr "" 1673 1804 1674 #: src/index.js:1457 1805 #: src/index.js:1604 1806 #: src/index.js:2117 1675 1807 msgid "Automatic preload" 1676 1808 msgstr "" 1677 1809 1678 #: src/index.js:1 4581810 #: src/index.js:1605 1679 1811 msgid "How often to re-warm the whole site. Choose “Every hour” if your content changes often or you use a short cache lifespan." 1680 1812 msgstr "" 1681 1813 1682 #: src/index.js:1470 1814 #. translators: %s: preload frequency, e.g. "twice a day". 1815 #: src/index.js:1618 1816 #, js-format 1817 msgid "Automatic preload runs %s for this site (set for the whole network in Network Admin → Optimazz)." 1818 msgstr "" 1819 1820 #: src/index.js:1621 1821 msgid "Automatic preload is off for the network. You can still preload this site now." 1822 msgstr "" 1823 1824 #: src/index.js:1626 1683 1825 msgid "WP-Cron is disabled on this site (DISABLE_WP_CRON). Scheduled preloads only run if a server cron job calls wp-cron.php — ask your host to set one up (e.g. every 5 minutes)." 1684 1826 msgstr "" 1685 1827 1686 #: src/index.js:1 4751828 #: src/index.js:1631 1687 1829 msgid "Preload now" 1688 1830 msgstr "" 1689 1831 1690 #: src/index.js:1 4831832 #: src/index.js:1639 1691 1833 msgid "Fine-tune which requests are cached." 1692 1834 msgstr "" 1693 1835 1694 #: src/index.js:1 4841836 #: src/index.js:1640 1695 1837 msgid "Never cache these URLs (contains)" 1696 1838 msgstr "" 1697 1839 1698 #: src/index.js:1 4861840 #: src/index.js:1642 1699 1841 msgid "Never cache when these cookies are set" 1700 1842 msgstr "" 1701 1843 1702 #: src/index.js:1 4881844 #: src/index.js:1644 1703 1845 msgid "Never cache for these user agents" 1704 1846 msgstr "" 1705 1847 1706 #: src/index.js:1 4901848 #: src/index.js:1646 1707 1849 msgid "Ignore these query arguments" 1708 1850 msgstr "" 1709 1851 1710 #: src/index.js:1 4981852 #: src/index.js:1654 1711 1853 msgid "Safe Mode" 1712 1854 msgstr "" 1713 1855 1714 #: src/index.js:1 4991856 #: src/index.js:1655 1715 1857 msgid "If a theme or page builder looks broken after optimizing, turn this on. It pauses the riskiest optimizations — combine CSS, async CSS, remove block CSS and JavaScript delay — while keeping caching, minify, defer and lazy-load. Your saved settings are not changed; they simply pause until you turn Safe Mode off." 1716 1858 msgstr "" 1717 1859 1718 #: src/index.js:1 5021860 #: src/index.js:1658 1719 1861 msgid "Pause risky optimizations (Safe Mode)" 1720 1862 msgstr "" 1721 1863 1722 #: src/index.js:1 5091864 #: src/index.js:1665 1723 1865 msgid "Safe Mode is ON — combine/async CSS, remove-block-CSS and JavaScript delay are paused site-wide. Turn it off to resume full optimization." 1724 1866 msgstr "" 1725 1867 1726 #: src/index.js:1 5141868 #: src/index.js:1670 1727 1869 msgid "Undo last change" 1728 1870 msgstr "" 1729 1871 1730 #: src/index.js:1 5181872 #: src/index.js:1674 1731 1873 msgid "Restores the settings from just before your last save, reset or Safe Mode toggle." 1732 1874 msgstr "" 1733 1875 1734 #: src/index.js:1 5191876 #: src/index.js:1675 1735 1877 msgid "Nothing to undo yet — this becomes available after your next change." 1736 1878 msgstr "" 1737 1879 1738 #: src/index.js:1 5231880 #: src/index.js:1679 1739 1881 msgid "Server Requirements" 1740 1882 msgstr "" 1741 1883 1742 #: src/index.js:1 5241884 #: src/index.js:1680 1743 1885 msgid "What your host supports. Warnings here explain why a feature (like WebP/AVIF) may be unavailable or slow." 1744 1886 msgstr "" 1745 1887 1746 #: src/index.js:1 5251888 #: src/index.js:1681 1747 1889 msgid "Checking your server…" 1748 1890 msgstr "" 1749 1891 1750 #: src/index.js:1 5261892 #: src/index.js:1682 1751 1893 msgid "Could not read the server report." 1752 1894 msgstr "" 1753 1895 1754 #: src/index.js:1 5421896 #: src/index.js:1698 1755 1897 msgid "Re-check" 1756 1898 msgstr "" 1757 1899 1758 #: src/index.js:1 5461900 #: src/index.js:1702 1759 1901 msgid "Import / Export Settings" 1760 1902 msgstr "" 1761 1903 1762 #: src/index.js:1 5471904 #: src/index.js:1703 1763 1905 msgid "Back up your configuration or copy it to another site." 1764 1906 msgstr "" 1765 1907 1766 #: src/index.js:1 5501908 #: src/index.js:1706 1767 1909 msgid "Export settings" 1768 1910 msgstr "" 1769 1911 1770 #: src/index.js:1 5531912 #: src/index.js:1709 1771 1913 msgid "Import settings" 1772 1914 msgstr "" 1773 1915 1774 #: src/index.js:1 5581916 #: src/index.js:1714 1775 1917 msgid "Reset" 1776 1918 msgstr "" 1777 1919 1778 #: src/index.js:1 5591920 #: src/index.js:1715 1779 1921 msgid "Restore every module to its default configuration. This cannot be undone." 1780 1922 msgstr "" 1781 1923 1782 #: src/index.js:1 5621924 #: src/index.js:1718 1783 1925 msgid "Reset all settings to defaults" 1784 1926 msgstr "" 1785 1927 1786 #: src/index.js:1 5711928 #: src/index.js:1727 1787 1929 msgid "Swap, preconnect, and preload fonts to remove render-blocking requests." 1788 1930 msgstr "" 1789 1931 1790 #: src/index.js:1 5741932 #: src/index.js:1730 1791 1933 msgid "Font loading (font-display)" 1792 1934 msgstr "" 1793 1935 1794 #: src/index.js:1 5751936 #: src/index.js:1731 1795 1937 msgid "Optional = no layout shift (best for CLS): the web font is skipped on the first uncached load and used once cached. Swap = text stays visible but the web font swaps in and can shift layout (higher CLS). Block = brief invisible text, then the web font." 1796 1938 msgstr "" 1797 1939 1798 #: src/index.js:1 5781940 #: src/index.js:1734 1799 1941 msgid "Optional — no layout shift (recommended for CLS)" 1800 1942 msgstr "" 1801 1943 1802 #: src/index.js:1 5791944 #: src/index.js:1735 1803 1945 msgid "Swap — always show text (may shift layout)" 1804 1946 msgstr "" 1805 1947 1806 #: src/index.js:1 5801948 #: src/index.js:1736 1807 1949 msgid "Block — brief hidden text, then web font" 1808 1950 msgstr "" 1809 1951 1810 #: src/index.js:1 5841952 #: src/index.js:1740 1811 1953 msgid "Combine Google Fonts into one request" 1812 1954 msgstr "" 1813 1955 1814 #: src/index.js:1 5851956 #: src/index.js:1741 1815 1957 msgid "Page builders often load each Google Font family separately. This merges them into a single request." 1816 1958 msgstr "" 1817 1959 1818 #: src/index.js:1 5891960 #: src/index.js:1745 1819 1961 msgid "Load only the font weights your pages use" 1820 1962 msgstr "" 1821 1963 1822 #: src/index.js:1 5901964 #: src/index.js:1746 1823 1965 msgid "Drops unused weights (e.g. 100, 200, 900) and italics your pages never use — often cutting font CSS by more than half. Weights 400 and 700 are always kept. Turn off if some text looks thinner or bolder than intended." 1824 1966 msgstr "" 1825 1967 1826 #: src/index.js:1594 1968 #: src/index.js:1751 1969 msgid "Load Google Fonts without blocking the page" 1970 msgstr "" 1971 1972 #: src/index.js:1752 1973 msgid "The page appears right away in a fallback font and switches to your Google Font as soon as it arrives, instead of waiting for Google first. A big win for the first paint on mobile. Applies when font display is Swap or Fallback; with Optional the stylesheet keeps loading first so text never shifts." 1974 msgstr "" 1975 1976 #: src/index.js:1756 1827 1977 msgid "Preload these font files (URLs)" 1828 1978 msgstr "" 1829 1979 1830 1980 #. translators: %d: number of items found in the database. 1831 #: src/index.js:1 6051981 #: src/index.js:1767 1832 1982 #, js-format 1833 1983 msgid "%d item in your database." … … 1836 1986 msgstr[1] "" 1837 1987 1838 #: src/index.js:1 6101988 #: src/index.js:1772 1839 1989 msgid "Database Optimization" 1840 1990 msgstr "" 1841 1991 1842 #: src/index.js:1 6111992 #: src/index.js:1773 1843 1993 msgid "Reduce bloat and reclaim space. Once a cleanup runs it cannot be undone, so back up your database first." 1844 1994 msgstr "" 1845 1995 1846 #: src/index.js:1 6271996 #: src/index.js:1789 1847 1997 msgid "Set it and forget it" 1848 1998 msgstr "" 1849 1999 1850 #: src/index.js:1 6282000 #: src/index.js:1790 1851 2001 msgid "Let Optimazz repeat the cleanups you picked above on a schedule, so your database stays lean without you lifting a finger." 1852 2002 msgstr "" 1853 2003 1854 #: src/index.js:1 6292004 #: src/index.js:1791 1855 2005 msgid "Schedule automatic cleanup" 1856 2006 msgstr "" 1857 2007 1858 #: src/index.js:1 6302008 #: src/index.js:1792 1859 2009 msgid "Uses WP-Cron to repeat the cleanups you enabled above." 1860 2010 msgstr "" 1861 2011 1862 #: src/index.js:1 6332012 #: src/index.js:1795 1863 2013 msgid "Cleanup frequency" 1864 2014 msgstr "" 1865 2015 1866 #: src/index.js:1 6372016 #: src/index.js:1799 1867 2017 msgid "Daily" 1868 2018 msgstr "" 1869 2019 1870 #: src/index.js:1 6382020 #: src/index.js:1800 1871 2021 msgid "Weekly" 1872 2022 msgstr "" 1873 2023 1874 #: src/index.js:1 6392024 #: src/index.js:1801 1875 2025 msgid "Monthly" 1876 2026 msgstr "" 1877 2027 1878 #: src/index.js:1 6462028 #: src/index.js:1808 1879 2029 msgid "Back up your database before you run a cleanup — a database optimization cannot be undone." 1880 2030 msgstr "" 1881 2031 1882 #: src/index.js:1 6512032 #: src/index.js:1813 1883 2033 msgid "Optimize now" 1884 2034 msgstr "" 1885 2035 1886 #: src/index.js:1 6542036 #: src/index.js:1816 1887 2037 msgid "Refresh counts" 1888 2038 msgstr "" 1889 2039 1890 #: src/index.js:1 6612040 #: src/index.js:1823 1891 2041 msgid "Heartbeat Control" 1892 2042 msgstr "" 1893 2043 1894 #: src/index.js:1 6622044 #: src/index.js:1824 1895 2045 msgid "Reduce or disable the WordPress Heartbeat API to lower server load." 1896 2046 msgstr "" 1897 2047 1898 #: src/index.js:1 6632048 #: src/index.js:1825 1899 2049 msgid "Enable Heartbeat control" 1900 2050 msgstr "" 1901 2051 1902 #: src/index.js:1 6672052 #: src/index.js:1829 1903 2053 msgid "Behavior in admin dashboard" 1904 2054 msgstr "" 1905 2055 1906 #: src/index.js:1 6692056 #: src/index.js:1831 1907 2057 msgid "Behavior in post editor" 1908 2058 msgstr "" 1909 2059 1910 #: src/index.js:1 6712060 #: src/index.js:1833 1911 2061 msgid "Behavior on front-end" 1912 2062 msgstr "" 1913 2063 1914 #: src/index.js:1 6802064 #: src/index.js:1842 1915 2065 msgid "Strip unnecessary WordPress front-end weight." 1916 2066 msgstr "" 1917 2067 1918 #: src/index.js:1 6822068 #: src/index.js:1844 1919 2069 msgid "Disable emojis" 1920 2070 msgstr "" 1921 2071 1922 #: src/index.js:1 6832072 #: src/index.js:1845 1923 2073 msgid "Remove the emoji detection script and styles." 1924 2074 msgstr "" 1925 2075 1926 #: src/index.js:1 6852076 #: src/index.js:1847 1927 2077 msgid "Disable embeds (oEmbed)" 1928 2078 msgstr "" 1929 2079 1930 #: src/index.js:1 6872080 #: src/index.js:1849 1931 2081 msgid "Disable dashicons on front-end" 1932 2082 msgstr "" 1933 2083 1934 #: src/index.js:1 6882084 #: src/index.js:1850 1935 2085 msgid "Only for anonymous visitors." 1936 2086 msgstr "" 1937 2087 1938 #: src/index.js:1 6902088 #: src/index.js:1852 1939 2089 msgid "Remove query strings from assets" 1940 2090 msgstr "" 1941 2091 1942 #: src/index.js:1 6912092 #: src/index.js:1853 1943 2093 msgid "Strip ?ver= for better proxy caching." 1944 2094 msgstr "" 1945 2095 1946 #: src/index.js:1 6932096 #: src/index.js:1855 1947 2097 msgid "Disable XML-RPC" 1948 2098 msgstr "" 1949 2099 1950 #: src/index.js:1 6952100 #: src/index.js:1857 1951 2101 msgid "Disable self-pingbacks" 1952 2102 msgstr "" 1953 2103 1954 #: src/index.js:1 6972104 #: src/index.js:1859 1955 2105 msgid "Remove head meta (generator, RSD, shortlink)" 1956 2106 msgstr "" 1957 2107 1958 #: src/index.js:1 6992108 #: src/index.js:1861 1959 2109 msgid "DNS-prefetch domains" 1960 2110 msgstr "" 1961 2111 1962 #: src/index.js:1 7012112 #: src/index.js:1863 1963 2113 msgid "Preconnect origins" 1964 2114 msgstr "" 1965 2115 1966 #: src/index.js:1 7072116 #: src/index.js:1869 1967 2117 msgid "Content Delivery Network" 1968 2118 msgstr "" 1969 2119 1970 #: src/index.js:1 7082120 #: src/index.js:1870 1971 2121 msgid "Rewrite static asset URLs to your CDN hostname." 1972 2122 msgstr "" 1973 2123 1974 #: src/index.js:1 7102124 #: src/index.js:1872 1975 2125 msgid "CDN URL (CNAME)" 1976 2126 msgstr "" 1977 2127 1978 #: src/index.js:1 7132128 #: src/index.js:1875 1979 2129 msgid "CSS, JS, image, and font URLs will be served from here." 1980 2130 msgstr "" 1981 2131 1982 #: src/index.js:1 7142132 #: src/index.js:1876 1983 2133 msgid "Exclude paths containing" 1984 2134 msgstr "" 1985 2135 1986 #: src/index.js:1781 1987 msgid "You have unsaved changes" 1988 msgstr "" 1989 1990 #: src/index.js:1781 1991 msgid "All changes saved" 1992 msgstr "" 1993 1994 #: src/index.js:1784 1995 msgid "Save changes" 1996 msgstr "" 2136 #: src/index.js:1952 2137 msgid "Preloading cache…" 2138 msgstr "" 2139 2140 #: src/index.js:2012 2141 #: src/index.js:2025 2142 msgid "Something went wrong." 2143 msgstr "" 2144 2145 #: src/index.js:2059 2146 msgid "Network settings saved." 2147 msgstr "" 2148 2149 #: src/index.js:2075 2150 msgid "Caching for every site" 2151 msgstr "" 2152 2153 #: src/index.js:2076 2154 msgid "Sites" 2155 msgstr "" 2156 2157 #: src/index.js:2076 2158 msgid "Cache per site" 2159 msgstr "" 2160 2161 #: src/index.js:2086 2162 msgid "Optimazz is not network-activated" 2163 msgstr "" 2164 2165 #: src/index.js:2087 2166 msgid "Only the sites where Optimazz is activated are cached. Network-activate it on Network Admin → Plugins to cache every site." 2167 msgstr "" 2168 2169 #: src/index.js:2092 2170 msgid "Serve ready-made HTML for every site instead of running PHP on each request. Each site keeps its own cache and its own exclusions." 2171 msgstr "" 2172 2173 #: src/index.js:2100 2174 msgid "Add Expires / Cache-Control rules to the shared .htaccess so repeat visits load CSS, JS, images and fonts from the browser cache. Apache and LiteSpeed only." 2175 msgstr "" 2176 2177 #: src/index.js:2103 2178 msgid "How long a cached page is kept. Pages are always purged as soon as their content changes." 2179 msgstr "" 2180 2181 #: src/index.js:2113 2182 msgid "Preload every site automatically" 2183 msgstr "" 2184 2185 #: src/index.js:2114 2186 msgid "Each site warms its own pages on a schedule and right after its cache is cleared." 2187 msgstr "" 2188 2189 #. translators: %s: name of another caching plugin. 2190 #: src/index.js:2135 2191 #, js-format 2192 msgid "%s controls page caching (advanced-cache.php)" 2193 msgstr "" 2194 2195 #: src/index.js:2138 2196 msgid "Optimazz cannot cache while another plugin owns the cache file. Deactivate that plugin, or let Optimazz take over." 2197 msgstr "" 2198 2199 #: src/index.js:2139 2200 msgid "Optimazz now handles page caching." 2201 msgstr "" 2202 2203 #: src/index.js:2146 2204 msgid "Cache engine installed (advanced-cache.php)" 2205 msgstr "" 2206 2207 #: src/index.js:2147 2208 msgid "WP_CACHE is on in wp-config.php" 2209 msgstr "" 2210 2211 #: src/index.js:2148 2212 msgid "wp-config.php is not writable — add define( 'WP_CACHE', true ); yourself" 2213 msgstr "" 2214 2215 #: src/index.js:2149 2216 msgid "The wp-content folder is not writable" 2217 msgstr "" 2218 2219 #: src/index.js:2167 2220 msgid "All sites" 2221 msgstr "" 2222 2223 #. translators: 1: sites with Optimazz, 2: all sites. 2224 #: src/index.js:2170 2225 #, js-format 2226 msgid "Optimazz runs on %1$d of %2$d sites. Each site has its own cache." 2227 msgstr "" 2228 2229 #: src/index.js:2172 2230 msgid "Page caching is off for the network — turn it on under Page Cache to purge or preload sites." 2231 msgstr "" 2232 2233 #: src/index.js:2175 2234 msgid "Every site’s cache was purged." 2235 msgstr "" 2236 2237 #: src/index.js:2176 2238 msgid "Purge all sites" 2239 msgstr "" 2240 2241 #: src/index.js:2194 2242 msgid "Site cache purged." 2243 msgstr "" 2244 2245 #: src/index.js:2215 2246 msgid "Optimazz is not active on this site, so its pages are not cached." 2247 msgstr "" 2248 2249 #: src/index.js:2225 2250 msgid "Network" 2251 msgstr "" -
optimazz/trunk/readme.txt
r3723776 r3743505 5 5 Tested up to: 7.1 6 6 Requires PHP: 7.4 7 Stable tag: 1. 0.247 Stable tag: 1.1.0 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 49 49 50 50 == Frequently Asked Questions == 51 52 = Does Optimazz work on WordPress multisite? = 53 54 Yes. Network-activate it, then switch page caching on for every site in Network Admin → Optimazz (super admins only). Each site gets its own cache and keeps its own settings and cache exclusions. You can also activate Optimazz on individual sites only; then only those sites are cached. 51 55 52 56 = Does Optimazz work alongside another caching plugin? = … … 100 104 101 105 == Changelog == 106 107 = 1.1.0 = 108 * Fixed: Google Fonts never loaded on sites whose theme or plugin requests them in the old weight format through the new Google Fonts address (for example "Open Sans:400,700" or "w400,700"). Google rejects those requests, so the font was missing while the broken stylesheet still held back the first paint on mobile by most of a second. Optimazz now repairs them into a valid request. 109 * New: Google Fonts load without blocking the page (on by default, Fonts settings). With font display Swap or Fallback the page appears right away in a fallback font and switches when the web font arrives, instead of waiting for Google first. With Optional (zero layout shift) the stylesheet still loads first, so text never shifts. 110 * Improved: sites using Jetpack's Site Accelerator load WordPress core stylesheets from c0.wp.com, which Optimazz used to skip as external, so they stayed render-blocking. When the version matches your WordPress, they are now minified, combined and loaded like local files. 111 * Improved: more chat widgets are delayed until the visitor interacts — OpenWidget, Zendesk, Drift, Freshchat / Freshworks, JivoChat, Smartsupp and Small Chat — cutting mobile Total Blocking Time. 112 * New: full multisite support. Network-activate Optimazz and manage page caching for the whole network in Network Admin → Optimazz: switch it on once, choose mobile and logged-in caching, cache lifetime, browser-cache rules and automatic preload, and see every site with its cached pages and its own Purge and Preload buttons. Each site gets its own cache — on subdirectory and subdomain networks — keeps its own exclusions (Advanced Rules) and all its other Optimazz settings, and can purge and preload its own pages. 113 * Improved: on multisite only the network administrator controls the files every site shares (wp-config.php, advanced-cache.php, .htaccess); a site's own settings never change them, so one site can't switch caching on or off for the others. 114 * Fixed: before this version, on multisite one site's settings controlled caching for every site, only one site of a subdomain network was actually cached, any site administrator could rewrite wp-config.php and .htaccess, and deactivating Optimazz on one site removed the cache for the whole network. 115 * Fixed: deleting Optimazz from a multisite network only cleaned up the main site; settings, scheduled tasks, image data and generated CSS of the other sites were left behind. Uninstall now cleans every site. 116 * Fixed: on sites using plain permalinks (addresses like ?page_id=5) the "Cached pages" count showed 1 no matter how many pages were cached. 117 * Improved: the preload summary now names the home page and counts each address once, so its parts add up to the total. 118 * Improved: changing a cache rule (for example a new exclusion) now also clears pages that were cached under the old rules. 119 * Improved: the Changelog section of this readme now lists recent versions only; the complete history is in changelog.txt. 102 120 103 121 = 1.0.24 = … … 153 171 == Upgrade Notice == 154 172 173 = 1.1.0 = 174 Multisite network support: manage page caching for every site from Network Admin → Optimazz, with a separate cache per site. Also a faster mobile first paint (Google Fonts) and many fixes. Recommended. 175 155 176 = 1.0.24 = 156 177 Scheduled cache preloading now runs on time even when every visitor gets a cached page, works behind 6G/7G firewalls, and the cache rebuilds itself after you change settings. Also warns you when another caching plugin is blocking the Optimazz page cache. Recommended. -
optimazz/trunk/src/index.js
r3723128 r3743505 58 58 const NAV_ALL = [ ...NAV, ...PRO_SECTIONS ]; 59 59 60 // wp_localize_script() sends top-level scalars as strings: false arrives as "" 61 // and true as "1", so test for every "off" form. 62 const isOn = ( v ) => ! [ false, '', '0', 0, undefined, null ].includes( v ); 63 // Single site: this site controls page caching itself. On multisite a super 64 // admin switches it on for the whole network (Network Admin → Optimazz) and 65 // each site keeps its own cache, exclusions, purge and preload. 66 const SERVER_FEATURES = ! [ false, '', '0', 0 ].includes( window.optimazzData?.serverFeatures ); 67 const NETWORK_CACHE = window.optimazzData?.networkCache || null; 68 const NETWORK_CACHE_ON = ! SERVER_FEATURES && !! NETWORK_CACHE && isOn( NETWORK_CACHE.enabled ); 69 const CACHE_AVAILABLE = SERVER_FEATURES || NETWORK_CACHE_ON; 70 const IS_SUPER_ADMIN = isOn( window.optimazzData?.isSuperAdmin ); 71 const NETWORK_URL = window.optimazzData?.networkAdminUrl || ''; 72 73 const formatBytes = ( b ) => { 74 if ( ! b ) return '0 KB'; 75 const u = [ 'B', 'KB', 'MB', 'GB' ]; 76 const i = Math.floor( Math.log( b ) / Math.log( 1024 ) ); 77 return `${ ( b / Math.pow( 1024, i ) ).toFixed( i ? 1 : 0 ) } ${ u[ i ] }`; 78 }; 79 80 const intervalLabel = ( v ) => ( { 81 hourly: __( 'every hour', 'optimazz' ), 82 twicedaily: __( 'twice a day', 'optimazz' ), 83 daily: __( 'once a day', 'optimazz' ), 84 }[ v ] || __( 'twice a day', 'optimazz' ) ); 85 86 const lifespanLabel = ( h ) => { 87 const map = { 88 0: __( 'never expires', 'optimazz' ), 89 12: __( '12 hours', 'optimazz' ), 90 24: __( '1 day', 'optimazz' ), 91 168: __( '1 week', 'optimazz' ), 92 720: __( '1 month', 'optimazz' ), 93 }; 94 /* translators: %d: number of hours. */ 95 return map[ Number( h ) ] ?? sprintf( _n( '%d hour', '%d hours', Number( h ), 'optimazz' ), Number( h ) ); 96 }; 97 98 function NetworkLink() { 99 if ( ! IS_SUPER_ADMIN || ! NETWORK_URL ) { 100 return null; 101 } 102 return <a className="oz-cachewarn__link" href={ NETWORK_URL }>{ __( 'Open Network Admin → Optimazz', 'optimazz' ) }</a>; 103 } 104 105 // Multisite, network caching off. 106 function MultisiteCacheNotice() { 107 return ( 108 <div className="oz-cachewarn" role="status"> 109 <span className="dashicons dashicons-info-outline" /> 110 <div className="oz-cachewarn__text"> 111 <strong>{ __( 'Page caching is off for this network', 'optimazz' ) }</strong> 112 <p>{ IS_SUPER_ADMIN 113 ? __( 'Turn it on for every site in Network Admin → Optimazz. Each site gets its own cache, and keeps its own exclusions here under Advanced Rules.', 'optimazz' ) 114 : __( 'Your network administrator can turn it on for every site in Network Admin → Optimazz. All other optimizations — CSS, JavaScript, images, fonts and more — work normally on this site.', 'optimazz' ) }</p> 115 <NetworkLink /> 116 </div> 117 </div> 118 ); 119 } 120 121 // Multisite, network caching on: this site's Page Cache screen. 122 function NetworkCacheOn() { 123 const n = NETWORK_CACHE || {}; 124 return ( 125 <div className="oz-cachewarn oz-cachewarn--ok" role="status"> 126 <span className="dashicons dashicons-yes-alt" /> 127 <div className="oz-cachewarn__text"> 128 <strong>{ __( 'Page caching is on — managed for the whole network', 'optimazz' ) }</strong> 129 <p>{ sprintf( 130 /* translators: 1: yes/no, 2: cache lifetime, 3: preload frequency. */ 131 __( 'Separate mobile cache: %1$s · Cache lifetime: %2$s · Automatic preload: %3$s.', 'optimazz' ), 132 isOn( n.cache_mobile ) ? __( 'yes', 'optimazz' ) : __( 'no', 'optimazz' ), 133 lifespanLabel( n.lifespan_hours ), 134 isOn( n.preload ) ? intervalLabel( n.preload_interval ) : __( 'off', 'optimazz' ) 135 ) }</p> 136 <p>{ __( 'This site has its own cache. Purge or preload it from the Dashboard or the Preload screen, and set its own exclusions under Advanced Rules.', 'optimazz' ) }</p> 137 <NetworkLink /> 138 </div> 139 </div> 140 ); 141 } 60 142 const MODULES = [ 'cache', 'js', 'css', 'media', 'fonts', 'database', 'heartbeat', 'tweaks', 'cdn' ]; 61 143 … … 407 489 408 490 /* ------------------------------------------------------------------ */ 491 492 /** 493 * The Optimazz admin layout shared by the site screen and Network Admin: 494 * brand + sidebar navigation, page header, content, save bar and toast. 495 */ 496 function AppShell( { className = '', items, nav, onNav, foot, topIcon, title, sub, dirty, saving, onSave, toast, onToastRemove, children, after } ) { 497 return ( 498 <div className={ `oz-shell ${ className }` }> 499 <aside className="oz-sidebar"> 500 <div className="oz-sidebar__brand"> 501 { window.optimazzData?.logoUrl ? ( 502 <img className="oz-logo-img" src={ window.optimazzData.logoUrl } alt="Optimazz" /> 503 ) : ( 504 <div className="oz-sidebar__brand-fallback"> 505 <span className="oz-logo dashicons dashicons-performance" /> 506 <strong>Optimazz</strong> 507 </div> 508 ) } 509 </div> 510 <nav> 511 { items.map( ( n ) => ( 512 <button key={ n.key } className={ `oz-navitem ${ nav === n.key ? 'is-active' : '' }` } onClick={ () => onNav( n.key ) }> 513 <Icon name={ n.svg || n.key } paths={ n.iconSvg } /> 514 <span className="oz-navitem__text"> 515 <strong>{ n.title }</strong> 516 <small>{ n.sub }</small> 517 </span> 518 { n.dot && <i className="oz-navitem__dot" /> } 519 </button> 520 ) ) } 521 </nav> 522 <div className="oz-sidebar__foot">{ foot }</div> 523 </aside> 524 525 <div className="oz-content"> 526 <header className="oz-topbar"> 527 <span className={ `oz-topbar__icon dashicons dashicons-${ topIcon }` } /> 528 <div> 529 <h1>{ title }</h1> 530 <p>{ sub }</p> 531 </div> 532 </header> 533 <main className="oz-main">{ children }</main> 534 </div> 535 536 <div className={ `oz-savebar ${ dirty ? 'is-dirty' : '' }` }> 537 <span className="oz-savebar__hint"> 538 { dirty ? __( 'You have unsaved changes', 'optimazz' ) : __( 'All changes saved', 'optimazz' ) } 539 </span> 540 <Button className="oz-btn-primary" isBusy={ saving } disabled={ ! dirty || saving } onClick={ onSave }> 541 { __( 'Save changes', 'optimazz' ) } 542 </Button> 543 </div> 544 545 { toast && ( 546 <div className="oz-toast"> 547 <Snackbar status={ toast.status } onRemove={ onToastRemove }> 548 { toast.text } 549 </Snackbar> 550 </div> 551 ) } 552 { after } 553 </div> 554 ); 555 } 409 556 410 557 function App() { … … 919 1066 }; 920 1067 921 const fmtBytes = ( b ) => { 922 if ( ! b ) return '0 KB'; 923 const u = [ 'B', 'KB', 'MB', 'GB' ]; 924 const i = Math.floor( Math.log( b ) / Math.log( 1024 ) ); 925 return `${ ( b / Math.pow( 1024, i ) ).toFixed( i ? 1 : 0 ) } ${ u[ i ] }`; 926 }; 927 928 const enabledCount = MODULES.filter( ( k ) => settings[ k ]?.enabled ).length; 1068 const fmtBytes = formatBytes; 1069 1070 // On multisite the page cache is unavailable, so it is not counted. 1071 const usableModules = SERVER_FEATURES ? MODULES : MODULES.filter( ( k ) => k !== 'cache' ); 1072 const enabledCount = usableModules.filter( ( k ) => settings[ k ]?.enabled ).length; 929 1073 const c = settings.cache || {}; 930 1074 const j = settings.js || {}; … … 942 1086 // Live performance score — weighted by which optimizations are enabled. 943 1087 const scoreParts = [ 944 { on: !! c.enabled, w: 30, label: __( 'Page caching', 'optimazz' ) },1088 { on: !! c.enabled, w: 30, label: __( 'Page caching', 'optimazz' ), server: true }, 945 1089 { on: !! ( j.enabled && j.delay ), w: 18, label: __( 'Delay JavaScript', 'optimazz' ) }, 946 1090 { on: !! ( j.enabled && j.defer ), w: 8, label: __( 'Defer JavaScript', 'optimazz' ) }, … … 949 1093 { on: !! ( m.enabled && ( m.webp || m.webp_generate ) ), w: 8, label: __( 'Next-gen images', 'optimazz' ) }, 950 1094 { on: !! ( f.enabled && f.display_swap ), w: 6, label: __( 'Font optimization', 'optimazz' ) }, 951 { on: !! c.preload, w: 5, label: __( 'Cache preloading', 'optimazz' ) },1095 { on: !! c.preload, w: 5, label: __( 'Cache preloading', 'optimazz' ), server: true }, 952 1096 { on: !! h.enabled, w: 3, label: __( 'Heartbeat control', 'optimazz' ) }, 953 1097 ]; 954 const score = scoreParts.reduce( ( a, p ) => a + ( p.on ? p.w : 0 ), 0 ); 955 const nextWin = scoreParts.filter( ( p ) => ! p.on ).sort( ( a, b ) => b.w - a.w )[ 0 ]; 1098 const usableParts = CACHE_AVAILABLE ? scoreParts : scoreParts.filter( ( p ) => p.server !== true ); 1099 const maxScore = usableParts.reduce( ( a, p ) => a + p.w, 0 ) || 1; 1100 const score = Math.round( ( usableParts.reduce( ( a, p ) => a + ( p.on ? p.w : 0 ), 0 ) / maxScore ) * 100 ); 1101 const nextWin = usableParts.filter( ( p ) => ! p.on ).sort( ( a, b ) => b.w - a.w )[ 0 ]; 956 1102 const grade = score >= 90 ? 'A' : score >= 75 ? 'B' : score >= 55 ? 'C' : score >= 35 ? 'D' : 'E'; 957 1103 const ring = 2 * Math.PI * 52; 958 1104 959 const optimizingWithoutCache = ( j.enabled || s.enabled || m.enabled ) && ! c.enabled;1105 const optimizingWithoutCache = SERVER_FEATURES && ( j.enabled || s.enabled || m.enabled ) && ! c.enabled; 960 1106 961 1107 const dashboard = ( … … 997 1143 </p> 998 1144 <div className="oz-score__chips"> 999 { scoreParts.map( ( p ) => (1145 { usableParts.map( ( p ) => ( 1000 1146 <span key={ p.label } className={ `oz-chip ${ p.on ? 'is-on' : '' }` }> 1001 1147 <span className={ `dashicons dashicons-${ p.on ? 'yes-alt' : 'marker' }` } /> … … 1007 1153 </div> 1008 1154 1009 <div className= "oz-kpis">1010 <div className="oz-kpi">1155 <div className={ `oz-kpis${ CACHE_AVAILABLE ? '' : ' oz-kpis--single' }` }> 1156 { CACHE_AVAILABLE && <div className="oz-kpi"> 1011 1157 <span className="dashicons dashicons-database" /> 1012 1158 <strong>{ stats ? ( stats.pages ?? stats.files ) : <Spinner /> }</strong> 1013 1159 <span>{ __( 'Cached pages', 'optimazz' ) }</span> 1014 </div> 1015 <div className="oz-kpi">1160 </div> } 1161 { CACHE_AVAILABLE && <div className="oz-kpi"> 1016 1162 <span className="dashicons dashicons-chart-area" /> 1017 1163 <strong>{ stats ? fmtBytes( stats.bytes ) : <Spinner /> }</strong> 1018 1164 <span>{ __( 'Cache size', 'optimazz' ) }</span> 1019 </div> 1165 </div> } 1020 1166 <div className="oz-kpi"> 1021 1167 <span className="dashicons dashicons-yes-alt" /> 1022 <strong>{ enabledCount }/{ MODULES.length }</strong>1168 <strong>{ enabledCount }/{ usableModules.length }</strong> 1023 1169 <span>{ __( 'Modules active', 'optimazz' ) }</span> 1024 1170 </div> … … 1047 1193 1048 1194 <div className="oz-quick"> 1049 <Panel title={ __( 'Cache Files', 'optimazz' ) } description={ __( 'Clear and preload all cache files.', 'optimazz' ) }>1195 { CACHE_AVAILABLE && <Panel title={ __( 'Cache Files', 'optimazz' ) } description={ __( 'Clear and preload all cache files.', 'optimazz' ) }> 1050 1196 <div className="oz-quick__actions"> 1051 1197 <Button className="oz-btn-primary" icon="trash" isBusy={ !! job?.running } onClick={ async () => { await purge(); preload(); } }> … … 1132 1278 </li> 1133 1279 </ul> 1134 </Panel> 1280 </Panel> } 1135 1281 <Panel title={ __( 'Module Status', 'optimazz' ) } description={ __( 'Enable or disable any module right here.', 'optimazz' ) }> 1136 1282 <ul className="oz-status"> 1137 { NAV.filter( ( n ) => n.mod ).map( ( n ) => (1283 { NAV.filter( ( n ) => n.mod && ( SERVER_FEATURES || n.mod !== 'cache' ) ).map( ( n ) => ( 1138 1284 <li key={ n.key }> 1139 1285 <Icon name={ n.key } /> … … 1168 1314 ); 1169 1315 1170 const cacheSection = (1316 const cacheSection = ! SERVER_FEATURES ? ( NETWORK_CACHE_ON ? <NetworkCacheOn /> : <MultisiteCacheNotice /> ) : ( 1171 1317 <Card icon="database" title={ __( 'Page Cache', 'optimazz' ) } 1172 1318 description={ __( 'Serve static HTML instead of running PHP on every request.', 'optimazz' ) } … … 1448 1594 ); 1449 1595 1450 const preloadSection = (1596 const preloadSection = ! CACHE_AVAILABLE ? <MultisiteCacheNotice /> : ( 1451 1597 <Panel title={ __( 'Preload Cache', 'optimazz' ) } 1452 1598 description={ __( 'Warm the cache automatically so visitors never hit a cold page.', 'optimazz' ) }> 1599 { SERVER_FEATURES ? ( <> 1453 1600 <ToggleControl __nextHasNoMarginBottom label={ __( 'Activate preloading', 'optimazz' ) } 1454 1601 help={ __( 'Warms the cache on a schedule and right after every full purge (for example after changing a setting). Warming follows the sitemap — home, pages, posts, custom post types, then the busiest category and tag archives — most-important first, for both mobile and desktop.', 'optimazz' ) } … … 1465 1612 ] } 1466 1613 onChange={ ( v ) => set( 'cache', 'preload_interval', v ) } /> 1614 </> ) : ( 1615 <p className="oz-muted">{ isOn( NETWORK_CACHE?.preload ) 1616 ? sprintf( 1617 /* translators: %s: preload frequency, e.g. "twice a day". */ 1618 __( 'Automatic preload runs %s for this site (set for the whole network in Network Admin → Optimazz).', 'optimazz' ), 1619 intervalLabel( NETWORK_CACHE?.preload_interval ) 1620 ) 1621 : __( 'Automatic preload is off for the network. You can still preload this site now.', 'optimazz' ) }</p> 1622 ) } 1467 1623 { stats && stats.wp_cron_disabled && ( 1468 1624 <div className="oz-notice oz-notice--warn" style={ { marginTop: 12 } }> … … 1479 1635 ); 1480 1636 1481 const advancedSection = (1637 const advancedSection = ! CACHE_AVAILABLE ? <MultisiteCacheNotice /> : ( 1482 1638 <Panel title={ __( 'Advanced Rules', 'optimazz' ) } 1483 1639 description={ __( 'Fine-tune which requests are cached.', 'optimazz' ) }> … … 1592 1748 disabled={ ! f.combine_google } 1593 1749 onChange={ ( v ) => set( 'fonts', 'trim_weights', v ) } /> 1750 <ToggleControl __nextHasNoMarginBottom 1751 label={ __( 'Load Google Fonts without blocking the page', 'optimazz' ) } 1752 help={ __( 'The page appears right away in a fallback font and switches to your Google Font as soon as it arrives, instead of waiting for Google first. A big win for the first paint on mobile. Applies when font display is Swap or Fallback; with Optional the stylesheet keeps loading first so text never shifts.', 'optimazz' ) } 1753 checked={ f.async_google !== false } 1754 disabled={ ! f.combine_google } 1755 onChange={ ( v ) => set( 'fonts', 'async_google', v ) } /> 1594 1756 <FormTokenField label={ __( 'Preload these font files (URLs)', 'optimazz' ) } 1595 1757 value={ f.preload || [] } onChange={ ( v ) => set( 'fonts', 'preload', v ) } /> … … 1739 1901 1740 1902 return ( 1741 <div className="oz-shell"> 1742 <aside className="oz-sidebar"> 1743 <div className="oz-sidebar__brand"> 1744 { window.optimazzData?.logoUrl ? ( 1745 <img className="oz-logo-img" src={ window.optimazzData.logoUrl } alt="Optimazz" /> 1746 ) : ( 1747 <div className="oz-sidebar__brand-fallback"> 1748 <span className="oz-logo dashicons dashicons-performance" /> 1749 <strong>Optimazz</strong> 1750 </div> 1751 ) } 1752 </div> 1753 <nav> 1754 { NAV_ALL.map( ( n ) => ( 1755 <button key={ n.key } className={ `oz-navitem ${ nav === n.key ? 'is-active' : '' }` } onClick={ () => setNav( n.key ) }> 1756 <Icon name={ n.key } paths={ n.iconSvg } /> 1757 <span className="oz-navitem__text"> 1758 <strong>{ n.title }</strong> 1759 <small>{ n.sub }</small> 1760 </span> 1761 { n.mod && settings[ n.mod ]?.enabled && <i className="oz-navitem__dot" /> } 1762 </button> 1763 ) ) } 1764 </nav> 1765 <div className="oz-sidebar__foot">v{ window.optimazzData?.version }</div> 1766 </aside> 1767 1768 <div className="oz-content"> 1769 <header className="oz-topbar"> 1770 <span className={ `oz-topbar__icon dashicons dashicons-${ active.icon }` } /> 1771 <div> 1772 <h1>{ active.title }</h1> 1773 <p>{ active.sub }</p> 1774 </div> 1775 </header> 1776 <main className="oz-main">{ sections[ nav ] }</main> 1777 </div> 1778 1779 <div className={ `oz-savebar ${ dirty ? 'is-dirty' : '' }` }> 1780 <span className="oz-savebar__hint"> 1781 { dirty ? __( 'You have unsaved changes', 'optimazz' ) : __( 'All changes saved', 'optimazz' ) } 1782 </span> 1783 <Button className="oz-btn-primary" isBusy={ saving } disabled={ ! dirty } onClick={ save }> 1784 { __( 'Save changes', 'optimazz' ) } 1785 </Button> 1786 </div> 1787 1788 { toast && ( 1789 <div className="oz-toast"> 1790 <Snackbar status={ toast.status } onRemove={ () => setToast( null ) }> 1791 { toast.text } 1792 </Snackbar> 1793 </div> 1794 ) } 1795 1796 <JobPanel 1797 job={ job } 1798 onStop={ stopJob } 1799 onClose={ closeJob } 1800 onToggleMin={ () => setJob( ( j ) => j && { ...j, minimized: ! j.minimized } ) } 1801 /> 1802 </div> 1903 <AppShell 1904 items={ NAV_ALL.map( ( n ) => ( { ...n, dot: !! ( n.mod && settings[ n.mod ]?.enabled ) } ) ) } 1905 nav={ nav } onNav={ setNav } 1906 foot={ `v${ window.optimazzData?.version }` } 1907 topIcon={ active.icon } title={ active.title } sub={ active.sub } 1908 dirty={ dirty } saving={ saving } onSave={ save } 1909 toast={ toast } onToastRemove={ () => setToast( null ) } 1910 after={ 1911 <JobPanel 1912 job={ job } 1913 onStop={ stopJob } 1914 onClose={ closeJob } 1915 onToggleMin={ () => setJob( ( j ) => j && { ...j, minimized: ! j.minimized } ) } 1916 /> 1917 } 1918 > 1919 { sections[ nav ] } 1920 </AppShell> 1803 1921 ); 1804 1922 } … … 1808 1926 createRoot( mount ).render( <App /> ); 1809 1927 } 1928 1929 /* ------------------------------------------------------------------ * 1930 * Network Admin → Optimazz (multisite, super admins) 1931 * ------------------------------------------------------------------ */ 1932 1933 const NET_DEFAULTS = { enabled: false, cache_mobile: true, cache_logged_in: false, lifespan_hours: 0, preload: true, preload_interval: 'twicedaily', browser_cache: false }; 1934 1935 function NetworkApp() { 1936 const [ data, setData ] = useState( null ); 1937 const [ draft, setDraft ] = useState( NET_DEFAULTS ); 1938 const [ dirty, setDirty ] = useState( false ); 1939 const [ saving, setSaving ] = useState( false ); 1940 const [ busy, setBusy ] = useState( '' ); 1941 const [ notice, setNotice ] = useState( null ); 1942 const [ nav, setNav ] = useState( 'cache' ); 1943 const [ job, setJob ] = useState( null ); 1944 const stopRef = useRef( false ); 1945 1946 // Live preload of one site, in the same progress panel as the site screen. 1947 const runPreload = async ( site ) => { 1948 if ( job?.running ) { 1949 return; 1950 } 1951 stopRef.current = false; 1952 const opening = __( 'Preloading cache…', 'optimazz' ); 1953 setJob( { title: __( 'Preloading cache', 'optimazz' ), subtitle: site.name || site.url, pct: 0, label: opening, step: '', logs: [ { level: 'INFO', text: opening } ], running: true, status: 'running', minimized: false } ); 1954 const acc = { cached: 0, missed: 0, diagnosis: '' }; 1955 const started = Date.now(); 1956 let offset = 0; 1957 let total = 0; 1958 const end = ( status, level, text, extra = {} ) => setJob( ( j ) => j && { ...j, running: false, status, ...extra, logs: [ ...j.logs, { level, text } ] } ); 1959 try { 1960 // eslint-disable-next-line no-constant-condition 1961 while ( true ) { 1962 if ( stopRef.current ) { 1963 end( 'stopped', 'WARN', __( 'Stopped by user.', 'optimazz' ) ); 1964 break; 1965 } 1966 const res = await apiFetch( { url: `${ restUrl }/network/preload-batch`, method: 'POST', data: { site_id: site.id, offset } } ); 1967 const prev = offset; 1968 offset = res.next_offset ?? offset + ( res.processed || 0 ); 1969 total = res.total || total; 1970 acc.cached += res.cached || 0; 1971 acc.missed += res.missed || 0; 1972 acc.diagnosis = res.diagnosis || acc.diagnosis; 1973 const pct = total ? Math.min( 100, Math.round( ( offset / total ) * 100 ) ) : 100; 1974 const line = `${ __( 'Warmed', 'optimazz' ) } ${ Math.min( offset, total ) } / ${ total } ${ __( 'pages', 'optimazz' ) } · ${ acc.cached } ${ __( 'cached', 'optimazz' ) }${ acc.missed ? ` · ${ acc.missed } ${ __( 'not cached', 'optimazz' ) }` : '' }`; 1975 const rate = offset / Math.max( ( Date.now() - started ) / 1000, 0.001 ); 1976 const left = rate > 0 && total && offset < total ? ( total - offset ) / rate : 0; 1977 const eta = left >= 60 ? ` · ~${ Math.round( left / 60 ) }m ${ __( 'left', 'optimazz' ) }` : ( left >= 1 ? ` · ~${ Math.round( left ) }s ${ __( 'left', 'optimazz' ) }` : '' ); 1978 setJob( ( j ) => j && { 1979 ...j, 1980 pct, 1981 label: line, 1982 step: total ? `${ Math.min( offset, total ) } / ${ total }${ eta }` : '', 1983 logs: [ 1984 ...j.logs.slice( -60 ), 1985 ...( res.breakdown ? [ { level: 'INFO', text: `${ sprintf( 1986 /* translators: %d: number of pages to warm. */ 1987 __( 'Found %d pages to warm:', 'optimazz' ), res.total || 0 1988 ) } ${ res.breakdown }.` } ] : [] ), 1989 { level: 'INFO', text: line }, 1990 ], 1991 } ); 1992 if ( res.blocked ) { 1993 end( 'error', 'ERROR', __( 'Warming stopped: the server returned 403/429, so a firewall or security plugin is blocking the preload requests. Cached what we could; allow-list the "Optimazz-CacheWarmer" user-agent in your firewall or security plugin (e.g. All-In-One Security, Wordfence, Cloudflare), or slow warming with the optimazz_preload_delay_ms filter.', 'optimazz' ) ); 1994 break; 1995 } 1996 if ( ! res.done && acc.cached === 0 && acc.missed >= 3 ) { 1997 end( 'error', 'ERROR', `${ __( 'Stopped: pages are loading but none are being saved to the cache.', 'optimazz' ) } ${ acc.diagnosis }`, { label: __( 'Pages are not being cached', 'optimazz' ) } ); 1998 break; 1999 } 2000 if ( res.done || offset <= prev ) { 2001 if ( acc.missed ) { 2002 setJob( ( j ) => j && { ...j, logs: [ ...j.logs, { level: 'WARN', text: `${ sprintf( 2003 /* translators: %d: number of pages. */ 2004 _n( '%d page was not cached.', '%d pages were not cached.', acc.missed, 'optimazz' ), acc.missed 2005 ) } ${ acc.diagnosis }` } ] } ); 2006 } 2007 end( 'done', 'DONE', __( 'Completed.', 'optimazz' ), { pct: 100 } ); 2008 break; 2009 } 2010 } 2011 } catch ( e ) { 2012 end( 'error', 'ERROR', e?.message || __( 'Something went wrong.', 'optimazz' ) ); 2013 } 2014 // Refresh the cached-page counts on the cards. 2015 apiFetch( { url: `${ restUrl }/network` } ).then( ( res ) => { 2016 setData( res ); 2017 } ).catch( () => {} ); 2018 }; 2019 2020 const apply = ( res ) => { 2021 setData( res ); 2022 setDraft( { ...NET_DEFAULTS, ...( res.settings || {} ) } ); 2023 setDirty( false ); 2024 }; 2025 const fail = ( e ) => setNotice( { status: 'error', text: e?.message || __( 'Something went wrong.', 'optimazz' ) } ); 2026 const call = async ( path, body, key, okText ) => { 2027 setBusy( key ); 2028 try { 2029 const res = await apiFetch( { url: `${ restUrl }${ path }`, method: 'POST', data: body } ); 2030 // Keep any unsaved Page Cache edits when acting on sites. 2031 setData( res ); 2032 if ( ! dirty ) { 2033 setDraft( { ...NET_DEFAULTS, ...( res.settings || {} ) } ); 2034 } 2035 if ( okText ) { 2036 setNotice( { status: 'success', text: okText } ); 2037 } 2038 setBusy( '' ); 2039 return true; 2040 } catch ( e ) { 2041 fail( e ); 2042 } 2043 setBusy( '' ); 2044 return false; 2045 }; 2046 2047 useEffect( () => { 2048 apiFetch( { url: `${ restUrl }/network` } ).then( apply ).catch( fail ); 2049 }, [] ); 2050 2051 const set = ( key, value ) => { 2052 setDraft( ( d ) => ( { ...d, [ key ]: value } ) ); 2053 setDirty( true ); 2054 }; 2055 const save = async () => { 2056 setSaving( true ); 2057 try { 2058 apply( await apiFetch( { url: `${ restUrl }/network/settings`, method: 'POST', data: { settings: draft } } ) ); 2059 setNotice( { status: 'success', text: __( 'Network settings saved.', 'optimazz' ) } ); 2060 } catch ( e ) { 2061 fail( e ); 2062 } 2063 setSaving( false ); 2064 }; 2065 2066 if ( ! data ) { 2067 return <div className="oz-shell oz-shell--network-app"><div className="oz-main"><Spinner /></div></div>; 2068 } 2069 const h = data.health || {}; 2070 const sites = data.sites || []; 2071 const cached = sites.filter( ( s ) => s.active ); 2072 const on = !! data.settings?.enabled; 2073 2074 const NET_NAV = [ 2075 { key: 'cache', svg: 'cache', icon: 'database', title: __( 'Page Cache', 'optimazz' ), sub: __( 'Caching for every site', 'optimazz' ), dot: on }, 2076 { key: 'sites', svg: 'dashboard', icon: 'admin-multisite', title: __( 'Sites', 'optimazz' ), sub: __( 'Cache per site', 'optimazz' ) }, 2077 ]; 2078 const active = NET_NAV.find( ( n ) => n.key === nav ) || NET_NAV[ 0 ]; 2079 2080 const cacheSection = ( 2081 <> 2082 { ! data.network_active && ( 2083 <div className="oz-cachewarn" role="status"> 2084 <span className="dashicons dashicons-info-outline" /> 2085 <div className="oz-cachewarn__text"> 2086 <strong>{ __( 'Optimazz is not network-activated', 'optimazz' ) }</strong> 2087 <p>{ __( 'Only the sites where Optimazz is activated are cached. Network-activate it on Network Admin → Plugins to cache every site.', 'optimazz' ) }</p> 2088 </div> 2089 </div> 2090 ) } 2091 <Card icon="database" title={ __( 'Page Cache', 'optimazz' ) } 2092 description={ __( 'Serve ready-made HTML for every site instead of running PHP on each request. Each site keeps its own cache and its own exclusions.', 'optimazz' ) } 2093 enabled={ !! draft.enabled } onToggle={ () => set( 'enabled', ! draft.enabled ) }> 2094 <ToggleControl __nextHasNoMarginBottom label={ __( 'Cache mobile devices separately', 'optimazz' ) } 2095 checked={ !! draft.cache_mobile } onChange={ () => set( 'cache_mobile', ! draft.cache_mobile ) } /> 2096 <ToggleControl __nextHasNoMarginBottom label={ __( 'Cache logged-in users', 'optimazz' ) } 2097 help={ __( 'Leave off unless you know what you are doing.', 'optimazz' ) } 2098 checked={ !! draft.cache_logged_in } onChange={ () => set( 'cache_logged_in', ! draft.cache_logged_in ) } /> 2099 <ToggleControl __nextHasNoMarginBottom label={ __( 'Browser cache headers', 'optimazz' ) } 2100 help={ __( 'Add Expires / Cache-Control rules to the shared .htaccess so repeat visits load CSS, JS, images and fonts from the browser cache. Apache and LiteSpeed only.', 'optimazz' ) } 2101 checked={ !! draft.browser_cache } onChange={ () => set( 'browser_cache', ! draft.browser_cache ) } /> 2102 <PillGroup label={ __( 'Cache lifetime', 'optimazz' ) } 2103 help={ __( 'How long a cached page is kept. Pages are always purged as soon as their content changes.', 'optimazz' ) } 2104 value={ Number( draft.lifespan_hours ) || 0 } 2105 options={ [ 2106 { value: 0, label: __( 'Never', 'optimazz' ) }, 2107 { value: 12, label: __( '12 hours', 'optimazz' ) }, 2108 { value: 24, label: __( '1 day', 'optimazz' ) }, 2109 { value: 168, label: __( '1 week', 'optimazz' ) }, 2110 { value: 720, label: __( '1 month', 'optimazz' ) }, 2111 ] } 2112 onChange={ ( v ) => set( 'lifespan_hours', v ) } /> 2113 <ToggleControl __nextHasNoMarginBottom label={ __( 'Preload every site automatically', 'optimazz' ) } 2114 help={ __( 'Each site warms its own pages on a schedule and right after its cache is cleared.', 'optimazz' ) } 2115 checked={ !! draft.preload } onChange={ () => set( 'preload', ! draft.preload ) } /> 2116 <SelectControl __nextHasNoMarginBottom __next40pxDefaultSize 2117 label={ __( 'Automatic preload', 'optimazz' ) } 2118 value={ draft.preload_interval || 'twicedaily' } 2119 disabled={ ! draft.preload } 2120 options={ [ 2121 { label: __( 'Every hour', 'optimazz' ), value: 'hourly' }, 2122 { label: __( 'Twice a day', 'optimazz' ), value: 'twicedaily' }, 2123 { label: __( 'Once a day', 'optimazz' ), value: 'daily' }, 2124 ] } 2125 onChange={ ( v ) => set( 'preload_interval', v ) } /> 2126 </Card> 2127 { ( on || h.conflict ) && ( 2128 <Panel title={ __( 'Cache health', 'optimazz' ) }> 2129 { h.conflict ? ( 2130 <div className="oz-cachewarn" role="status"> 2131 <span className="dashicons dashicons-warning" /> 2132 <div className="oz-cachewarn__text"> 2133 <strong>{ sprintf( 2134 /* translators: %s: name of another caching plugin. */ 2135 __( '%s controls page caching (advanced-cache.php)', 'optimazz' ), 2136 h.conflict_name || __( 'Another plugin', 'optimazz' ) 2137 ) }</strong> 2138 <p>{ __( 'Optimazz cannot cache while another plugin owns the cache file. Deactivate that plugin, or let Optimazz take over.', 'optimazz' ) }</p> 2139 <Button className="oz-btn-primary" isBusy={ 'takeover' === busy } onClick={ () => call( '/network/takeover', {}, 'takeover', __( 'Optimazz now handles page caching.', 'optimazz' ) ) }> 2140 { __( 'Use Optimazz for caching', 'optimazz' ) } 2141 </Button> 2142 </div> 2143 </div> 2144 ) : ( 2145 <> 2146 <HealthRow ok={ h.dropin_ours } label={ __( 'Cache engine installed (advanced-cache.php)', 'optimazz' ) } /> 2147 <HealthRow ok={ h.wp_cache } label={ __( 'WP_CACHE is on in wp-config.php', 'optimazz' ) } /> 2148 { ! h.wpconfig_writable && ! h.wp_cache && <HealthRow ok={ false } label={ __( 'wp-config.php is not writable — add define( \'WP_CACHE\', true ); yourself', 'optimazz' ) } /> } 2149 { ! h.content_writable && <HealthRow ok={ false } label={ __( 'The wp-content folder is not writable', 'optimazz' ) } /> } 2150 </> 2151 ) } 2152 </Panel> 2153 ) } 2154 </> 2155 ); 2156 2157 // Clear this site's cache, then warm it live in the progress panel. 2158 const clearAndPreload = async ( site ) => { 2159 const ok = await call( '/network/purge', { site_id: site.id }, `clear-${ site.id }` ); 2160 if ( ok ) { 2161 runPreload( site ); 2162 } 2163 }; 2164 2165 const sitesSection = ( 2166 <> 2167 <Panel title={ __( 'All sites', 'optimazz' ) } 2168 description={ sprintf( 2169 /* translators: 1: sites with Optimazz, 2: all sites. */ 2170 __( 'Optimazz runs on %1$d of %2$d sites. Each site has its own cache.', 'optimazz' ), cached.length, sites.length 2171 ) }> 2172 { ! on && <p className="oz-muted">{ __( 'Page caching is off for the network — turn it on under Page Cache to purge or preload sites.', 'optimazz' ) }</p> } 2173 <div className="oz-quick__actions"> 2174 <Button className="oz-btn-ghost oz-btn--x" icon="dismiss" disabled={ ! on } isBusy={ 'purge-all' === busy } 2175 onClick={ () => call( '/network/purge', {}, 'purge-all', __( 'Every site’s cache was purged.', 'optimazz' ) ) }> 2176 { __( 'Purge all sites', 'optimazz' ) } 2177 </Button> 2178 </div> 2179 </Panel> 2180 <div className="oz-quick oz-network__sites"> 2181 { sites.map( ( s ) => ( 2182 <Panel key={ s.id } title={ s.name || s.url } description={ s.url }> 2183 { s.active ? ( 2184 <> 2185 <div className="oz-quick__actions"> 2186 <Button className="oz-btn-primary" icon="trash" disabled={ ! on || !! job?.running } isBusy={ `clear-${ s.id }` === busy } onClick={ () => clearAndPreload( s ) }> 2187 { __( 'Clear & Preload', 'optimazz' ) } 2188 </Button> 2189 <Button className="oz-btn-ghost" icon="update" disabled={ ! on || !! job?.running } 2190 onClick={ () => runPreload( s ) }> 2191 { __( 'Preload', 'optimazz' ) } 2192 </Button> 2193 <Button className="oz-btn-ghost oz-btn--x" icon="dismiss" disabled={ ! on } isBusy={ `purge-${ s.id }` === busy } 2194 onClick={ () => call( '/network/purge', { site_id: s.id }, `purge-${ s.id }`, __( 'Site cache purged.', 'optimazz' ) ) }> 2195 { __( 'Purge cache', 'optimazz' ) } 2196 </Button> 2197 <Button className="oz-btn-ghost" icon="admin-generic" href={ s.admin_url }> 2198 { __( 'Settings', 'optimazz' ) } 2199 </Button> 2200 </div> 2201 <ul className="oz-cachelist"> 2202 <li> 2203 <span className="dashicons dashicons-media-text" /> 2204 { __( 'Cached pages', 'optimazz' ) } 2205 <strong>{ s.pages }</strong> 2206 </li> 2207 <li> 2208 <span className="dashicons dashicons-database" /> 2209 { __( 'Page cache size', 'optimazz' ) } 2210 <strong>{ formatBytes( s.bytes ) }</strong> 2211 </li> 2212 </ul> 2213 </> 2214 ) : ( 2215 <p className="oz-muted">{ __( 'Optimazz is not active on this site, so its pages are not cached.', 'optimazz' ) }</p> 2216 ) } 2217 </Panel> 2218 ) ) } 2219 </div> 2220 </> 2221 ); 2222 return ( 2223 <AppShell className="oz-shell--network-app" 2224 items={ NET_NAV } nav={ nav } onNav={ setNav } 2225 foot={ `${ __( 'Network', 'optimazz' ) } · v${ window.optimazzData?.version }` } 2226 topIcon={ active.icon } title={ active.title } sub={ active.sub } 2227 dirty={ dirty } saving={ saving } onSave={ save } 2228 toast={ notice } onToastRemove={ () => setNotice( null ) } 2229 after={ 2230 <JobPanel 2231 job={ job } 2232 onStop={ () => { 2233 stopRef.current = true; 2234 } } 2235 onClose={ () => { 2236 stopRef.current = true; 2237 setJob( null ); 2238 } } 2239 onToggleMin={ () => setJob( ( j ) => j && { ...j, minimized: ! j.minimized } ) } 2240 /> 2241 } 2242 > 2243 { 'sites' === nav ? sitesSection : cacheSection } 2244 </AppShell> 2245 ); 2246 } 2247 2248 const networkMount = document.getElementById( 'optimazz-network-app' ); 2249 if ( networkMount ) { 2250 createRoot( networkMount ).render( <NetworkApp /> ); 2251 } -
optimazz/trunk/src/index.scss
r3723128 r3743505 402 402 grid-template-columns: repeat(3, 1fr); 403 403 gap: 14px; 404 405 // Multisite: only "Modules active" remains, so it spans the row. 406 &--single { 407 grid-template-columns: 1fr; 408 } 404 409 } 405 410 … … 1802 1807 .dashicons { color: #e0900f; } 1803 1808 } 1809 &--ok { 1810 background: #eaf8f3; 1811 border-color: #b8e6d6; 1812 color: #0f5e48; 1813 .dashicons { color: #0e9f7e; } 1814 } 1804 1815 } 1805 1816 … … 1882 1893 } 1883 1894 } 1895 1896 /* ------------------------------------------------------------------ * 1897 * Multisite: network-managed cache states + Network Admin screen 1898 * ------------------------------------------------------------------ */ 1899 .oz-cachewarn--ok { 1900 background: #eaf8f3; 1901 border-color: #b8e6d6; 1902 color: #0f5e48; 1903 } 1904 1905 .oz-cachewarn__text p + p { 1906 margin-top: 6px; 1907 } 1908 1909 .oz-cachewarn__link { 1910 display: inline-block; 1911 margin-top: 8px; 1912 font-weight: 600; 1913 color: inherit; 1914 text-decoration: underline; 1915 } 1916 1917 // Network → Sites: one Cache Files-style card per site, two per row. 1918 .oz-network__sites { 1919 margin-top: 16px; 1920 align-items: start; 1921 1922 .oz-panel__head h2, 1923 .oz-panel__head p { 1924 overflow-wrap: anywhere; 1925 } 1926 } -
optimazz/trunk/uninstall.php
r3723128 r3743505 18 18 // phpcs:disable WordPress.WP.AlternativeFunctions.file_system_operations_rmdir 19 19 20 global $wpdb; 20 // Per-site data (options, transients, image meta, scheduled events). On a 21 // multisite network uninstall runs once, so it is repeated for every site. 22 $optimazz_cleanup_site = static function () { 23 global $wpdb; 21 24 22 // 1. Settings option (and the single-level undo backup). 23 delete_option( 'optimazz_settings' ); 24 delete_option( 'optimazz_settings_backup' ); 25 delete_option( 'optimazz_version' ); // Post-update reconciliation marker. 26 delete_option( 'optimazz_guard' ); // Update safety guard state. 27 delete_option( 'optimazz_last_preload' ); // Last auto-preload run (dashboard). 28 delete_option( 'optimazz_preload_cursor' ); // Position of a half-finished automatic preload. 29 delete_transient( 'optimazz_preload_urls' ); // Cached preload URL list. 30 delete_transient( 'optimazz_preload_urls_breakdown' ); // Counts behind the preload list. 31 delete_transient( 'optimazz_preload_lock' ); // Running-chunk lock. 32 delete_metadata( 'user', 0, 'optimazz_conflict_dismissed', '', true ); // Dismissed cache-conflict notice. 33 wp_clear_scheduled_hook( 'optimazz_health_check' ); 25 // 1. Settings option (and the single-level undo backup). 26 delete_option( 'optimazz_settings' ); 27 delete_option( 'optimazz_settings_backup' ); 28 delete_option( 'optimazz_version' ); // Post-update reconciliation marker. 29 delete_option( 'optimazz_guard' ); // Update safety guard state. 30 delete_option( 'optimazz_last_preload' ); // Last auto-preload run (dashboard). 31 delete_option( 'optimazz_preload_cursor' ); // Position of a half-finished automatic preload. 32 delete_transient( 'optimazz_preload_urls' ); // Cached preload URL list. 33 delete_transient( 'optimazz_preload_urls_breakdown' ); // Counts behind the preload list. 34 delete_transient( 'optimazz_preload_lock' ); // Running-chunk lock. 35 wp_clear_scheduled_hook( 'optimazz_health_check' ); 34 36 35 // 2. Per-image optimization meta.36 $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => '_optimazz_image' ) ); // phpcs:ignore WordPress.DB.SlowDBQuery37 // 2. Per-image optimization meta. 38 $wpdb->delete( $wpdb->postmeta, array( 'meta_key' => '_optimazz_image' ) ); // phpcs:ignore WordPress.DB.SlowDBQuery 37 39 38 // 3. Scheduled events.39 foreach ( array( 'optimazz_preload_cache', 'optimazz_preload_cache_once', 'optimazz_purge_expired', 'optimazz_db_cleanup' ) as $hook ) {40 wp_clear_scheduled_hook( $hook );41 }40 // 3. Scheduled events. 41 foreach ( array( 'optimazz_preload_cache', 'optimazz_preload_cache_once', 'optimazz_purge_expired', 'optimazz_db_cleanup' ) as $hook ) { 42 wp_clear_scheduled_hook( $hook ); 43 } 42 44 43 // 4. Transients (incl. the per-stylesheet font-weight cache). 44 delete_transient( 'optimazz_wpconfig_unwritable' ); 45 $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- one-off cleanup of our own transients on uninstall. 46 $wpdb->prepare( 47 "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s", 48 $wpdb->esc_like( '_transient_optimazz_fw_' ) . '%', 49 $wpdb->esc_like( '_transient_timeout_optimazz_fw_' ) . '%' 50 ) 51 ); 45 // 4. Transients (incl. the per-stylesheet font-weight cache). 46 delete_transient( 'optimazz_wpconfig_unwritable' ); 47 $wpdb->query( // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- one-off cleanup of our own transients on uninstall. 48 $wpdb->prepare( 49 "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s", 50 $wpdb->esc_like( '_transient_optimazz_fw_' ) . '%', 51 $wpdb->esc_like( '_transient_timeout_optimazz_fw_' ) . '%' 52 ) 53 ); 54 }; 55 56 delete_metadata( 'user', 0, 'optimazz_conflict_dismissed', '', true ); // Dismissed cache-conflict notice (users are network-wide). 57 delete_site_option( 'optimazz_network' ); // Network page-cache settings (multisite). 58 delete_site_option( 'optimazz_network_version' ); 59 delete_site_transient( 'optimazz_heal' ); // Self-heal throttle (multisite). 52 60 53 61 // 5. advanced-cache.php drop-in (only if it is ours). … … 111 119 $optimazz_rrmdir( WP_CONTENT_DIR . '/cache/optimazz' ); 112 120 113 $uploads = wp_get_upload_dir(); 114 if ( ! empty( $uploads['basedir'] ) ) { 115 $optimazz_rrmdir( trailingslashit( $uploads['basedir'] ) . 'optimazz-backup' ); 116 // Generated CSS now lives under uploads/optimazz (moved out of cache/). 117 $optimazz_rrmdir( trailingslashit( $uploads['basedir'] ) . 'optimazz' ); 121 // Generated CSS (uploads/optimazz) and original-image backups 122 // (uploads/optimazz-backup) live in each site's own uploads folder. 123 $optimazz_clean_uploads = static function () use ( $optimazz_rrmdir ) { 124 $uploads = wp_get_upload_dir(); 125 if ( ! empty( $uploads['basedir'] ) ) { 126 $optimazz_rrmdir( trailingslashit( $uploads['basedir'] ) . 'optimazz-backup' ); 127 $optimazz_rrmdir( trailingslashit( $uploads['basedir'] ) . 'optimazz' ); 128 } 129 }; 130 // 8. Per-site cleanup — every site of a network in one pass (uninstall runs once). 131 if ( is_multisite() ) { 132 foreach ( get_sites( array( 'fields' => 'ids', 'number' => 0 ) ) as $optimazz_site_id ) { 133 switch_to_blog( $optimazz_site_id ); 134 $optimazz_cleanup_site(); 135 $optimazz_clean_uploads(); 136 restore_current_blog(); 137 } 138 } else { 139 $optimazz_cleanup_site(); 140 $optimazz_clean_uploads(); 118 141 }
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)