diff --git a/assets/dist/seo.css b/assets/dist/seo.css index 8cf8bf2..2ac2e84 100644 --- a/assets/dist/seo.css +++ b/assets/dist/seo.css @@ -1 +1 @@ -golem15-seo-text,golem15-seo-robots,golem15-seo-serp,golem15-seo-image,golem15-seo-robotstxt,golem15-seo-robotstester{display:block;color:var(--c-text);font-size:14px}golem15-seo-text .seo-visually-hidden,golem15-seo-image .seo-visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}golem15-seo-text .seo-text__control{box-sizing:border-box;width:100%;height:44px;padding:0 14px;border:1px solid var(--c-border-strong);border-radius:10px;background:var(--c-surface);color:var(--c-text);font:inherit;font-size:14px;transition:border-color .15s,box-shadow .15s}golem15-seo-text .seo-text__control--area{height:auto;min-height:96px;padding:10px 14px;resize:vertical}golem15-seo-text .seo-text__control:focus{outline:none;border-color:var(--c-primary);box-shadow:0 0 0 3px var(--c-ring)}golem15-seo-text.seo-text--invalid .seo-text__control{border-color:var(--c-danger)}golem15-seo-text .seo-text__counter{display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap;margin-top:8px;font-size:13px}golem15-seo-text .seo-text__status{display:inline-flex;align-items:center;gap:6px}golem15-seo-text .seo-text__icon{display:inline-flex}golem15-seo-text .seo-text__count{color:var(--c-muted);font-variant-numeric:tabular-nums}golem15-seo-text[data-status=empty] .seo-text__status{color:var(--c-muted)}golem15-seo-text[data-status=good] .seo-text__status{color:var(--c-ok-text, var(--c-text))}golem15-seo-robots .seo-robots__groups{display:flex;flex-wrap:wrap;gap:24px}golem15-seo-robots .seo-robots__group{display:flex;flex-direction:column;gap:6px}golem15-seo-robots .seo-robots__caption,golem15-seo-robots .seo-robots__summary,golem15-seo-robots .seo-robots__hint{color:var(--c-muted);font-size:13px}golem15-seo-robots .seo-robots__track,golem15-seo-serp .seo-serp__track{display:inline-flex;gap:4px;padding:4px;border:1px solid var(--c-border);border-radius:12px;background:var(--c-subtle)}golem15-seo-robots .seo-robots__segment,golem15-seo-serp .seo-serp__segment{height:34px;padding:0 16px;border:0;border-radius:9px;background:transparent;color:var(--c-muted);font:inherit;font-size:13px;cursor:pointer;transition:background-color .15s,color .15s}golem15-seo-robots .seo-robots__segment[aria-checked=true],golem15-seo-serp .seo-serp__segment[aria-checked=true]{font-size:14px;font-weight:600;background:var(--c-surface);color:var(--c-text);box-shadow:var(--shadow-tab, none)}golem15-seo-robots .seo-robots__segment--ok[aria-checked=true]{background:var(--c-ok-bg, var(--c-surface));color:var(--c-ok-text, var(--c-text))}golem15-seo-robots .seo-robots__segment--danger[aria-checked=true]{background:var(--c-danger-soft);color:var(--c-danger)}golem15-seo-robots .seo-robots__segment:focus-visible,golem15-seo-robots .seo-robots__toggle:focus-visible,golem15-seo-serp .seo-serp__segment:focus-visible,golem15-seo-serp .seo-serp__link:focus-visible,golem15-seo-image .seo-image__button:focus-visible{outline:3px solid var(--c-ring);outline-offset:2px}golem15-seo-robots .seo-robots__advanced-row{display:flex;align-items:center;gap:12px;margin-top:12px}golem15-seo-robots .seo-robots__toggle{display:inline-flex;align-items:center;gap:6px;padding:0;border:0;background:none;color:var(--c-text);font:inherit;font-size:14px;font-weight:600;cursor:pointer}golem15-seo-robots .seo-robots__toggle[aria-expanded=true] .seo-icon{transform:rotate(180deg)}golem15-seo-robots .seo-robots__panel{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-top:12px}golem15-seo-robots .seo-robots__panel[hidden]{display:none}golem15-seo-robots .seo-robots__field{display:flex;flex-direction:column;gap:6px}golem15-seo-robots .seo-robots__input{box-sizing:border-box;height:44px;padding:0 14px;border:1px solid var(--c-border-strong);border-radius:10px;background:var(--c-surface);color:var(--c-text);font:inherit}golem15-seo-robots .seo-robots__input:focus{outline:none;border-color:var(--c-primary);box-shadow:0 0 0 3px var(--c-ring)}golem15-seo-robots .seo-robots__error{color:var(--c-danger);font-size:13px}golem15-seo-robots .seo-robots__checks{grid-column:1 / -1;display:flex;flex-direction:column;gap:8px}golem15-seo-robots .seo-robots__check{display:inline-flex;align-items:center;gap:8px}golem15-seo-robots .seo-robots__check input{width:20px;height:20px;border-radius:5px;accent-color:var(--c-primary)}golem15-seo-robots.seo-robots--invalid .seo-robots__track{border-color:var(--c-danger)}golem15-seo-serp .seo-serp__skeleton{max-width:600px;height:132px;border-radius:12px;background:var(--c-skel)}golem15-seo-serp .seo-serp__header{display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap;margin-bottom:12px}golem15-seo-serp .seo-serp__language,golem15-seo-serp .seo-serp__notes{color:var(--c-muted);font-size:13px}golem15-seo-serp .serp-card{--serp-bg: #ffffff;--serp-site: #202124;--serp-title: #1a0dab;--serp-text: #4d5156;box-sizing:border-box;max-width:600px;padding:16px;border:1px solid var(--c-border);border-radius:12px;background:var(--serp-bg);font-family:Arial,sans-serif;overflow-wrap:anywhere}golem15-seo-serp .serp-card--mobile{max-width:360px}golem15-seo-serp .serp-card__site{display:flex;align-items:center;gap:10px}golem15-seo-serp .serp-card__favicon{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border:1px solid var(--c-border);border-radius:50%;background:var(--c-subtle);color:var(--c-muted);font-size:14px;font-weight:600}golem15-seo-serp .serp-card__names{display:flex;flex-direction:column;color:var(--serp-site)}golem15-seo-serp .serp-card__sitename{font-size:14px}golem15-seo-serp .serp-card__crumbs{font-size:12px}golem15-seo-serp .serp-card__title{margin-top:6px;color:var(--serp-title);font-size:20px;line-height:1.3;white-space:nowrap}golem15-seo-serp .serp-card--mobile .serp-card__title{white-space:normal}golem15-seo-serp .serp-card__description{margin-top:4px;color:var(--serp-text);font-size:14px;line-height:1.58}golem15-seo-serp .serp-card__description--missing{font-style:italic}golem15-seo-serp .seo-serp__notes{display:flex;flex-direction:column;gap:8px;margin-top:8px}golem15-seo-serp .seo-serp__error{display:inline-flex;align-items:center;gap:6px;color:var(--c-danger)}golem15-seo-serp .seo-serp__checks,golem15-seo-serp .seo-serp__keyphrase{display:flex;flex-direction:column;gap:6px;margin-top:12px;font-size:13px}golem15-seo-serp .seo-serp__checking{color:var(--c-muted)}golem15-seo-serp .seo-serp__keyphrase-title{font-size:14px;font-weight:600}golem15-seo-serp .seo-serp__check{display:inline-flex;align-items:flex-start;gap:6px}golem15-seo-serp .seo-serp__check--ok{color:var(--c-ok-text, var(--c-text))}golem15-seo-serp .seo-serp__check .seo-icon{flex:none;margin-top:2px}golem15-seo-serp .seo-serp__link{color:var(--c-text);text-decoration:underline}golem15-seo-image .seo-image__preview{margin-bottom:8px}golem15-seo-image .seo-image__img{display:block;width:240px;height:126px;object-fit:cover;border:1px solid var(--c-border);border-radius:10px;background:var(--c-subtle)}golem15-seo-image .seo-image__actions{display:flex;gap:8px}golem15-seo-image .seo-image__button{display:inline-flex;align-items:center;height:36px;padding:0 14px;border:1px solid var(--c-border-strong);border-radius:10px;background:var(--c-surface);color:var(--c-text);font:inherit;font-size:14px;cursor:pointer}golem15-seo-image .seo-image__status{margin-top:6px;color:var(--c-muted);font-size:13px}golem15-seo-image .seo-image__status--error{color:var(--c-danger)}golem15-seo-image.seo-image--invalid .seo-image__img{border-color:var(--c-danger)}golem15-seo-robotstxt .seo-robotstxt__control{box-sizing:border-box;display:block;width:100%;min-height:320px;padding:12px 14px;border:1px solid var(--c-border-strong);border-radius:10px;background:var(--c-surface);color:var(--c-text);font-family:DM Mono,ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;line-height:1.6;white-space:pre;overflow-x:auto;resize:vertical;transition:border-color .15s,box-shadow .15s}golem15-seo-robotstxt .seo-robotstxt__control:focus{outline:none;border-color:var(--c-primary);box-shadow:0 0 0 3px var(--c-ring)}golem15-seo-robotstxt.seo-robotstxt--invalid .seo-robotstxt__control,golem15-seo-robotstxt.seo-robotstxt--over .seo-robotstxt__control{border-color:var(--c-danger)}golem15-seo-robotstxt .seo-robotstxt__lint{margin-top:8px;padding:12px 14px;border:1px solid var(--c-border);border-radius:10px;background:var(--c-subtle);font-size:13px}golem15-seo-robotstxt .seo-robotstxt__finding{display:flex;align-items:flex-start;gap:6px;overflow-wrap:anywhere}golem15-seo-robotstxt .seo-robotstxt__finding+.seo-robotstxt__finding{margin-top:4px}golem15-seo-robotstxt .seo-robotstxt__finding .seo-icon{flex:none;margin-top:3px}golem15-seo-robotstxt .seo-robotstxt__finding--clean{color:var(--c-ok-text, var(--c-text))}golem15-seo-robotstxt .seo-robotstxt__more{margin-top:4px;color:var(--c-muted)}golem15-seo-robotstxt .seo-robotstxt__size{display:flex;justify-content:flex-end;gap:12px;margin-top:6px;color:var(--c-muted);font-size:13px;font-variant-numeric:tabular-nums}golem15-seo-robotstxt .seo-robotstxt__over{color:var(--c-danger)}golem15-seo-robotstester .seo-robotstester__row{display:flex;flex-wrap:wrap;align-items:flex-end;gap:8px}golem15-seo-robotstester .seo-robotstester__field{display:flex;flex-direction:column;gap:4px}golem15-seo-robotstester .seo-robotstester__field--url{flex:1 1 240px}golem15-seo-robotstester .seo-robotstester__field--agent{min-width:200px}golem15-seo-robotstester .seo-robotstester__label{color:var(--c-muted);font-size:13px}golem15-seo-robotstester .seo-robotstester__input,golem15-seo-robotstester .seo-robotstester__select{box-sizing:border-box;height:44px;padding:0 14px;border:1px solid var(--c-border-strong);border-radius:10px;background:var(--c-surface);color:var(--c-text);font:inherit;font-size:14px;transition:border-color .15s,box-shadow .15s}golem15-seo-robotstester .seo-robotstester__input:focus,golem15-seo-robotstester .seo-robotstester__select:focus{outline:none;border-color:var(--c-primary);box-shadow:0 0 0 3px var(--c-ring)}golem15-seo-robotstester .seo-robotstester__button{height:42px;padding:0 16px;border:1px solid var(--c-border-strong);border-radius:10px;background:var(--c-surface);color:var(--c-text);font:inherit;font-size:14px;cursor:pointer}golem15-seo-robotstester .seo-robotstester__button:focus-visible{outline:none;box-shadow:0 0 0 3px var(--c-ring)}golem15-seo-robotstester .seo-robotstester__button:disabled{cursor:default;opacity:.6}golem15-seo-robotstester .seo-robotstester__result{display:flex;align-items:flex-start;gap:6px;margin-top:10px;font-size:14px;overflow-wrap:anywhere}golem15-seo-robotstester .seo-robotstester__result .seo-icon{flex:none;margin-top:3px}golem15-seo-robotstester .seo-robotstester__result--allowed{color:var(--c-ok-text, var(--c-text))}golem15-seo-robotstester .seo-robotstester__result--blocked{color:var(--c-text)}golem15-seo-robotstester .seo-robotstester__result--error{color:var(--c-danger)}golem15-seo-robotstester .seo-robotstester__note{margin:6px 0 0;color:var(--c-muted);font-size:13px}@media(max-width:600px){golem15-seo-robots .seo-robots__panel{grid-template-columns:1fr}golem15-seo-serp .serp-card,golem15-seo-serp .serp-card--mobile{max-width:none}}@media(prefers-reduced-motion:reduce){golem15-seo-robotstxt .seo-robotstxt__control,golem15-seo-robotstester .seo-robotstester__input,golem15-seo-robotstester .seo-robotstester__select,golem15-seo-text .seo-text__control,golem15-seo-robots .seo-robots__segment,golem15-seo-serp .seo-serp__segment{transition:none}} +golem15-seo-text,golem15-seo-robots,golem15-seo-serp,golem15-seo-image,golem15-seo-robotstxt,golem15-seo-robotstester,golem15-seo-transfer{display:block;color:var(--c-text);font-size:14px}golem15-seo-text .seo-visually-hidden,golem15-seo-image .seo-visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}golem15-seo-text .seo-text__control{box-sizing:border-box;width:100%;height:44px;padding:0 14px;border:1px solid var(--c-border-strong);border-radius:10px;background:var(--c-surface);color:var(--c-text);font:inherit;font-size:14px;transition:border-color .15s,box-shadow .15s}golem15-seo-text .seo-text__control--area{height:auto;min-height:96px;padding:10px 14px;resize:vertical}golem15-seo-text .seo-text__control:focus{outline:none;border-color:var(--c-primary);box-shadow:0 0 0 3px var(--c-ring)}golem15-seo-text.seo-text--invalid .seo-text__control{border-color:var(--c-danger)}golem15-seo-text .seo-text__counter{display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap;margin-top:8px;font-size:13px}golem15-seo-text .seo-text__status{display:inline-flex;align-items:center;gap:6px}golem15-seo-text .seo-text__icon{display:inline-flex}golem15-seo-text .seo-text__count{color:var(--c-muted);font-variant-numeric:tabular-nums}golem15-seo-text[data-status=empty] .seo-text__status{color:var(--c-muted)}golem15-seo-text[data-status=good] .seo-text__status{color:var(--c-ok-text, var(--c-text))}golem15-seo-robots .seo-robots__groups{display:flex;flex-wrap:wrap;gap:24px}golem15-seo-robots .seo-robots__group{display:flex;flex-direction:column;gap:6px}golem15-seo-robots .seo-robots__caption,golem15-seo-robots .seo-robots__summary,golem15-seo-robots .seo-robots__hint{color:var(--c-muted);font-size:13px}golem15-seo-robots .seo-robots__track,golem15-seo-serp .seo-serp__track{display:inline-flex;gap:4px;padding:4px;border:1px solid var(--c-border);border-radius:12px;background:var(--c-subtle)}golem15-seo-robots .seo-robots__segment,golem15-seo-serp .seo-serp__segment{height:34px;padding:0 16px;border:0;border-radius:9px;background:transparent;color:var(--c-muted);font:inherit;font-size:13px;cursor:pointer;transition:background-color .15s,color .15s}golem15-seo-robots .seo-robots__segment[aria-checked=true],golem15-seo-serp .seo-serp__segment[aria-checked=true]{font-size:14px;font-weight:600;background:var(--c-surface);color:var(--c-text);box-shadow:var(--shadow-tab, none)}golem15-seo-robots .seo-robots__segment--ok[aria-checked=true]{background:var(--c-ok-bg, var(--c-surface));color:var(--c-ok-text, var(--c-text))}golem15-seo-robots .seo-robots__segment--danger[aria-checked=true]{background:var(--c-danger-soft);color:var(--c-danger)}golem15-seo-robots .seo-robots__segment:focus-visible,golem15-seo-robots .seo-robots__toggle:focus-visible,golem15-seo-serp .seo-serp__segment:focus-visible,golem15-seo-serp .seo-serp__link:focus-visible,golem15-seo-image .seo-image__button:focus-visible{outline:3px solid var(--c-ring);outline-offset:2px}golem15-seo-robots .seo-robots__advanced-row{display:flex;align-items:center;gap:12px;margin-top:12px}golem15-seo-robots .seo-robots__toggle{display:inline-flex;align-items:center;gap:6px;padding:0;border:0;background:none;color:var(--c-text);font:inherit;font-size:14px;font-weight:600;cursor:pointer}golem15-seo-robots .seo-robots__toggle[aria-expanded=true] .seo-icon{transform:rotate(180deg)}golem15-seo-robots .seo-robots__panel{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-top:12px}golem15-seo-robots .seo-robots__panel[hidden]{display:none}golem15-seo-robots .seo-robots__field{display:flex;flex-direction:column;gap:6px}golem15-seo-robots .seo-robots__input{box-sizing:border-box;height:44px;padding:0 14px;border:1px solid var(--c-border-strong);border-radius:10px;background:var(--c-surface);color:var(--c-text);font:inherit}golem15-seo-robots .seo-robots__input:focus{outline:none;border-color:var(--c-primary);box-shadow:0 0 0 3px var(--c-ring)}golem15-seo-robots .seo-robots__error{color:var(--c-danger);font-size:13px}golem15-seo-robots .seo-robots__checks{grid-column:1 / -1;display:flex;flex-direction:column;gap:8px}golem15-seo-robots .seo-robots__check{display:inline-flex;align-items:center;gap:8px}golem15-seo-robots .seo-robots__check input{width:20px;height:20px;border-radius:5px;accent-color:var(--c-primary)}golem15-seo-robots.seo-robots--invalid .seo-robots__track{border-color:var(--c-danger)}golem15-seo-serp .seo-serp__skeleton{max-width:600px;height:132px;border-radius:12px;background:var(--c-skel)}golem15-seo-serp .seo-serp__header{display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap;margin-bottom:12px}golem15-seo-serp .seo-serp__language,golem15-seo-serp .seo-serp__notes{color:var(--c-muted);font-size:13px}golem15-seo-serp .serp-card{--serp-bg: #ffffff;--serp-site: #202124;--serp-title: #1a0dab;--serp-text: #4d5156;box-sizing:border-box;max-width:600px;padding:16px;border:1px solid var(--c-border);border-radius:12px;background:var(--serp-bg);font-family:Arial,sans-serif;overflow-wrap:anywhere}golem15-seo-serp .serp-card--mobile{max-width:360px}golem15-seo-serp .serp-card__site{display:flex;align-items:center;gap:10px}golem15-seo-serp .serp-card__favicon{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;border:1px solid var(--c-border);border-radius:50%;background:var(--c-subtle);color:var(--c-muted);font-size:14px;font-weight:600}golem15-seo-serp .serp-card__names{display:flex;flex-direction:column;color:var(--serp-site)}golem15-seo-serp .serp-card__sitename{font-size:14px}golem15-seo-serp .serp-card__crumbs{font-size:12px}golem15-seo-serp .serp-card__title{margin-top:6px;color:var(--serp-title);font-size:20px;line-height:1.3;white-space:nowrap}golem15-seo-serp .serp-card--mobile .serp-card__title{white-space:normal}golem15-seo-serp .serp-card__description{margin-top:4px;color:var(--serp-text);font-size:14px;line-height:1.58}golem15-seo-serp .serp-card__description--missing{font-style:italic}golem15-seo-serp .seo-serp__notes{display:flex;flex-direction:column;gap:8px;margin-top:8px}golem15-seo-serp .seo-serp__error{display:inline-flex;align-items:center;gap:6px;color:var(--c-danger)}golem15-seo-serp .seo-serp__checks,golem15-seo-serp .seo-serp__keyphrase{display:flex;flex-direction:column;gap:6px;margin-top:12px;font-size:13px}golem15-seo-serp .seo-serp__checking{color:var(--c-muted)}golem15-seo-serp .seo-serp__keyphrase-title{font-size:14px;font-weight:600}golem15-seo-serp .seo-serp__check{display:inline-flex;align-items:flex-start;gap:6px}golem15-seo-serp .seo-serp__check--ok{color:var(--c-ok-text, var(--c-text))}golem15-seo-serp .seo-serp__check .seo-icon{flex:none;margin-top:2px}golem15-seo-serp .seo-serp__link{color:var(--c-text);text-decoration:underline}golem15-seo-image .seo-image__preview{margin-bottom:8px}golem15-seo-image .seo-image__img{display:block;width:240px;height:126px;object-fit:cover;border:1px solid var(--c-border);border-radius:10px;background:var(--c-subtle)}golem15-seo-image .seo-image__actions{display:flex;gap:8px}golem15-seo-image .seo-image__button{display:inline-flex;align-items:center;height:36px;padding:0 14px;border:1px solid var(--c-border-strong);border-radius:10px;background:var(--c-surface);color:var(--c-text);font:inherit;font-size:14px;cursor:pointer}golem15-seo-image .seo-image__status{margin-top:6px;color:var(--c-muted);font-size:13px}golem15-seo-image .seo-image__status--error{color:var(--c-danger)}golem15-seo-image.seo-image--invalid .seo-image__img{border-color:var(--c-danger)}golem15-seo-robotstxt .seo-robotstxt__control{box-sizing:border-box;display:block;width:100%;min-height:320px;padding:12px 14px;border:1px solid var(--c-border-strong);border-radius:10px;background:var(--c-surface);color:var(--c-text);font-family:DM Mono,ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;line-height:1.6;white-space:pre;overflow-x:auto;resize:vertical;transition:border-color .15s,box-shadow .15s}golem15-seo-robotstxt .seo-robotstxt__control:focus{outline:none;border-color:var(--c-primary);box-shadow:0 0 0 3px var(--c-ring)}golem15-seo-robotstxt.seo-robotstxt--invalid .seo-robotstxt__control,golem15-seo-robotstxt.seo-robotstxt--over .seo-robotstxt__control{border-color:var(--c-danger)}golem15-seo-robotstxt .seo-robotstxt__lint{margin-top:8px;padding:12px 14px;border:1px solid var(--c-border);border-radius:10px;background:var(--c-subtle);font-size:13px}golem15-seo-robotstxt .seo-robotstxt__finding{display:flex;align-items:flex-start;gap:6px;overflow-wrap:anywhere}golem15-seo-robotstxt .seo-robotstxt__finding+.seo-robotstxt__finding{margin-top:4px}golem15-seo-robotstxt .seo-robotstxt__finding .seo-icon{flex:none;margin-top:3px}golem15-seo-robotstxt .seo-robotstxt__finding--clean{color:var(--c-ok-text, var(--c-text))}golem15-seo-robotstxt .seo-robotstxt__more{margin-top:4px;color:var(--c-muted)}golem15-seo-robotstxt .seo-robotstxt__size{display:flex;justify-content:flex-end;gap:12px;margin-top:6px;color:var(--c-muted);font-size:13px;font-variant-numeric:tabular-nums}golem15-seo-robotstxt .seo-robotstxt__over{color:var(--c-danger)}golem15-seo-robotstester .seo-robotstester__row{display:flex;flex-wrap:wrap;align-items:flex-end;gap:8px}golem15-seo-robotstester .seo-robotstester__field{display:flex;flex-direction:column;gap:4px}golem15-seo-robotstester .seo-robotstester__field--url{flex:1 1 240px}golem15-seo-robotstester .seo-robotstester__field--agent{min-width:200px}golem15-seo-robotstester .seo-robotstester__label{color:var(--c-muted);font-size:13px}golem15-seo-robotstester .seo-robotstester__input,golem15-seo-robotstester .seo-robotstester__select{box-sizing:border-box;height:44px;padding:0 14px;border:1px solid var(--c-border-strong);border-radius:10px;background:var(--c-surface);color:var(--c-text);font:inherit;font-size:14px;transition:border-color .15s,box-shadow .15s}golem15-seo-robotstester .seo-robotstester__input:focus,golem15-seo-robotstester .seo-robotstester__select:focus{outline:none;border-color:var(--c-primary);box-shadow:0 0 0 3px var(--c-ring)}golem15-seo-robotstester .seo-robotstester__button{height:42px;padding:0 16px;border:1px solid var(--c-border-strong);border-radius:10px;background:var(--c-surface);color:var(--c-text);font:inherit;font-size:14px;cursor:pointer}golem15-seo-robotstester .seo-robotstester__button:focus-visible{outline:none;box-shadow:0 0 0 3px var(--c-ring)}golem15-seo-robotstester .seo-robotstester__button:disabled{cursor:default;opacity:.6}golem15-seo-robotstester .seo-robotstester__result{display:flex;align-items:flex-start;gap:6px;margin-top:10px;font-size:14px;overflow-wrap:anywhere}golem15-seo-robotstester .seo-robotstester__result .seo-icon{flex:none;margin-top:3px}golem15-seo-robotstester .seo-robotstester__result--allowed{color:var(--c-ok-text, var(--c-text))}golem15-seo-robotstester .seo-robotstester__result--blocked{color:var(--c-text)}golem15-seo-robotstester .seo-robotstester__result--error{color:var(--c-danger)}golem15-seo-robotstester .seo-robotstester__note{margin:6px 0 0;color:var(--c-muted);font-size:13px}golem15-seo-transfer .seo-transfer__section{display:flex;flex-direction:column;gap:12px}golem15-seo-transfer .seo-transfer__section--import{margin-top:24px;padding-top:24px;border-top:1px solid var(--c-border)}golem15-seo-transfer .seo-transfer__heading{margin:0;color:var(--c-text);font-size:14px;font-weight:600}golem15-seo-transfer .seo-transfer__row{display:flex;flex-wrap:wrap;align-items:flex-end;gap:8px}golem15-seo-transfer .seo-transfer__field{display:flex;flex-direction:column;gap:4px}golem15-seo-transfer .seo-transfer__label{color:var(--c-muted);font-size:13px}golem15-seo-transfer .seo-transfer__select{box-sizing:border-box;min-width:240px;height:44px;padding:0 14px;border:1px solid var(--c-border-strong);border-radius:10px;background:var(--c-surface);color:var(--c-text);font:inherit;font-size:14px;transition:border-color .15s,box-shadow .15s}golem15-seo-transfer .seo-transfer__select:focus{outline:none;border-color:var(--c-primary);box-shadow:0 0 0 3px var(--c-ring)}golem15-seo-transfer .seo-transfer__button{height:42px;padding:0 16px;border:1px solid var(--c-border-strong);border-radius:10px;background:var(--c-surface);color:var(--c-text);font:inherit;font-size:14px;cursor:pointer}golem15-seo-transfer .seo-transfer__button--ghost{border-color:transparent;background:transparent}golem15-seo-transfer .seo-transfer__button--primary{border-color:var(--c-primary);background:var(--c-primary);color:var(--c-on-primary, var(--c-surface))}golem15-seo-transfer .seo-transfer__button:focus-visible{outline:none;box-shadow:0 0 0 3px var(--c-ring)}golem15-seo-transfer .seo-transfer__button:disabled{cursor:default;opacity:.6}golem15-seo-transfer .seo-transfer__filename{align-self:center;color:var(--c-text);font-size:14px;overflow-wrap:anywhere}golem15-seo-transfer .seo-transfer__filename--none{color:var(--c-muted)}golem15-seo-transfer .seo-transfer__error{margin:0;color:var(--c-danger);font-size:14px}golem15-seo-transfer .seo-transfer__note,golem15-seo-transfer .seo-transfer__done{margin:0;color:var(--c-muted);font-size:13px}golem15-seo-transfer .seo-transfer__link{color:var(--c-primary)}golem15-seo-transfer .seo-transfer__preview{display:flex;flex-direction:column;gap:12px}golem15-seo-transfer .seo-transfer__summary{margin:0;font-size:14px;font-weight:600}golem15-seo-transfer .seo-transfer__table-wrap{max-height:480px;overflow:auto}golem15-seo-transfer .seo-transfer__num{font-variant-numeric:tabular-nums;text-align:right}golem15-seo-transfer .seo-transfer__mono{font-family:DM Mono,ui-monospace,monospace;overflow-wrap:anywhere}golem15-seo-transfer .seo-transfer__badge{display:inline-flex;align-items:center;height:24px;margin-right:4px;padding:0 10px;border-radius:999px;font-size:12px;white-space:nowrap}golem15-seo-transfer .seo-transfer__badge--new{background:var(--c-ok-bg);color:var(--c-ok-text)}golem15-seo-transfer .seo-transfer__badge--changed,golem15-seo-transfer .seo-transfer__badge--warning{background:var(--c-sel);color:var(--c-text)}golem15-seo-transfer .seo-transfer__badge--unchanged{background:var(--c-subtle);color:var(--c-muted)}golem15-seo-transfer .seo-transfer__badge--refused{background:var(--c-danger-soft);color:var(--c-danger)}golem15-seo-transfer .seo-transfer__actions{display:flex;justify-content:flex-end;gap:10px}@media(max-width:600px){golem15-seo-robots .seo-robots__panel{grid-template-columns:1fr}golem15-seo-serp .serp-card,golem15-seo-serp .serp-card--mobile{max-width:none}}@media(prefers-reduced-motion:reduce){golem15-seo-robotstxt .seo-robotstxt__control,golem15-seo-robotstester .seo-robotstester__input,golem15-seo-robotstester .seo-robotstester__select,golem15-seo-transfer .seo-transfer__select,golem15-seo-text .seo-text__control,golem15-seo-robots .seo-robots__segment,golem15-seo-serp .seo-serp__segment{transition:none}} diff --git a/assets/dist/seo.js b/assets/dist/seo.js index 572243d..6e98440 100644 --- a/assets/dist/seo.js +++ b/assets/dist/seo.js @@ -1 +1 @@ -const H={count:":chars characters · :px / :max px",good:"Good length",emptyTitle:"Empty: the title template is used",emptyDescription:"Empty: the template or the excerpt is used",shortTitle:"Short: aim for 30–60 characters",shortDescription:"Short: aim for 120–160 characters",longTitle:"Too long: Google cuts titles after about 580 px",longDescription:"Too long: Google cuts descriptions after about 920 px on desktop and 680 px on mobile",indexing:"Indexing",links:"Links",inherit:"Inherit",siteDefault:"Site default",index:"Index",noindex:"Noindex",follow:"Follow",nofollow:"Nofollow",advanced:"Advanced directives",advancedSet:{one:":count set",other:":count set"},maxSnippet:"Maximum snippet length (characters)",maxSnippetHint:"Empty: no limit. 0: no snippet. -1: no limit.",maxImagePreview:"Image preview size",notSet:"Not set",none:"None",standard:"Standard",large:"Large",maxVideoPreview:"Maximum video preview (seconds)",noarchive:"Do not show a cached copy (noarchive)",nosnippet:"Do not show a snippet (nosnippet)",noimageindex:"Do not index images (noimageindex)",unavailableAfter:"Remove from results after",invalidNumber:"Enter a whole number, -1 or more.",desktop:"Desktop",mobile:"Mobile",device:"Preview device",language:"Language: :locale",fromTemplate:"Title from the template “:template”",fromContent:"Description from the content excerpt",noDescription:"No description: Google picks text from the page.",noUrl:"The address appears after the first save.",savedNote:"The address and the H1 come from the saved record. Save to refresh them.",checking:"Checking for duplicates…",dupTitle:{one:"Same title as :count other page: :list",other:"Same title as :count other pages: :list"},dupDescription:{one:"Same description as :count other page: :list",other:"Same description as :count other pages: :list"},andMore:"and :count more",keyphrase:"Focus keyphrase “:phrase”",kpTitle:"In the SEO title",kpTitleNo:"Not in the SEO title",kpH1:"In the H1",kpH1No:"Not in the H1",kpDesc:"In the meta description",kpDescNo:"Not in the meta description",kpUrl:"In the address",kpUrlNo:"Not in the address",kpLength:"Text length: :count words",kpLengthNo:"Text length: :count words, aim for at least 300",contextFailed:"The preview data could not be loaded.",imageUpload:"Upload image",imageReplace:"Replace image",imageRemove:"Remove",imageUploading:"Uploading…",imageFailed:"The image could not be uploaded.",imageAlt:"Social image preview",size:":size of 500 KB",clean:"No problems found.",unknown:"Line :line: “:word” is not a robots.txt directive.",unknownSuggest:"Line :line: “:word” is not a robots.txt directive. Did you mean “:suggestion”?",noGroup:"Line :line: this rule has no User-agent line above it, so crawlers ignore it.",badPath:"Line :line: a path must start with / or *.",noColon:"Line :line: write the directive as “Name: value”.",ownSitemap:"Line :line: Sitemap lines are added automatically. This one is kept as written.",crawlDelay:"Line :line: Google ignores Crawl-delay. Bing and Yandex use it.",moreFindings:"and :count more",tooLarge:"Over 500 KB: the file cannot be saved.",url:"Address",urlPlaceholder:"/konto/ustawienia",agent:"Crawler",allowedRule:"Allowed for :agent. Rule: line :line “:rule”.",allowedNone:"Allowed for :agent: no rule matches this address.",blocked:"Blocked for :agent by line :line “:rule”.",blockedBot:"Blocked for :agent by its switch on the AI bots tab.",unsaved:"Tests the text in the editor and the AI bot switches, including unsaved changes.",stagingNote:"On this environment every address is blocked. The result shows the production behaviour.",urlRequired:"Enter an address starting with /."},Y={count:"Znaki: :chars · :px / :max px",good:"Dobra długość",emptyTitle:"Puste: używany jest szablon tytułu",emptyDescription:"Puste: używany jest szablon lub zajawka",shortTitle:"Krótki: celuj w 30–60 znaków",shortDescription:"Krótki: celuj w 120–160 znaków",longTitle:"Za długi: Google ucina tytuły po ok. 580 px",longDescription:"Za długi: Google ucina opisy po ok. 920 px na komputerze i 680 px na telefonie",indexing:"Indeksowanie",links:"Linki",inherit:"Dziedzicz",siteDefault:"Domyślne",index:"Indeksuj",noindex:"Nie indeksuj",follow:"Śledź",nofollow:"Nie śledź",advanced:"Dyrektywy zaawansowane",advancedSet:{one:"ustawiona :count",few:"ustawione :count",many:"ustawionych :count",other:"ustawione :count"},maxSnippet:"Maksymalna długość fragmentu (znaki)",maxSnippetHint:"Puste: bez limitu. 0: bez fragmentu. -1: bez limitu.",maxImagePreview:"Rozmiar podglądu obrazu",notSet:"Nie ustawiono",none:"Brak",standard:"Standardowy",large:"Duży",maxVideoPreview:"Maksymalny podgląd wideo (sekundy)",noarchive:"Nie pokazuj kopii (noarchive)",nosnippet:"Nie pokazuj fragmentu (nosnippet)",noimageindex:"Nie indeksuj obrazów (noimageindex)",unavailableAfter:"Usuń z wyników po dniu",invalidNumber:"Wpisz liczbę całkowitą, -1 lub większą.",desktop:"Komputer",mobile:"Telefon",device:"Urządzenie podglądu",language:"Język: :locale",fromTemplate:"Tytuł z szablonu „:template”",fromContent:"Opis z zajawki treści",noDescription:"Brak opisu: Google wybierze tekst ze strony.",noUrl:"Adres pojawi się po pierwszym zapisie.",savedNote:"Adres i H1 pochodzą z zapisanego rekordu. Zapisz, aby je odświeżyć.",checking:"Sprawdzanie duplikatów…",dupTitle:{one:"Ten sam tytuł co :count inna strona: :list",few:"Ten sam tytuł co :count inne strony: :list",many:"Ten sam tytuł co :count innych stron: :list",other:"Ten sam tytuł co :count innej strony: :list"},dupDescription:{one:"Ten sam opis co :count inna strona: :list",few:"Ten sam opis co :count inne strony: :list",many:"Ten sam opis co :count innych stron: :list",other:"Ten sam opis co :count innej strony: :list"},andMore:"i :count więcej",keyphrase:"Fraza kluczowa „:phrase”",kpTitle:"W tytule SEO",kpTitleNo:"Brak w tytule SEO",kpH1:"W nagłówku H1",kpH1No:"Brak w nagłówku H1",kpDesc:"W opisie meta",kpDescNo:"Brak w opisie meta",kpUrl:"W adresie",kpUrlNo:"Brak w adresie",kpLength:"Długość tekstu: :count słów",kpLengthNo:"Długość tekstu: :count słów, celuj w co najmniej 300",contextFailed:"Nie udało się wczytać danych podglądu.",imageUpload:"Wgraj obraz",imageReplace:"Zmień obraz",imageRemove:"Usuń",imageUploading:"Wgrywanie…",imageFailed:"Nie udało się wgrać obrazu.",imageAlt:"Podgląd obrazu w mediach społecznościowych",size:":size z 500 KB",clean:"Nie znaleziono problemów.",unknown:"Wiersz :line: „:word” nie jest dyrektywą robots.txt.",unknownSuggest:"Wiersz :line: „:word” nie jest dyrektywą robots.txt. Czy chodziło o „:suggestion”?",noGroup:"Wiersz :line: nad tą regułą nie ma wiersza User-agent, więc roboty ją ignorują.",badPath:"Wiersz :line: ścieżka musi zaczynać się od / lub *.",noColon:"Wiersz :line: zapisz dyrektywę jako „Nazwa: wartość”.",ownSitemap:"Wiersz :line: wiersze Sitemap są dodawane automatycznie. Ten zostaje bez zmian.",crawlDelay:"Wiersz :line: Google ignoruje Crawl-delay. Bing i Yandex go stosują.",moreFindings:"i :count więcej",tooLarge:"Ponad 500 KB: nie można zapisać pliku.",url:"Adres",urlPlaceholder:"/konto/ustawienia",agent:"Robot",allowedRule:"Dozwolone dla :agent. Reguła: wiersz :line „:rule”.",allowedNone:"Dozwolone dla :agent: żadna reguła nie pasuje do tego adresu.",blocked:"Zablokowane dla :agent przez wiersz :line „:rule”.",blockedBot:"Zablokowane dla :agent przełącznikiem w zakładce Boty AI.",unsaved:"Sprawdza tekst w edytorze i przełączniki botów AI, także niezapisane zmiany.",stagingNote:"W tym środowisku każdy adres jest zablokowany. Wynik pokazuje zachowanie na produkcji.",urlRequired:"Wpisz adres zaczynający się od /."},J={en:H,pl:Y};function k(n){return(n.getAttribute("ui-locale")||document.documentElement.lang||"en").toLowerCase().split(/[-_]/)[0]||"en"}function C(n){return J[n]??H}function g(n,e={}){const t=Object.keys(e).sort((i,o)=>o.length-i.length);let s=n;for(const i of t)s=s.split(":"+i).join(String(e[i]));return s}function q(n,e,t,s={}){let i="other";try{i=new Intl.PluralRules(t).select(e)}catch{i=e===1?"one":"other"}const o=i==="one"&&n.one||i==="few"&&n.few||i==="many"&&n.many||n.other;return g(o,{...s,count:new Intl.NumberFormat(t).format(e)})}function I(n,e){try{return new Intl.NumberFormat(e).format(n)}catch{return String(n)}}const O="http://www.w3.org/2000/svg",Q={info:["M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20z","M12 16v-4","M12 8h.01"],"triangle-alert":["m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3","M12 9v4","M12 17h.01"],"circle-check":["M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20z","m9 12 2 2 4-4"],"circle-alert":["M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20z","M12 8v4","M12 16h.01"],"chevron-down":["m6 9 6 6 6-6"]};function z(n,e=14){const t=document.createElementNS(O,"svg");t.setAttribute("width",String(e)),t.setAttribute("height",String(e)),t.setAttribute("viewBox","0 0 24 24"),t.setAttribute("fill","none"),t.setAttribute("stroke","currentColor"),t.setAttribute("stroke-width","2"),t.setAttribute("stroke-linecap","round"),t.setAttribute("stroke-linejoin","round"),t.setAttribute("aria-hidden","true"),t.setAttribute("class","seo-icon seo-icon--"+n);for(const s of Q[n]??[]){const i=document.createElementNS(O,"path");i.setAttribute("d",s),t.appendChild(i)}return t}function r(n,e="",t){const s=document.createElement(n);return e&&(s.className=e),t!==void 0&&(s.textContent=t),s}function D(n,e,t){const s=n.getAttribute(e);if(s===null||s==="")return t;try{return JSON.parse(s)}catch{return t}}function T(n,e,t){n.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0,composed:!0}))}let U=0;function S(n){return U+=1,`${n}-${U}`}const ee="golem15-seo-image";function te(n){return/^\/(?!\/)/.test(n)||/^https?:\/\//i.test(n)}class se extends HTMLElement{static observedAttributes=["disabled","invalid","ui-locale"];current="";pending="";error="";onUploadResult=e=>{const t=e.detail;!t||t.id!==this.pending||(this.pending="",typeof t.url=="string"&&t.url!==""?(this.current=t.url,this.error="",T(this,"summer-change",{value:this.current})):this.error=t.error||this.labels().imageFailed,this.render())};get value(){return this.current}set value(e){this.current=typeof e=="string"?e:"",this.render()}connectedCallback(){this.addEventListener("summer-upload-result",this.onUploadResult),this.render()}disconnectedCallback(){this.removeEventListener("summer-upload-result",this.onUploadResult)}attributeChangedCallback(){this.isConnected&&this.render()}labels(){return C(k(this))}render(){const e=this.labels(),t=this.hasAttribute("disabled"),s=[];if(this.current&&te(this.current)){const o=r("div","seo-image__preview"),a=r("img","seo-image__img");a.src=this.current,a.alt=e.imageAlt,a.width=240,a.height=126,o.append(a),s.push(o)}if(!t){const o=r("div","seo-image__actions"),a=S("seo-image-file"),l=r("input","seo-visually-hidden");l.type="file",l.accept="image/jpeg,image/png,image/gif,image/webp",l.id=a,l.addEventListener("change",()=>{const u=l.files?.[0];u&&(this.pending=S("seo-image-upload"),this.error="",this.render(),T(this,"summer-upload",{id:this.pending,file:u}))});const c=r("label","seo-image__button",this.current?e.imageReplace:e.imageUpload);if(c.htmlFor=a,o.append(l,c),this.current){const u=r("button","seo-image__button seo-image__button--secondary",e.imageRemove);u.type="button",u.addEventListener("click",()=>{this.current="",this.error="",T(this,"summer-change",{value:""}),this.render()}),o.append(u)}s.push(o)}const i=r("div","seo-image__status"+(this.error?" seo-image__status--error":""));i.setAttribute("role","status"),i.textContent=this.pending?e.imageUploading:this.error,s.push(i),this.classList.toggle("seo-image--invalid",this.hasAttribute("invalid")),this.replaceChildren(...s)}}const ne="golem15-seo-robots";function P(){return{index:"",follow:"",noarchive:!1,nosnippet:!1,noimageindex:!1,maxSnippet:null,maxImagePreview:"",maxVideoPreview:null,unavailableAfter:""}}function ie(n){const e=P(),t=n.trim();if(t==="")return e;const s=new Set;for(const i of t.split(",")){const o=i.trim().toLowerCase();if(o==="")return null;const a=o.indexOf(":"),l=(a<0?o:o.slice(0,a)).trim(),c=a<0?null:o.slice(a+1).trim(),u=l==="noindex"?"index":l==="nofollow"?"follow":l;if(s.has(u))return null;switch(s.add(u),l){case"index":case"noindex":if(c!==null)return null;e.index=l;break;case"follow":case"nofollow":if(c!==null)return null;e.follow=l;break;case"noarchive":case"nosnippet":case"noimageindex":if(c!==null)return null;e[l]=!0;break;case"max-snippet":case"max-video-preview":{if(c===null||!/^-?\d+$/.test(c))return null;const d=Number.parseInt(c,10);if(d<-1)return null;l==="max-snippet"?e.maxSnippet=d:e.maxVideoPreview=d;break}case"max-image-preview":if(c!=="none"&&c!=="standard"&&c!=="large")return null;e.maxImagePreview=c;break;case"unavailable_after":if(c===null||!/^\d{4}-\d{2}-\d{2}$/.test(c)||Number.isNaN(Date.parse(c+"T00:00:00Z")))return null;e.unavailableAfter=c;break;default:return null}}return e}function oe(n){const e=[];return n.index&&e.push(n.index),n.follow&&e.push(n.follow),n.noarchive&&e.push("noarchive"),n.nosnippet&&e.push("nosnippet"),n.noimageindex&&e.push("noimageindex"),n.maxSnippet!==null&&e.push("max-snippet:"+n.maxSnippet),n.maxImagePreview&&e.push("max-image-preview:"+n.maxImagePreview),n.maxVideoPreview!==null&&e.push("max-video-preview:"+n.maxVideoPreview),n.unavailableAfter&&e.push("unavailable_after:"+n.unavailableAfter),e.join(",")}function R(n){return[n.noarchive,n.nosnippet,n.noimageindex,n.maxSnippet!==null,n.maxImagePreview!=="",n.maxVideoPreview!==null,n.unavailableAfter!==""].filter(Boolean).length}class re extends HTMLElement{static observedAttributes=["options","disabled","invalid","ui-locale","locale"];current="";model=P();open=!1;get value(){return this.current}set value(e){const t=typeof e=="string"?e:e==null?"":String(e);t===this.current&&this.childElementCount>0||(this.current=t,this.model=ie(t)??P(),this.open=this.open||R(this.model)>0,this.render())}connectedCallback(){this.open=R(this.model)>0,this.render()}attributeChangedCallback(){this.isConnected&&this.render()}labels(){return C(k(this))}commit(){this.current=oe(this.model),T(this,"summer-change",{value:this.current}),this.render()}render(){const e=this.labels(),t=this.hasAttribute("disabled"),i=D(this,"options",{}).scope==="type"?e.siteDefault:e.inherit;this.classList.toggle("seo-robots--invalid",this.hasAttribute("invalid"));const o=document.activeElement&&this.contains(document.activeElement)?document.activeElement.dataset.focusKey:void 0,a=r("div","seo-robots__groups");a.append(this.radioGroup(e.indexing,"index",[["",i,"inherit"],["index",e.index,"ok"],["noindex",e.noindex,"danger"]],t),this.radioGroup(e.links,"follow",[["",i,"inherit"],["follow",e.follow,"ok"],["nofollow",e.nofollow,"danger"]],t));const l=S("seo-robots-advanced"),c=r("button","seo-robots__toggle");c.type="button",c.setAttribute("aria-expanded",this.open?"true":"false"),c.setAttribute("aria-controls",l),c.dataset.focusKey="toggle",c.append(r("span","",e.advanced),z("chevron-down")),c.addEventListener("click",()=>{this.open=!this.open,this.render()});const u=r("div","seo-robots__advanced-row");u.append(c);const d=R(this.model);d>0&&u.append(r("span","seo-robots__summary",q(e.advancedSet,d,k(this))));const p=r("div","seo-robots__panel");p.id=l,p.hidden=!this.open,p.append(this.numberField(e.maxSnippet,e.maxSnippetHint,"maxSnippet",t,e),this.selectField(e,t),this.numberField(e.maxVideoPreview,"","maxVideoPreview",t,e),this.dateField(e,t));const f=r("div","seo-robots__checks");for(const b of["noarchive","nosnippet","noimageindex"]){const h=r("label","seo-robots__check"),m=r("input");m.type="checkbox",m.checked=this.model[b],m.disabled=t,m.dataset.focusKey=b,m.addEventListener("change",()=>{this.model[b]=m.checked,this.commit()}),h.append(m,r("span","",e[b])),f.append(h)}p.append(f),this.replaceChildren(a,u,p),o&&this.querySelector(`[data-focus-key="${o}"]`)?.focus()}radioGroup(e,t,s,i){const o=r("div","seo-robots__group"),a=S("seo-robots-caption"),l=r("span","seo-robots__caption",e);l.id=a;const c=r("div","seo-robots__track");c.setAttribute("role","radiogroup"),c.setAttribute("aria-labelledby",a);const u=this.model[t];return s.forEach(([d,p,f],b)=>{const h=r("button","seo-robots__segment seo-robots__segment--"+f,p);h.type="button",h.setAttribute("role","radio");const m=u===d;h.setAttribute("aria-checked",m?"true":"false"),h.tabIndex=m?0:-1,h.disabled=i,h.dataset.focusKey=t+"-"+b,h.addEventListener("click",()=>this.choose(t,d)),h.addEventListener("keydown",v=>{const _=v.key==="ArrowRight"||v.key==="ArrowDown"?1:v.key==="ArrowLeft"||v.key==="ArrowUp"?-1:0;if(_===0||i)return;v.preventDefault();const w=(b+_+s.length)%s.length;this.choose(t,s[w][0]),this.querySelector(`[data-focus-key="${t}-${w}"]`)?.focus()}),c.append(h)}),o.append(l,c),o}choose(e,t){this.hasAttribute("disabled")||(e==="index"?this.model.index=t:this.model.follow=t,this.commit())}numberField(e,t,s,i,o){const a=r("label","seo-robots__field");a.append(r("span","seo-robots__label",e));const l=r("input","seo-robots__input");l.type="number",l.min="-1",l.step="1",l.disabled=i,l.dataset.focusKey=s;const c=this.model[s];l.value=c===null?"":String(c);const u=r("span","seo-robots__error");return u.setAttribute("role","status"),l.addEventListener("change",()=>{const d=l.value.trim();if(d===""){this.model[s]=null,this.commit();return}if(!/^-?\d+$/.test(d)||Number.parseInt(d,10)<-1){u.textContent=o.invalidNumber;return}this.model[s]=Number.parseInt(d,10),this.commit()}),a.append(l),t&&a.append(r("span","seo-robots__hint",t)),a.append(u),a}selectField(e,t){const s=r("label","seo-robots__field");s.append(r("span","seo-robots__label",e.maxImagePreview));const i=r("select","seo-robots__input");i.disabled=t,i.dataset.focusKey="maxImagePreview";for(const[o,a]of[["",e.notSet],["none",e.none],["standard",e.standard],["large",e.large]]){const l=r("option","",a);l.value=o,l.selected=this.model.maxImagePreview===o,i.append(l)}return i.addEventListener("change",()=>{this.model.maxImagePreview=i.value,this.commit()}),s.append(i),s}dateField(e,t){const s=r("label","seo-robots__field");s.append(r("span","seo-robots__label",e.unavailableAfter));const i=r("input","seo-robots__input");return i.type="date",i.disabled=t,i.dataset.focusKey="unavailableAfter",i.value=this.model.unavailableAfter,i.addEventListener("change",()=>{this.model.unavailableAfter=/^\d{4}-\d{2}-\d{2}$/.test(i.value)?i.value:"",this.commit()}),s.append(i),s}}const ae="golem15-seo-robotstester",B=["Googlebot","Googlebot-Image","Bingbot","*"];function le(n,e){const t={agent:e.agent,line:e.line,rule:e.rule};return e.allowed?e.line>0?g(n.allowedRule,t):g(n.allowedNone,t):e.bot?g(n.blockedBot,t):g(n.blocked,t)}class ce extends HTMLElement{static observedAttributes=["busy","state","ui-locale","disabled","label","busy-label"];context=null;result=null;error="";inflight=null;queued=null;connected=!1;input=null;select=null;button=null;line=null;notes=null;builtFor="";onResult=e=>{const s=e.detail?.data;this.inflight=null,s&&s.op==="context"?(this.context=s,this.build(!0)):s&&s.op==="test"&&(this.result=s,this.error="",this.renderResult()),this.flush()};connectedCallback(){this.connected=!0,this.addEventListener("summer-result",this.onResult),this.build(!1),this.request({op:"context"})}disconnectedCallback(){this.connected=!1,this.removeEventListener("summer-result",this.onResult)}attributeChangedCallback(e,t,s){if(this.connected){if(e==="busy"&&s===null&&this.flush(),e==="state"&&s==="error"&&(this.inflight=null),e==="ui-locale"){this.build(!0);return}this.syncControls()}}labels(){return C(k(this))}request(e){if(this.hasAttribute("busy")||this.inflight){this.queued=e;return}this.inflight=e,T(this,"summer-action",{payload:e})}flush(){if(this.hasAttribute("busy")||this.inflight||!this.queued)return;const e=this.queued;this.queued=null,this.request(e)}build(e){const t=k(this)+"|"+(this.context?.agents??B).join(",");if(!e&&this.builtFor===t&&this.input){this.syncControls();return}this.builtFor=t;const s=this.labels(),i=this.input?.value??"",o=this.select?.value??"Googlebot";this.replaceChildren();const a=r("div","seo-robotstester__row"),l=S("seo-robotstester-url"),c=r("div","seo-robotstester__field seo-robotstester__field--url"),u=r("label","seo-robotstester__label",s.url);u.htmlFor=l;const d=r("input","seo-robotstester__input");d.id=l,d.type="text",d.placeholder=s.urlPlaceholder,d.value=i,d.addEventListener("keydown",w=>{w.key==="Enter"&&(w.preventDefault(),this.test())}),c.append(u,d);const p=S("seo-robotstester-agent"),f=r("div","seo-robotstester__field seo-robotstester__field--agent"),b=r("label","seo-robotstester__label",s.agent);b.htmlFor=p;const h=r("select","seo-robotstester__select");h.id=p;for(const w of this.context?.agents??B){const L=r("option","",w);L.value=w,h.append(L)}h.value=o,h.value!==o&&(h.value="Googlebot"),f.append(b,h);const m=r("button","seo-robotstester__button");m.type="button",m.addEventListener("click",()=>this.test()),a.append(c,f,m);const v=r("div","seo-robotstester__result");v.setAttribute("role","status");const _=r("div","seo-robotstester__notes");this.input=d,this.select=h,this.button=m,this.line=v,this.notes=_,this.append(a,v,_),this.syncControls(),this.renderResult()}syncControls(){if(!this.button||!this.input||!this.select)return;const e=this.hasAttribute("disabled"),t=this.hasAttribute("busy");this.input.readOnly=e,this.select.disabled=e,this.button.hidden=e,this.button.disabled=t,this.button.textContent=t?this.getAttribute("busy-label")||this.getAttribute("label")||"":this.getAttribute("label")||""}test(){if(this.hasAttribute("disabled")||!this.input||!this.select)return;const e=this.input.value.trim();if(!e.startsWith("/")){this.error=this.labels().urlRequired,this.result=null,this.renderResult();return}this.error="",this.request({op:"test",url:e,agent:this.select.value})}renderResult(){if(!this.line||!this.notes)return;const e=this.labels();if(this.line.className="seo-robotstester__result",this.error)this.line.classList.add("seo-robotstester__result--error"),this.line.replaceChildren(r("span","",this.error));else if(this.result){const s=this.result.allowed;this.line.classList.add(s?"seo-robotstester__result--allowed":"seo-robotstester__result--blocked"),this.line.replaceChildren(z(s?"circle-check":"triangle-alert"),r("span","",le(e,this.result)))}else this.line.replaceChildren();const t=[];(this.context?.staging||D(this,"data",{}).staging)&&t.push(r("p","seo-robotstester__note",e.stagingNote)),t.push(r("p","seo-robotstester__note",e.unsaved)),this.notes.replaceChildren(...t)}}const ue="golem15-seo-robotstxt",de=500*1024,j=10,he=["User-agent","Allow","Disallow","Sitemap","Crawl-delay","Host","Clean-param"];function pe(n,e){const t=Array.from({length:e.length+1},(s,i)=>i);for(let s=1;s<=n.length;s++){let i=t[0];t[0]=s;for(let o=1;o<=e.length;o++){const a=t[o];t[o]=Math.min(t[o]+1,t[o-1]+1,i+(n[s-1]===e[o-1]?0:1)),i=a}}return t[e.length]}function me(n){const e=n.toLowerCase();let t="",s=3;for(const i of he){const o=pe(e,i.toLowerCase());o{const o=i+1,a=s.indexOf("#"),l=(a>=0?s.slice(0,a):s).trim();if(l==="")return;const c=l.indexOf(":");if(c<0){e.push({kind:"noColon",line:o});return}const u=l.slice(0,c).trim(),d=l.slice(c+1).trim();switch(u.toLowerCase()){case"user-agent":t=!0;return;case"allow":case"disallow":t?d!==""&&!d.startsWith("/")&&!d.startsWith("*")&&e.push({kind:"badPath",line:o}):e.push({kind:"noGroup",line:o});return;case"sitemap":e.push({kind:"ownSitemap",line:o});return;case"crawl-delay":e.push({kind:"crawlDelay",line:o});return;case"host":case"clean-param":return}const p=me(u);e.push(p?{kind:"unknownSuggest",line:o,word:u,suggestion:p}:{kind:"unknown",line:o,word:u})}),e}function be(n){return new TextEncoder().encode(n).length}function ge(n,e){return g(n[e.kind],{line:e.line,word:e.word??"",suggestion:e.suggestion??""})}class ve extends HTMLElement{static observedAttributes=["disabled","invalid","ui-locale"];current="";control=null;panel=null;meter=null;builtFor="";get value(){return this.current}set value(e){this.current=typeof e=="string"?e:e==null?"":String(e),this.control&&this.control.value!==this.current&&(this.control.value=this.current),this.update()}connectedCallback(){this.build()}attributeChangedCallback(e){if(this.isConnected){if(e==="ui-locale"){this.build();return}this.sync()}}labels(){return C(k(this))}build(){const e=k(this);if(this.builtFor===e&&this.control){this.sync();return}this.builtFor=e,this.replaceChildren();const t=S("seo-robotstxt-lint"),s=r("textarea","seo-robotstxt__control");s.spellcheck=!1,s.setAttribute("wrap","off"),s.value=this.current,s.setAttribute("aria-describedby",t);const i=this.getAttribute("field-name");i&&s.setAttribute("aria-label",i),s.addEventListener("input",()=>{this.hasAttribute("disabled")||(this.current=s.value,this.update(),T(this,"summer-change",{value:this.current}))});const o=r("div","seo-robotstxt__lint");o.id=t,o.setAttribute("role","status");const a=r("div","seo-robotstxt__size");this.control=s,this.panel=o,this.meter=a,this.append(s,o,a),this.sync()}sync(){this.control&&(this.control.readOnly=this.hasAttribute("disabled"),this.classList.toggle("seo-robotstxt--invalid",this.hasAttribute("invalid")),this.control.setAttribute("aria-invalid",this.hasAttribute("invalid")?"true":"false"),this.update())}update(){if(!this.panel||!this.meter)return;const e=this.labels(),t=k(this),s=fe(this.current),i=[];if(s.length===0){const c=r("div","seo-robotstxt__finding seo-robotstxt__finding--clean");c.append(z("circle-check"),r("span","",e.clean)),i.push(c)}else{for(const c of s.slice(0,j)){const u=r("div","seo-robotstxt__finding");u.append(z("triangle-alert"),r("span","",ge(e,c))),i.push(u)}s.length>j&&i.push(r("div","seo-robotstxt__more",g(e.moreFindings,{count:I(s.length-j,t)})))}this.panel.replaceChildren(...i);const o=be(this.current),a=new Intl.NumberFormat(t,{maximumFractionDigits:1}).format(o/1024)+" KB";this.meter.replaceChildren(r("span","",g(e.size,{size:a})));const l=o>de;this.classList.toggle("seo-robotstxt--over",l),l&&this.meter.append(r("span","seo-robotstxt__over",e.tooLarge))}}const K="20px Arial, sans-serif",V="14px Arial, sans-serif",X=580,$=920,we=680,xe=30,ke=120;let N;function _e(){if(N!==void 0)return N;try{const e=document.createElement("canvas").getContext("2d");N=e&&typeof e.measureText=="function"?e:null}catch{N=null}return N}function ye(n,e){let t=0;for(const s of n)/[ilIj.,:;'!|]/.test(s)?t+=.28:/[mwMW@]/.test(s)?t+=.85:/[A-Z]/.test(s)?t+=.67:s===" "?t+=.28:t+=.53;return t*e}function M(n,e){const t=_e();if(t){t.font=e;const i=t.measureText(n).width;if(Number.isFinite(i)&&(i>0||n===""))return i}const s=Number.parseFloat(e)||14;return ye(n,s)}function W(n,e,t){if(M(n,e)<=t)return n;const s=Array.from(n);let i=0,o=s.length;for(;is?c="long":a=i}function E(n,e){const t=G(n);if(t.length===0)return!1;const s=G(e);for(let i=0;i+t.length<=s.length;i++){let o=!0;for(let a=0;a{const s=e.detail?.data;this.inflight=null,s&&s.op==="context"?(this.context=s,this.contextFailed=!1,this.render(),this.scheduleDuplicates(!0)):s&&s.op==="duplicates"&&(this.duplicates.set(s.locale,s),this.checking=!1,this.render()),this.flush()};onLocale=e=>{const t=e.detail;typeof t=="string"&&t!==""&&t!==this.shownLocale&&(this.shownLocale=t,this.render(),this.scheduleDuplicates(!1))};connectedCallback(){this.connected=!0,this.addEventListener("summer-result",this.onResult),window.addEventListener("summer:ml-locale",this.onLocale),this.render(),this.request({op:"context"})}disconnectedCallback(){this.connected=!1,this.removeEventListener("summer-result",this.onResult),window.removeEventListener("summer:ml-locale",this.onLocale),clearTimeout(this.timer)}attributeChangedCallback(e,t,s){if(this.connected){if(e==="busy"&&s===null){this.flush();return}if(e==="state"&&s==="error"){this.inflight?.op==="context"&&(this.contextFailed=!0),this.checking=!1,this.inflight=null,this.render();return}if(e==="fill-values"){this.render(),this.scheduleDuplicates(!1);return}this.render()}}labels(){return C(k(this))}request(e){if(this.hasAttribute("busy")||this.inflight){this.queued=e;return}this.inflight=e,T(this,"summer-action",{payload:e})}flush(){if(this.hasAttribute("busy")||this.inflight||!this.queued)return;const e=this.queued;this.queued=null,this.request(e)}fill(){return D(this,"fill-values",{})}locale(){const e=this.context?.locales??[];return e.length===0?null:e.find(t=>t.locale===this.shownLocale)??e[0]}scheduleDuplicates(e){const t=this.locale();if(!t)return;const s=this.fill(),i=y(s,"seo_title",t.locale)||y(s,"title",t.locale),o=y(s,"seo_description",t.locale)||y(s,"description",t.locale),a=JSON.stringify([t.locale,i,o]);if(a===this.lastChecked)return;clearTimeout(this.timer);const l=()=>{this.lastChecked=a,this.checking=!0,this.render(),this.request({op:"duplicates",locale:t.locale,title:i,description:o})};e?l():this.timer=setTimeout(l,Se)}render(){const e=this.labels(),t=k(this);if(!this.context&&!this.contextFailed){this.setAttribute("aria-busy","true"),this.replaceChildren(r("div","seo-serp__skeleton"));return}this.removeAttribute("aria-busy");const s=this.locale(),i=s?.locale??this.shownLocale,o=this.fill(),a=(y(o,"seo_title",i)||y(o,"title",i)).trim(),l=(y(o,"seo_description",i)||y(o,"description",i)).trim(),c=y(o,"seo_keyphrase",i).trim(),u=s?.siteName??"",d=a||Le(s?.titleTemplate??"",{title:s?.defaultTitle??"",site:u})||s?.defaultTitle||"",p=l||s?.defaultDescription||"",f=r("div","seo-serp__header"),b=r("div","seo-serp__track");b.setAttribute("role","radiogroup"),b.setAttribute("aria-label",e.device);for(const x of["desktop","mobile"]){const A=r("button","seo-serp__segment",e[x]);A.type="button",A.setAttribute("role","radio"),A.setAttribute("aria-checked",this.device===x?"true":"false"),A.addEventListener("click",()=>{this.device=x,this.render()}),b.append(A)}f.append(b),i&&f.append(r("span","seo-serp__language",g(e.language,{locale:i.toUpperCase()})));const h=r("div","serp-card"+(this.device==="mobile"?" serp-card--mobile":"")),m=r("div","serp-card__site");m.append(r("span","serp-card__favicon",Array.from(u||this.context?.host||"?")[0]?.toUpperCase()??"?"));const v=r("div","serp-card__names");if(v.append(r("span","serp-card__sitename",u||this.context?.host||"")),v.append(r("span","serp-card__crumbs",De(this.context?.host??"",s?.url??null))),m.append(v),h.append(m),h.append(r("div","serp-card__title",W(d,K,X))),p){const x=this.device==="mobile"?we:$;h.append(r("div","serp-card__description",W(p,V,x)))}else h.append(r("div","serp-card__description serp-card__description--missing",e.noDescription));const _=r("div","seo-serp__notes");if(this.contextFailed){const x=r("div","seo-serp__error");x.setAttribute("role","status"),x.append(z("circle-alert"),r("span","",e.contextFailed)),_.append(x)}!a&&s?.titleTemplate&&_.append(r("div","",g(e.fromTemplate,{template:s.titleTemplate}))),!l&&s?.defaultDescription&&s.descriptionSource==="content"&&_.append(r("div","",e.fromContent)),s&&s.url===null&&_.append(r("div","",e.noUrl)),(this.getAttribute("record-id")??"")!==""&&s&&_.append(r("div","",e.savedNote));const w=r("div","seo-serp__checks");if(w.setAttribute("role","status"),this.checking)w.append(r("div","seo-serp__checking",e.checking));else{const x=this.duplicates.get(i);if(x)for(const[A,Z]of[[x.title,e.dupTitle],[x.description,e.dupDescription]])A.length>0&&w.append(Re(A,Z,e,t))}const L=[f,h,_,w];c&&s&&L.push(je(c,{title:d,description:p,h1:s.h1,url:s.url,bodyWords:s.bodyWords},e,t)),this.replaceChildren(...L)}}function De(n,e){const t=(e??"").split("?")[0].split("/").filter(Boolean).map(s=>{try{return decodeURIComponent(s)}catch{return s}});return[n,...t].filter(Boolean).join(" › ")}function Re(n,e,t,s){const i=r("div","seo-serp__check seo-serp__check--warn");i.append(z("triangle-alert"));const o=r("span"),a="\0list\0",l=q(e,n.length,s,{list:a}),[c,u]=l.split(a);return o.append(document.createTextNode(c??"")),n.slice(0,F).forEach((d,p)=>{if(p>0&&o.append(document.createTextNode(", ")),d.url.startsWith("/")&&!d.url.startsWith("//")){const f=r("a","seo-serp__link",d.label);f.href=d.url,f.target="_blank",f.rel="noopener",o.append(f)}else o.append(document.createTextNode(d.label))}),n.length>F&&o.append(document.createTextNode(" "+g(t.andMore,{count:n.length-F}))),o.append(document.createTextNode(u??"")),i.append(o),i}function je(n,e,t,s){const i=r("div","seo-serp__keyphrase");i.append(r("div","seo-serp__keyphrase-title",g(t.keyphrase,{phrase:n})));const o=(a,l,c)=>{const u=r("div","seo-serp__check "+(a?"seo-serp__check--ok":"seo-serp__check--warn"));u.append(z(a?"circle-check":"triangle-alert"),r("span","",a?l:c)),i.append(u)};if(o(E(n,e.title),t.kpTitle,t.kpTitleNo),e.h1!==null&&o(E(n,e.h1),t.kpH1,t.kpH1No),o(E(n,e.description),t.kpDesc,t.kpDescNo),e.url!==null&&o(E(n,e.url.replace(/[-_/]+/g," ")),t.kpUrl,t.kpUrlNo),e.bodyWords!==null){const a=new Intl.NumberFormat(s).format(e.bodyWords);o(e.bodyWords>=Ce,g(t.kpLength,{count:a}),g(t.kpLengthNo,{count:a}))}return i}const Fe="golem15-seo-text",Pe={empty:"info",short:"triangle-alert",long:"triangle-alert",good:"circle-check"};class Me extends HTMLElement{static observedAttributes=["options","disabled","invalid","ui-locale","locale"];current="";control=null;statusIcon=null;statusText=null;countText=null;live=null;lastStatus=null;builtFor="";get value(){return this.current}set value(e){this.current=typeof e=="string"?e:e==null?"":String(e),this.control&&this.control.value!==this.current&&(this.control.value=this.current),this.update(!1)}connectedCallback(){this.build()}attributeChangedCallback(e){if(this.isConnected){if(e==="options"||e==="ui-locale"){this.build();return}this.sync()}}measure(){return D(this,"options",{}).measure==="description"?"description":"title"}labels(){return C(k(this))}build(){const e=this.measure()+"|"+k(this);if(this.builtFor===e&&this.control){this.sync();return}this.builtFor=e,this.replaceChildren();const t=S("seo-text-counter"),i=this.measure()==="description"?r("textarea","seo-text__control seo-text__control--area"):r("input","seo-text__control");i instanceof HTMLTextAreaElement?i.rows=3:i.type="text",i.value=this.current,i.setAttribute("aria-describedby",t);const o=this.getAttribute("field-name");o&&i.setAttribute("aria-label",o),i.addEventListener("input",()=>{this.hasAttribute("disabled")||(this.current=i.value.replace(/\s+$/u,""),this.update(!0),T(this,"summer-change",{value:this.current}))});const a=r("div","seo-text__counter");a.id=t;const l=r("span","seo-text__status");this.statusIcon=r("span","seo-text__icon"),this.statusText=r("span","seo-text__status-text"),l.append(this.statusIcon,this.statusText),this.countText=r("span","seo-text__count"),a.append(l,this.countText),this.live=r("span","seo-visually-hidden"),this.live.setAttribute("aria-live","polite"),this.live.setAttribute("role","status"),this.control=i,this.lastStatus=null,this.append(i,a,this.live),this.sync()}sync(){if(!this.control)return;const e=this.hasAttribute("disabled");this.control.readOnly=e,this.classList.toggle("seo-text--invalid",this.hasAttribute("invalid")),this.control.setAttribute("aria-invalid",this.hasAttribute("invalid")?"true":"false"),this.update(!1)}update(e){if(!this.statusText||!this.countText||!this.statusIcon)return;const t=this.labels(),s=k(this),i=this.measure(),o=Te(this.current,i),a=Oe(t,o.status,i);this.statusIcon.replaceChildren(z(Pe[o.status])),this.statusText.textContent=a,this.dataset.status=o.status,this.countText.textContent=g(t.count,{chars:I(o.chars,s),px:I(o.px,s),max:I(o.maxPx,s)}),e&&this.live&&o.status!==this.lastStatus&&(this.live.textContent=a),this.lastStatus=o.status}}function Oe(n,e,t){switch(e){case"empty":return t==="title"?n.emptyTitle:n.emptyDescription;case"short":return t==="title"?n.shortTitle:n.shortDescription;case"long":return t==="title"?n.longTitle:n.longDescription;default:return n.good}}const Ue=[[Fe,Me],[ne,re],[ze,Ie],[ee,se],[ue,ve],[ae,ce]];for(const[n,e]of Ue)customElements.get(n)||customElements.define(n,e);export{ee as IMAGE_TAG,ae as ROBOTSTESTER_TAG,ue as ROBOTSTXT_TAG,ne as ROBOTS_TAG,ze as SERP_TAG,se as SeoImageElement,re as SeoRobotsElement,ce as SeoRobotsTesterElement,ve as SeoRobotsTxtElement,Ie as SeoSerpElement,Me as SeoTextElement,Fe as TEXT_TAG}; +const Z={count:":chars characters · :px / :max px",good:"Good length",emptyTitle:"Empty: the title template is used",emptyDescription:"Empty: the template or the excerpt is used",shortTitle:"Short: aim for 30–60 characters",shortDescription:"Short: aim for 120–160 characters",longTitle:"Too long: Google cuts titles after about 580 px",longDescription:"Too long: Google cuts descriptions after about 920 px on desktop and 680 px on mobile",indexing:"Indexing",links:"Links",inherit:"Inherit",siteDefault:"Site default",index:"Index",noindex:"Noindex",follow:"Follow",nofollow:"Nofollow",advanced:"Advanced directives",advancedSet:{one:":count set",other:":count set"},maxSnippet:"Maximum snippet length (characters)",maxSnippetHint:"Empty: no limit. 0: no snippet. -1: no limit.",maxImagePreview:"Image preview size",notSet:"Not set",none:"None",standard:"Standard",large:"Large",maxVideoPreview:"Maximum video preview (seconds)",noarchive:"Do not show a cached copy (noarchive)",nosnippet:"Do not show a snippet (nosnippet)",noimageindex:"Do not index images (noimageindex)",unavailableAfter:"Remove from results after",invalidNumber:"Enter a whole number, -1 or more.",desktop:"Desktop",mobile:"Mobile",device:"Preview device",language:"Language: :locale",fromTemplate:"Title from the template “:template”",fromContent:"Description from the content excerpt",noDescription:"No description: Google picks text from the page.",noUrl:"The address appears after the first save.",savedNote:"The address and the H1 come from the saved record. Save to refresh them.",checking:"Checking for duplicates…",dupTitle:{one:"Same title as :count other page: :list",other:"Same title as :count other pages: :list"},dupDescription:{one:"Same description as :count other page: :list",other:"Same description as :count other pages: :list"},andMore:"and :count more",keyphrase:"Focus keyphrase “:phrase”",kpTitle:"In the SEO title",kpTitleNo:"Not in the SEO title",kpH1:"In the H1",kpH1No:"Not in the H1",kpDesc:"In the meta description",kpDescNo:"Not in the meta description",kpUrl:"In the address",kpUrlNo:"Not in the address",kpLength:"Text length: :count words",kpLengthNo:"Text length: :count words, aim for at least 300",contextFailed:"The preview data could not be loaded.",imageUpload:"Upload image",imageReplace:"Replace image",imageRemove:"Remove",imageUploading:"Uploading…",imageFailed:"The image could not be uploaded.",imageAlt:"Social image preview",size:":size of 500 KB",clean:"No problems found.",unknown:"Line :line: “:word” is not a robots.txt directive.",unknownSuggest:"Line :line: “:word” is not a robots.txt directive. Did you mean “:suggestion”?",noGroup:"Line :line: this rule has no User-agent line above it, so crawlers ignore it.",badPath:"Line :line: a path must start with / or *.",noColon:"Line :line: write the directive as “Name: value”.",ownSitemap:"Line :line: Sitemap lines are added automatically. This one is kept as written.",crawlDelay:"Line :line: Google ignores Crawl-delay. Bing and Yandex use it.",moreFindings:"and :count more",tooLarge:"Over 500 KB: the file cannot be saved.",url:"Address",urlPlaceholder:"/konto/ustawienia",agent:"Crawler",allowedRule:"Allowed for :agent. Rule: line :line “:rule”.",allowedNone:"Allowed for :agent: no rule matches this address.",blocked:"Blocked for :agent by line :line “:rule”.",blockedBot:"Blocked for :agent by its switch on the AI bots tab.",unsaved:"Tests the text in the editor and the AI bot switches, including unsaved changes.",stagingNote:"On this environment every address is blocked. The result shows the production behaviour.",urlRequired:"Enter an address starting with /.",exportHeading:"Export",importHeading:"Import",dataset:"Data",dsRedirects:"Redirects",dsRules:"URL rules",dsRecords:"SEO of records",download:"Download CSV",chooseFile:"Choose CSV file",noFile:"No file chosen",summary:":new new, :changed changed, :unchanged unchanged, :refused refused",bNew:"New",bChanged:"Changed",bUnchanged:"Unchanged",bRefused:"Refused",bWarning:"Warning",colLine:"Line",colChange:"Change",colAddress:"Address",colField:"Field",colBefore:"Before",colAfter:"After",colNote:"Note",rowCap:"Showing the first 200 of :count rows. All rows are applied.",refusedBlocks:"Fix the refused rows in the file and preview it again. Nothing is applied while a row is refused.",nothing:"The file changes nothing.",discard:"Discard preview",fileTooLarge:"This file is larger than :limit. Split it into smaller files, or import it through MCP.",readFailed:"The file could not be read.",openHistory:"Open History",applyCount:{one:"Apply :count change",other:"Apply :count changes"}},Q={count:"Znaki: :chars · :px / :max px",good:"Dobra długość",emptyTitle:"Puste: używany jest szablon tytułu",emptyDescription:"Puste: używany jest szablon lub zajawka",shortTitle:"Krótki: celuj w 30–60 znaków",shortDescription:"Krótki: celuj w 120–160 znaków",longTitle:"Za długi: Google ucina tytuły po ok. 580 px",longDescription:"Za długi: Google ucina opisy po ok. 920 px na komputerze i 680 px na telefonie",indexing:"Indeksowanie",links:"Linki",inherit:"Dziedzicz",siteDefault:"Domyślne",index:"Indeksuj",noindex:"Nie indeksuj",follow:"Śledź",nofollow:"Nie śledź",advanced:"Dyrektywy zaawansowane",advancedSet:{one:"ustawiona :count",few:"ustawione :count",many:"ustawionych :count",other:"ustawione :count"},maxSnippet:"Maksymalna długość fragmentu (znaki)",maxSnippetHint:"Puste: bez limitu. 0: bez fragmentu. -1: bez limitu.",maxImagePreview:"Rozmiar podglądu obrazu",notSet:"Nie ustawiono",none:"Brak",standard:"Standardowy",large:"Duży",maxVideoPreview:"Maksymalny podgląd wideo (sekundy)",noarchive:"Nie pokazuj kopii (noarchive)",nosnippet:"Nie pokazuj fragmentu (nosnippet)",noimageindex:"Nie indeksuj obrazów (noimageindex)",unavailableAfter:"Usuń z wyników po dniu",invalidNumber:"Wpisz liczbę całkowitą, -1 lub większą.",desktop:"Komputer",mobile:"Telefon",device:"Urządzenie podglądu",language:"Język: :locale",fromTemplate:"Tytuł z szablonu „:template”",fromContent:"Opis z zajawki treści",noDescription:"Brak opisu: Google wybierze tekst ze strony.",noUrl:"Adres pojawi się po pierwszym zapisie.",savedNote:"Adres i H1 pochodzą z zapisanego rekordu. Zapisz, aby je odświeżyć.",checking:"Sprawdzanie duplikatów…",dupTitle:{one:"Ten sam tytuł co :count inna strona: :list",few:"Ten sam tytuł co :count inne strony: :list",many:"Ten sam tytuł co :count innych stron: :list",other:"Ten sam tytuł co :count innej strony: :list"},dupDescription:{one:"Ten sam opis co :count inna strona: :list",few:"Ten sam opis co :count inne strony: :list",many:"Ten sam opis co :count innych stron: :list",other:"Ten sam opis co :count innej strony: :list"},andMore:"i :count więcej",keyphrase:"Fraza kluczowa „:phrase”",kpTitle:"W tytule SEO",kpTitleNo:"Brak w tytule SEO",kpH1:"W nagłówku H1",kpH1No:"Brak w nagłówku H1",kpDesc:"W opisie meta",kpDescNo:"Brak w opisie meta",kpUrl:"W adresie",kpUrlNo:"Brak w adresie",kpLength:"Długość tekstu: :count słów",kpLengthNo:"Długość tekstu: :count słów, celuj w co najmniej 300",contextFailed:"Nie udało się wczytać danych podglądu.",imageUpload:"Wgraj obraz",imageReplace:"Zmień obraz",imageRemove:"Usuń",imageUploading:"Wgrywanie…",imageFailed:"Nie udało się wgrać obrazu.",imageAlt:"Podgląd obrazu w mediach społecznościowych",size:":size z 500 KB",clean:"Nie znaleziono problemów.",unknown:"Wiersz :line: „:word” nie jest dyrektywą robots.txt.",unknownSuggest:"Wiersz :line: „:word” nie jest dyrektywą robots.txt. Czy chodziło o „:suggestion”?",noGroup:"Wiersz :line: nad tą regułą nie ma wiersza User-agent, więc roboty ją ignorują.",badPath:"Wiersz :line: ścieżka musi zaczynać się od / lub *.",noColon:"Wiersz :line: zapisz dyrektywę jako „Nazwa: wartość”.",ownSitemap:"Wiersz :line: wiersze Sitemap są dodawane automatycznie. Ten zostaje bez zmian.",crawlDelay:"Wiersz :line: Google ignoruje Crawl-delay. Bing i Yandex go stosują.",moreFindings:"i :count więcej",tooLarge:"Ponad 500 KB: nie można zapisać pliku.",url:"Adres",urlPlaceholder:"/konto/ustawienia",agent:"Robot",allowedRule:"Dozwolone dla :agent. Reguła: wiersz :line „:rule”.",allowedNone:"Dozwolone dla :agent: żadna reguła nie pasuje do tego adresu.",blocked:"Zablokowane dla :agent przez wiersz :line „:rule”.",blockedBot:"Zablokowane dla :agent przełącznikiem w zakładce Boty AI.",unsaved:"Sprawdza tekst w edytorze i przełączniki botów AI, także niezapisane zmiany.",stagingNote:"W tym środowisku każdy adres jest zablokowany. Wynik pokazuje zachowanie na produkcji.",urlRequired:"Wpisz adres zaczynający się od /.",exportHeading:"Eksport",importHeading:"Import",dataset:"Dane",dsRedirects:"Przekierowania",dsRules:"Reguły adresów",dsRecords:"SEO rekordów",download:"Pobierz CSV",chooseFile:"Wybierz plik CSV",noFile:"Nie wybrano pliku",summary:"Nowe: :new, zmienione: :changed, bez zmian: :unchanged, odrzucone: :refused",bNew:"Nowy",bChanged:"Zmieniony",bUnchanged:"Bez zmian",bRefused:"Odrzucony",bWarning:"Ostrzeżenie",colLine:"Wiersz",colChange:"Zmiana",colAddress:"Adres",colField:"Pole",colBefore:"Przed",colAfter:"Po",colNote:"Uwagi",rowCap:"Pokazano pierwsze 200 z :count wierszy. Zastosowane zostaną wszystkie.",refusedBlocks:"Popraw odrzucone wiersze w pliku i ponownie wyświetl podgląd. Nic nie zostanie zastosowane, dopóki jakiś wiersz jest odrzucony.",nothing:"Plik niczego nie zmienia.",discard:"Odrzuć podgląd",fileTooLarge:"Ten plik jest większy niż :limit. Podziel go na mniejsze pliki lub zaimportuj przez MCP.",readFailed:"Nie udało się odczytać pliku.",openHistory:"Otwórz Historię zmian",applyCount:{one:"Zastosuj :count zmianę",few:"Zastosuj :count zmiany",many:"Zastosuj :count zmian",other:"Zastosuj :count zmiany"}},ee={en:Z,pl:Q};function y(n){return(n.getAttribute("ui-locale")||document.documentElement.lang||"en").toLowerCase().split(/[-_]/)[0]||"en"}function L(n){return ee[n]??Z}function v(n,e={}){const t=Object.keys(e).sort((i,r)=>r.length-i.length);let s=n;for(const i of t)s=s.split(":"+i).join(String(e[i]));return s}function U(n,e,t,s={}){let i="other";try{i=new Intl.PluralRules(t).select(e)}catch{i=e===1?"one":"other"}const r=i==="one"&&n.one||i==="few"&&n.few||i==="many"&&n.many||n.other;return v(r,{...s,count:new Intl.NumberFormat(t).format(e)})}function A(n,e){try{return new Intl.NumberFormat(e).format(n)}catch{return String(n)}}const B="http://www.w3.org/2000/svg",te={info:["M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20z","M12 16v-4","M12 8h.01"],"triangle-alert":["m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3","M12 9v4","M12 17h.01"],"circle-check":["M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20z","m9 12 2 2 4-4"],"circle-alert":["M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20z","M12 8v4","M12 16h.01"],"chevron-down":["m6 9 6 6 6-6"]};function S(n,e=14){const t=document.createElementNS(B,"svg");t.setAttribute("width",String(e)),t.setAttribute("height",String(e)),t.setAttribute("viewBox","0 0 24 24"),t.setAttribute("fill","none"),t.setAttribute("stroke","currentColor"),t.setAttribute("stroke-width","2"),t.setAttribute("stroke-linecap","round"),t.setAttribute("stroke-linejoin","round"),t.setAttribute("aria-hidden","true"),t.setAttribute("class","seo-icon seo-icon--"+n);for(const s of te[n]??[]){const i=document.createElementNS(B,"path");i.setAttribute("d",s),t.appendChild(i)}return t}function o(n,e="",t){const s=document.createElement(n);return e&&(s.className=e),t!==void 0&&(s.textContent=t),s}function D(n,e,t){const s=n.getAttribute(e);if(s===null||s==="")return t;try{return JSON.parse(s)}catch{return t}}function T(n,e,t){n.dispatchEvent(new CustomEvent(e,{detail:t,bubbles:!0,composed:!0}))}let H=0;function E(n){return H+=1,`${n}-${H}`}const se="golem15-seo-image";function ne(n){return/^\/(?!\/)/.test(n)||/^https?:\/\//i.test(n)}class ie extends HTMLElement{static observedAttributes=["disabled","invalid","ui-locale"];current="";pending="";error="";onUploadResult=e=>{const t=e.detail;!t||t.id!==this.pending||(this.pending="",typeof t.url=="string"&&t.url!==""?(this.current=t.url,this.error="",T(this,"summer-change",{value:this.current})):this.error=t.error||this.labels().imageFailed,this.render())};get value(){return this.current}set value(e){this.current=typeof e=="string"?e:"",this.render()}connectedCallback(){this.addEventListener("summer-upload-result",this.onUploadResult),this.render()}disconnectedCallback(){this.removeEventListener("summer-upload-result",this.onUploadResult)}attributeChangedCallback(){this.isConnected&&this.render()}labels(){return L(y(this))}render(){const e=this.labels(),t=this.hasAttribute("disabled"),s=[];if(this.current&&ne(this.current)){const r=o("div","seo-image__preview"),a=o("img","seo-image__img");a.src=this.current,a.alt=e.imageAlt,a.width=240,a.height=126,r.append(a),s.push(r)}if(!t){const r=o("div","seo-image__actions"),a=E("seo-image-file"),l=o("input","seo-visually-hidden");l.type="file",l.accept="image/jpeg,image/png,image/gif,image/webp",l.id=a,l.addEventListener("change",()=>{const d=l.files?.[0];d&&(this.pending=E("seo-image-upload"),this.error="",this.render(),T(this,"summer-upload",{id:this.pending,file:d}))});const c=o("label","seo-image__button",this.current?e.imageReplace:e.imageUpload);if(c.htmlFor=a,r.append(l,c),this.current){const d=o("button","seo-image__button seo-image__button--secondary",e.imageRemove);d.type="button",d.addEventListener("click",()=>{this.current="",this.error="",T(this,"summer-change",{value:""}),this.render()}),r.append(d)}s.push(r)}const i=o("div","seo-image__status"+(this.error?" seo-image__status--error":""));i.setAttribute("role","status"),i.textContent=this.pending?e.imageUploading:this.error,s.push(i),this.classList.toggle("seo-image--invalid",this.hasAttribute("invalid")),this.replaceChildren(...s)}}const oe="golem15-seo-robots";function P(){return{index:"",follow:"",noarchive:!1,nosnippet:!1,noimageindex:!1,maxSnippet:null,maxImagePreview:"",maxVideoPreview:null,unavailableAfter:""}}function re(n){const e=P(),t=n.trim();if(t==="")return e;const s=new Set;for(const i of t.split(",")){const r=i.trim().toLowerCase();if(r==="")return null;const a=r.indexOf(":"),l=(a<0?r:r.slice(0,a)).trim(),c=a<0?null:r.slice(a+1).trim(),d=l==="noindex"?"index":l==="nofollow"?"follow":l;if(s.has(d))return null;switch(s.add(d),l){case"index":case"noindex":if(c!==null)return null;e.index=l;break;case"follow":case"nofollow":if(c!==null)return null;e.follow=l;break;case"noarchive":case"nosnippet":case"noimageindex":if(c!==null)return null;e[l]=!0;break;case"max-snippet":case"max-video-preview":{if(c===null||!/^-?\d+$/.test(c))return null;const u=Number.parseInt(c,10);if(u<-1)return null;l==="max-snippet"?e.maxSnippet=u:e.maxVideoPreview=u;break}case"max-image-preview":if(c!=="none"&&c!=="standard"&&c!=="large")return null;e.maxImagePreview=c;break;case"unavailable_after":if(c===null||!/^\d{4}-\d{2}-\d{2}$/.test(c)||Number.isNaN(Date.parse(c+"T00:00:00Z")))return null;e.unavailableAfter=c;break;default:return null}}return e}function ae(n){const e=[];return n.index&&e.push(n.index),n.follow&&e.push(n.follow),n.noarchive&&e.push("noarchive"),n.nosnippet&&e.push("nosnippet"),n.noimageindex&&e.push("noimageindex"),n.maxSnippet!==null&&e.push("max-snippet:"+n.maxSnippet),n.maxImagePreview&&e.push("max-image-preview:"+n.maxImagePreview),n.maxVideoPreview!==null&&e.push("max-video-preview:"+n.maxVideoPreview),n.unavailableAfter&&e.push("unavailable_after:"+n.unavailableAfter),e.join(",")}function I(n){return[n.noarchive,n.nosnippet,n.noimageindex,n.maxSnippet!==null,n.maxImagePreview!=="",n.maxVideoPreview!==null,n.unavailableAfter!==""].filter(Boolean).length}class le extends HTMLElement{static observedAttributes=["options","disabled","invalid","ui-locale","locale"];current="";model=P();open=!1;get value(){return this.current}set value(e){const t=typeof e=="string"?e:e==null?"":String(e);t===this.current&&this.childElementCount>0||(this.current=t,this.model=re(t)??P(),this.open=this.open||I(this.model)>0,this.render())}connectedCallback(){this.open=I(this.model)>0,this.render()}attributeChangedCallback(){this.isConnected&&this.render()}labels(){return L(y(this))}commit(){this.current=ae(this.model),T(this,"summer-change",{value:this.current}),this.render()}render(){const e=this.labels(),t=this.hasAttribute("disabled"),i=D(this,"options",{}).scope==="type"?e.siteDefault:e.inherit;this.classList.toggle("seo-robots--invalid",this.hasAttribute("invalid"));const r=document.activeElement&&this.contains(document.activeElement)?document.activeElement.dataset.focusKey:void 0,a=o("div","seo-robots__groups");a.append(this.radioGroup(e.indexing,"index",[["",i,"inherit"],["index",e.index,"ok"],["noindex",e.noindex,"danger"]],t),this.radioGroup(e.links,"follow",[["",i,"inherit"],["follow",e.follow,"ok"],["nofollow",e.nofollow,"danger"]],t));const l=E("seo-robots-advanced"),c=o("button","seo-robots__toggle");c.type="button",c.setAttribute("aria-expanded",this.open?"true":"false"),c.setAttribute("aria-controls",l),c.dataset.focusKey="toggle",c.append(o("span","",e.advanced),S("chevron-down")),c.addEventListener("click",()=>{this.open=!this.open,this.render()});const d=o("div","seo-robots__advanced-row");d.append(c);const u=I(this.model);u>0&&d.append(o("span","seo-robots__summary",U(e.advancedSet,u,y(this))));const p=o("div","seo-robots__panel");p.id=l,p.hidden=!this.open,p.append(this.numberField(e.maxSnippet,e.maxSnippetHint,"maxSnippet",t,e),this.selectField(e,t),this.numberField(e.maxVideoPreview,"","maxVideoPreview",t,e),this.dateField(e,t));const g=o("div","seo-robots__checks");for(const b of["noarchive","nosnippet","noimageindex"]){const h=o("label","seo-robots__check"),f=o("input");f.type="checkbox",f.checked=this.model[b],f.disabled=t,f.dataset.focusKey=b,f.addEventListener("change",()=>{this.model[b]=f.checked,this.commit()}),h.append(f,o("span","",e[b])),g.append(h)}p.append(g),this.replaceChildren(a,d,p),r&&this.querySelector(`[data-focus-key="${r}"]`)?.focus()}radioGroup(e,t,s,i){const r=o("div","seo-robots__group"),a=E("seo-robots-caption"),l=o("span","seo-robots__caption",e);l.id=a;const c=o("div","seo-robots__track");c.setAttribute("role","radiogroup"),c.setAttribute("aria-labelledby",a);const d=this.model[t];return s.forEach(([u,p,g],b)=>{const h=o("button","seo-robots__segment seo-robots__segment--"+g,p);h.type="button",h.setAttribute("role","radio");const f=d===u;h.setAttribute("aria-checked",f?"true":"false"),h.tabIndex=f?0:-1,h.disabled=i,h.dataset.focusKey=t+"-"+b,h.addEventListener("click",()=>this.choose(t,u)),h.addEventListener("keydown",w=>{const x=w.key==="ArrowRight"||w.key==="ArrowDown"?1:w.key==="ArrowLeft"||w.key==="ArrowUp"?-1:0;if(x===0||i)return;w.preventDefault();const m=(b+x+s.length)%s.length;this.choose(t,s[m][0]),this.querySelector(`[data-focus-key="${t}-${m}"]`)?.focus()}),c.append(h)}),r.append(l,c),r}choose(e,t){this.hasAttribute("disabled")||(e==="index"?this.model.index=t:this.model.follow=t,this.commit())}numberField(e,t,s,i,r){const a=o("label","seo-robots__field");a.append(o("span","seo-robots__label",e));const l=o("input","seo-robots__input");l.type="number",l.min="-1",l.step="1",l.disabled=i,l.dataset.focusKey=s;const c=this.model[s];l.value=c===null?"":String(c);const d=o("span","seo-robots__error");return d.setAttribute("role","status"),l.addEventListener("change",()=>{const u=l.value.trim();if(u===""){this.model[s]=null,this.commit();return}if(!/^-?\d+$/.test(u)||Number.parseInt(u,10)<-1){d.textContent=r.invalidNumber;return}this.model[s]=Number.parseInt(u,10),this.commit()}),a.append(l),t&&a.append(o("span","seo-robots__hint",t)),a.append(d),a}selectField(e,t){const s=o("label","seo-robots__field");s.append(o("span","seo-robots__label",e.maxImagePreview));const i=o("select","seo-robots__input");i.disabled=t,i.dataset.focusKey="maxImagePreview";for(const[r,a]of[["",e.notSet],["none",e.none],["standard",e.standard],["large",e.large]]){const l=o("option","",a);l.value=r,l.selected=this.model.maxImagePreview===r,i.append(l)}return i.addEventListener("change",()=>{this.model.maxImagePreview=i.value,this.commit()}),s.append(i),s}dateField(e,t){const s=o("label","seo-robots__field");s.append(o("span","seo-robots__label",e.unavailableAfter));const i=o("input","seo-robots__input");return i.type="date",i.disabled=t,i.dataset.focusKey="unavailableAfter",i.value=this.model.unavailableAfter,i.addEventListener("change",()=>{this.model.unavailableAfter=/^\d{4}-\d{2}-\d{2}$/.test(i.value)?i.value:"",this.commit()}),s.append(i),s}}const ce="golem15-seo-robotstester",W=["Googlebot","Googlebot-Image","Bingbot","*"];function de(n,e){const t={agent:e.agent,line:e.line,rule:e.rule};return e.allowed?e.line>0?v(n.allowedRule,t):v(n.allowedNone,t):e.bot?v(n.blockedBot,t):v(n.blocked,t)}class ue extends HTMLElement{static observedAttributes=["busy","state","ui-locale","disabled","label","busy-label"];context=null;result=null;error="";inflight=null;queued=null;connected=!1;input=null;select=null;button=null;line=null;notes=null;builtFor="";onResult=e=>{const s=e.detail?.data;this.inflight=null,s&&s.op==="context"?(this.context=s,this.build(!0)):s&&s.op==="test"&&(this.result=s,this.error="",this.renderResult()),this.flush()};connectedCallback(){this.connected=!0,this.addEventListener("summer-result",this.onResult),this.build(!1),this.request({op:"context"})}disconnectedCallback(){this.connected=!1,this.removeEventListener("summer-result",this.onResult)}attributeChangedCallback(e,t,s){if(this.connected){if(e==="busy"&&s===null&&this.flush(),e==="state"&&s==="error"&&(this.inflight=null),e==="ui-locale"){this.build(!0);return}this.syncControls()}}labels(){return L(y(this))}request(e){if(this.hasAttribute("busy")||this.inflight){this.queued=e;return}this.inflight=e,T(this,"summer-action",{payload:e})}flush(){if(this.hasAttribute("busy")||this.inflight||!this.queued)return;const e=this.queued;this.queued=null,this.request(e)}build(e){const t=y(this)+"|"+(this.context?.agents??W).join(",");if(!e&&this.builtFor===t&&this.input){this.syncControls();return}this.builtFor=t;const s=this.labels(),i=this.input?.value??"",r=this.select?.value??"Googlebot";this.replaceChildren();const a=o("div","seo-robotstester__row"),l=E("seo-robotstester-url"),c=o("div","seo-robotstester__field seo-robotstester__field--url"),d=o("label","seo-robotstester__label",s.url);d.htmlFor=l;const u=o("input","seo-robotstester__input");u.id=l,u.type="text",u.placeholder=s.urlPlaceholder,u.value=i,u.addEventListener("keydown",m=>{m.key==="Enter"&&(m.preventDefault(),this.test())}),c.append(d,u);const p=E("seo-robotstester-agent"),g=o("div","seo-robotstester__field seo-robotstester__field--agent"),b=o("label","seo-robotstester__label",s.agent);b.htmlFor=p;const h=o("select","seo-robotstester__select");h.id=p;for(const m of this.context?.agents??W){const k=o("option","",m);k.value=m,h.append(k)}h.value=r,h.value!==r&&(h.value="Googlebot"),g.append(b,h);const f=o("button","seo-robotstester__button");f.type="button",f.addEventListener("click",()=>this.test()),a.append(c,g,f);const w=o("div","seo-robotstester__result");w.setAttribute("role","status");const x=o("div","seo-robotstester__notes");this.input=u,this.select=h,this.button=f,this.line=w,this.notes=x,this.append(a,w,x),this.syncControls(),this.renderResult()}syncControls(){if(!this.button||!this.input||!this.select)return;const e=this.hasAttribute("disabled"),t=this.hasAttribute("busy");this.input.readOnly=e,this.select.disabled=e,this.button.hidden=e,this.button.disabled=t,this.button.textContent=t?this.getAttribute("busy-label")||this.getAttribute("label")||"":this.getAttribute("label")||""}test(){if(this.hasAttribute("disabled")||!this.input||!this.select)return;const e=this.input.value.trim();if(!e.startsWith("/")){this.error=this.labels().urlRequired,this.result=null,this.renderResult();return}this.error="",this.request({op:"test",url:e,agent:this.select.value})}renderResult(){if(!this.line||!this.notes)return;const e=this.labels();if(this.line.className="seo-robotstester__result",this.error)this.line.classList.add("seo-robotstester__result--error"),this.line.replaceChildren(o("span","",this.error));else if(this.result){const s=this.result.allowed;this.line.classList.add(s?"seo-robotstester__result--allowed":"seo-robotstester__result--blocked"),this.line.replaceChildren(S(s?"circle-check":"triangle-alert"),o("span","",de(e,this.result)))}else this.line.replaceChildren();const t=[];(this.context?.staging||D(this,"data",{}).staging)&&t.push(o("p","seo-robotstester__note",e.stagingNote)),t.push(o("p","seo-robotstester__note",e.unsaved)),this.notes.replaceChildren(...t)}}const he="golem15-seo-robotstxt",pe=500*1024,F=10,fe=["User-agent","Allow","Disallow","Sitemap","Crawl-delay","Host","Clean-param"];function me(n,e){const t=Array.from({length:e.length+1},(s,i)=>i);for(let s=1;s<=n.length;s++){let i=t[0];t[0]=s;for(let r=1;r<=e.length;r++){const a=t[r];t[r]=Math.min(t[r]+1,t[r-1]+1,i+(n[s-1]===e[r-1]?0:1)),i=a}}return t[e.length]}function ge(n){const e=n.toLowerCase();let t="",s=3;for(const i of fe){const r=me(e,i.toLowerCase());r{const r=i+1,a=s.indexOf("#"),l=(a>=0?s.slice(0,a):s).trim();if(l==="")return;const c=l.indexOf(":");if(c<0){e.push({kind:"noColon",line:r});return}const d=l.slice(0,c).trim(),u=l.slice(c+1).trim();switch(d.toLowerCase()){case"user-agent":t=!0;return;case"allow":case"disallow":t?u!==""&&!u.startsWith("/")&&!u.startsWith("*")&&e.push({kind:"badPath",line:r}):e.push({kind:"noGroup",line:r});return;case"sitemap":e.push({kind:"ownSitemap",line:r});return;case"crawl-delay":e.push({kind:"crawlDelay",line:r});return;case"host":case"clean-param":return}const p=ge(d);e.push(p?{kind:"unknownSuggest",line:r,word:d,suggestion:p}:{kind:"unknown",line:r,word:d})}),e}function we(n){return new TextEncoder().encode(n).length}function ve(n,e){return v(n[e.kind],{line:e.line,word:e.word??"",suggestion:e.suggestion??""})}class _e extends HTMLElement{static observedAttributes=["disabled","invalid","ui-locale"];current="";control=null;panel=null;meter=null;builtFor="";get value(){return this.current}set value(e){this.current=typeof e=="string"?e:e==null?"":String(e),this.control&&this.control.value!==this.current&&(this.control.value=this.current),this.update()}connectedCallback(){this.build()}attributeChangedCallback(e){if(this.isConnected){if(e==="ui-locale"){this.build();return}this.sync()}}labels(){return L(y(this))}build(){const e=y(this);if(this.builtFor===e&&this.control){this.sync();return}this.builtFor=e,this.replaceChildren();const t=E("seo-robotstxt-lint"),s=o("textarea","seo-robotstxt__control");s.spellcheck=!1,s.setAttribute("wrap","off"),s.value=this.current,s.setAttribute("aria-describedby",t);const i=this.getAttribute("field-name");i&&s.setAttribute("aria-label",i),s.addEventListener("input",()=>{this.hasAttribute("disabled")||(this.current=s.value,this.update(),T(this,"summer-change",{value:this.current}))});const r=o("div","seo-robotstxt__lint");r.id=t,r.setAttribute("role","status");const a=o("div","seo-robotstxt__size");this.control=s,this.panel=r,this.meter=a,this.append(s,r,a),this.sync()}sync(){this.control&&(this.control.readOnly=this.hasAttribute("disabled"),this.classList.toggle("seo-robotstxt--invalid",this.hasAttribute("invalid")),this.control.setAttribute("aria-invalid",this.hasAttribute("invalid")?"true":"false"),this.update())}update(){if(!this.panel||!this.meter)return;const e=this.labels(),t=y(this),s=be(this.current),i=[];if(s.length===0){const c=o("div","seo-robotstxt__finding seo-robotstxt__finding--clean");c.append(S("circle-check"),o("span","",e.clean)),i.push(c)}else{for(const c of s.slice(0,F)){const d=o("div","seo-robotstxt__finding");d.append(S("triangle-alert"),o("span","",ve(e,c))),i.push(d)}s.length>F&&i.push(o("div","seo-robotstxt__more",v(e.moreFindings,{count:A(s.length-F,t)})))}this.panel.replaceChildren(...i);const r=we(this.current),a=new Intl.NumberFormat(t,{maximumFractionDigits:1}).format(r/1024)+" KB";this.meter.replaceChildren(o("span","",v(e.size,{size:a})));const l=r>pe;this.classList.toggle("seo-robotstxt--over",l),l&&this.meter.append(o("span","seo-robotstxt__over",e.tooLarge))}}const V="20px Arial, sans-serif",$="14px Arial, sans-serif",X=580,Y=920,ye=680,xe=30,ke=120;let N;function Ae(){if(N!==void 0)return N;try{const e=document.createElement("canvas").getContext("2d");N=e&&typeof e.measureText=="function"?e:null}catch{N=null}return N}function ze(n,e){let t=0;for(const s of n)/[ilIj.,:;'!|]/.test(s)?t+=.28:/[mwMW@]/.test(s)?t+=.85:/[A-Z]/.test(s)?t+=.67:s===" "?t+=.28:t+=.53;return t*e}function M(n,e){const t=Ae();if(t){t.font=e;const i=t.measureText(n).width;if(Number.isFinite(i)&&(i>0||n===""))return i}const s=Number.parseFloat(e)||14;return ze(n,s)}function G(n,e,t){if(M(n,e)<=t)return n;const s=Array.from(n);let i=0,r=s.length;for(;is?c="long":a=i}function R(n,e){const t=q(n);if(t.length===0)return!1;const s=q(e);for(let i=0;i+t.length<=s.length;i++){let r=!0;for(let a=0;a{const s=e.detail?.data;this.inflight=null,s&&s.op==="context"?(this.context=s,this.contextFailed=!1,this.render(),this.scheduleDuplicates(!0)):s&&s.op==="duplicates"&&(this.duplicates.set(s.locale,s),this.checking=!1,this.render()),this.flush()};onLocale=e=>{const t=e.detail;typeof t=="string"&&t!==""&&t!==this.shownLocale&&(this.shownLocale=t,this.render(),this.scheduleDuplicates(!1))};connectedCallback(){this.connected=!0,this.addEventListener("summer-result",this.onResult),window.addEventListener("summer:ml-locale",this.onLocale),this.render(),this.request({op:"context"})}disconnectedCallback(){this.connected=!1,this.removeEventListener("summer-result",this.onResult),window.removeEventListener("summer:ml-locale",this.onLocale),clearTimeout(this.timer)}attributeChangedCallback(e,t,s){if(this.connected){if(e==="busy"&&s===null){this.flush();return}if(e==="state"&&s==="error"){this.inflight?.op==="context"&&(this.contextFailed=!0),this.checking=!1,this.inflight=null,this.render();return}if(e==="fill-values"){this.render(),this.scheduleDuplicates(!1);return}this.render()}}labels(){return L(y(this))}request(e){if(this.hasAttribute("busy")||this.inflight){this.queued=e;return}this.inflight=e,T(this,"summer-action",{payload:e})}flush(){if(this.hasAttribute("busy")||this.inflight||!this.queued)return;const e=this.queued;this.queued=null,this.request(e)}fill(){return D(this,"fill-values",{})}locale(){const e=this.context?.locales??[];return e.length===0?null:e.find(t=>t.locale===this.shownLocale)??e[0]}scheduleDuplicates(e){const t=this.locale();if(!t)return;const s=this.fill(),i=z(s,"seo_title",t.locale)||z(s,"title",t.locale),r=z(s,"seo_description",t.locale)||z(s,"description",t.locale),a=JSON.stringify([t.locale,i,r]);if(a===this.lastChecked)return;clearTimeout(this.timer);const l=()=>{this.lastChecked=a,this.checking=!0,this.render(),this.request({op:"duplicates",locale:t.locale,title:i,description:r})};e?l():this.timer=setTimeout(l,Se)}render(){const e=this.labels(),t=y(this);if(!this.context&&!this.contextFailed){this.setAttribute("aria-busy","true"),this.replaceChildren(o("div","seo-serp__skeleton"));return}this.removeAttribute("aria-busy");const s=this.locale(),i=s?.locale??this.shownLocale,r=this.fill(),a=(z(r,"seo_title",i)||z(r,"title",i)).trim(),l=(z(r,"seo_description",i)||z(r,"description",i)).trim(),c=z(r,"seo_keyphrase",i).trim(),d=s?.siteName??"",u=a||Ne(s?.titleTemplate??"",{title:s?.defaultTitle??"",site:d})||s?.defaultTitle||"",p=l||s?.defaultDescription||"",g=o("div","seo-serp__header"),b=o("div","seo-serp__track");b.setAttribute("role","radiogroup"),b.setAttribute("aria-label",e.device);for(const _ of["desktop","mobile"]){const C=o("button","seo-serp__segment",e[_]);C.type="button",C.setAttribute("role","radio"),C.setAttribute("aria-checked",this.device===_?"true":"false"),C.addEventListener("click",()=>{this.device=_,this.render()}),b.append(C)}g.append(b),i&&g.append(o("span","seo-serp__language",v(e.language,{locale:i.toUpperCase()})));const h=o("div","serp-card"+(this.device==="mobile"?" serp-card--mobile":"")),f=o("div","serp-card__site");f.append(o("span","serp-card__favicon",Array.from(d||this.context?.host||"?")[0]?.toUpperCase()??"?"));const w=o("div","serp-card__names");if(w.append(o("span","serp-card__sitename",d||this.context?.host||"")),w.append(o("span","serp-card__crumbs",Fe(this.context?.host??"",s?.url??null))),f.append(w),h.append(f),h.append(o("div","serp-card__title",G(u,V,X))),p){const _=this.device==="mobile"?ye:Y;h.append(o("div","serp-card__description",G(p,$,_)))}else h.append(o("div","serp-card__description serp-card__description--missing",e.noDescription));const x=o("div","seo-serp__notes");if(this.contextFailed){const _=o("div","seo-serp__error");_.setAttribute("role","status"),_.append(S("circle-alert"),o("span","",e.contextFailed)),x.append(_)}!a&&s?.titleTemplate&&x.append(o("div","",v(e.fromTemplate,{template:s.titleTemplate}))),!l&&s?.defaultDescription&&s.descriptionSource==="content"&&x.append(o("div","",e.fromContent)),s&&s.url===null&&x.append(o("div","",e.noUrl)),(this.getAttribute("record-id")??"")!==""&&s&&x.append(o("div","",e.savedNote));const m=o("div","seo-serp__checks");if(m.setAttribute("role","status"),this.checking)m.append(o("div","seo-serp__checking",e.checking));else{const _=this.duplicates.get(i);if(_)for(const[C,J]of[[_.title,e.dupTitle],[_.description,e.dupDescription]])C.length>0&&m.append(je(C,J,e,t))}const k=[g,h,x,m];c&&s&&k.push(Pe(c,{title:u,description:p,h1:s.h1,url:s.url,bodyWords:s.bodyWords},e,t)),this.replaceChildren(...k)}}function Fe(n,e){const t=(e??"").split("?")[0].split("/").filter(Boolean).map(s=>{try{return decodeURIComponent(s)}catch{return s}});return[n,...t].filter(Boolean).join(" › ")}function je(n,e,t,s){const i=o("div","seo-serp__check seo-serp__check--warn");i.append(S("triangle-alert"));const r=o("span"),a="\0list\0",l=U(e,n.length,s,{list:a}),[c,d]=l.split(a);return r.append(document.createTextNode(c??"")),n.slice(0,j).forEach((u,p)=>{if(p>0&&r.append(document.createTextNode(", ")),u.url.startsWith("/")&&!u.url.startsWith("//")){const g=o("a","seo-serp__link",u.label);g.href=u.url,g.target="_blank",g.rel="noopener",r.append(g)}else r.append(document.createTextNode(u.label))}),n.length>j&&r.append(document.createTextNode(" "+v(t.andMore,{count:n.length-j}))),r.append(document.createTextNode(d??"")),i.append(r),i}function Pe(n,e,t,s){const i=o("div","seo-serp__keyphrase");i.append(o("div","seo-serp__keyphrase-title",v(t.keyphrase,{phrase:n})));const r=(a,l,c)=>{const d=o("div","seo-serp__check "+(a?"seo-serp__check--ok":"seo-serp__check--warn"));d.append(S(a?"circle-check":"triangle-alert"),o("span","",a?l:c)),i.append(d)};if(r(R(n,e.title),t.kpTitle,t.kpTitleNo),e.h1!==null&&r(R(n,e.h1),t.kpH1,t.kpH1No),r(R(n,e.description),t.kpDesc,t.kpDescNo),e.url!==null&&r(R(n,e.url.replace(/[-_/]+/g," ")),t.kpUrl,t.kpUrlNo),e.bodyWords!==null){const a=new Intl.NumberFormat(s).format(e.bodyWords);r(e.bodyWords>=Le,v(t.kpLength,{count:a}),v(t.kpLengthNo,{count:a}))}return i}const Me="golem15-seo-text",Oe={empty:"info",short:"triangle-alert",long:"triangle-alert",good:"circle-check"};class Ue extends HTMLElement{static observedAttributes=["options","disabled","invalid","ui-locale","locale"];current="";control=null;statusIcon=null;statusText=null;countText=null;live=null;lastStatus=null;builtFor="";get value(){return this.current}set value(e){this.current=typeof e=="string"?e:e==null?"":String(e),this.control&&this.control.value!==this.current&&(this.control.value=this.current),this.update(!1)}connectedCallback(){this.build()}attributeChangedCallback(e){if(this.isConnected){if(e==="options"||e==="ui-locale"){this.build();return}this.sync()}}measure(){return D(this,"options",{}).measure==="description"?"description":"title"}labels(){return L(y(this))}build(){const e=this.measure()+"|"+y(this);if(this.builtFor===e&&this.control){this.sync();return}this.builtFor=e,this.replaceChildren();const t=E("seo-text-counter"),i=this.measure()==="description"?o("textarea","seo-text__control seo-text__control--area"):o("input","seo-text__control");i instanceof HTMLTextAreaElement?i.rows=3:i.type="text",i.value=this.current,i.setAttribute("aria-describedby",t);const r=this.getAttribute("field-name");r&&i.setAttribute("aria-label",r),i.addEventListener("input",()=>{this.hasAttribute("disabled")||(this.current=i.value.replace(/\s+$/u,""),this.update(!0),T(this,"summer-change",{value:this.current}))});const a=o("div","seo-text__counter");a.id=t;const l=o("span","seo-text__status");this.statusIcon=o("span","seo-text__icon"),this.statusText=o("span","seo-text__status-text"),l.append(this.statusIcon,this.statusText),this.countText=o("span","seo-text__count"),a.append(l,this.countText),this.live=o("span","seo-visually-hidden"),this.live.setAttribute("aria-live","polite"),this.live.setAttribute("role","status"),this.control=i,this.lastStatus=null,this.append(i,a,this.live),this.sync()}sync(){if(!this.control)return;const e=this.hasAttribute("disabled");this.control.readOnly=e,this.classList.toggle("seo-text--invalid",this.hasAttribute("invalid")),this.control.setAttribute("aria-invalid",this.hasAttribute("invalid")?"true":"false"),this.update(!1)}update(e){if(!this.statusText||!this.countText||!this.statusIcon)return;const t=this.labels(),s=y(this),i=this.measure(),r=Ee(this.current,i),a=Be(t,r.status,i);this.statusIcon.replaceChildren(S(Oe[r.status])),this.statusText.textContent=a,this.dataset.status=r.status,this.countText.textContent=v(t.count,{chars:A(r.chars,s),px:A(r.px,s),max:A(r.maxPx,s)}),e&&this.live&&r.status!==this.lastStatus&&(this.live.textContent=a),this.lastStatus=r.status}}function Be(n,e,t){switch(e){case"empty":return t==="title"?n.emptyTitle:n.emptyDescription;case"short":return t==="title"?n.shortTitle:n.shortDescription;case"long":return t==="title"?n.longTitle:n.longDescription;default:return n.good}}const He="golem15-seo-transfer",K=60*1024,O=200;function We(n){return n.summary.refused>0?"refused":n.count>0?"ready":"nothing"}function Ge(n){const e=n.filter(s=>s.change==="refused"),t=n.filter(s=>s.change!=="refused");return[...e,...t].slice(0,O)}function qe(n,e){const t=new Blob([e],{type:"text/csv;charset=utf-8"}),s=URL.createObjectURL(t),i=document.createElement("a");i.href=s,i.download=n,i.rel="noopener",document.body.append(i),i.click(),i.remove(),setTimeout(()=>URL.revokeObjectURL(s),0)}function Ke(){return window.location.pathname.replace(/\/transfer(\/.*)?$/,"/history")}class Ze extends HTMLElement{static observedAttributes=["busy","state","ui-locale","disabled","label","busy-label"];connected=!1;pending=null;exportDataset="redirects";importDataset="redirects";fileName="";csv="";fileError="";importError="";exportError="";applyError="";preview=null;applied=!1;onResult=e=>{const s=e.detail?.data;if(this.pending=null,!s){this.render();return}s.error!==void 0?s.op==="export"?this.exportError=s.message:s.op==="preview"?(this.importError=s.message,this.preview=null):this.applyError=s.message:s.op==="export"?(this.exportError="",qe(s.filename,s.csv)):s.op==="preview"?(this.importError="",this.applyError="",this.applied=!1,this.preview=s):s.op==="apply"&&(this.preview=null,this.applyError="",this.applied=!0),this.render()};connectedCallback(){this.connected=!0,this.addEventListener("summer-result",this.onResult),this.render()}disconnectedCallback(){this.connected=!1,this.removeEventListener("summer-result",this.onResult)}attributeChangedCallback(e,t,s){this.connected&&(e==="state"&&s==="error"&&(this.pending=null),e==="busy"&&s===null&&(this.pending=null),this.render())}labels(){return L(y(this))}request(e){this.hasAttribute("disabled")||this.hasAttribute("busy")||this.pending||(this.pending=e.op,T(this,"summer-action",{payload:e}),this.render())}busyLabel(e,t){return this.pending===e&&this.getAttribute("busy-label")||t}datasetSelect(e,t,s){const i=o("div","seo-transfer__field"),r=E("seo-transfer-dataset"),a=o("label","seo-transfer__label",e.dataset);a.htmlFor=r;const l=o("select","seo-transfer__select");l.id=r;const c=[["redirects",e.dsRedirects],["rules",e.dsRules],["records",e.dsRecords]];for(const[d,u]of c){const p=o("option","",u);p.value=d,l.append(p)}return l.value=t,l.disabled=this.hasAttribute("disabled"),l.addEventListener("change",()=>s(l.value)),i.append(a,l),i}button(e,t,s){const i=o("button","seo-transfer__button seo-transfer__button--"+t,e);return i.type="button",i.addEventListener("click",s),i}message(e,t){const s=o("p",t,e);return s.setAttribute("role","status"),s}async chooseFile(e){const t=e.files?.[0];if(this.preview=null,this.applied=!1,this.importError="",this.applyError="",this.csv="",!t){this.fileName="",this.fileError="",this.render();return}if(this.fileName=t.name,t.size>K){this.fileError=v(this.labels().fileTooLarge,{limit:A(K/1024,y(this))+" KB"}),this.render();return}try{this.csv=await t.text(),this.fileError=""}catch{this.fileError=this.labels().readFailed}this.render()}render(){const e=this.labels(),t=y(this),s=this.hasAttribute("disabled"),i=this.hasAttribute("busy")||this.pending!==null,r=this.getAttribute("label")||e.chooseFile,a=o("section","seo-transfer__section");a.append(o("h3","seo-transfer__heading",e.exportHeading));const l=o("div","seo-transfer__row");l.append(this.datasetSelect(e,this.exportDataset,f=>this.exportDataset=f));const c=this.button(this.busyLabel("export",e.download),"outline",()=>this.request({op:"export",dataset:this.exportDataset}));c.disabled=i,c.hidden=s,l.append(c),a.append(l),this.exportError&&a.append(this.message(this.exportError,"seo-transfer__error"));const d=o("section","seo-transfer__section seo-transfer__section--import");d.append(o("h3","seo-transfer__heading",e.importHeading));const u=o("div","seo-transfer__row");u.append(this.datasetSelect(e,this.importDataset,f=>{this.importDataset=f,this.preview=null,this.render()}));const p=o("input","seo-transfer__file");p.type="file",p.accept=".csv,text/csv",p.hidden=!0,p.addEventListener("change",()=>{this.chooseFile(p)});const g=this.button(e.chooseFile,"outline",()=>p.click());g.hidden=s,g.disabled=i;const b=o("span",this.fileName?"seo-transfer__filename":"seo-transfer__filename seo-transfer__filename--none",this.fileName||e.noFile),h=this.button(this.busyLabel("preview",r),"outline",()=>this.request({op:"preview",dataset:this.importDataset,csv:this.csv}));if(h.disabled=i||this.csv===""||this.fileError!=="",h.hidden=s,u.append(p,g,b,h),d.append(u),(this.fileError||this.importError)&&d.append(this.message(this.fileError||this.importError,"seo-transfer__error")),this.applied){const f=o("p","seo-transfer__done"),w=o("a","seo-transfer__link",e.openHistory);w.href=Ke(),f.append(w),d.append(f)}this.preview&&d.append(this.renderPreview(e,t,i,s)),this.replaceChildren(a,d)}renderPreview(e,t,s,i){const r=this.preview,a=o("div","seo-transfer__preview"),l=r.summary;a.append(this.message(v(e.summary,{new:A(l.new,t),changed:A(l.changed,t),unchanged:A(l.unchanged,t),refused:A(l.refused,t)}),"seo-transfer__summary"));const c=o("div","summer-table-wrap seo-transfer__table-wrap"),d=o("table","summer-table seo-transfer__table"),u=o("tr");for(const m of[e.colLine,e.colChange,e.colAddress,e.colField,e.colBefore,e.colAfter,e.colNote])u.append(o("th","",m));const p=o("thead");p.append(u);const g=o("tbody"),b={new:e.bNew,changed:e.bChanged,unchanged:e.bUnchanged,refused:e.bRefused};for(const m of Ge(r.rows)){const k=o("tr","seo-transfer__tr--"+m.change);k.append(o("td","seo-transfer__num",A(m.line,t)));const _=o("td");_.append(o("span","seo-transfer__badge seo-transfer__badge--"+m.change,b[m.change])),m.warning&&_.append(o("span","seo-transfer__badge seo-transfer__badge--warning",e.bWarning)),k.append(_),k.append(o("td","seo-transfer__mono",m.address)),k.append(o("td","",m.field)),k.append(o("td","",m.before)),k.append(o("td","",m.after)),k.append(o("td","",m.note)),g.append(k)}d.append(p,g),c.append(d),a.append(c),(r.total>O||r.rows.length>O)&&a.append(o("p","seo-transfer__note",v(e.rowCap,{count:A(Math.max(r.total,r.rows.length),t)})));const h=We(r);h==="refused"?a.append(this.message(e.refusedBlocks,"seo-transfer__error")):h==="nothing"&&a.append(this.message(e.nothing,"seo-transfer__note")),this.applyError&&a.append(this.message(this.applyError,"seo-transfer__error"));const f=o("div","seo-transfer__actions"),w=this.button(e.discard,"ghost",()=>{this.preview=null,this.applyError="",this.render()});w.disabled=s;const x=this.button(this.busyLabel("apply",U(e.applyCount,r.count,t)),"primary",()=>this.request({op:"apply",token:r.token}));return x.disabled=s||h!=="ready",f.append(w,x),f.hidden=i,a.append(f),a}}const Ve=[[Me,Ue],[oe,le],[Ce,Ie],[se,ie],[he,_e],[ce,ue],[He,Ze]];for(const[n,e]of Ve)customElements.get(n)||customElements.define(n,e);export{se as IMAGE_TAG,ce as ROBOTSTESTER_TAG,he as ROBOTSTXT_TAG,oe as ROBOTS_TAG,Ce as SERP_TAG,ie as SeoImageElement,le as SeoRobotsElement,ue as SeoRobotsTesterElement,_e as SeoRobotsTxtElement,Ie as SeoSerpElement,Ue as SeoTextElement,Ze as SeoTransferElement,Me as TEXT_TAG,He as TRANSFER_TAG}; diff --git a/assets/src/elements/index.ts b/assets/src/elements/index.ts index 25162b8..b565f4e 100644 --- a/assets/src/elements/index.ts +++ b/assets/src/elements/index.ts @@ -5,6 +5,7 @@ import { ROBOTSTESTER_TAG, SeoRobotsTesterElement } from './robotstester' import { ROBOTSTXT_TAG, SeoRobotsTxtElement } from './robotstxt' import { SERP_TAG, SeoSerpElement } from './serp' import { SeoTextElement, TEXT_TAG } from './text' +import { SeoTransferElement, TRANSFER_TAG } from './transfer' // The admin SPA loads this script once on every admin page (the SEO field // types list it in Scripts); defining a tag twice would throw, so a second @@ -16,6 +17,7 @@ const elements: Array<[string, CustomElementConstructor]> = [ [IMAGE_TAG, SeoImageElement], [ROBOTSTXT_TAG, SeoRobotsTxtElement], [ROBOTSTESTER_TAG, SeoRobotsTesterElement], + [TRANSFER_TAG, SeoTransferElement], ] for (const [tag, ctor] of elements) { if (!customElements.get(tag)) { @@ -35,5 +37,7 @@ export { SeoRobotsTxtElement, SeoSerpElement, SeoTextElement, + SeoTransferElement, TEXT_TAG, + TRANSFER_TAG, } diff --git a/assets/src/elements/labels.ts b/assets/src/elements/labels.ts index 7d612ce..7494cc1 100644 --- a/assets/src/elements/labels.ts +++ b/assets/src/elements/labels.ts @@ -1,6 +1,5 @@ -// Element copy of the SEO field elements (UI-SPEC copywriting E1, E2, E3, -// E4 and E5, plus E7 for the social image), chosen by the ui-locale -// attribute. Plural +// Element copy of the SEO field elements (UI-SPEC copywriting E1 to E6, +// plus E7 for the social image), chosen by the ui-locale attribute. Plural // texts are CLDR maps; placeholders use :name. /** A CLDR plural map: one/other in English, one/few/many/other in Polish. */ @@ -101,6 +100,37 @@ export interface Labels { unsaved: string stagingNote: string urlRequired: string + // E6 + exportHeading: string + importHeading: string + dataset: string + dsRedirects: string + dsRules: string + dsRecords: string + download: string + chooseFile: string + noFile: string + summary: string + bNew: string + bChanged: string + bUnchanged: string + bRefused: string + bWarning: string + colLine: string + colChange: string + colAddress: string + colField: string + colBefore: string + colAfter: string + colNote: string + rowCap: string + refusedBlocks: string + nothing: string + discard: string + fileTooLarge: string + readFailed: string + openHistory: string + applyCount: Plural } const en: Labels = { @@ -187,6 +217,36 @@ const en: Labels = { unsaved: 'Tests the text in the editor and the AI bot switches, including unsaved changes.', stagingNote: 'On this environment every address is blocked. The result shows the production behaviour.', urlRequired: 'Enter an address starting with /.', + exportHeading: 'Export', + importHeading: 'Import', + dataset: 'Data', + dsRedirects: 'Redirects', + dsRules: 'URL rules', + dsRecords: 'SEO of records', + download: 'Download CSV', + chooseFile: 'Choose CSV file', + noFile: 'No file chosen', + summary: ':new new, :changed changed, :unchanged unchanged, :refused refused', + bNew: 'New', + bChanged: 'Changed', + bUnchanged: 'Unchanged', + bRefused: 'Refused', + bWarning: 'Warning', + colLine: 'Line', + colChange: 'Change', + colAddress: 'Address', + colField: 'Field', + colBefore: 'Before', + colAfter: 'After', + colNote: 'Note', + rowCap: 'Showing the first 200 of :count rows. All rows are applied.', + refusedBlocks: 'Fix the refused rows in the file and preview it again. Nothing is applied while a row is refused.', + nothing: 'The file changes nothing.', + discard: 'Discard preview', + fileTooLarge: 'This file is larger than :limit. Split it into smaller files, or import it through MCP.', + readFailed: 'The file could not be read.', + openHistory: 'Open History', + applyCount: { one: 'Apply :count change', other: 'Apply :count changes' }, } const pl: Labels = { @@ -283,6 +343,36 @@ const pl: Labels = { unsaved: 'Sprawdza tekst w edytorze i przełączniki botów AI, także niezapisane zmiany.', stagingNote: 'W tym środowisku każdy adres jest zablokowany. Wynik pokazuje zachowanie na produkcji.', urlRequired: 'Wpisz adres zaczynający się od /.', + exportHeading: 'Eksport', + importHeading: 'Import', + dataset: 'Dane', + dsRedirects: 'Przekierowania', + dsRules: 'Reguły adresów', + dsRecords: 'SEO rekordów', + download: 'Pobierz CSV', + chooseFile: 'Wybierz plik CSV', + noFile: 'Nie wybrano pliku', + summary: 'Nowe: :new, zmienione: :changed, bez zmian: :unchanged, odrzucone: :refused', + bNew: 'Nowy', + bChanged: 'Zmieniony', + bUnchanged: 'Bez zmian', + bRefused: 'Odrzucony', + bWarning: 'Ostrzeżenie', + colLine: 'Wiersz', + colChange: 'Zmiana', + colAddress: 'Adres', + colField: 'Pole', + colBefore: 'Przed', + colAfter: 'Po', + colNote: 'Uwagi', + rowCap: 'Pokazano pierwsze 200 z :count wierszy. Zastosowane zostaną wszystkie.', + refusedBlocks: 'Popraw odrzucone wiersze w pliku i ponownie wyświetl podgląd. Nic nie zostanie zastosowane, dopóki jakiś wiersz jest odrzucony.', + nothing: 'Plik niczego nie zmienia.', + discard: 'Odrzuć podgląd', + fileTooLarge: 'Ten plik jest większy niż :limit. Podziel go na mniejsze pliki lub zaimportuj przez MCP.', + readFailed: 'Nie udało się odczytać pliku.', + openHistory: 'Otwórz Historię zmian', + applyCount: { one: 'Zastosuj :count zmianę', few: 'Zastosuj :count zmiany', many: 'Zastosuj :count zmian', other: 'Zastosuj :count zmiany' }, } const dictionaries: Record = { en, pl } diff --git a/assets/src/elements/styles.css b/assets/src/elements/styles.css index b9aa097..7c94a9c 100644 --- a/assets/src/elements/styles.css +++ b/assets/src/elements/styles.css @@ -1,4 +1,4 @@ -/* SEO field elements (UI-SPEC C4, C5, C6, C10, C14). Light DOM, every rule +/* SEO field elements (UI-SPEC C4, C5, C6, C10, C13, C14). Light DOM, every rule scoped under its element tag, colours from the public --c-* variables only (except the E-1 mimic palette inside golem15-seo-serp .serp-card). */ @@ -7,7 +7,8 @@ golem15-seo-robots, golem15-seo-serp, golem15-seo-image, golem15-seo-robotstxt, -golem15-seo-robotstester { +golem15-seo-robotstester, +golem15-seo-transfer { display: block; color: var(--c-text); font-size: 14px; @@ -623,6 +624,190 @@ golem15-seo-robotstester .seo-robotstester__note { font-size: 13px; } +/* E6 golem15-seo-transfer */ +golem15-seo-transfer .seo-transfer__section { + display: flex; + flex-direction: column; + gap: 12px; +} + +golem15-seo-transfer .seo-transfer__section--import { + margin-top: 24px; + padding-top: 24px; + border-top: 1px solid var(--c-border); +} + +golem15-seo-transfer .seo-transfer__heading { + margin: 0; + color: var(--c-text); + font-size: 14px; + font-weight: 600; +} + +golem15-seo-transfer .seo-transfer__row { + display: flex; + flex-wrap: wrap; + align-items: flex-end; + gap: 8px; +} + +golem15-seo-transfer .seo-transfer__field { + display: flex; + flex-direction: column; + gap: 4px; +} + +golem15-seo-transfer .seo-transfer__label { + color: var(--c-muted); + font-size: 13px; +} + +golem15-seo-transfer .seo-transfer__select { + box-sizing: border-box; + min-width: 240px; + height: 44px; + padding: 0 14px; + border: 1px solid var(--c-border-strong); + border-radius: 10px; + background: var(--c-surface); + color: var(--c-text); + font: inherit; + font-size: 14px; + transition: border-color 150ms, box-shadow 150ms; +} + +golem15-seo-transfer .seo-transfer__select:focus { + outline: none; + border-color: var(--c-primary); + box-shadow: 0 0 0 3px var(--c-ring); +} + +golem15-seo-transfer .seo-transfer__button { + height: 42px; + padding: 0 16px; + border: 1px solid var(--c-border-strong); + border-radius: 10px; + background: var(--c-surface); + color: var(--c-text); + font: inherit; + font-size: 14px; + cursor: pointer; +} + +golem15-seo-transfer .seo-transfer__button--ghost { + border-color: transparent; + background: transparent; +} + +golem15-seo-transfer .seo-transfer__button--primary { + border-color: var(--c-primary); + background: var(--c-primary); + color: var(--c-on-primary, var(--c-surface)); +} + +golem15-seo-transfer .seo-transfer__button:focus-visible { + outline: none; + box-shadow: 0 0 0 3px var(--c-ring); +} + +golem15-seo-transfer .seo-transfer__button:disabled { + cursor: default; + opacity: 0.6; +} + +golem15-seo-transfer .seo-transfer__filename { + align-self: center; + color: var(--c-text); + font-size: 14px; + overflow-wrap: anywhere; +} + +golem15-seo-transfer .seo-transfer__filename--none { + color: var(--c-muted); +} + +golem15-seo-transfer .seo-transfer__error { + margin: 0; + color: var(--c-danger); + font-size: 14px; +} + +golem15-seo-transfer .seo-transfer__note, +golem15-seo-transfer .seo-transfer__done { + margin: 0; + color: var(--c-muted); + font-size: 13px; +} + +golem15-seo-transfer .seo-transfer__link { + color: var(--c-primary); +} + +golem15-seo-transfer .seo-transfer__preview { + display: flex; + flex-direction: column; + gap: 12px; +} + +golem15-seo-transfer .seo-transfer__summary { + margin: 0; + font-size: 14px; + font-weight: 600; +} + +golem15-seo-transfer .seo-transfer__table-wrap { + max-height: 480px; + overflow: auto; +} + +golem15-seo-transfer .seo-transfer__num { + font-variant-numeric: tabular-nums; + text-align: right; +} + +golem15-seo-transfer .seo-transfer__mono { + font-family: 'DM Mono', ui-monospace, monospace; + overflow-wrap: anywhere; +} + +golem15-seo-transfer .seo-transfer__badge { + display: inline-flex; + align-items: center; + height: 24px; + margin-right: 4px; + padding: 0 10px; + border-radius: 999px; + font-size: 12px; + white-space: nowrap; +} + +golem15-seo-transfer .seo-transfer__badge--new { + background: var(--c-ok-bg); + color: var(--c-ok-text); +} + +golem15-seo-transfer .seo-transfer__badge--changed, +golem15-seo-transfer .seo-transfer__badge--warning { + background: var(--c-sel); + color: var(--c-text); +} + +golem15-seo-transfer .seo-transfer__badge--unchanged { + background: var(--c-subtle); + color: var(--c-muted); +} + +golem15-seo-transfer .seo-transfer__badge--refused { + background: var(--c-danger-soft); + color: var(--c-danger); +} + +golem15-seo-transfer .seo-transfer__actions { + display: flex; + justify-content: flex-end; + gap: 10px; +} + @media (max-width: 600px) { golem15-seo-robots .seo-robots__panel { grid-template-columns: 1fr; @@ -637,6 +822,7 @@ golem15-seo-robotstester .seo-robotstester__note { golem15-seo-robotstxt .seo-robotstxt__control, golem15-seo-robotstester .seo-robotstester__input, golem15-seo-robotstester .seo-robotstester__select, + golem15-seo-transfer .seo-transfer__select, golem15-seo-text .seo-text__control, golem15-seo-robots .seo-robots__segment, golem15-seo-serp .seo-serp__segment { diff --git a/assets/src/elements/transfer.ts b/assets/src/elements/transfer.ts new file mode 100644 index 0000000..755e5a2 --- /dev/null +++ b/assets/src/elements/transfer.ts @@ -0,0 +1,382 @@ +import { el, emit, fmt, labelsFor, num, plural, uid, uiLocaleOf, type Labels } from './labels' + +export const TRANSFER_TAG = 'golem15-seo-transfer' + +/** The largest file the screen previews (UI-SPEC Q-8 assumption). */ +export const IMPORT_LIMIT = 60 * 1024 + +/** The preview rows the screen shows. */ +export const ROW_CAP = 200 + +type Dataset = 'redirects' | 'rules' | 'records' + +/** One preview row of the seo_transfer action. */ +export interface PreviewRow { + line: number + change: 'new' | 'changed' | 'unchanged' | 'refused' + address: string + field: string + before: string + after: string + note: string + warning?: boolean +} + +/** The preview answer. */ +export interface PreviewData { + op: 'preview' + token: string + dataset: Dataset + summary: { new: number; changed: number; unchanged: number; refused: number; warning: number } + rows: PreviewRow[] + total: number + count: number +} + +/** Every answer of the action: an op's data or an op's refusal. */ +type Answer = + | { op: 'export'; filename: string; csv: string; error?: undefined } + | (PreviewData & { error?: undefined }) + | { op: 'apply'; applied: boolean; count: number; batch_id: string; error?: undefined } + | { op: 'export' | 'preview' | 'apply'; error: string; message: string } + +type Payload = + | { op: 'export'; dataset: Dataset } + | { op: 'preview'; dataset: Dataset; csv: string } + | { op: 'apply'; token: string } + +/** The apply button's state: why it is disabled, if it is. */ +export function applyState(p: PreviewData): 'refused' | 'nothing' | 'ready' { + if (p.summary.refused > 0) { + return 'refused' + } + return p.count > 0 ? 'ready' : 'nothing' +} + +/** The rows the table shows: refused first, at most ROW_CAP. */ +export function shownRows(rows: PreviewRow[]): PreviewRow[] { + const refused = rows.filter((r) => r.change === 'refused') + const others = rows.filter((r) => r.change !== 'refused') + return [...refused, ...others].slice(0, ROW_CAP) +} + +/** Saves text as a file through a Blob link. */ +function download(filename: string, text: string): void { + const blob = new Blob([text], { type: 'text/csv;charset=utf-8' }) + const url = URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = filename + a.rel = 'noopener' + document.body.append(a) + a.click() + a.remove() + setTimeout(() => URL.revokeObjectURL(url), 0) +} + +/** The History list of the admin, next to this screen's route. */ +function historyHref(): string { + const path = window.location.pathname + return path.replace(/\/transfer(\/.*)?$/, '/history') +} + +/** + * E6 golem15-seo-transfer (UI-SPEC C13): an export section (dataset and + * Download CSV) and an import section (dataset, the file, Preview import, + * the preview table and Apply). Every server call is the seo_transfer + * action through the widget; the file is read in the browser and refused + * over 60 KB before the action is called. Apply is enabled only after a + * fresh preview with no refused row and at least one change; a stale + * preview keeps the table and shows the server's message above the + * actions. + */ +export class SeoTransferElement extends HTMLElement { + static observedAttributes = ['busy', 'state', 'ui-locale', 'disabled', 'label', 'busy-label'] + + private connected = false + private pending: Payload['op'] | null = null + private exportDataset: Dataset = 'redirects' + private importDataset: Dataset = 'redirects' + private fileName = '' + private csv = '' + private fileError = '' + private importError = '' + private exportError = '' + private applyError = '' + private preview: PreviewData | null = null + private applied = false + + private readonly onResult = (ev: Event): void => { + const detail = (ev as CustomEvent).detail as { data?: unknown } | undefined + const data = detail?.data as Answer | undefined + this.pending = null + if (!data) { + this.render() + return + } + if (data.error !== undefined) { + if (data.op === 'export') { + this.exportError = data.message + } else if (data.op === 'preview') { + this.importError = data.message + this.preview = null + } else { + this.applyError = data.message + } + } else if (data.op === 'export') { + this.exportError = '' + download(data.filename, data.csv) + } else if (data.op === 'preview') { + this.importError = '' + this.applyError = '' + this.applied = false + this.preview = data + } else if (data.op === 'apply') { + this.preview = null + this.applyError = '' + this.applied = true + } + this.render() + } + + connectedCallback(): void { + this.connected = true + this.addEventListener('summer-result', this.onResult) + this.render() + } + + disconnectedCallback(): void { + this.connected = false + this.removeEventListener('summer-result', this.onResult) + } + + attributeChangedCallback(name: string, _old: string | null, value: string | null): void { + if (!this.connected) { + return + } + if (name === 'state' && value === 'error') { + this.pending = null + } + if (name === 'busy' && value === null) { + this.pending = null + } + this.render() + } + + private labels(): Labels { + return labelsFor(uiLocaleOf(this)) + } + + private request(payload: Payload): void { + if (this.hasAttribute('disabled') || this.hasAttribute('busy') || this.pending) { + return + } + this.pending = payload.op + emit(this, 'summer-action', { payload }) + this.render() + } + + private busyLabel(op: Payload['op'], idle: string): string { + if (this.pending === op) { + return this.getAttribute('busy-label') || idle + } + return idle + } + + private datasetSelect(labels: Labels, value: Dataset, onChange: (v: Dataset) => void): HTMLDivElement { + const wrap = el('div', 'seo-transfer__field') + const id = uid('seo-transfer-dataset') + const label = el('label', 'seo-transfer__label', labels.dataset) + label.htmlFor = id + const select = el('select', 'seo-transfer__select') + select.id = id + const options: Array<[Dataset, string]> = [ + ['redirects', labels.dsRedirects], + ['rules', labels.dsRules], + ['records', labels.dsRecords], + ] + for (const [v, text] of options) { + const option = el('option', '', text) + option.value = v + select.append(option) + } + select.value = value + select.disabled = this.hasAttribute('disabled') + select.addEventListener('change', () => onChange(select.value as Dataset)) + wrap.append(label, select) + return wrap + } + + private button(text: string, kind: 'outline' | 'ghost' | 'primary', onClick: () => void): HTMLButtonElement { + const b = el('button', 'seo-transfer__button seo-transfer__button--' + kind, text) + b.type = 'button' + b.addEventListener('click', onClick) + return b + } + + private message(text: string, className: string): HTMLParagraphElement { + const p = el('p', className, text) + p.setAttribute('role', 'status') + return p + } + + private async chooseFile(input: HTMLInputElement): Promise { + const file = input.files?.[0] + this.preview = null + this.applied = false + this.importError = '' + this.applyError = '' + this.csv = '' + if (!file) { + this.fileName = '' + this.fileError = '' + this.render() + return + } + this.fileName = file.name + if (file.size > IMPORT_LIMIT) { + this.fileError = fmt(this.labels().fileTooLarge, { limit: num(IMPORT_LIMIT / 1024, uiLocaleOf(this)) + ' KB' }) + this.render() + return + } + try { + this.csv = await file.text() + this.fileError = '' + } catch { + this.fileError = this.labels().readFailed + } + this.render() + } + + private render(): void { + const labels = this.labels() + const locale = uiLocaleOf(this) + const disabled = this.hasAttribute('disabled') + const busy = this.hasAttribute('busy') || this.pending !== null + const actionLabel = this.getAttribute('label') || labels.chooseFile + + // Export. + const exportSection = el('section', 'seo-transfer__section') + exportSection.append(el('h3', 'seo-transfer__heading', labels.exportHeading)) + const exportRow = el('div', 'seo-transfer__row') + exportRow.append(this.datasetSelect(labels, this.exportDataset, (v) => (this.exportDataset = v))) + const downloadButton = this.button(this.busyLabel('export', labels.download), 'outline', () => this.request({ op: 'export', dataset: this.exportDataset })) + downloadButton.disabled = busy + downloadButton.hidden = disabled + exportRow.append(downloadButton) + exportSection.append(exportRow) + if (this.exportError) { + exportSection.append(this.message(this.exportError, 'seo-transfer__error')) + } + + // Import. + const importSection = el('section', 'seo-transfer__section seo-transfer__section--import') + importSection.append(el('h3', 'seo-transfer__heading', labels.importHeading)) + const importRow = el('div', 'seo-transfer__row') + importRow.append( + this.datasetSelect(labels, this.importDataset, (v) => { + this.importDataset = v + this.preview = null + this.render() + }), + ) + const input = el('input', 'seo-transfer__file') + input.type = 'file' + input.accept = '.csv,text/csv' + input.hidden = true + input.addEventListener('change', () => void this.chooseFile(input)) + const choose = this.button(labels.chooseFile, 'outline', () => input.click()) + choose.hidden = disabled + choose.disabled = busy + const name = el('span', this.fileName ? 'seo-transfer__filename' : 'seo-transfer__filename seo-transfer__filename--none', this.fileName || labels.noFile) + const previewButton = this.button(this.busyLabel('preview', actionLabel), 'outline', () => + this.request({ op: 'preview', dataset: this.importDataset, csv: this.csv }), + ) + previewButton.disabled = busy || this.csv === '' || this.fileError !== '' + previewButton.hidden = disabled + importRow.append(input, choose, name, previewButton) + importSection.append(importRow) + if (this.fileError || this.importError) { + importSection.append(this.message(this.fileError || this.importError, 'seo-transfer__error')) + } + if (this.applied) { + const done = el('p', 'seo-transfer__done') + const link = el('a', 'seo-transfer__link', labels.openHistory) + link.href = historyHref() + done.append(link) + importSection.append(done) + } + if (this.preview) { + importSection.append(this.renderPreview(labels, locale, busy, disabled)) + } + this.replaceChildren(exportSection, importSection) + } + + private renderPreview(labels: Labels, locale: string, busy: boolean, disabled: boolean): HTMLDivElement { + const p = this.preview as PreviewData + const box = el('div', 'seo-transfer__preview') + const s = p.summary + box.append( + this.message( + fmt(labels.summary, { new: num(s.new, locale), changed: num(s.changed, locale), unchanged: num(s.unchanged, locale), refused: num(s.refused, locale) }), + 'seo-transfer__summary', + ), + ) + const wrap = el('div', 'summer-table-wrap seo-transfer__table-wrap') + const table = el('table', 'summer-table seo-transfer__table') + const head = el('tr') + for (const text of [labels.colLine, labels.colChange, labels.colAddress, labels.colField, labels.colBefore, labels.colAfter, labels.colNote]) { + head.append(el('th', '', text)) + } + const thead = el('thead') + thead.append(head) + const tbody = el('tbody') + const badge: Record = { new: labels.bNew, changed: labels.bChanged, unchanged: labels.bUnchanged, refused: labels.bRefused } + for (const row of shownRows(p.rows)) { + const tr = el('tr', 'seo-transfer__tr--' + row.change) + tr.append(el('td', 'seo-transfer__num', num(row.line, locale))) + const change = el('td') + change.append(el('span', 'seo-transfer__badge seo-transfer__badge--' + row.change, badge[row.change])) + if (row.warning) { + change.append(el('span', 'seo-transfer__badge seo-transfer__badge--warning', labels.bWarning)) + } + tr.append(change) + tr.append(el('td', 'seo-transfer__mono', row.address)) + tr.append(el('td', '', row.field)) + tr.append(el('td', '', row.before)) + tr.append(el('td', '', row.after)) + tr.append(el('td', '', row.note)) + tbody.append(tr) + } + table.append(thead, tbody) + wrap.append(table) + box.append(wrap) + if (p.total > ROW_CAP || p.rows.length > ROW_CAP) { + box.append(el('p', 'seo-transfer__note', fmt(labels.rowCap, { count: num(Math.max(p.total, p.rows.length), locale) }))) + } + const state = applyState(p) + if (state === 'refused') { + box.append(this.message(labels.refusedBlocks, 'seo-transfer__error')) + } else if (state === 'nothing') { + box.append(this.message(labels.nothing, 'seo-transfer__note')) + } + if (this.applyError) { + box.append(this.message(this.applyError, 'seo-transfer__error')) + } + const actions = el('div', 'seo-transfer__actions') + const discard = this.button(labels.discard, 'ghost', () => { + this.preview = null + this.applyError = '' + this.render() + }) + discard.disabled = busy + const apply = this.button(this.busyLabel('apply', plural(labels.applyCount, p.count, locale)), 'primary', () => + this.request({ op: 'apply', token: p.token }), + ) + apply.disabled = busy || state !== 'ready' + actions.append(discard, apply) + actions.hidden = disabled + box.append(actions) + return box + } +}