@wcag-checkr/ci 1.0.10 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/ErrorBoundary-CGD9kz0E.js +594 -0
- package/dist/assets/{ai-usage-log-CYuyLsU4.js → ai-usage-log-C9Opn0x0.js} +1 -1
- package/dist/assets/{content-script.ts-CsBWyYDh.js → content-script.ts-DxtHl1_w.js} +1 -1
- package/dist/assets/{content-script.ts-loader-H5dU9ywW.js → content-script.ts-loader-CdztdNRK.js} +1 -1
- package/dist/assets/{copy-ai-fixer-prompt-CPoiD1Jp.js → copy-ai-fixer-prompt-DYAVFhB3.js} +2 -2
- package/dist/assets/{crash-reporter-KD5f4utB.js → crash-reporter-DAZnNefT.js} +1 -1
- package/dist/assets/{devtools-panel-DnGlvUWo.js → devtools-panel-BCDqUAiS.js} +1 -1
- package/dist/assets/{diff-Dc5xUcCG.js → diff-CECbooHD.js} +1 -1
- package/dist/assets/{options-BXcWL5FW.js → options-Bm-ovz5q.js} +1 -1
- package/dist/assets/{parallel-tab-flow-BmRaOzXY.js → parallel-tab-flow-f0rYsTcq.js} +1 -1
- package/dist/assets/{rubric-config-client-VUdHdJxq.js → rubric-config-client-CEBmuFV7.js} +1 -1
- package/dist/assets/{scheduled-audit-runner-D_ZjzHg0.js → scheduled-audit-runner-EQBltZPz.js} +15 -15
- package/dist/assets/{service-worker.ts-CnfhezBo.js → service-worker.ts-BcgJ4Lq9.js} +2 -2
- package/dist/assets/side-panel-Bgh2EbIa.js +4 -0
- package/dist/assets/{site-report-renderer-Cy-J72oi.js → site-report-renderer-C3U9cFY3.js} +1 -1
- package/dist/assets/{styles-Bik0yRU1.js → styles-DlQee1FU.js} +1 -1
- package/dist/assets/{tier-config-client-CT-oOti3.js → tier-config-client-CGmK5CXV.js} +1 -1
- package/dist/devtools/panel.html +7 -7
- package/dist/manifest.json +5 -5
- package/dist/options/options.html +5 -5
- package/dist/service-worker-loader.js +1 -1
- package/dist/side-panel/side-panel.html +10 -10
- package/package.json +1 -1
- package/dist/assets/ErrorBoundary-Dio2MH5c.js +0 -594
- package/dist/assets/side-panel-D3xkdQNu.js +0 -4
package/dist/assets/{scheduled-audit-runner-D_ZjzHg0.js → scheduled-audit-runner-EQBltZPz.js}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/parallel-tab-flow-
|
|
2
|
-
var es=Object.defineProperty;var ts=(e,t,a)=>t in e?es(e,t,{enumerable:!0,configurable:!0,writable:!0,value:a}):e[t]=a;var _t=(e,t,a)=>ts(e,typeof t!="symbol"?t+"":t,a);import{_ as bn}from"./preload-helper-D7HrI6pR.js";import{c as Z,f as ce,o as Ce,k as B,r as Ae,h as $t,j as Xo}from"./crash-reporter-KD5f4utB.js";import{b as le,m as qn,d as as,T as ns,e as zn,c as yn,a as pa,i as Kn}from"./diff-Dc5xUcCG.js";import{i as os,j as De,J as rs,T as wn,x as Mt,v as fa,z as ye,D as is,e as Qa,K as ss,L as cs,M as ls,N as ds,O as us,P as hs,W as ps,b as fs,g as gs,u as ms,r as bs,f as ys}from"./ai-usage-log-CYuyLsU4.js";const Za=(e,t)=>t.some(a=>e instanceof a);let Yn,Jn;function ws(){return Yn||(Yn=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction])}function vs(){return Jn||(Jn=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey])}const en=new WeakMap,La=new WeakMap,ga=new WeakMap;function As(e){const t=new Promise((a,n)=>{const o=()=>{e.removeEventListener("success",r),e.removeEventListener("error",i)},r=()=>{a(Qe(e.result)),o()},i=()=>{n(e.error),o()};e.addEventListener("success",r),e.addEventListener("error",i)});return ga.set(t,e),t}function ks(e){if(en.has(e))return;const t=new Promise((a,n)=>{const o=()=>{e.removeEventListener("complete",r),e.removeEventListener("error",i),e.removeEventListener("abort",i)},r=()=>{a(),o()},i=()=>{n(e.error||new DOMException("AbortError","AbortError")),o()};e.addEventListener("complete",r),e.addEventListener("error",i),e.addEventListener("abort",i)});en.set(e,t)}let tn={get(e,t,a){if(e instanceof IDBTransaction){if(t==="done")return en.get(e);if(t==="store")return a.objectStoreNames[1]?void 0:a.objectStore(a.objectStoreNames[0])}return Qe(e[t])},set(e,t,a){return e[t]=a,!0},has(e,t){return e instanceof IDBTransaction&&(t==="done"||t==="store")?!0:t in e}};function Qo(e){tn=e(tn)}function xs(e){return vs().includes(e)?function(...t){return e.apply(an(this),t),Qe(this.request)}:function(...t){return Qe(e.apply(an(this),t))}}function Is(e){return typeof e=="function"?xs(e):(e instanceof IDBTransaction&&ks(e),Za(e,ws())?new Proxy(e,tn):e)}function Qe(e){if(e instanceof IDBRequest)return As(e);if(La.has(e))return La.get(e);const t=Is(e);return t!==e&&(La.set(e,t),ga.set(t,e)),t}const an=e=>ga.get(e);function vn(e,t,{blocked:a,upgrade:n,blocking:o,terminated:r}={}){const i=indexedDB.open(e,t),c=Qe(i);return n&&i.addEventListener("upgradeneeded",d=>{n(Qe(i.result),d.oldVersion,d.newVersion,Qe(i.transaction),d)}),a&&i.addEventListener("blocked",d=>a(d.oldVersion,d.newVersion,d)),c.then(d=>{r&&d.addEventListener("close",()=>r()),o&&d.addEventListener("versionchange",l=>o(l.oldVersion,l.newVersion,l))}).catch(()=>{}),c}const Ss=["get","getKey","getAll","getAllKeys","count"],Cs=["put","add","delete","clear"],Pa=new Map;function Xn(e,t){if(!(e instanceof IDBDatabase&&!(t in e)&&typeof t=="string"))return;if(Pa.get(t))return Pa.get(t);const a=t.replace(/FromIndex$/,""),n=t!==a,o=Cs.includes(a);if(!(a in(n?IDBIndex:IDBObjectStore).prototype)||!(o||Ss.includes(a)))return;const r=async function(i,...c){const d=this.transaction(i,o?"readwrite":"readonly");let l=d.store;return n&&(l=l.index(c.shift())),(await Promise.all([l[a](...c),o&&d.done]))[0]};return Pa.set(t,r),r}Qo(e=>({...e,get:(t,a,n)=>Xn(t,a)||e.get(t,a,n),has:(t,a)=>!!Xn(t,a)||e.has(t,a)}));const $s=["continue","continuePrimaryKey","advance"],Qn={},nn=new WeakMap,Zo=new WeakMap,Ts={get(e,t){if(!$s.includes(t))return e[t];let a=Qn[t];return a||(a=Qn[t]=function(...n){nn.set(this,Zo.get(this)[t](...n))}),a}};async function*Es(...e){let t=this;if(t instanceof IDBCursor||(t=await t.openCursor(...e)),!t)return;t=t;const a=new Proxy(t,Ts);for(Zo.set(a,t),ga.set(a,an(t));t;)yield a,t=await(nn.get(a)||t.continue()),nn.delete(a)}function Zn(e,t){return t===Symbol.asyncIterator&&Za(e,[IDBIndex,IDBObjectStore,IDBCursor])||t==="iterate"&&Za(e,[IDBIndex,IDBObjectStore])}Qo(e=>({...e,get(t,a,n){return Zn(t,a)?Es:e.get(t,a,n)},has(t,a){return Zn(t,a)||e.has(t,a)}}));const Rs="wcag-component-auditor",Os=3;let rt=null;function re(){return rt||(rt=vn(Rs,Os,{upgrade(e,t){if(t<1){const a=e.createObjectStore("baselines",{keyPath:"componentId"});a.createIndex("byUrl","snapshotMeta.url"),a.createIndex("byDate","lastUpdated");const n=e.createObjectStore("audit-history",{keyPath:"auditId",autoIncrement:!0});n.createIndex("byComponentId","componentId"),n.createIndex("byDate","runDate")}if(t<2&&e.createObjectStore("site-crawl-per-url",{keyPath:"slot"}),t<3){e.createObjectStore("scheduled-audits",{keyPath:"id"});const a=e.createObjectStore("scheduled-audit-results",{keyPath:"resultId",autoIncrement:!0});a.createIndex("byScheduleId","scheduleId"),a.createIndex("byRanAt","ranAt")}}})),rt}async function Ms(){rt&&(await rt).close(),rt=null}async function ma(e){return(await re()).get("baselines",e)}async function An(e){await(await re()).put("baselines",e)}async function er(e){await(await re()).delete("baselines",e)}async function tr(){return(await re()).getAll("baselines")}async function Ds(e){return(await re()).getAllFromIndex("baselines","byUrl",e)}async function Ns(e){return(await re()).add("audit-history",e)}async function Ls(e){return(await re()).getAllFromIndex("audit-history","byComponentId",e)}async function ar(){return(await re()).get("site-crawl-per-url","current")}async function nr(e){await(await re()).put("site-crawl-per-url",{...e,slot:"current"})}async function kn(){await(await re()).delete("site-crawl-per-url","current")}async function or(){return(await re()).getAll("scheduled-audits")}async function rr(e){return(await re()).get("scheduled-audits",e)}async function xn(e){await(await re()).put("scheduled-audits",e)}async function Ps(e){await(await re()).delete("scheduled-audits",e)}async function ir(e){return(await re()).add("scheduled-audit-results",e)}async function Fs(e){return(await re()).getAllFromIndex("scheduled-audit-results","byScheduleId",e)}async function _s(){return(await re()).getAll("scheduled-audit-results")}async function sr(e,t=50){const a=await re(),n=await a.getAllFromIndex("scheduled-audit-results","byScheduleId",e);if(n.length<=t)return;n.sort((i,c)=>c.ranAt.localeCompare(i.ranAt));const o=n.slice(t),r=a.transaction("scheduled-audit-results","readwrite");for(const i of o)i.resultId!==void 0&&await r.store.delete(i.resultId);await r.done}async function Us(e){const a=(await re()).transaction("scheduled-audit-results","readwrite");let o=await a.store.index("byScheduleId").openCursor(IDBKeyRange.only(e));for(;o;)await o.delete(),o=await o.continue();await a.done}const Ws=Object.freeze(Object.defineProperty({__proto__:null,_resetDbForTesting:Ms,appendAuditHistory:Ns,appendScheduledAuditResult:ir,deleteBaseline:er,deleteScheduledAudit:Ps,deleteScheduledAuditResultsForScheduleId:Us,deleteSiteCrawlPerUrl:kn,getBaseline:ma,getDb:re,getScheduledAudit:rr,getSiteCrawlPerUrl:ar,listAllScheduledAuditResults:_s,listAuditHistoryForComponent:Ls,listBaselines:tr,listBaselinesByUrl:Ds,listScheduledAuditResultsByScheduleId:Fs,listScheduledAudits:or,pruneScheduledAuditResults:sr,putScheduledAudit:xn,setBaseline:An,setSiteCrawlPerUrl:nr},Symbol.toStringTag,{value:"Module"})),Yt="siteCrawlReport";async function In(){var a;if(typeof chrome>"u"||!((a=chrome.storage)!=null&&a.local))return null;const t=(await chrome.storage.local.get([Yt]))[Yt];return t&&typeof t=="object"?t:null}async function cr(e){var t;if(!(typeof chrome>"u"||!((t=chrome.storage)!=null&&t.local))){if(e===null){await chrome.storage.local.remove([Yt]),await kn().catch(()=>{});return}await chrome.storage.local.set({[Yt]:e})}}async function Jt(){try{const e=await ar();return(e==null?void 0:e.perUrl)??null}catch{return null}}async function lr(e,t){if(e===null){await kn();return}await nr({startUrl:(t==null?void 0:t.startUrl)??"",finishedAt:(t==null?void 0:t.finishedAt)??new Date().toISOString(),perUrl:e})}const vp=Object.freeze(Object.defineProperty({__proto__:null,loadSiteCrawlPerUrlResults:Jt,loadSiteCrawlReport:In,saveSiteCrawlPerUrlResults:lr,saveSiteCrawlReport:cr},Symbol.toStringTag,{value:"Module"}));async function je(){return!1}function Ap(e){return!1}const Dt="HEADLESS_BUILD_LOCKOUT",Ze="wcagcheckr is in private build phase. A team license is required for access. If you believe you should have access, contact cliff@locustware.com.";async function dr(){const e=await chrome.tabs.query({currentWindow:!0}),t=e.find(n=>n.active);if(t!=null&&t.id&&eo(t.url))return t.id;const a=e.find(n=>n.id&&eo(n.url));return(a==null?void 0:a.id)??null}function eo(e){return typeof e=="string"&&/^https?:\/\//.test(e)}const on="rubricConfigCache",Gs=7*24*60*60*1e3;function kp(e){const t=Date.now()-e.fetchedAt;return t>=Gs?"expired":t>=60*60*1e3?"stale":"fresh"}async function xp(){var a;if(typeof chrome>"u"||!((a=chrome.storage)!=null&&a.local))return null;const t=(await chrome.storage.local.get(on))[on];return!t||typeof t!="object"?null:t}async function Ip(e){var t;typeof chrome>"u"||!((t=chrome.storage)!=null&&t.local)||await chrome.storage.local.set({[on]:e})}let ur=null;function Sp(e){ur=e}function hr(){return ur}const rn="acknowledgedFindings";async function ba(){var a;if(typeof chrome>"u"||!((a=chrome.storage)!=null&&a.local))return[];const t=(await chrome.storage.local.get([rn]))[rn];return Array.isArray(t)?t:[]}async function ya(e){const t=await ba();return new Set(t.filter(a=>a.componentId===e).map(a=>a.matchKey))}async function Vs(e){const a=(await ba()).filter(n=>!(n.componentId===e.componentId&&n.matchKey===e.matchKey));a.push({...e,acknowledgedAt:new Date().toISOString()}),await pr(a)}async function js(e,t){const n=(await ba()).filter(o=>!(o.componentId===e&&o.matchKey===t));await pr(n)}async function pr(e){var t;typeof chrome>"u"||!((t=chrome.storage)!=null&&t.local)||await chrome.storage.local.set({[rn]:e})}const Cp=Object.freeze(Object.defineProperty({__proto__:null,acknowledgeFinding:Vs,loadAcknowledged:ba,loadAcknowledgedKeysForComponent:ya,unacknowledgeFinding:js},Symbol.toStringTag,{value:"Module"})),Hs=/:nth-(?:last-)?(?:child|of-type)\([^)]+\)/g,Bs=/\[id="[^"]*"\]/g,qs=/\[data-testid="[^"]*"\]/g,zs=/#[\w-]*\d[\w-]*/g,Ks=/\[href(?:[\^$*~|]?=("|')[^"']*\1)?\]/g,Ys=/\[aria-label(?:[\^$*~|]?=("|')[^"']*\1)?\]/g,Js=new Set(["region","landmark-one-main","page-has-heading-one","landmark-no-duplicate-banner","landmark-no-duplicate-contentinfo","landmark-no-duplicate-main","landmark-main-is-top-level","landmark-banner-is-top-level","landmark-contentinfo-is-top-level","landmark-complementary-is-top-level","landmark-unique","bypass","document-title","html-has-lang","meta-viewport"]);function Xs(e){return e&&e.replace(Hs,"").replace(Bs,"").replace(qs,"").replace(zs,"").replace(Ks,"").replace(Ys,"").replace(/\s+/g," ").trim()}function Ne(e,t){return Js.has(e)?`${e}::__structural__`:`${e}::${Xs(t)}`}const to={minor:0,moderate:1,serious:2,critical:3};function fr(e){const t=new Map;for(const a of e){const n=Ne(a.ruleId,a.target.selector),o=t.get(n);if(!o){t.set(n,a);continue}(to[a.impact]??0)>(to[o.impact]??0)&&t.set(n,a)}return Array.from(t.values())}const gr=[{id:"contrast",label:"Color & contrast",axeRules:["color-contrast","color-contrast-enhanced","link-in-text-block"],igtWorkflowIds:["visual-indicators"]},{id:"alt-text",label:"Alt text",axeRules:["image-alt","input-image-alt","object-alt","svg-img-alt","area-alt","role-img-alt"],igtWorkflowIds:[]},{id:"forms",label:"Forms",axeRules:["label","select-name","aria-input-field-name","aria-toggle-field-name","form-field-multiple-labels","label-content-name-mismatch","autocomplete-valid","label-title-only","wcc-onfocus-context-change","wcc-oninput-context-change","wcc-cognitive-auth-challenge"],igtWorkflowIds:["forms"]},{id:"keyboard",label:"Keyboard",axeRules:["nested-interactive","tabindex","frame-focusable-content","no-focusable-content","focus-order-semantics","wcc-gesture-no-alternative","wcc-drag-no-alternative"],igtWorkflowIds:["keyboard","focus-management"]},{id:"tab-order",label:"Tab order",axeRules:[],igtWorkflowIds:[],verifiedByWorkflowIds:["keyboard"],heuristic:"tab-order"},{id:"screen-reader",label:"Screen reader / ARIA",axeRules:["aria-allowed-attr","aria-required-attr","aria-required-children","aria-required-parent","aria-roles","aria-valid-attr-value","aria-valid-attr","aria-hidden-body","aria-hidden-focus","button-name","input-button-name","link-name","frame-title","presentation-role-conflict","aria-allowed-role","aria-prohibited-attr","aria-text","aria-deprecated-role","aria-conditional-attr","aria-braille-equivalent","empty-button","empty-heading","empty-link"],igtWorkflowIds:["screen-reader"]},{id:"reading-order",label:"Reading order",axeRules:[],igtWorkflowIds:[],verifiedByWorkflowIds:["page-structure"],heuristic:"reading-order"},{id:"page-structure",label:"Page structure",axeRules:["heading-order","page-has-heading-one","landmark-one-main","landmark-no-duplicate-banner","landmark-no-duplicate-contentinfo","landmark-no-duplicate-main","region","bypass","empty-table-header","landmark-banner-is-top-level","landmark-complementary-is-top-level","landmark-contentinfo-is-top-level","landmark-main-is-top-level","landmark-unique"],igtWorkflowIds:["page-structure"]}];function $p(e,t){const a=new Set;for(const n of t)n.wcagCriterion===e&&a.add(n.ruleId);if(a.size===0)return null;for(const n of gr)for(const o of n.axeRules)if(a.has(o))return n.id;return null}function Qs(e){const t=gr.map(o=>ec(o,e)),a=Zs(t.map(o=>o.letter)),n=t.filter(o=>o.letter==="D"||o.letter==="F");return{overallLetter:a,subGrades:t,warningAreas:n,isLawsuitRisk:n.length>0}}function Zs(e){const t=e.filter(i=>i==="A").length,a=e.filter(i=>i==="C").length,n=e.filter(i=>i==="D").length,o=e.filter(i=>i==="F").length,r=e.length-t;return t===0&&n+o>a?"F":r===0?"A":r<=3?"B":r<=6?"C":"D"}function ec(e,t){const a=new Set(e.axeRules),n=t.violations.filter(M=>a.has(M.ruleId)),o=t.manualRuns.filter(M=>e.igtWorkflowIds.includes(M.workflowId)),r=e.igtWorkflowIds.map(M=>t.workflows.find(L=>L.id===M)).filter(M=>M!=null);let i=r.length>0;for(const M of r){const L=o.find(P=>P.workflowId===M.id);if((L?Object.keys(L.steps).length:0)!==M.steps.length){i=!1;break}}const c=o.some(M=>Object.keys(M.steps).length>0),d=e.verifiedByWorkflowIds??[],l=t.manualRuns.filter(M=>d.includes(M.workflowId)),p=d.map(M=>t.workflows.find(L=>L.id===M)).filter(M=>M!=null);let m=d.length>0&&p.length===d.length;for(const M of p){const L=l.find(P=>P.workflowId===M.id);if((L?Object.keys(L.steps).length:0)!==M.steps.length){m=!1;break}}let u=null;if(m)for(const M of l)M.completedAt&&(u===null||M.completedAt>u)&&(u=M.completedAt);const g=e.heuristic==="tab-order"?t.heuristicCounts.tabOrder??0:e.heuristic==="reading-order"?t.heuristicCounts.readingOrder??0:0,h=d[0]??e.igtWorkflowIds[0]??null,f=t.interactiveAuditVerdicts,w=[];f&&(e.id==="tab-order"&&f.focusOrder&&w.push({verdict:f.focusOrder,criterionId:"2.4.3"}),e.id==="keyboard"&&(f.keyboardTrap&&w.push({verdict:f.keyboardTrap,criterionId:"2.1.2"}),f.focusVisible&&w.push({verdict:f.focusVisible,criterionId:"2.4.7"})),e.id==="reading-order"&&f.readingOrder&&w.push({verdict:f.readingOrder,criterionId:"1.3.2"}),e.id==="contrast"&&f.nonTextContrast&&w.push({verdict:f.nonTextContrast,criterionId:"1.4.11"}));const b=w.some(M=>M.verdict==="fail");if(w.length>0&&w.every(M=>M.verdict==="pass")&&d.length>0&&!m)return{id:e.id,label:e.label,letter:"A",untested:!1,needsIgt:!1,igtCompleted:!1,verifiedByIgt:!1,verifiedAt:w.length>0?new Date().toISOString():null,suppressedHeuristicCount:g,primaryVerifierWorkflowId:h,counts:{critical:0,serious:0,moderate:0,minor:0},caps:{cappedAtC:!1,cappedAtB:!1},aiVerifiedCriteria:w.map(M=>M.criterionId)};if(m&&d.length>0)return{id:e.id,label:e.label,letter:"A",untested:!1,needsIgt:!1,igtCompleted:!0,verifiedByIgt:!0,verifiedAt:u,suppressedHeuristicCount:g,primaryVerifierWorkflowId:h,counts:{critical:0,serious:0,moderate:0,minor:0},caps:{cappedAtC:!1,cappedAtB:!1}};const v=e.axeRules.length>0||e.heuristic!=null,s=e.igtWorkflowIds.length>0;if(v?!t.auditRan:!c)return{id:e.id,label:e.label,letter:"F",untested:!0,needsIgt:s||d.length>0,igtCompleted:!1,verifiedByIgt:!1,verifiedAt:null,suppressedHeuristicCount:0,primaryVerifierWorkflowId:h,counts:{critical:0,serious:0,moderate:0,minor:0},caps:{cappedAtC:!1,cappedAtB:!1}};const $={critical:0,serious:0,moderate:0,minor:0},A=fr(n);for(const M of A)$[M.impact]++;const S=(t.aiResolvedFails??[]).filter(M=>a.has(M.ruleId));if(S.length>0){const M=new Set(A.map(L=>`${L.ruleId}::${L.target.selector}`));for(const L of S){const W=`${L.ruleId}::${L.selector}`;M.has(W)||(M.add(W),$[L.impact]++)}}const F=w.length>0&&w.some(M=>M.verdict==="pass"||M.verdict==="fail"),T=F?g:0;F||($.serious+=g),b&&($.serious+=w.filter(M=>M.verdict==="fail").length);for(const M of o){const L=r.find(W=>W.id===M.workflowId);if(L)for(const W of L.steps){const P=M.steps[W.id];(P==null?void 0:P.status)==="fail"&&(W.severity==="required"?$.serious++:$.moderate++)}}const I=$.critical>0,N=$.serious>0;let C;$.critical>=5?C="F":$.critical>=1?C="D":$.serious>=1?C="C":$.moderate>=1?C="B":C="A";const _=C!=="A"&&(s&&!i||d.length>0&&!m);return{id:e.id,label:e.label,letter:C,untested:!1,needsIgt:_,igtCompleted:i,verifiedByIgt:!1,verifiedAt:null,suppressedHeuristicCount:T,primaryVerifierWorkflowId:h,counts:$,caps:{cappedAtC:I,cappedAtB:N}}}const it="incompleteResolutions",ao=2e3,no=600;function Sn(e,t,a){return`${e}::${t}::${a}`}function mr(e){return e.reasoning&&e.reasoning.length>no?{...e,reasoning:e.reasoning.slice(0,no-1)+"…"}:e}function tc(e){const t=Object.entries(e);if(t.length<=ao)return e;const a=t.sort(([,n],[,o])=>{const r=Date.parse(n.resolvedAt)||0;return(Date.parse(o.resolvedAt)||0)-r});return Object.fromEntries(a.slice(0,ao))}async function wa(){try{const t=(await chrome.storage.local.get(it))[it];return t&&typeof t=="object"?t:{}}catch{return{}}}async function Cn(e){const t=tc(e);try{await chrome.storage.local.set({[it]:t})}catch(a){const n=Object.entries(t).sort(([,r],[,i])=>{const c=Date.parse(r.resolvedAt)||0;return(Date.parse(i.resolvedAt)||0)-c}),o=Object.fromEntries(n.slice(0,Math.floor(n.length/2)));try{await chrome.storage.local.set({[it]:o}),console.warn("[incomplete-resolutions] storage quota hit; pruned to",Object.keys(o).length,"entries",a)}catch(r){await chrome.storage.local.remove(it).catch(()=>{}),console.warn("[incomplete-resolutions] storage quota still exceeded after prune; wiped key",r)}}}async function We(e){const t=await wa();t[Sn(e.pageUrl,e.ruleId,e.selector)]=mr(e),await Cn(t)}async function ac(e){if(e.length===0)return;const t=await wa();for(const a of e)t[Sn(a.pageUrl,a.ruleId,a.selector)]=mr(a);await Cn(t)}async function br(){const e=await wa();return Object.values(e)}async function va(e){return(await br()).filter(a=>a.pageUrl===e)}async function nc(e,t,a){const n=await wa();delete n[Sn(e,t,a)],await Cn(n)}async function oc(){await chrome.storage.local.remove(it)}function yr(e){if(!e.reasoning)return e;const t=e.reasoning.toLowerCase(),a=Ut(t,/verdict\s+corrected\s+to\s+pass|verdict:\s*pass\s+\(corrected\)/gi),n=Ut(t,/verdict\s+corrected\s+to\s+fail|verdict:\s*fail\s+\(corrected\)/gi);if(a>n&&a>=0)return{...e,verdict:"pass"};if(n>a&&n>=0)return{...e,verdict:"fail"};const o=Ut(t,/actually\s+passes|in\s+fact\s+passes|passes\s+aa/gi),r=Ut(t,/actually\s+fails|in\s+fact\s+fails|fails\s+aa/gi);return o<0&&r<0?e:o>r&&o/t.length>=.5?{...e,verdict:"pass"}:r>o&&r/t.length>=.5?{...e,verdict:"fail"}:e}function Ut(e,t){let a=-1;const n=t.flags.includes("g")?t.flags:t.flags+"g",o=new RegExp(t.source,n);let r;for(;(r=o.exec(e))!==null;)a=r.index,r.index===o.lastIndex&&o.lastIndex++;return a}function wr(e){if(e.ruleId!=="color-contrast"&&e.ruleId!=="color-contrast-enhanced"||!e.reasoning||e.verdict!=="fail")return e;const t=rc(e.reasoning);if(t.length<2)return e;const a=t[0],n=t[1];if(n.a<.999)return e;const o=a.a<.999?{r:Math.round(a.r*a.a+n.r*(1-a.a)),g:Math.round(a.g*a.a+n.g*(1-a.a)),b:Math.round(a.b*a.a+n.b*(1-a.a))}:{r:a.r,g:a.g,b:a.b},r=sc(o,{r:n.r,g:n.g,b:n.b}),i=ic(e.reasoning)??4.5;return r<i+.05?e:{...e,verdict:"pass",reasoning:`${e.reasoning}
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/parallel-tab-flow-f0rYsTcq.js","assets/crash-reporter-DAZnNefT.js","assets/preload-helper-D7HrI6pR.js","assets/diff-CECbooHD.js","assets/ai-usage-log-C9Opn0x0.js"])))=>i.map(i=>d[i]);
|
|
2
|
+
var es=Object.defineProperty;var ts=(e,t,a)=>t in e?es(e,t,{enumerable:!0,configurable:!0,writable:!0,value:a}):e[t]=a;var _t=(e,t,a)=>ts(e,typeof t!="symbol"?t+"":t,a);import{_ as bn}from"./preload-helper-D7HrI6pR.js";import{c as Z,e as ce,o as Ce,k as B,h as Ae,f as $t,j as Xo}from"./crash-reporter-DAZnNefT.js";import{b as le,m as qn,d as as,T as ns,e as zn,c as yn,g as pa,i as Kn}from"./diff-CECbooHD.js";import{i as os,j as De,J as rs,T as wn,x as Mt,v as fa,z as ye,D as is,e as Qa,K as ss,L as cs,M as ls,N as ds,O as us,P as hs,W as ps,b as fs,g as gs,u as ms,r as bs,f as ys}from"./ai-usage-log-C9Opn0x0.js";const Za=(e,t)=>t.some(a=>e instanceof a);let Yn,Jn;function ws(){return Yn||(Yn=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction])}function vs(){return Jn||(Jn=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey])}const en=new WeakMap,La=new WeakMap,ga=new WeakMap;function As(e){const t=new Promise((a,n)=>{const o=()=>{e.removeEventListener("success",r),e.removeEventListener("error",i)},r=()=>{a(Qe(e.result)),o()},i=()=>{n(e.error),o()};e.addEventListener("success",r),e.addEventListener("error",i)});return ga.set(t,e),t}function ks(e){if(en.has(e))return;const t=new Promise((a,n)=>{const o=()=>{e.removeEventListener("complete",r),e.removeEventListener("error",i),e.removeEventListener("abort",i)},r=()=>{a(),o()},i=()=>{n(e.error||new DOMException("AbortError","AbortError")),o()};e.addEventListener("complete",r),e.addEventListener("error",i),e.addEventListener("abort",i)});en.set(e,t)}let tn={get(e,t,a){if(e instanceof IDBTransaction){if(t==="done")return en.get(e);if(t==="store")return a.objectStoreNames[1]?void 0:a.objectStore(a.objectStoreNames[0])}return Qe(e[t])},set(e,t,a){return e[t]=a,!0},has(e,t){return e instanceof IDBTransaction&&(t==="done"||t==="store")?!0:t in e}};function Qo(e){tn=e(tn)}function xs(e){return vs().includes(e)?function(...t){return e.apply(an(this),t),Qe(this.request)}:function(...t){return Qe(e.apply(an(this),t))}}function Is(e){return typeof e=="function"?xs(e):(e instanceof IDBTransaction&&ks(e),Za(e,ws())?new Proxy(e,tn):e)}function Qe(e){if(e instanceof IDBRequest)return As(e);if(La.has(e))return La.get(e);const t=Is(e);return t!==e&&(La.set(e,t),ga.set(t,e)),t}const an=e=>ga.get(e);function vn(e,t,{blocked:a,upgrade:n,blocking:o,terminated:r}={}){const i=indexedDB.open(e,t),c=Qe(i);return n&&i.addEventListener("upgradeneeded",d=>{n(Qe(i.result),d.oldVersion,d.newVersion,Qe(i.transaction),d)}),a&&i.addEventListener("blocked",d=>a(d.oldVersion,d.newVersion,d)),c.then(d=>{r&&d.addEventListener("close",()=>r()),o&&d.addEventListener("versionchange",l=>o(l.oldVersion,l.newVersion,l))}).catch(()=>{}),c}const Ss=["get","getKey","getAll","getAllKeys","count"],Cs=["put","add","delete","clear"],Pa=new Map;function Xn(e,t){if(!(e instanceof IDBDatabase&&!(t in e)&&typeof t=="string"))return;if(Pa.get(t))return Pa.get(t);const a=t.replace(/FromIndex$/,""),n=t!==a,o=Cs.includes(a);if(!(a in(n?IDBIndex:IDBObjectStore).prototype)||!(o||Ss.includes(a)))return;const r=async function(i,...c){const d=this.transaction(i,o?"readwrite":"readonly");let l=d.store;return n&&(l=l.index(c.shift())),(await Promise.all([l[a](...c),o&&d.done]))[0]};return Pa.set(t,r),r}Qo(e=>({...e,get:(t,a,n)=>Xn(t,a)||e.get(t,a,n),has:(t,a)=>!!Xn(t,a)||e.has(t,a)}));const $s=["continue","continuePrimaryKey","advance"],Qn={},nn=new WeakMap,Zo=new WeakMap,Ts={get(e,t){if(!$s.includes(t))return e[t];let a=Qn[t];return a||(a=Qn[t]=function(...n){nn.set(this,Zo.get(this)[t](...n))}),a}};async function*Es(...e){let t=this;if(t instanceof IDBCursor||(t=await t.openCursor(...e)),!t)return;t=t;const a=new Proxy(t,Ts);for(Zo.set(a,t),ga.set(a,an(t));t;)yield a,t=await(nn.get(a)||t.continue()),nn.delete(a)}function Zn(e,t){return t===Symbol.asyncIterator&&Za(e,[IDBIndex,IDBObjectStore,IDBCursor])||t==="iterate"&&Za(e,[IDBIndex,IDBObjectStore])}Qo(e=>({...e,get(t,a,n){return Zn(t,a)?Es:e.get(t,a,n)},has(t,a){return Zn(t,a)||e.has(t,a)}}));const Rs="wcag-component-auditor",Os=3;let rt=null;function re(){return rt||(rt=vn(Rs,Os,{upgrade(e,t){if(t<1){const a=e.createObjectStore("baselines",{keyPath:"componentId"});a.createIndex("byUrl","snapshotMeta.url"),a.createIndex("byDate","lastUpdated");const n=e.createObjectStore("audit-history",{keyPath:"auditId",autoIncrement:!0});n.createIndex("byComponentId","componentId"),n.createIndex("byDate","runDate")}if(t<2&&e.createObjectStore("site-crawl-per-url",{keyPath:"slot"}),t<3){e.createObjectStore("scheduled-audits",{keyPath:"id"});const a=e.createObjectStore("scheduled-audit-results",{keyPath:"resultId",autoIncrement:!0});a.createIndex("byScheduleId","scheduleId"),a.createIndex("byRanAt","ranAt")}}})),rt}async function Ms(){rt&&(await rt).close(),rt=null}async function ma(e){return(await re()).get("baselines",e)}async function An(e){await(await re()).put("baselines",e)}async function er(e){await(await re()).delete("baselines",e)}async function tr(){return(await re()).getAll("baselines")}async function Ds(e){return(await re()).getAllFromIndex("baselines","byUrl",e)}async function Ns(e){return(await re()).add("audit-history",e)}async function Ls(e){return(await re()).getAllFromIndex("audit-history","byComponentId",e)}async function ar(){return(await re()).get("site-crawl-per-url","current")}async function nr(e){await(await re()).put("site-crawl-per-url",{...e,slot:"current"})}async function kn(){await(await re()).delete("site-crawl-per-url","current")}async function or(){return(await re()).getAll("scheduled-audits")}async function rr(e){return(await re()).get("scheduled-audits",e)}async function xn(e){await(await re()).put("scheduled-audits",e)}async function Ps(e){await(await re()).delete("scheduled-audits",e)}async function ir(e){return(await re()).add("scheduled-audit-results",e)}async function Fs(e){return(await re()).getAllFromIndex("scheduled-audit-results","byScheduleId",e)}async function _s(){return(await re()).getAll("scheduled-audit-results")}async function sr(e,t=50){const a=await re(),n=await a.getAllFromIndex("scheduled-audit-results","byScheduleId",e);if(n.length<=t)return;n.sort((i,c)=>c.ranAt.localeCompare(i.ranAt));const o=n.slice(t),r=a.transaction("scheduled-audit-results","readwrite");for(const i of o)i.resultId!==void 0&&await r.store.delete(i.resultId);await r.done}async function Us(e){const a=(await re()).transaction("scheduled-audit-results","readwrite");let o=await a.store.index("byScheduleId").openCursor(IDBKeyRange.only(e));for(;o;)await o.delete(),o=await o.continue();await a.done}const Ws=Object.freeze(Object.defineProperty({__proto__:null,_resetDbForTesting:Ms,appendAuditHistory:Ns,appendScheduledAuditResult:ir,deleteBaseline:er,deleteScheduledAudit:Ps,deleteScheduledAuditResultsForScheduleId:Us,deleteSiteCrawlPerUrl:kn,getBaseline:ma,getDb:re,getScheduledAudit:rr,getSiteCrawlPerUrl:ar,listAllScheduledAuditResults:_s,listAuditHistoryForComponent:Ls,listBaselines:tr,listBaselinesByUrl:Ds,listScheduledAuditResultsByScheduleId:Fs,listScheduledAudits:or,pruneScheduledAuditResults:sr,putScheduledAudit:xn,setBaseline:An,setSiteCrawlPerUrl:nr},Symbol.toStringTag,{value:"Module"})),Yt="siteCrawlReport";async function In(){var a;if(typeof chrome>"u"||!((a=chrome.storage)!=null&&a.local))return null;const t=(await chrome.storage.local.get([Yt]))[Yt];return t&&typeof t=="object"?t:null}async function cr(e){var t;if(!(typeof chrome>"u"||!((t=chrome.storage)!=null&&t.local))){if(e===null){await chrome.storage.local.remove([Yt]),await kn().catch(()=>{});return}await chrome.storage.local.set({[Yt]:e})}}async function Jt(){try{const e=await ar();return(e==null?void 0:e.perUrl)??null}catch{return null}}async function lr(e,t){if(e===null){await kn();return}await nr({startUrl:(t==null?void 0:t.startUrl)??"",finishedAt:(t==null?void 0:t.finishedAt)??new Date().toISOString(),perUrl:e})}const vp=Object.freeze(Object.defineProperty({__proto__:null,loadSiteCrawlPerUrlResults:Jt,loadSiteCrawlReport:In,saveSiteCrawlPerUrlResults:lr,saveSiteCrawlReport:cr},Symbol.toStringTag,{value:"Module"}));async function je(){return!1}function Ap(e){return!1}const Dt="HEADLESS_BUILD_LOCKOUT",Ze="wcagcheckr is in private build phase. A team license is required for access. If you believe you should have access, contact cliff@locustware.com.";async function dr(){const e=await chrome.tabs.query({currentWindow:!0}),t=e.find(n=>n.active);if(t!=null&&t.id&&eo(t.url))return t.id;const a=e.find(n=>n.id&&eo(n.url));return(a==null?void 0:a.id)??null}function eo(e){return typeof e=="string"&&/^https?:\/\//.test(e)}const on="rubricConfigCache",Gs=7*24*60*60*1e3;function kp(e){const t=Date.now()-e.fetchedAt;return t>=Gs?"expired":t>=60*60*1e3?"stale":"fresh"}async function xp(){var a;if(typeof chrome>"u"||!((a=chrome.storage)!=null&&a.local))return null;const t=(await chrome.storage.local.get(on))[on];return!t||typeof t!="object"?null:t}async function Ip(e){var t;typeof chrome>"u"||!((t=chrome.storage)!=null&&t.local)||await chrome.storage.local.set({[on]:e})}let ur=null;function Sp(e){ur=e}function hr(){return ur}const rn="acknowledgedFindings";async function ba(){var a;if(typeof chrome>"u"||!((a=chrome.storage)!=null&&a.local))return[];const t=(await chrome.storage.local.get([rn]))[rn];return Array.isArray(t)?t:[]}async function ya(e){const t=await ba();return new Set(t.filter(a=>a.componentId===e).map(a=>a.matchKey))}async function Vs(e){const a=(await ba()).filter(n=>!(n.componentId===e.componentId&&n.matchKey===e.matchKey));a.push({...e,acknowledgedAt:new Date().toISOString()}),await pr(a)}async function js(e,t){const n=(await ba()).filter(o=>!(o.componentId===e&&o.matchKey===t));await pr(n)}async function pr(e){var t;typeof chrome>"u"||!((t=chrome.storage)!=null&&t.local)||await chrome.storage.local.set({[rn]:e})}const Cp=Object.freeze(Object.defineProperty({__proto__:null,acknowledgeFinding:Vs,loadAcknowledged:ba,loadAcknowledgedKeysForComponent:ya,unacknowledgeFinding:js},Symbol.toStringTag,{value:"Module"})),Hs=/:nth-(?:last-)?(?:child|of-type)\([^)]+\)/g,Bs=/\[id="[^"]*"\]/g,qs=/\[data-testid="[^"]*"\]/g,zs=/#[\w-]*\d[\w-]*/g,Ks=/\[href(?:[\^$*~|]?=("|')[^"']*\1)?\]/g,Ys=/\[aria-label(?:[\^$*~|]?=("|')[^"']*\1)?\]/g,Js=new Set(["region","landmark-one-main","page-has-heading-one","landmark-no-duplicate-banner","landmark-no-duplicate-contentinfo","landmark-no-duplicate-main","landmark-main-is-top-level","landmark-banner-is-top-level","landmark-contentinfo-is-top-level","landmark-complementary-is-top-level","landmark-unique","bypass","document-title","html-has-lang","meta-viewport"]);function Xs(e){return e&&e.replace(Hs,"").replace(Bs,"").replace(qs,"").replace(zs,"").replace(Ks,"").replace(Ys,"").replace(/\s+/g," ").trim()}function Ne(e,t){return Js.has(e)?`${e}::__structural__`:`${e}::${Xs(t)}`}const to={minor:0,moderate:1,serious:2,critical:3};function fr(e){const t=new Map;for(const a of e){const n=Ne(a.ruleId,a.target.selector),o=t.get(n);if(!o){t.set(n,a);continue}(to[a.impact]??0)>(to[o.impact]??0)&&t.set(n,a)}return Array.from(t.values())}const gr=[{id:"contrast",label:"Color & contrast",axeRules:["color-contrast","color-contrast-enhanced","link-in-text-block"],igtWorkflowIds:["visual-indicators"]},{id:"alt-text",label:"Alt text",axeRules:["image-alt","input-image-alt","object-alt","svg-img-alt","area-alt","role-img-alt"],igtWorkflowIds:[]},{id:"forms",label:"Forms",axeRules:["label","select-name","aria-input-field-name","aria-toggle-field-name","form-field-multiple-labels","label-content-name-mismatch","autocomplete-valid","label-title-only","wcc-onfocus-context-change","wcc-oninput-context-change","wcc-cognitive-auth-challenge"],igtWorkflowIds:["forms"]},{id:"keyboard",label:"Keyboard",axeRules:["nested-interactive","tabindex","frame-focusable-content","no-focusable-content","focus-order-semantics","wcc-gesture-no-alternative","wcc-drag-no-alternative"],igtWorkflowIds:["keyboard","focus-management"]},{id:"tab-order",label:"Tab order",axeRules:[],igtWorkflowIds:[],verifiedByWorkflowIds:["keyboard"],heuristic:"tab-order"},{id:"screen-reader",label:"Screen reader / ARIA",axeRules:["aria-allowed-attr","aria-required-attr","aria-required-children","aria-required-parent","aria-roles","aria-valid-attr-value","aria-valid-attr","aria-hidden-body","aria-hidden-focus","button-name","input-button-name","link-name","frame-title","presentation-role-conflict","aria-allowed-role","aria-prohibited-attr","aria-text","aria-deprecated-role","aria-conditional-attr","aria-braille-equivalent","empty-button","empty-heading","empty-link"],igtWorkflowIds:["screen-reader"]},{id:"reading-order",label:"Reading order",axeRules:[],igtWorkflowIds:[],verifiedByWorkflowIds:["page-structure"],heuristic:"reading-order"},{id:"page-structure",label:"Page structure",axeRules:["heading-order","page-has-heading-one","landmark-one-main","landmark-no-duplicate-banner","landmark-no-duplicate-contentinfo","landmark-no-duplicate-main","region","bypass","empty-table-header","landmark-banner-is-top-level","landmark-complementary-is-top-level","landmark-contentinfo-is-top-level","landmark-main-is-top-level","landmark-unique"],igtWorkflowIds:["page-structure"]}];function $p(e,t){const a=new Set;for(const n of t)n.wcagCriterion===e&&a.add(n.ruleId);if(a.size===0)return null;for(const n of gr)for(const o of n.axeRules)if(a.has(o))return n.id;return null}function Qs(e){const t=gr.map(o=>ec(o,e)),a=Zs(t.map(o=>o.letter)),n=t.filter(o=>o.letter==="D"||o.letter==="F");return{overallLetter:a,subGrades:t,warningAreas:n,isLawsuitRisk:n.length>0}}function Zs(e){const t=e.filter(i=>i==="A").length,a=e.filter(i=>i==="C").length,n=e.filter(i=>i==="D").length,o=e.filter(i=>i==="F").length,r=e.length-t;return t===0&&n+o>a?"F":r===0?"A":r<=3?"B":r<=6?"C":"D"}function ec(e,t){const a=new Set(e.axeRules),n=t.violations.filter(M=>a.has(M.ruleId)),o=t.manualRuns.filter(M=>e.igtWorkflowIds.includes(M.workflowId)),r=e.igtWorkflowIds.map(M=>t.workflows.find(L=>L.id===M)).filter(M=>M!=null);let i=r.length>0;for(const M of r){const L=o.find(P=>P.workflowId===M.id);if((L?Object.keys(L.steps).length:0)!==M.steps.length){i=!1;break}}const c=o.some(M=>Object.keys(M.steps).length>0),d=e.verifiedByWorkflowIds??[],l=t.manualRuns.filter(M=>d.includes(M.workflowId)),p=d.map(M=>t.workflows.find(L=>L.id===M)).filter(M=>M!=null);let m=d.length>0&&p.length===d.length;for(const M of p){const L=l.find(P=>P.workflowId===M.id);if((L?Object.keys(L.steps).length:0)!==M.steps.length){m=!1;break}}let u=null;if(m)for(const M of l)M.completedAt&&(u===null||M.completedAt>u)&&(u=M.completedAt);const g=e.heuristic==="tab-order"?t.heuristicCounts.tabOrder??0:e.heuristic==="reading-order"?t.heuristicCounts.readingOrder??0:0,h=d[0]??e.igtWorkflowIds[0]??null,f=t.interactiveAuditVerdicts,w=[];f&&(e.id==="tab-order"&&f.focusOrder&&w.push({verdict:f.focusOrder,criterionId:"2.4.3"}),e.id==="keyboard"&&(f.keyboardTrap&&w.push({verdict:f.keyboardTrap,criterionId:"2.1.2"}),f.focusVisible&&w.push({verdict:f.focusVisible,criterionId:"2.4.7"})),e.id==="reading-order"&&f.readingOrder&&w.push({verdict:f.readingOrder,criterionId:"1.3.2"}),e.id==="contrast"&&f.nonTextContrast&&w.push({verdict:f.nonTextContrast,criterionId:"1.4.11"}));const b=w.some(M=>M.verdict==="fail");if(w.length>0&&w.every(M=>M.verdict==="pass")&&d.length>0&&!m)return{id:e.id,label:e.label,letter:"A",untested:!1,needsIgt:!1,igtCompleted:!1,verifiedByIgt:!1,verifiedAt:w.length>0?new Date().toISOString():null,suppressedHeuristicCount:g,primaryVerifierWorkflowId:h,counts:{critical:0,serious:0,moderate:0,minor:0},caps:{cappedAtC:!1,cappedAtB:!1},aiVerifiedCriteria:w.map(M=>M.criterionId)};if(m&&d.length>0)return{id:e.id,label:e.label,letter:"A",untested:!1,needsIgt:!1,igtCompleted:!0,verifiedByIgt:!0,verifiedAt:u,suppressedHeuristicCount:g,primaryVerifierWorkflowId:h,counts:{critical:0,serious:0,moderate:0,minor:0},caps:{cappedAtC:!1,cappedAtB:!1}};const v=e.axeRules.length>0||e.heuristic!=null,s=e.igtWorkflowIds.length>0;if(v?!t.auditRan:!c)return{id:e.id,label:e.label,letter:"F",untested:!0,needsIgt:s||d.length>0,igtCompleted:!1,verifiedByIgt:!1,verifiedAt:null,suppressedHeuristicCount:0,primaryVerifierWorkflowId:h,counts:{critical:0,serious:0,moderate:0,minor:0},caps:{cappedAtC:!1,cappedAtB:!1}};const $={critical:0,serious:0,moderate:0,minor:0},A=fr(n);for(const M of A)$[M.impact]++;const S=(t.aiResolvedFails??[]).filter(M=>a.has(M.ruleId));if(S.length>0){const M=new Set(A.map(L=>`${L.ruleId}::${L.target.selector}`));for(const L of S){const W=`${L.ruleId}::${L.selector}`;M.has(W)||(M.add(W),$[L.impact]++)}}const F=w.length>0&&w.some(M=>M.verdict==="pass"||M.verdict==="fail"),T=F?g:0;F||($.serious+=g),b&&($.serious+=w.filter(M=>M.verdict==="fail").length);for(const M of o){const L=r.find(W=>W.id===M.workflowId);if(L)for(const W of L.steps){const P=M.steps[W.id];(P==null?void 0:P.status)==="fail"&&(W.severity==="required"?$.serious++:$.moderate++)}}const I=$.critical>0,N=$.serious>0;let C;$.critical>=5?C="F":$.critical>=1?C="D":$.serious>=1?C="C":$.moderate>=1?C="B":C="A";const _=C!=="A"&&(s&&!i||d.length>0&&!m);return{id:e.id,label:e.label,letter:C,untested:!1,needsIgt:_,igtCompleted:i,verifiedByIgt:!1,verifiedAt:null,suppressedHeuristicCount:T,primaryVerifierWorkflowId:h,counts:$,caps:{cappedAtC:I,cappedAtB:N}}}const it="incompleteResolutions",ao=2e3,no=600;function Sn(e,t,a){return`${e}::${t}::${a}`}function mr(e){return e.reasoning&&e.reasoning.length>no?{...e,reasoning:e.reasoning.slice(0,no-1)+"…"}:e}function tc(e){const t=Object.entries(e);if(t.length<=ao)return e;const a=t.sort(([,n],[,o])=>{const r=Date.parse(n.resolvedAt)||0;return(Date.parse(o.resolvedAt)||0)-r});return Object.fromEntries(a.slice(0,ao))}async function wa(){try{const t=(await chrome.storage.local.get(it))[it];return t&&typeof t=="object"?t:{}}catch{return{}}}async function Cn(e){const t=tc(e);try{await chrome.storage.local.set({[it]:t})}catch(a){const n=Object.entries(t).sort(([,r],[,i])=>{const c=Date.parse(r.resolvedAt)||0;return(Date.parse(i.resolvedAt)||0)-c}),o=Object.fromEntries(n.slice(0,Math.floor(n.length/2)));try{await chrome.storage.local.set({[it]:o}),console.warn("[incomplete-resolutions] storage quota hit; pruned to",Object.keys(o).length,"entries",a)}catch(r){await chrome.storage.local.remove(it).catch(()=>{}),console.warn("[incomplete-resolutions] storage quota still exceeded after prune; wiped key",r)}}}async function We(e){const t=await wa();t[Sn(e.pageUrl,e.ruleId,e.selector)]=mr(e),await Cn(t)}async function ac(e){if(e.length===0)return;const t=await wa();for(const a of e)t[Sn(a.pageUrl,a.ruleId,a.selector)]=mr(a);await Cn(t)}async function br(){const e=await wa();return Object.values(e)}async function va(e){return(await br()).filter(a=>a.pageUrl===e)}async function nc(e,t,a){const n=await wa();delete n[Sn(e,t,a)],await Cn(n)}async function oc(){await chrome.storage.local.remove(it)}function yr(e){if(!e.reasoning)return e;const t=e.reasoning.toLowerCase(),a=Ut(t,/verdict\s+corrected\s+to\s+pass|verdict:\s*pass\s+\(corrected\)/gi),n=Ut(t,/verdict\s+corrected\s+to\s+fail|verdict:\s*fail\s+\(corrected\)/gi);if(a>n&&a>=0)return{...e,verdict:"pass"};if(n>a&&n>=0)return{...e,verdict:"fail"};const o=Ut(t,/actually\s+passes|in\s+fact\s+passes|passes\s+aa/gi),r=Ut(t,/actually\s+fails|in\s+fact\s+fails|fails\s+aa/gi);return o<0&&r<0?e:o>r&&o/t.length>=.5?{...e,verdict:"pass"}:r>o&&r/t.length>=.5?{...e,verdict:"fail"}:e}function Ut(e,t){let a=-1;const n=t.flags.includes("g")?t.flags:t.flags+"g",o=new RegExp(t.source,n);let r;for(;(r=o.exec(e))!==null;)a=r.index,r.index===o.lastIndex&&o.lastIndex++;return a}function wr(e){if(e.ruleId!=="color-contrast"&&e.ruleId!=="color-contrast-enhanced"||!e.reasoning||e.verdict!=="fail")return e;const t=rc(e.reasoning);if(t.length<2)return e;const a=t[0],n=t[1];if(n.a<.999)return e;const o=a.a<.999?{r:Math.round(a.r*a.a+n.r*(1-a.a)),g:Math.round(a.g*a.a+n.g*(1-a.a)),b:Math.round(a.b*a.a+n.b*(1-a.a))}:{r:a.r,g:a.g,b:a.b},r=sc(o,{r:n.r,g:n.g,b:n.b}),i=ic(e.reasoning)??4.5;return r<i+.05?e:{...e,verdict:"pass",reasoning:`${e.reasoning}
|
|
3
3
|
|
|
4
4
|
(rc.299 math-salvage: re-computed from cited rgb(${a.r},${a.g},${a.b}) on rgb(${n.r},${n.g},${n.b}) → ${r.toFixed(2)}:1 against threshold ${i}:1 → PASS. Original AI verdict was FAIL but its arithmetic appears wrong on this element. One-way salvage: only flips fail→pass, never pass→fail.)`}}function vr(e,t){var n;const a=new Set;for(const o of t)for(const r of((n=o.axeRulesEvaluated)==null?void 0:n.incomplete)??[])for(const i of r.elements??[])a.add(`${r.ruleId}::${i.selector}`);return e.map(yr).map(wr).filter(o=>o.verdict==="fail"&&a.has(`${o.ruleId}::${o.selector}`)&&!/aria-hidden=["']true["']/.test(o.selector))}function rc(e){const t=/rgba?\(\s*([0-9.]+)[,\s]+([0-9.]+)[,\s]+([0-9.]+)(?:[,/\s]+([0-9.]+))?\s*\)/gi,a=[];let n;for(;(n=t.exec(e))!==null&&(a.push({r:Math.round(parseFloat(n[1])),g:Math.round(parseFloat(n[2])),b:Math.round(parseFloat(n[3])),a:n[4]!==void 0?parseFloat(n[4]):1}),!(a.length>=4)););return a}function ic(e){const t=/([34](?:\.\d+)?|7(?:\.\d+)?)\s*:\s*1/g,a=[];let n;for(;(n=t.exec(e))!==null;){const o=parseFloat(n[1]);(o===3||o===4.5||o===7)&&a.push(o)}return a.length>0?a[a.length-1]:null}function sc(e,t){const a=d=>{const l=d/255;return l<=.03928?l/12.92:Math.pow((l+.055)/1.055,2.4)},n=d=>.2126*a(d.r)+.7152*a(d.g)+.0722*a(d.b),o=n(e),r=n(t),i=Math.max(o,r),c=Math.min(o,r);return(i+.05)/(c+.05)}const cc=Object.freeze(Object.defineProperty({__proto__:null,applyContrastMathSalvage:wr,applySelfCorrectionSalvage:yr,clearAllResolutions:oc,deleteResolution:nc,filterRelevant:vr,getAllResolutions:br,getResolutionsForPage:va,saveResolution:We,saveResolutions:ac},Symbol.toStringTag,{value:"Module"})),Et="interactiveAuditResults";function $n(e,t){return`${e}::${t}`}async function Nt(){try{const t=(await chrome.storage.local.get(Et))[Et];return t&&typeof t=="object"?t:{}}catch{return{}}}async function Tn(e){await chrome.storage.local.set({[Et]:e})}async function te(e){const t=await Nt(),a={componentId:e.componentId,criterionId:e.criterionId,verdict:e.verdict,reasoning:e.reasoning,confidence:e.confidence,pageUrl:e.pageUrl,finishedAt:e.finishedAt,costUsd:e.costUsd,model:e.model,inputHash:e.inputHash};t[$n(e.componentId,e.criterionId)]=a,await Tn(t)}async function _e(e,t){return(await Nt())[$n(e,t)]??null}async function Ar(){const e=await Nt();return Object.values(e)}async function Xt(e){return(await Ar()).filter(a=>a.componentId===e)}async function lc(e,t){const a=await Nt();delete a[$n(e,t)],await Tn(a)}async function dc(){await chrome.storage.local.remove(Et)}async function Aa(e){const t=new Set;for(const n of e)n&&t.add(n);if(t.size===0)return[];const a=[];for(const n of t)try{const o=await Xt(n);for(const r of o)a.push({criterionId:r.criterionId,ruleId:`ai-interactive::${r.criterionId}`,pageUrl:r.pageUrl,verdict:r.verdict,reasoning:r.reasoning})}catch{}return a}async function uc(){try{const e=await Nt();let t=!1;for(const a of Object.keys(e)){const n=e[a];if(n.steps&&n.steps.length>0){const o={componentId:n.componentId,criterionId:n.criterionId,verdict:n.verdict,reasoning:n.reasoning,confidence:n.confidence,pageUrl:n.pageUrl,finishedAt:n.finishedAt,costUsd:n.costUsd,model:n.model};e[a]=o,t=!0}}t&&await Tn(e)}catch{try{await chrome.storage.local.remove(Et)}catch{}}}const Tp=Object.freeze(Object.defineProperty({__proto__:null,clearAllInteractiveAuditResults:dc,getAllInteractiveAuditResults:Ar,getInteractiveAuditResult:_e,getInteractiveAuditResultsForComponent:Xt,loadInteractiveResultsForScoring:Aa,migrateStrippedShape:uc,removeInteractiveAuditResult:lc,saveInteractiveAuditResult:te},Symbol.toStringTag,{value:"Module"})),hc=Z("walkthrough-acks"),sn="walkthroughAcks";function En(e,t,a){return`${e}::${t}::${a}`}async function ka(){try{const t=(await chrome.storage.local.get(sn))[sn];return t&&typeof t=="object"?t:{}}catch{return{}}}async function kr(e){await chrome.storage.local.set({[sn]:e})}async function Ep(e){if(!e.note||!e.note.trim())throw new Error("Acknowledgement requires a non-empty note explaining manual verification.");const t=await ka();t[En(e.componentId,e.criterionId,e.pageUrl)]={...e,note:e.note.trim()},await kr(t),hc.info(`acknowledged ${e.criterionId} on ${e.pageUrl}`)}async function Rp(e,t,a){const n=await ka();delete n[En(e,t,a)],await kr(n)}async function pc(e,t,a){return(await ka())[En(e,t,a)]??null}async function fc(){const e=await ka();return Object.values(e)}async function xr(e){const t=new Set;for(const n of e)n&&t.add(n);return t.size===0?[]:(await fc()).filter(n=>t.has(n.componentId))}const Qt={A:4,B:3,C:2,D:1,F:0},gc=["F","D","C","B","A"];function mc(e,t){const a=(Qt[e]+Qt[t])/2,n=Math.ceil(a-.5);return gc[n]}function bc(e,t){return Qt[e]<=Qt[t]?e:t}const Fa=[{id:"contrast",label:"Color contrast",rules:["color-contrast","color-contrast-enhanced","link-in-text-block"],needsManualCheck:!1},{id:"alt",label:"Image alt text",rules:["image-alt","input-image-alt","area-alt","svg-img-alt","object-alt","role-img-alt"],needsManualCheck:!1},{id:"labels",label:"Form labels",rules:["label","select-name","aria-input-field-name","aria-toggle-field-name","form-field-multiple-labels","label-content-name-mismatch","autocomplete-valid"],needsManualCheck:!1},{id:"names",label:"Buttons / links named",rules:["button-name","input-button-name","link-name","empty-button","empty-heading","empty-link"],needsManualCheck:!1},{id:"keyboard",label:"Keyboard operability",rules:["nested-interactive","tabindex","frame-focusable-content","no-focusable-content"],needsManualCheck:!0},{id:"structure",label:"Landmarks + heading structure",rules:["heading-order","page-has-heading-one","landmark-one-main","landmark-no-duplicate-banner","landmark-no-duplicate-contentinfo","landmark-no-duplicate-main","region","bypass"],needsManualCheck:!1}];function yc(e){const t=new Map;for(const a of e){const n=`${a.ruleId}::${a.target.selector}`;t.has(n)||t.set(n,a)}return Array.from(t.values())}function wc(e,t,a="2.1",n="AA",o,r,i,c,d){const l=os({audits:e??[],igtRuns:(t==null?void 0:t.runs)??[],workflows:(t==null?void 0:t.workflows)??De,acknowledgedMatchKeys:o??new Set,incompleteResolutions:r,interactiveAuditResults:i,auditScope:c,humanCriterionVerdicts:d}),p={},m={},u=[],g=[],h=[];let f=0;for(const b of l.values()){if(b.state==="not-evaluated"){u.push(b.criterionId);continue}if(f++,b.state==="fail"){h.push(b.criterionId);const k=[];for(const v of b.evidence)v.outcome==="fail"&&k.push({source:v.source,ruleOrStepId:v.ruleOrStepId,selector:v.selector,notes:v.notes,pageUrl:v.pageUrl});k.length>0&&(m[b.criterionId]=k)}if(b.state==="inconclusive"){g.push(b.criterionId);const k=[];for(const v of b.evidence)if(v.outcome==="incomplete"){const s=v.source==="ai"?"ai-uncertain":"axe-incomplete";k.push({source:s,ruleOrStepId:v.ruleOrStepId,resolutionHint:v.resolutionHint,elements:v.elements,pageUrl:v.pageUrl})}else v.outcome==="skip"&&k.push({source:"igt-skip",ruleOrStepId:v.ruleOrStepId,resolutionHint:v.resolutionHint});k.length>0&&(p[b.criterionId]=k)}}const w=rs(l);return{targetVersion:Mt,targetLevel:wn,totalApplicable:l.size,evaluated:f,untestedCriteria:u,inconclusiveCriteria:g,failingCriteria:h,inconclusiveReasons:p,failingReasons:m,canClaimConformance:w.canClaimConformance}}function xa(e,t,a,n,o,r,i,c,d){const l=n??new Set,p=yc(e).filter(N=>!l.has(N.matchKey)),m=p.filter(N=>N.needsReview),u=p.filter(N=>!N.needsReview),g={critical:0,serious:0,moderate:0,minor:0,unique:u.length};for(const N of u)g[N.impact]++;let h;t&&(h=vc(t.runs,t.workflows),g.serious+=h.failedRequired,g.moderate+=h.failedAdvisory,g.unique+=h.failedRequired+h.failedAdvisory);const f=[],w=[];if(o&&o.length>0){const N=new Set;if(r)for(const _ of r)N.add(`${_.criterionId}::${_.pageUrl}`);const C=new Set;for(const _ of o){if(_.verdict==="pass")continue;const M=`${_.criterionId}::${_.pageUrl}`;if(C.has(M))continue;C.add(M);const L=_.verdict==="fail"?"serious":"moderate",W={criterionId:_.criterionId,pageUrl:_.pageUrl,verdict:_.verdict,impact:L,reasoning:_.reasoning};if(N.has(M)){w.push(W);continue}f.push(W),g[L]++,g.unique++}}const b=new Map;for(const N of Fa)for(const C of N.rules)b.set(C,N);const k=new Map;for(const N of Fa)k.set(N.id,{count:0,rules:new Set});for(const N of fr(u)){const C=b.get(N.ruleId);if(!C)continue;const _=k.get(C.id);_.count++,_.rules.add(N.ruleId)}const v=Fa.map(N=>{const C=k.get(N.id);let _="pass";return C.count>0?_="fail":N.needsManualCheck&&(_="unchecked"),{id:N.id,label:N.label,status:_,violationCount:C.count,rules:Array.from(C.rules),needsManualCheck:N.needsManualCheck}}),s=g.critical>0,x=g.serious>0,$=(()=>{if(!o||o.length===0||!r||r.length===0)return o;const N=new Set;for(const C of r)N.add(`${C.criterionId}::${C.pageUrl}`);return o.map(C=>N.has(`${C.criterionId}::${C.pageUrl}`)?{...C,verdict:"pass",reasoning:`Human verified: ${C.reasoning}`}:C)})(),A=a?wc(a,t,"2.1","AA",n,d,$,c,i):void 0,S=A?!A.canClaimConformance:!1;let F;g.critical>=5?F="F":g.critical>=1?F="D":g.serious>=5||g.serious>=1?F="C":g.moderate>=3||g.moderate>=1?F="B":(g.minor>=1,F="A"),S&&F==="A"&&(F="B");const T=v.filter(N=>N.status==="fail").length;let I;return g.critical>0||T>=4?I="critical":g.serious>=3||T>=2?I="high":g.serious>=1||g.moderate>=3?I="moderate":I="low",{letter:F,risk:I,caps:{cappedAtC:s,cappedAtB:x,cappedAtBByCoverage:S},totals:g,categories:v,coverage:A,manual:h,reviewQueue:{count:m.length,rules:Array.from(new Set(m.map(N=>N.ruleId)))},walkthroughFindings:f,walkthroughAcknowledgements:w}}function vc(e,t){new Map(t.map(c=>[c.id,c]));const a=new Map(e.map(c=>[c.workflowId,c]));let n=0,o=0,r=0;const i=t.map(c=>{const d=a.get(c.id);let l=0,p=0,m=0,u=0,g=0;for(const h of c.steps){const f=d==null?void 0:d.steps[h.id];f&&(g++,f.status==="pass"?l++:f.status==="skip"?u++:f.status==="fail"&&(h.severity==="required"?p++:m++))}return g===c.steps.length&&r++,n+=p,o+=m,{workflowId:c.id,name:c.name,passed:l,failedRequired:p,failedAdvisory:m,skipped:u,answered:g,total:c.steps.length}});return{workflowsCompleted:r,workflowsTotal:t.length,failedRequired:n,failedAdvisory:o,perWorkflow:i}}const Ac={low:"Low exposure — no critical or serious violations across the three layers (axe-core, wcagcheckr DOM analyzers, AI walkthroughs). All 50 WCAG 2.1 AA criteria have an evaluation path; spot-check content-meaning criteria (caption accuracy, alt-text correctness) before any formal conformance claim.",moderate:"Moderate exposure — some serious or multiple moderate violations. Should be addressed but not lawsuit-emergency level.",high:"High exposure — multiple serious violations OR multiple lawsuit-magnet categories failing. ADA Title III suits target sites at this level.",critical:"Critical exposure — at least one critical-impact violation OR most lawsuit-magnet categories failing. Sites in this state are demand-letter targets."},Ir={low:"✓ Off the radar",moderate:"⚡ Some lawsuit exposure",high:"⚠️ High target risk",critical:"🚨 Lawsuit magnet"},Sr={low:"Your site clears the automated checks ADA-targeting tools use to identify lawsuit victims. wcagcheckr runs three layers (axe-core, our DOM analyzers, AI walkthroughs) covering all 50 WCAG 2.1 AA criteria — most demand-letter mills only run axe.",moderate:"Some failures. Not the easiest target, but the issues here are exactly what automated targeting tools look for. Switch to Developer mode for the issue list and fix-it walkthroughs.",high:"Multiple serious failures. Automated lawsuit-screening tools will flag this site. Real risk of being targeted with a demand letter — Developer mode has the full issue list and fix-it walkthroughs.",critical:"Your site fails the automated WCAG checks demand-letter mills run on thousands of sites to identify lawsuit targets. Without remediation, expect to be flagged. Developer mode has the full issue list plus fix-it guidance for each."},oo={A:"No critical, serious, or moderate violations across the three layers (axe-core, wcagcheckr DOM analyzers, AI walkthroughs). All 50 WCAG 2.1 AA criteria evaluated; content-meaning checks (caption accuracy, alt-text correctness, sensory cues) benefit from a human spot-check before formal conformance claims.",B:"Only moderate-impact violations detected. No serious or critical findings.",C:"Has at least one serious-impact violation. Automatically capped here regardless of how few.",D:"Has at least one critical-impact violation. Automatically capped here regardless of how few.",F:"Multiple critical-impact violations detected. Lawsuit-magnet profile."};function Op(e,t){if(e!=="B")return oo[e];const{moderateCount:a,blockingCriteriaCount:n}=t;return n>0&&a===0?`No critical, serious, or moderate violations detected — but ${n} WCAG 2.1 AA criteri${n===1?"on has":"a have"} not yet been affirmatively evaluated (untested or inconclusive). The grade is capped at B until coverage is complete. Open the side panel's "Coverage gaps" panel under the grade and record a verdict for each.`:n>0&&a>0?`${a} moderate WCAG finding${a===1?"":"s"} detected, AND ${n} criteri${n===1?"on has":"a have"} not yet been affirmatively evaluated. Address both before any formal conformance claim.`:oo.B}const lt={"ai-alt-misleading":{summary:`The AI vision model judged this image's alt text is empty, misleading, or generic when the image is actually informative or functional. Use the AI assessment (shown above this recipe) to understand what the image depicts, then write descriptive alt text conveying its meaning to a screen-reader user. Use alt="" only when the image is genuinely decorative (a divider, a flourish, an icon next to text that already labels the action). Decorative is rare — when in doubt, describe.`,snippetLang:"html",snippet:`<!-- Failing — empty alt on an informational logo -->
|
|
5
5
|
<img src="/assets/logo.png" alt="">
|
|
@@ -369,13 +369,13 @@ p a { color: #06f; text-decoration: underline; }`,snippetLang:"css"},"document-t
|
|
|
369
369
|
<track kind="captions" src="talk.en.vtt" srclang="en" label="English">
|
|
370
370
|
</video>`},"no-autoplay-audio":{summary:"WCAG 1.4.2 — Audio (or video with audio) plays automatically for more than 3 seconds with no built-in control to pause/stop or mute it. Remove autoplay, or provide an obvious, keyboard-operable pause/mute control near the start of the page."},"scrollable-region-focusable":{summary:`WCAG 2.1.1 — A scrollable region (overflow content) can't be reached or scrolled by keyboard. Make the container focusable with tabindex="0" (and an accessible name) so keyboard users can scroll it, or ensure a focusable child sits inside it.`},"server-side-image-map":{summary:"WCAG 2.1.1 — A server-side image map (<img ismap>) can only be used with a pointer. Replace it with a client-side image map (<map>/<area> with alt text) or, better, real <a> links so keyboard users can operate it."},accesskeys:{summary:"WCAG 4.1.1 — Two elements share the same accesskey value, so the shortcut is ambiguous. Make every accesskey unique, or remove them — accesskeys frequently collide with browser/AT shortcuts and are rarely worth keeping."},"css-orientation-lock":{summary:"WCAG 1.3.4 — CSS (a transform/orientation media query) locks content to a single orientation. Remove the lock so the page works in both portrait and landscape; users with fixed-mount devices depend on this."},"avoid-inline-spacing":{summary:'WCAG 1.4.12 — Inline style="" forces line-height, letter-spacing, or word-spacing with !important, preventing users from overriding text spacing. Move spacing to a stylesheet without !important so the text-spacing bookmarklet/user styles can adjust it.'},"summary-name":{summary:"WCAG 4.1.2 — A <summary> (disclosure control) has no accessible name. Give it visible text describing what it expands."},"frame-title-unique":{summary:"WCAG 4.1.2 — Multiple frames share the same title, so users can't tell them apart. Give each <iframe>/<frame> a unique, descriptive title naming its specific content."},"frame-focusable-content":{summary:'WCAG 2.1.1 — An <iframe> with tabindex="-1" contains focusable elements, creating a keyboard trap / unreachable content. Remove tabindex="-1" from the frame, or make the embedded content non-focusable.'}};function Mp(e){return lt[e]??null}const kc=new Set(["region","landmark-complementary-is-top-level","landmark-banner-is-top-level","landmark-contentinfo-is-top-level","landmark-main-is-top-level","link-in-text-block","scrollable-region-focusable","label-content-name-mismatch","identical-links-same-purpose"]);function xc(e){if(e.needsReview)return{level:"needs-review",label:"Needs human confirmation",rationale:"Automation could not decide this either way — confirm against the live page before treating it as a defect."};if(e.ai){const t=Math.round((e.ai.confidence??0)*100);return{level:"ai-verified",label:`AI-verified (${t}%)`,rationale:t>=80?"A vision/text model judged this with high confidence (reasoning shown above).":"A model flagged this with moderate confidence — sanity-check its reasoning above before applying."}}return e.flakyAcrossRuns?{level:"medium",label:"Medium",rationale:"This did not reproduce in every audit run — it may be a transient (animation, lazy load, third-party script). Confirm it on a stable render before fixing."}:e.target.opacityContext&&(e.target.opacityContext.self<1||e.target.opacityContext.ancestor<1)?{level:"medium",label:"Medium",rationale:"The element (or an ancestor) was partially transparent at audit time — likely a mid-animation opacity, not a real color choice. Verify it is still low-contrast once fully rendered before changing colors."}:kc.has(e.ruleId)?{level:"medium",label:"Medium",rationale:"This is a context-dependent best-practice check that can over-flag legitimate patterns. Apply the fix, but first confirm the flagged element genuinely fits the rule."}:{level:"high",label:"High",rationale:""}}function Cr(e){const t=xc(e);return t.rationale?`**Confidence:** ${t.label} — ${t.rationale}`:`**Confidence:** ${t.label}`}const cn="aiColorSuggestions";async function Ia(){try{const t=(await chrome.storage.local.get(cn))[cn];return t&&typeof t=="object"?t:{}}catch{return{}}}async function $r(e){await chrome.storage.local.set({[cn]:e})}async function Dp(e){const t=await Ia();t[e.matchKey]=e,await $r(t)}async function Np(e){if(e.length===0)return;const t=await Ia();for(const a of e)t[a.matchKey]=a;await $r(t)}async function Lp(e){return(await Ia())[e]??null}async function Ic(){return await Ia()}const Tr={AT:{name:"Austria — Beschwerdestelle Bundessozialamt",url:"https://www.sozialministeriumservice.at/"},BE:{name:"Belgium — Unia / Bosa",url:"https://accessibility.belgium.be/"},BG:{name:"Bulgaria — State e-Government Agency",url:"https://e-gov.bg/"},CY:{name:"Cyprus — Department of Electronic Communications",url:"https://www.mtcw.gov.cy/"},CZ:{name:"Czechia — Ministry of the Interior",url:"https://www.mvcr.cz/"},DE:{name:"Germany — Bundesfachstelle Barrierefreiheit",url:"https://www.bundesfachstelle-barrierefreiheit.de/"},DK:{name:"Denmark — Digitaliseringsstyrelsen",url:"https://www.digst.dk/"},EE:{name:"Estonia — Ministry of Economic Affairs and Communications",url:"https://mkm.ee/"},ES:{name:"Spain — Observatorio de Accesibilidad Web",url:"https://administracionelectronica.gob.es/"},FI:{name:"Finland — Etelä-Suomen aluehallintovirasto",url:"https://www.saavutettavuusvaatimukset.fi/"},FR:{name:"France — Défenseur des droits",url:"https://www.defenseurdesdroits.fr/"},GR:{name:"Greece — Ministry of Digital Governance",url:"https://mindigital.gr/"},HR:{name:"Croatia — Information Commissioner",url:"https://www.pristupinfo.hr/"},HU:{name:"Hungary — Information Society Authority",url:"https://nmhh.hu/"},IE:{name:"Ireland — National Disability Authority",url:"https://nda.ie/"},IT:{name:"Italy — Agenzia per l'Italia Digitale",url:"https://www.agid.gov.it/"},LT:{name:"Lithuania — Information Society Development Committee",url:"https://ivpk.lrv.lt/"},LU:{name:"Luxembourg — Service Information et Presse",url:"https://accessibilite.public.lu/"},LV:{name:"Latvia — VARAM",url:"https://www.varam.gov.lv/"},MT:{name:"Malta — MITA",url:"https://www.mita.gov.mt/"},NL:{name:"Netherlands — Logius",url:"https://www.digitoegankelijk.nl/"},PL:{name:"Poland — Ministry of Digital Affairs",url:"https://www.gov.pl/web/dostepnosc-cyfrowa/"},PT:{name:"Portugal — AMA",url:"https://accessmonitor.acessibilidade.gov.pt/"},RO:{name:"Romania — ANCOM",url:"https://www.ancom.ro/"},SE:{name:"Sweden — DIGG",url:"https://www.digg.se/"},SI:{name:"Slovenia — Ministry of Public Administration",url:"https://www.gov.si/"},SK:{name:"Slovakia — Ministry of Investments",url:"https://www.mirri.gov.sk/"}},Er=(e,t)=>`${e} is committed to making its website accessible, in accordance with national legislation transposing Directive (EU) 2016/2102 of the European Parliament and of the Council. This accessibility statement applies to ${t}.`,Rr=(e,t)=>`${e} is committed to making its products and services accessible to all users, in accordance with the European Accessibility Act (Directive (EU) 2019/882) and its national transpositions, enforceable from 28 June 2025. This accessibility statement applies to ${t}.`,Or=(e,t,a,n="EN 301 549 v3.2.1")=>{const o=`Web Content Accessibility Guidelines (WCAG) ${t} level ${a}`;switch(e){case"full":return`This website is fully conformant with ${o} and the corresponding ${n} clauses.`;case"partial":return`This website is partially conformant with ${o} and the corresponding ${n} clauses. "Partially conformant" means that some parts of the content do not fully conform to the accessibility standard. The non-accessible content is listed below.`;case"non-conformant":return`This website is currently NOT conformant with ${o}. The non-accessible content is listed below, along with our plan to address it.`}},Mr=(e,t)=>`The accessibility of this website was last evaluated on ${t}. The evaluation was conducted by ${e} using automated testing (wcagcheckr — multi-state matrix audit across 144 keyboard / focus / theme / breakpoint / direction combinations, AI-augmented walkthroughs for 5 criteria axe-core cannot evaluate, plus DOM-criterion analyzers for 16 additional criteria). The audit is conformant with the EN 301 549 evaluation methodology.`;function Sc(e,t){const a=e.language||"English",n=e.auditedBy||"wcagcheckr (self-evaluation)",o=e.wcagVersion||"2.1",r=e.wcagLevel||"AA",i=[];if(i.push(`# Accessibility Statement — ${e.organizationName}`),i.push(""),i.push(`*Statement prepared on ${e.preparedAt}. Language: ${a}.*`),i.push(""),i.push(e.framing==="wad"?Er(e.organizationName,e.siteUrl):Rr(e.organizationName,e.siteUrl)),i.push(""),i.push("## Conformance status"),i.push(""),i.push(Or(e.conformanceStatus,o,r)),i.push(""),t.length>0){i.push("## Non-accessible content"),i.push(""),i.push("The following content is not yet accessible:"),i.push("");for(const c of t){const d=c.exemption?c.exemption==="disproportionate-burden"?" *(claimed under disproportionate burden — Article 5 WAD / Article 14 EAA)*":" *(not subject to applicable legislation)*":"";i.push(`- **WCAG ${c.criterion}** — ${c.description}${d}`)}i.push("")}else e.conformanceStatus==="full"&&(i.push("## Non-accessible content"),i.push(""),i.push("No non-accessible content is currently known. If you encounter any accessibility barriers, please contact us using the feedback details below."),i.push(""));if(i.push("## Preparation of this accessibility statement"),i.push(""),i.push(Mr(n,e.evaluatedAt)),i.push(""),i.push("## Feedback and contact information"),i.push(""),i.push(`If you experience accessibility barriers on ${e.siteUrl}, please contact us so we can address them:`),i.push(""),e.contact.email&&i.push(`- **Email:** ${e.contact.email}`),e.contact.phone&&i.push(`- **Phone:** ${e.contact.phone}`),e.contact.formUrl&&i.push(`- **Web form:** ${e.contact.formUrl}`),i.push(""),e.framing==="wad"&&e.countryCode){const c=Tr[e.countryCode];c&&(i.push("## Enforcement procedure"),i.push(""),i.push(`If you are not satisfied with the response to your accessibility complaint, you may contact the national enforcement body for ${c.name}:`),i.push(""),i.push(`${c.url}`),i.push(""))}return e.framing==="eaa"&&(i.push("## Enforcement"),i.push(""),i.push("Enforcement of the European Accessibility Act is carried out by national market-surveillance authorities in each EU member state. If your accessibility complaint is not addressed satisfactorily, you may contact the market-surveillance authority of the EU member state in which you are located."),i.push("")),i.push("---"),i.push(""),i.push(`_Statement generated by wcagcheckr. Audit methodology details + cryptographic anchoring of the audit results are available on request from ${e.contact.email??"the contact above"}._`),i.join(`
|
|
371
371
|
`)}function Cc(e,t){const a=l=>l==null?"":String(l).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,"""),n=e.language||"English",o=e.languageCode||"en",r=e.auditedBy||"wcagcheckr (self-evaluation)",i=e.wcagVersion||"2.1",c=e.wcagLevel||"AA",d=[];if(d.push(`<article lang="${a(o)}">`),d.push(`<h1>Accessibility Statement — ${a(e.organizationName)}</h1>`),d.push(`<p><em>Statement prepared on ${a(e.preparedAt)}. Language: ${a(n)}.</em></p>`),d.push(`<p>${a(e.framing==="wad"?Er(e.organizationName,e.siteUrl):Rr(e.organizationName,e.siteUrl))}</p>`),d.push("<h2>Conformance status</h2>"),d.push(`<p>${a(Or(e.conformanceStatus,i,c))}</p>`),t.length>0){d.push("<h2>Non-accessible content</h2>"),d.push("<p>The following content is not yet accessible:</p>"),d.push("<ul>");for(const l of t){const p=l.exemption?l.exemption==="disproportionate-burden"?" <em>(claimed under disproportionate burden — Article 5 WAD / Article 14 EAA)</em>":" <em>(not subject to applicable legislation)</em>":"";d.push(`<li><strong>WCAG ${a(l.criterion)}</strong> — ${a(l.description)}${p}</li>`)}d.push("</ul>")}else e.conformanceStatus==="full"&&(d.push("<h2>Non-accessible content</h2>"),d.push("<p>No non-accessible content is currently known. If you encounter any accessibility barriers, please contact us using the feedback details below.</p>"));if(d.push("<h2>Preparation of this accessibility statement</h2>"),d.push(`<p>${a(Mr(r,e.evaluatedAt))}</p>`),d.push("<h2>Feedback and contact information</h2>"),d.push(`<p>If you experience accessibility barriers on ${a(e.siteUrl)}, please contact us so we can address them:</p>`),d.push("<ul>"),e.contact.email&&d.push(`<li><strong>Email:</strong> <a href="mailto:${a(e.contact.email)}">${a(e.contact.email)}</a></li>`),e.contact.phone&&d.push(`<li><strong>Phone:</strong> ${a(e.contact.phone)}</li>`),e.contact.formUrl&&d.push(`<li><strong>Web form:</strong> <a href="${a(e.contact.formUrl)}">${a(e.contact.formUrl)}</a></li>`),d.push("</ul>"),e.framing==="wad"&&e.countryCode){const l=Tr[e.countryCode];l&&(d.push("<h2>Enforcement procedure</h2>"),d.push(`<p>If you are not satisfied with the response to your accessibility complaint, you may contact the national enforcement body for ${a(l.name)}: <a href="${a(l.url)}">${a(l.url)}</a></p>`))}return e.framing==="eaa"&&(d.push("<h2>Enforcement</h2>"),d.push("<p>Enforcement of the European Accessibility Act is carried out by national market-surveillance authorities in each EU member state. If your accessibility complaint is not addressed satisfactorily, you may contact the market-surveillance authority of the EU member state in which you are located.</p>")),d.push("<hr>"),d.push(`<p><small>Statement generated by wcagcheckr. Audit methodology details + cryptographic anchoring of the audit results are available on request from ${a(e.contact.email??"the contact above")}.</small></p>`),d.push("</article>"),d.join(`
|
|
372
|
-
`)}function $c(e,t={},a=new Set){var c;const n=e.flatMap(d=>d.violations),o=new Map;for(const d of n){const l=Ne(d.ruleId,d.target.selector);o.has(l)||o.set(l,d)}const r=new Map;for(const d of o.values()){const l=d.wcagCriterion;if(!l||a.has(l))continue;const p=r.get(l)??[];p.push(d),r.set(l,p)}const i=[];for(const[d,l]of r.entries()){const p=l[0];let m=t[d];if(!m){const u=lt[p.ruleId];u!=null&&u.summary?m=((c=u.summary.split(/[.!?]\s+/)[0])==null?void 0:c.trim())??p.description:m=p.description}m=m.replace(new RegExp("^\\s*WCAG\\s+"+d.replace(/\./g,"\\.")+"\\b[\\s:.\\u2014-]*","i"),"").trim(),l.length>1&&(m+=` (affects ${l.length} components on the audited page${e.length>1?"s":""})`),i.push({criterion:d,description:m})}return i.sort((d,l)=>{const p=g=>g.split(".").map(h=>parseInt(h,10)),m=p(d.criterion),u=p(l.criterion);for(let g=0;g<Math.max(m.length,u.length);g++){const h=(m[g]??0)-(u[g]??0);if(h!==0)return h}return 0}),i}function Tc(e){const t=$c(e.results,e.customDescriptions,e.dismissedCriteria);return e.outputFormat==="html"?Cc(e.inputs,t):Sc(e.inputs,t)}function Pp(e){if(e.reduce((n,o)=>n+o.violations.length,0)===0)return"full";const a=new Set;for(const n of e)for(const o of n.violations)o.wcagCriterion&&a.add(o.wcagCriterion);return a.size>=10?"non-conformant":"partial"}function et(e,t,a,n,o,r,i,c,d){var k,v,s,x;const l=xa(e,t,a,n,o,r,i,c,d),p=a??[],m=new Set((r??[]).map($=>$.criterionId)),u={};for(const $ of o??[]){const A=m.has($.criterionId)?"pass":$.verdict;$.criterionId==="2.4.3"?u.focusOrder=A:$.criterionId==="2.1.2"?u.keyboardTrap=A:$.criterionId==="2.4.7"?u.focusVisible=A:$.criterionId==="1.3.2"?u.readingOrder=A:$.criterionId==="1.4.11"&&(u.nonTextContrast=A)}const g=vr(d??[],p).map($=>({ruleId:$.ruleId,impact:"serious",selector:$.selector})),h=Qs({violations:e,auditRan:p.length>0,manualRuns:(t==null?void 0:t.runs)??[],workflows:(t==null?void 0:t.workflows)??[],heuristicCounts:{tabOrder:((v=(k=p[0])==null?void 0:k.tabOrderIssues)==null?void 0:v.length)??0,readingOrder:((x=(s=p[0])==null?void 0:s.readingOrderIssues)==null?void 0:x.length)??0},interactiveAuditVerdicts:u,aiResolvedFails:g}),f=mc(l.letter,h.overallLetter);return{combined:l.totals.critical>0||l.totals.serious>0?bc(f,l.letter):f,severity:l.letter,breadth:h.overallLetter,score:l,subScore:h}}const ln="humanCriterionVerdicts";async function Rn(){var a;if(typeof chrome>"u"||!((a=chrome.storage)!=null&&a.local))return[];const t=(await chrome.storage.local.get(ln))[ln];return Array.isArray(t)?t:[]}async function Dr(e){const t=new Set;for(const n of e)n&&t.add(n);return t.size===0?[]:(await Rn()).filter(n=>t.has(n.componentId))}async function Fp(e){if(!e.note||!e.note.trim())throw new Error("Human criterion verdict requires a non-empty note explaining the determination.");const a=(await Rn()).filter(n=>!(n.componentId===e.componentId&&n.criterionId===e.criterionId));a.push({...e,note:e.note.trim()}),await Nr(a)}async function _p(e,t){const n=(await Rn()).filter(o=>!(o.componentId===e&&o.criterionId===t));await Nr(n)}async function Nr(e){var t;typeof chrome>"u"||!((t=chrome.storage)!=null&&t.local)||await chrome.storage.local.set({[ln]:e})}function qt(e){return/\[Cascade resolution — rc\.\d+ escalation\]/.test(e)}function Up(e){return/\[Manual verification — rc\.\d+\]/.test(e)}function Wp(e){const t=e.match(/\[Cascade resolution — rc\.\d+ (fallback-\d|escalation)\]/);if(!t)return null;const a=t[1];return a==="fallback-1"||a==="fallback-2"||a==="escalation"?a:null}const Lr={ruleIds:["image-alt","label","button-name","link-name","color-contrast","document-title","html-has-lang"],thresholds:{moderate:1,high:2,critical:4},headlines:{low:"Low risk. Your site passes the checks common automated lawsuit-targeting scanners run today — not likely automated lawfare target. This isn't a guarantee: scanners add checks over time, so re-audit as your site changes.",moderate:"Moderate risk. A demand-letter scanner could flag at least one issue. Fix to drop off the easy-target list.",high:"High risk. Multiple findings here are common patterns demand-letter mills target.",critical:"Critical risk. Your site has many high-frequency patterns ADA-targeting firms scan for."},version:0};Lr.ruleIds;function Pr(){var e;return((e=hr())==null?void 0:e.lawfare)??Lr}function Fr(e,t=Pr()){return t.ruleIds.includes(e)}function ro(e,t=Pr()){const a=new Set(t.ruleIds),n=new Map;for(const l of e)a.has(l.ruleId)&&n.set(l.ruleId,(n.get(l.ruleId)??0)+1);const o=Array.from(n.entries()).map(([l,p])=>({ruleId:l,instanceCount:p})).sort((l,p)=>p.instanceCount-l.instanceCount),r=o.length,i=Array.from(n.values()).reduce((l,p)=>l+p,0);let c;return r<t.thresholds.moderate?c="low":r<t.thresholds.high?c="moderate":r<t.thresholds.critical?c="high":c="critical",{tier:c,letter:c==="low"?"A":c==="moderate"?"C":c==="high"?"D":"F",flaggedRuleCount:r,flaggedInstanceCount:i,flaggedRules:o,headline:t.headlines[c],rubricVersion:t.version}}function Ec(e,t){return e.flatMap(a=>a.violations).filter(a=>!a.needsReview&&!t.has(a.matchKey))}function Rc(e,t){const a=[],n={};for(const o of e){const r=Ec(o.results,t);a.push(...r),n[o.url]=ro(r).letter}return{site:ro(a),perUrlLetter:n}}function Gp(e){return e.filter(t=>!t.needsReview&&Fr(t.ruleId))}const dn="manualCriterionAttestations";function Vp(e,t,a){const n=a==="attested-conforms"?"conforms":"does not apply",o=t.slice(0,10);return`I, the operator, manually evaluated ${e} on ${o} and, to the best of my knowledge, it ${n}. This is my own assessment; wcagcheckr did not verify it.`}const _r=`About this grade — basis and limits. This grade reflects (1) what wcagcheckr verified programmatically and by AI evaluation ("proven in code") and (2) the operator's own attestations on the content-meaning criteria the tool cannot verify ("claimed by the operator"). An "A" is issued only after the operator has affirmatively confirmed every such criterion as conforming or not-applicable; any criterion the operator leaves "not addressed" holds the grade below A. This grade is NOT a certification, warranty, or guarantee of conformance with WCAG, the ADA, or any law, and must not be relied upon as proof of legal compliance. The operator is solely responsible for the accuracy of their attestations and for any criteria outside wcagcheckr's automated scope.`,Sa=[{id:"1.1.1",reason:"alt text exists, but only a human can confirm it conveys the image's meaning/purpose"},{id:"1.2.1",reason:"a transcript/alternative exists and is accurate for audio-only / video-only media"},{id:"1.2.2",reason:"captions are present AND accurate/synced for prerecorded audio"},{id:"1.2.3",reason:"audio description or a full text alternative covers the prerecorded video"},{id:"1.2.4",reason:"captions are present AND accurate for LIVE audio"},{id:"1.2.5",reason:"audio description quality is sufficient for prerecorded video"},{id:"1.3.3",reason:"instructions do not rely on sensory characteristics (shape/size/position/sound) alone"},{id:"1.4.1",reason:"information is not conveyed by color alone in content the tool could not classify"}];async function On(){var a;if(typeof chrome>"u"||!((a=chrome.storage)!=null&&a.local))return[];const t=(await chrome.storage.local.get(dn))[dn];return Array.isArray(t)?t:[]}async function Ur(e){const t=new Set;for(const n of e)n&&t.add(n);return t.size===0?[]:(await On()).filter(n=>t.has(n.componentId))}async function jp(e){if(e.kind!=="not-addressed"&&(!e.note||!e.note.trim()))throw new Error("An attestation of conformance / N-A requires a note describing what you checked.");const a=(await On()).filter(n=>!(n.componentId===e.componentId&&n.criterionId===e.criterionId));a.push({...e,note:e.note.trim()}),await Wr(a)}async function Hp(e,t){const n=(await On()).filter(o=>!(o.componentId===e&&o.criterionId===t));await Wr(n)}async function Wr(e){var t;typeof chrome>"u"||!((t=chrome.storage)!=null&&t.local)||await chrome.storage.local.set({[dn]:e})}function ft(e){return new Set(e.map(a=>a.pageUrl??a.scope??"").filter(Boolean)).size>1?"site-crawl":"single-page"}function Oc(e){const t=new Set;for(const a of e)a.componentId&&t.add(a.componentId);return Array.from(t)}async function Me(e,t=[]){const a=Oc(e),n=new Set(t.filter(w=>!!w)),o=Array.from(new Set([...a,...n]));if(o.length===0)return{walkthroughs:[],walkthroughAcks:[],acknowledgedMatchKeys:new Set,humanCriterionVerdicts:[],incompleteResolutions:[],attestations:[],walkthroughsByComponent:new Map,walkthroughAcksByComponent:new Map,ackedKeysByComponent:new Map,humanVerdictsByComponent:new Map,incompleteResolutionsByComponent:new Map};const[r,i,c,d,l,p]=await Promise.all([Mc(o),Dc(o),Nc(o),Lc(o),Pc(e,o),Ur(o)]);if(n.size>0&&a.length>0){const w=new Map;for(const b of n){const k=d.get(b)??[];for(const v of k)w.has(v.criterionId)||w.set(v.criterionId,v)}if(w.size>0)for(const b of a){const k=d.get(b)??[],v=new Set(k.map(x=>x.criterionId)),s=[...k];for(const x of w.values())v.has(x.criterionId)||s.push({...x,componentId:b});d.set(b,s)}}const m=[];for(const w of r.values())m.push(...w);const u=[];for(const w of i.values())u.push(...w);const g=new Set;for(const w of c.values())for(const b of w)g.add(b);const h=[];for(const w of d.values())h.push(...w);const f=[];for(const w of l.values())f.push(...w);return{walkthroughs:m,walkthroughAcks:u,acknowledgedMatchKeys:g,humanCriterionVerdicts:h,incompleteResolutions:f,attestations:p,walkthroughsByComponent:r,walkthroughAcksByComponent:i,ackedKeysByComponent:c,humanVerdictsByComponent:d,incompleteResolutionsByComponent:l}}async function Mc(e){const t=new Map;return await Promise.all(e.map(async a=>{const n=await Aa([a]);t.set(a,n)})),t}async function Dc(e){const t=new Map,a=await xr(e);for(const n of e)t.set(n,[]);for(const n of a){const o=t.get(n.componentId);o&&o.push(n)}return t}async function Nc(e){const t=new Map;return await Promise.all(e.map(async a=>{const n=await ya(a);t.set(a,n)})),t}async function Lc(e){const t=new Map,a=await Dr(e);for(const n of e)t.set(n,[]);for(const n of a){const o=t.get(n.componentId);o&&o.push(n)}return t}async function Pc(e,t){const a=new Map;for(const o of e)if(o.componentId&&!a.has(o.componentId)){const r=o.pageUrl??o.scope;r&&a.set(o.componentId,r)}const n=new Map;return await Promise.all(t.map(async o=>{const r=a.get(o);n.set(o,r?await va(r):[])})),n}function tt(e){return/<main[\s>]/i.test(e)?e:e.replace(/(<body[^>]*>)/i,`$1
|
|
372
|
+
`)}function $c(e,t={},a=new Set){var c;const n=e.flatMap(d=>d.violations),o=new Map;for(const d of n){const l=Ne(d.ruleId,d.target.selector);o.has(l)||o.set(l,d)}const r=new Map;for(const d of o.values()){const l=d.wcagCriterion;if(!l||a.has(l))continue;const p=r.get(l)??[];p.push(d),r.set(l,p)}const i=[];for(const[d,l]of r.entries()){const p=l[0];let m=t[d];if(!m){const u=lt[p.ruleId];u!=null&&u.summary?m=((c=u.summary.split(/[.!?]\s+/)[0])==null?void 0:c.trim())??p.description:m=p.description}m=m.replace(new RegExp("^\\s*WCAG\\s+"+d.replace(/\./g,"\\.")+"\\b[\\s:.\\u2014-]*","i"),"").trim(),l.length>1&&(m+=` (affects ${l.length} components on the audited page${e.length>1?"s":""})`),i.push({criterion:d,description:m})}return i.sort((d,l)=>{const p=g=>g.split(".").map(h=>parseInt(h,10)),m=p(d.criterion),u=p(l.criterion);for(let g=0;g<Math.max(m.length,u.length);g++){const h=(m[g]??0)-(u[g]??0);if(h!==0)return h}return 0}),i}function Tc(e){const t=$c(e.results,e.customDescriptions,e.dismissedCriteria);return e.outputFormat==="html"?Cc(e.inputs,t):Sc(e.inputs,t)}function Pp(e){if(e.reduce((n,o)=>n+o.violations.length,0)===0)return"full";const a=new Set;for(const n of e)for(const o of n.violations)o.wcagCriterion&&a.add(o.wcagCriterion);return a.size>=10?"non-conformant":"partial"}function et(e,t,a,n,o,r,i,c,d){var k,v,s,x;const l=xa(e,t,a,n,o,r,i,c,d),p=a??[],m=new Set((r??[]).map($=>$.criterionId)),u={};for(const $ of o??[]){const A=m.has($.criterionId)?"pass":$.verdict;$.criterionId==="2.4.3"?u.focusOrder=A:$.criterionId==="2.1.2"?u.keyboardTrap=A:$.criterionId==="2.4.7"?u.focusVisible=A:$.criterionId==="1.3.2"?u.readingOrder=A:$.criterionId==="1.4.11"&&(u.nonTextContrast=A)}const g=vr(d??[],p).map($=>({ruleId:$.ruleId,impact:"serious",selector:$.selector})),h=Qs({violations:e,auditRan:p.length>0,manualRuns:(t==null?void 0:t.runs)??[],workflows:(t==null?void 0:t.workflows)??[],heuristicCounts:{tabOrder:((v=(k=p[0])==null?void 0:k.tabOrderIssues)==null?void 0:v.length)??0,readingOrder:((x=(s=p[0])==null?void 0:s.readingOrderIssues)==null?void 0:x.length)??0},interactiveAuditVerdicts:u,aiResolvedFails:g}),f=mc(l.letter,h.overallLetter);return{combined:l.totals.critical>0||l.totals.serious>0?bc(f,l.letter):f,severity:l.letter,breadth:h.overallLetter,score:l,subScore:h}}const ln="humanCriterionVerdicts";async function Rn(){var a;if(typeof chrome>"u"||!((a=chrome.storage)!=null&&a.local))return[];const t=(await chrome.storage.local.get(ln))[ln];return Array.isArray(t)?t:[]}async function Dr(e){const t=new Set;for(const n of e)n&&t.add(n);return t.size===0?[]:(await Rn()).filter(n=>t.has(n.componentId))}async function Fp(e){if(!e.note||!e.note.trim())throw new Error("Human criterion verdict requires a non-empty note explaining the determination.");const a=(await Rn()).filter(n=>!(n.componentId===e.componentId&&n.criterionId===e.criterionId));a.push({...e,note:e.note.trim()}),await Nr(a)}async function _p(e,t){const n=(await Rn()).filter(o=>!(o.componentId===e&&o.criterionId===t));await Nr(n)}async function Nr(e){var t;typeof chrome>"u"||!((t=chrome.storage)!=null&&t.local)||await chrome.storage.local.set({[ln]:e})}function qt(e){return/\[Cascade resolution — rc\.\d+ escalation\]/.test(e)}function Up(e){return/\[Manual verification — rc\.\d+\]/.test(e)}function Wp(e){const t=e.match(/\[Cascade resolution — rc\.\d+ (fallback-\d|escalation)\]/);if(!t)return null;const a=t[1];return a==="fallback-1"||a==="fallback-2"||a==="escalation"?a:null}const Lr={ruleIds:["image-alt","label","button-name","link-name","color-contrast","document-title","html-has-lang"],thresholds:{moderate:1,high:2,critical:4},headlines:{low:"Low risk. Your site passes the checks common automated lawsuit-targeting scanners run today — not likely automated lawfare target. This isn't a guarantee: scanners add checks over time, so re-audit as your site changes.",moderate:"Moderate risk. A demand-letter scanner could flag at least one issue. Fix to drop off the easy-target list.",high:"High risk. Multiple findings here are common patterns demand-letter mills target.",critical:"Critical risk. Your site has many high-frequency patterns ADA-targeting firms scan for."},version:0};Lr.ruleIds;function Pr(){var e;return((e=hr())==null?void 0:e.lawfare)??Lr}function Fr(e,t=Pr()){return t.ruleIds.includes(e)}function ro(e,t=Pr()){const a=new Set(t.ruleIds),n=new Map;for(const l of e)a.has(l.ruleId)&&n.set(l.ruleId,(n.get(l.ruleId)??0)+1);const o=Array.from(n.entries()).map(([l,p])=>({ruleId:l,instanceCount:p})).sort((l,p)=>p.instanceCount-l.instanceCount),r=o.length,i=Array.from(n.values()).reduce((l,p)=>l+p,0);let c;return r<t.thresholds.moderate?c="low":r<t.thresholds.high?c="moderate":r<t.thresholds.critical?c="high":c="critical",{tier:c,letter:c==="low"?"A":c==="moderate"?"C":c==="high"?"D":"F",flaggedRuleCount:r,flaggedInstanceCount:i,flaggedRules:o,headline:t.headlines[c],rubricVersion:t.version}}function Ec(e,t){return e.flatMap(a=>a.violations).filter(a=>!a.needsReview&&!t.has(a.matchKey))}function Rc(e,t){const a=[],n={};for(const o of e){const r=Ec(o.results,t);a.push(...r),n[o.url]=ro(r).letter}return{site:ro(a),perUrlLetter:n}}function Gp(e){return e.filter(t=>!t.needsReview&&Fr(t.ruleId))}const dn="manualCriterionAttestations";function Vp(e,t,a){const n=a==="attested-conforms"?"conforms":"does not apply",o=t.slice(0,10);return`I, the operator, manually evaluated ${e} on ${o} and, to the best of my knowledge, it ${n}. This is my own assessment; wcagcheckr did not verify it.`}const _r=`About this grade — basis and limits. This grade reflects (1) what wcagcheckr verified programmatically and by AI evaluation ("proven in code") and (2) the operator's own attestations on the content-meaning criteria the tool cannot verify ("claimed by the operator"). An "A" is issued only after the operator has affirmatively confirmed every such criterion as conforming or not-applicable; any criterion the operator leaves "not addressed" holds the grade below A. This grade is NOT a certification, warranty, or guarantee of conformance with WCAG, the ADA, or any law, and must not be relied upon as proof of legal compliance. The operator is solely responsible for the accuracy of their attestations and for any criteria outside wcagcheckr's automated scope.`,Sa=[{id:"1.1.1",reason:"alt text exists, but only a human can confirm it conveys the image's meaning/purpose"},{id:"1.2.1",reason:"a transcript/alternative exists and is accurate for audio-only / video-only media"},{id:"1.2.2",reason:"captions are present AND accurate/synced for prerecorded audio"},{id:"1.2.3",reason:"audio description or a full text alternative covers the prerecorded video"},{id:"1.2.4",reason:"captions are present AND accurate for LIVE audio"},{id:"1.2.5",reason:"audio description quality is sufficient for prerecorded video"},{id:"1.3.3",reason:"instructions do not rely on sensory characteristics (shape/size/position/sound) alone"},{id:"1.4.1",reason:"information is not conveyed by color alone in content the tool could not classify"}];async function On(){var a;if(typeof chrome>"u"||!((a=chrome.storage)!=null&&a.local))return[];const t=(await chrome.storage.local.get(dn))[dn];return Array.isArray(t)?t:[]}async function Ur(e){const t=new Set;for(const n of e)n&&t.add(n);return t.size===0?[]:(await On()).filter(n=>t.has(n.componentId))}async function jp(e){if(e.kind!=="not-addressed"&&(!e.note||!e.note.trim()))throw new Error("An attestation of conformance / N-A requires a note describing what you checked.");const a=(await On()).filter(n=>!(n.componentId===e.componentId&&n.criterionId===e.criterionId));a.push({...e,note:e.note.trim()}),await Wr(a)}async function Hp(e,t){const n=(await On()).filter(o=>!(o.componentId===e&&o.criterionId===t));await Wr(n)}async function Wr(e){var t;typeof chrome>"u"||!((t=chrome.storage)!=null&&t.local)||await chrome.storage.local.set({[dn]:e})}function ft(e){return new Set(e.map(a=>a.pageUrl??a.scope??"").filter(Boolean)).size>1?"site-crawl":"single-page"}function Oc(e){const t=new Set;for(const a of e)a.componentId&&t.add(a.componentId);return Array.from(t)}async function Me(e,t=[]){const a=Oc(e),n=new Set(t.filter(w=>!!w)),o=Array.from(new Set([...a,...n]));if(o.length===0)return{walkthroughs:[],walkthroughAcks:[],acknowledgedMatchKeys:new Set,humanCriterionVerdicts:[],incompleteResolutions:[],attestations:[],walkthroughsByComponent:new Map,walkthroughAcksByComponent:new Map,ackedKeysByComponent:new Map,humanVerdictsByComponent:new Map,incompleteResolutionsByComponent:new Map};const[r,i,c,d,l,p]=await Promise.all([Mc(o),Dc(o),Nc(o),Lc(o),Pc(e,o),Ur(o)]);if(n.size>0&&a.length>0){const w=new Map;for(const b of n){const k=d.get(b)??[];for(const v of k)w.has(v.criterionId)||w.set(v.criterionId,v)}if(w.size>0)for(const b of a){const k=d.get(b)??[],v=new Set(k.map(x=>x.criterionId)),s=[...k];for(const x of w.values())v.has(x.criterionId)||s.push({...x,componentId:b});d.set(b,s)}}const m=[];for(const w of r.values())m.push(...w);const u=[];for(const w of i.values())u.push(...w);const g=new Set;for(const w of c.values())for(const b of w)g.add(b);const h=[];for(const w of d.values())h.push(...w);const f=[];for(const w of l.values())f.push(...w);return{walkthroughs:m,walkthroughAcks:u,acknowledgedMatchKeys:g,humanCriterionVerdicts:h,incompleteResolutions:f,attestations:p,walkthroughsByComponent:r,walkthroughAcksByComponent:i,ackedKeysByComponent:c,humanVerdictsByComponent:d,incompleteResolutionsByComponent:l}}async function Mc(e){const t=new Map;return await Promise.all(e.map(async a=>{const n=await Aa([a]);t.set(a,n)})),t}async function Dc(e){const t=new Map,a=await xr(e);for(const n of e)t.set(n,[]);for(const n of a){const o=t.get(n.componentId);o&&o.push(n)}return t}async function Nc(e){const t=new Map;return await Promise.all(e.map(async a=>{const n=await ya(a);t.set(a,n)})),t}async function Lc(e){const t=new Map,a=await Dr(e);for(const n of e)t.set(n,[]);for(const n of a){const o=t.get(n.componentId);o&&o.push(n)}return t}async function Pc(e,t){const a=new Map;for(const o of e)if(o.componentId&&!a.has(o.componentId)){const r=o.pageUrl??o.scope;r&&a.set(o.componentId,r)}const n=new Map;return await Promise.all(t.map(async o=>{const r=a.get(o);n.set(o,r?await va(r):[])})),n}function Fc(e){return e.length<2?[]:[...Uc(e),...Gc(e),...Vc(e),...jc(e)]}function _c(e){const t=new Set;if(e.length<2)return t;for(const o of["primary","footer","utility"]){const r=e.map(i=>i.navLinks.filter(c=>c.region===o&&c.href).map(c=>c.href)).filter(i=>i.length>0);for(let i=0;i<r.length-1;i++)if(r[i].filter(d=>r[i+1].includes(d)).length>=2){t.add("3.2.3");break}if(t.has("3.2.3"))break}const a=new Map;e.forEach((o,r)=>{for(const i of o.sharedComponents)a.has(i.fingerprint)||a.set(i.fingerprint,new Set),a.get(i.fingerprint).add(r)}),[...a.values()].some(o=>o.size>=2)&&t.add("3.2.4");const n=new Map;return e.forEach((o,r)=>{for(const i of o.helpMechanisms){const c=Ca(i.label);c&&(n.has(c)||n.set(c,new Set),n.get(c).add(r))}}),[...n.values()].some(o=>o.size>=2)&&t.add("3.2.6"),t}function Uc(e){const t=[];for(const a of["primary","footer","utility"]){const n=e.map(o=>({snapshot:o,hrefs:o.navLinks.filter(r=>r.region===a&&r.href).map(r=>r.href)})).filter(o=>o.hrefs.length>0);if(!(n.length<2))for(let o=0;o<n.length-1;o++){const r=n[o],i=n[o+1],c=r.hrefs.filter(h=>i.hrefs.includes(h));if(c.length<2)continue;const d=c.map(h=>r.hrefs.indexOf(h)),l=c.map(h=>i.hrefs.indexOf(h)),p=[...d].sort((h,f)=>h-f),m=[...l].sort((h,f)=>h-f),u=p.map(h=>c[d.indexOf(h)]),g=m.map(h=>c[l.indexOf(h)]);Wc(u,g)||t.push({ruleId:"wcc-consistent-navigation",wcagCriterion:"3.2.3",wcagLevel:"AA",impact:"moderate",description:`${a}-region nav links appear in a different order on these pages.`,details:[`Page A: ${r.snapshot.url}`,` order: [${u.join(", ")}]`,`Page B: ${i.snapshot.url}`,` order: [${g.join(", ")}]`,"WCAG 3.2.3 requires that components that appear on multiple pages occur in the same relative order. Additions are fine; reorders are not."].join(`
|
|
373
|
+
`),pages:[{url:r.snapshot.url,evidence:u.join(" → ")},{url:i.snapshot.url,evidence:g.join(" → ")}]})}}return t}function Wc(e,t){if(e.length!==t.length)return!1;for(let a=0;a<e.length;a++)if(e[a]!==t[a])return!1;return!0}function Gc(e){const t=[],a=new Map;for(const n of e)for(const o of n.sharedComponents)a.has(o.fingerprint)||a.set(o.fingerprint,[]),a.get(o.fingerprint).push({url:n.url,accessibleName:o.accessibleName,role:o.role});for(const[n,o]of a.entries()){if(o.length<2)continue;const r=new Set(o.map(i=>Ca(i.accessibleName)));r.size<=1||t.push({ruleId:"wcc-consistent-identification",wcagCriterion:"3.2.4",wcagLevel:"AA",impact:"moderate",description:`Same component (${n}) has different accessible names across pages.`,details:[`Fingerprint: ${n}`,`Role: ${o[0].role}`,`Distinct names observed: ${[...r].map(i=>`"${i}"`).join(", ")}`,...o.map(i=>` ${i.url} → "${i.accessibleName}"`),"WCAG 3.2.4 requires components with identical functionality to have identical accessible names across pages."].join(`
|
|
374
|
+
`),pages:o.map(i=>({url:i.url,evidence:i.accessibleName}))})}return t}function Ca(e){return e.toLowerCase().replace(/\s+/g," ").trim()}function Vc(e){const t=[],a=new Map;for(const n of e)for(const o of n.helpMechanisms){const r=Ca(o.label);r&&(a.has(r)||a.set(r,[]),a.get(r).push({url:n.url,landmark:o.landmark,rawLabel:o.label}))}for(const[n,o]of a.entries()){if(o.length<2)continue;const r=new Set(o.map(i=>i.landmark));r.size<=1||t.push({ruleId:"wcc-consistent-help",wcagCriterion:"3.2.6",wcagLevel:"A",impact:"minor",description:`Help mechanism "${n}" appears in different landmarks across pages.`,details:[`Help label: "${n}"`,`Distinct landmarks observed: ${[...r].join(", ")}`,...o.map(i=>` ${i.url} → ${i.landmark}`),"WCAG 3.2.6 (AA, WCAG 2.2): when a help mechanism exists on multiple pages, it must appear in the same relative order. Same-label help that moves between header/footer/nav is a divergence."].join(`
|
|
375
|
+
`),pages:o.map(i=>({url:i.url,evidence:`${i.rawLabel} in ${i.landmark}`}))})}return t}function jc(e){const t=[],a=e.filter(o=>o.isMultiStep);if(a.length<2)return t;const n=new Map;for(const o of a)for(const r of o.formFields){const i=Ca(r.name);i&&(n.has(i)||n.set(i,[]),n.get(i).push({url:o.url,label:r.label,type:r.type,isSecurityEssential:r.isSecurityEssential}))}for(const[o,r]of n.entries())r.length<2||r.some(i=>i.isSecurityEssential)||t.push({ruleId:"wcc-redundant-entry",wcagCriterion:"3.3.7",wcagLevel:"A",impact:"minor",description:`Field "${o}" is requested on multiple steps of a multi-step flow.`,details:[`Field name: "${o}"`,"Occurrences:",...r.map(i=>` ${i.url} → label "${i.label}", type ${i.type}`),"WCAG 3.3.7 (A, WCAG 2.2): information previously entered in a multi-step process must be auto-populated or selectable, unless re-entry is essential (e.g., password confirmation, security questions)."].join(`
|
|
376
|
+
`),pages:r.map(i=>({url:i.url,evidence:`${i.label} (${i.type})`}))});return t}const Hc=Sa.length;function Bc(e,t){const a=Sa.map(c=>c.id),n=a.length,o=a.filter(c=>!t.has(c)),r=n-o.length;return e!=="A"?{status:"none",displayGrade:e,automationGrade:e,answered:r,total:n,unanswered:o,reason:null}:o.length>0?{status:"pending",displayGrade:"pending",automationGrade:e,answered:r,total:n,unanswered:o,reason:null}:a.some(c=>t.get(c)==="not-addressed")?{status:"resolved",displayGrade:"B",automationGrade:e,answered:r,total:n,unanswered:o,reason:"spot-checks-not-addressed"}:{status:"resolved",displayGrade:"A",automationGrade:e,answered:r,total:n,unanswered:o,reason:null}}function qc(e){const t=new Set(Sa.map(n=>n.id)),a=new Map;for(const n of e)t.has(n.criterionId)&&a.set(n.criterionId,n.kind);return a}function Gr(e,t){if(e==="pending"){const a=(t==null?void 0:t.answered)??0,n=(t==null?void 0:t.total)??Hc;return`Pending — ${a} of ${n} content-meaning spot-checks answered; no letter grade is issued until all are`}return e}const Zt={A:0,B:1,C:2,D:3,F:4},io={low:0,moderate:1,high:2,critical:3};function zc(e,t){return Zt[e]>Zt[t]?e:t}function Kc(e,t){return io[e]>io[t]?e:t}function Yc(e){const t=[];for(const a of e)for(const n of a.results)for(const o of n.violations)o.needsReview||t.push({url:a.url,componentId:a.componentId,v:o});return t}function Jc(e,t){const a=[];for(const n of e){const o=t==null?void 0:t.get(n.componentId);for(const r of n.results)for(const i of r.violations)i.needsReview&&(o!=null&&o.has(i.matchKey)||a.push({url:n.url,v:i}))}return a}function Vr(e,t,a,n,o,r,i,c,d,l,p){const m=t.filter(y=>!y.error).length,u=t.filter(y=>!!y.error&&!y.skipped).length,g=t.filter(y=>!!y.skipped).length,h=t.length<=1?"single-page":"site-crawl",f=t.map(y=>y.snapshot).filter(y=>!!y),w=Fc(f),b=new Set(w.map(y=>y.wcagCriterion)),k=[..._c(f)].filter(y=>!b.has(y)),v=()=>k.map(y=>({criterionId:y,verdict:"pass",note:`[auto] cross-page consistency comparator found no divergence across ${f.length} crawled pages`,verifiedAt:n})),s=t.filter(y=>!y.skipped).map(y=>{if(y.error)return{url:y.url,grade:"F",risk:"critical",uniqueViolations:0,totals:{critical:0,serious:0,moderate:0,minor:0},durationMs:y.durationMs,error:y.error,walkthroughFindings:[],walkthroughAcknowledgements:[]};const D=y.results.flatMap(Re=>Re.violations),R=r==null?void 0:r.get(y.componentId),G=i==null?void 0:i.get(y.componentId),Y=c==null?void 0:c.get(y.componentId),H=[...(d==null?void 0:d.get(y.componentId))??[],...v()],K=l==null?void 0:l.get(y.componentId),Q=et(D,{runs:[],workflows:De},y.results,Y?new Set(Y):void 0,R?[...R]:void 0,G?[...G]:void 0,H?[...H]:void 0,h,K?[...K]:void 0),ae=Q.score;return{url:y.url,grade:Q.combined,risk:ae.risk,uniqueViolations:ae.totals.unique,totals:{critical:ae.totals.critical,serious:ae.totals.serious,moderate:ae.totals.moderate,minor:ae.totals.minor},durationMs:y.durationMs,walkthroughFindings:ae.walkthroughFindings,walkthroughAcknowledgements:ae.walkthroughAcknowledgements}});s.sort((y,D)=>{const R=Zt[D.grade]-Zt[y.grade];return R!==0?R:D.uniqueViolations-y.uniqueViolations});let x="A",$="low";for(const y of s)x=zc(x,y.grade),$=Kc($,y.risk);const A=s.length>0?Bc(x,qc(p??[])):void 0,S=(A==null?void 0:A.displayGrade)??x,F=Yc(t).filter(({v:y,componentId:D})=>{const R=c==null?void 0:c.get(D);return!(R!=null&&R.has(y.matchKey))}),T=y=>`${y.ruleId}::${y.target.selector}`,I=new Map;for(const{v:y}of F){const D=T(y);I.has(D)||I.set(D,y)}const N=I.size,C={critical:0,serious:0,moderate:0,minor:0};for(const y of I.values())C[y.impact]++;const _=new Map;for(const{url:y,v:D}of F){let R=_.get(D.ruleId);R||(R={ruleId:D.ruleId,description:D.description,impact:D.impact,urls:new Set,totalOccurrences:0},_.set(D.ruleId,R)),R.urls.add(y),R.totalOccurrences++}const M=Array.from(_.values()).map(y=>({ruleId:y.ruleId,description:y.description,impact:y.impact,urlsAffected:y.urls.size,totalOccurrences:y.totalOccurrences,sampleUrls:Array.from(y.urls).slice(0,5)})).sort((y,D)=>D.urlsAffected!==y.urlsAffected?D.urlsAffected-y.urlsAffected:D.totalOccurrences-y.totalOccurrences),L=Jc(t,c),W=new Map,P=new Set;for(const{url:y,v:D}of L){P.add(`${D.ruleId}::${D.target.selector}`);let R=W.get(D.ruleId);R||(R={ruleId:D.ruleId,description:D.description,impact:D.impact,urls:new Set,totalOccurrences:0},W.set(D.ruleId,R)),R.urls.add(y),R.totalOccurrences++}const V=Array.from(W.values()).map(y=>({ruleId:y.ruleId,description:y.description,impact:y.impact,urlsAffected:y.urls.size,totalOccurrences:y.totalOccurrences,sampleUrls:Array.from(y.urls).slice(0,5)})).sort((y,D)=>D.urlsAffected!==y.urlsAffected?D.urlsAffected-y.urlsAffected:D.totalOccurrences-y.totalOccurrences),O=P.size,j={minor:0,moderate:1,serious:2,critical:3},q=new Map;for(const y of t){if(y.error)continue;const D=c==null?void 0:c.get(y.componentId);for(const R of y.results)for(const G of R.violations){if(D!=null&&D.has(G.matchKey))continue;const Y=`${Ne(G.ruleId,G.target.selector)}::${G.needsReview?"NR":"ACT"}`;let H=q.get(Y);H||(H={ruleId:G.ruleId,representativeSelector:G.target.selector,representativeOuterHTML:G.target.outerHTML??"",impact:G.impact,wcagCriterion:G.wcagCriterion??null,axeDescription:G.description??"",pages:new Map,totalOccurrences:0,needsReview:G.needsReview??!1,failureSummary:G.target.failureSummary??null},q.set(Y,H)),j[G.impact]>j[H.impact]&&(H.impact=G.impact),H.totalOccurrences++,H.pages.has(y.url)||H.pages.set(y.url,{selector:G.target.selector,outerHTML:G.target.outerHTML??""}),!H.failureSummary&&G.target.failureSummary&&(H.failureSummary=G.target.failureSummary)}}const z=Array.from(q.values()).map(y=>({ruleId:y.ruleId,representativeSelector:y.representativeSelector,representativeOuterHTML:y.representativeOuterHTML,impact:y.impact,wcagCriterion:y.wcagCriterion,axeDescription:y.axeDescription,pages:Array.from(y.pages.entries()).map(([D,R])=>({url:D,selector:R.selector,outerHTML:R.outerHTML})),totalOccurrences:y.totalOccurrences,isShared:y.pages.size>=2,needsReview:y.needsReview,failureSummary:y.failureSummary})).sort((y,D)=>j[D.impact]!==j[y.impact]?j[D.impact]-j[y.impact]:D.pages.length-y.pages.length),X={A:0,B:0,C:0,D:0,F:0};for(const y of s)X[y.grade]++;const ge=new Set,ue=[],we=[];for(const y of s){for(const D of y.walkthroughFindings){const R=`${D.criterionId}::${D.pageUrl}`;ge.has(R)||(ge.add(R),ue.push(D))}for(const D of y.walkthroughAcknowledgements){const R=`${D.criterionId}::${D.pageUrl}::ack`;ge.has(R)||(ge.add(R),we.push(D))}}const ve=new Map,Ie={untested:0,inconclusive:1,failing:2},Be=new Map;for(const y of fa("2.1","AA"))Be.set(y.id,y.title);const qe=new Map;function $e(y,D){const R=`${D.pageUrl}::${D.selector??""}::${D.source}::${D.ruleOrStepId}`;let G=qe.get(y);if(G||(G=new Set,qe.set(y,G)),G.has(R))return;G.add(R);const Y=ve.get(y);Y&&(Y.evidence.length>=6||Y.evidence.push(D))}for(const y of t){if(y.error)continue;const D=y.results.flatMap(be=>be.violations),R=r==null?void 0:r.get(y.componentId),G=i==null?void 0:i.get(y.componentId),Y=c==null?void 0:c.get(y.componentId),H=[...(d==null?void 0:d.get(y.componentId))??[],...v()],K=l==null?void 0:l.get(y.componentId),Q=xa(D,void 0,y.results,Y?new Set(Y):void 0,R?[...R]:void 0,G?[...G]:void 0,H?[...H]:void 0,h,K?[...K]:void 0);if(!Q.coverage)continue;const ae=(be,Se)=>{for(const ne of be){const Pe=Be.get(ne)??ne,Fe=ve.get(ne);Fe?Ie[Se]>Ie[Fe.state]&&ve.set(ne,{state:Se,title:Pe,evidence:Fe.evidence}):ve.set(ne,{state:Se,title:Pe,evidence:[]})}};ae(Q.coverage.untestedCriteria,"untested"),ae(Q.coverage.inconclusiveCriteria,"inconclusive"),ae(Q.coverage.failingCriteria,"failing");const Re=Q.coverage.inconclusiveReasons??{};for(const[be,Se]of Object.entries(Re))for(const ne of Se)if(ne.elements&&ne.elements.length>0)for(const Pe of ne.elements)$e(be,{pageUrl:ne.pageUrl??y.url,selector:Pe.selector,ruleOrStepId:ne.ruleOrStepId,source:ne.source,notes:Pe.failureSummary});else $e(be,{pageUrl:ne.pageUrl??y.url,ruleOrStepId:ne.ruleOrStepId,source:ne.source,notes:ne.resolutionHint});const ee=Q.coverage.failingReasons??{};for(const[be,Se]of Object.entries(ee))for(const ne of Se)$e(be,{pageUrl:ne.pageUrl??y.url,selector:ne.selector,ruleOrStepId:ne.ruleOrStepId,source:ne.source,notes:ne.notes})}const ie=Array.from(ve.entries()).map(([y,D])=>({criterionId:y,title:D.title,state:D.state,evidence:D.evidence})).sort((y,D)=>y.criterionId.localeCompare(D.criterionId,void 0,{numeric:!0}));return{startedAt:a,finishedAt:n,startUrl:e,pagesAudited:m,pagesFailed:u,pagesSkipped:g,totalDurationMs:o,siteGrade:S,spotCheckGate:A,siteRisk:$,totalUniqueViolations:N,totals:C,topViolations:M,needsReviewItems:V,totalNeedsReview:O,findingGroups:z,pages:s,pagesByGrade:X,consistencyFindings:w,walkthroughFindings:ue,walkthroughAcknowledgements:we,totalWalkthroughFindings:ue.length,blockingCriteria:ie}}function tt(e){return/<main[\s>]/i.test(e)?e:e.replace(/(<body[^>]*>)/i,`$1
|
|
373
377
|
<main>`).replace(/(<\/body>)/i,`</main>
|
|
374
|
-
$1`)}function Zt(e){return e===null||typeof e!="object"?JSON.stringify(e):Array.isArray(e)?"["+e.map(n=>Zt(n)).join(",")+"]":"{"+Object.keys(e).sort().map(n=>{const o=e[n];return JSON.stringify(n)+":"+Zt(o)}).join(",")+"}"}const Fc="wcag-forensic-log",_c=1,Ve="audits";let _a=null;function Ca(){return _a||(_a=vn(Fc,_c,{upgrade(e){if(!e.objectStoreNames.contains(Ve)){const t=e.createObjectStore(Ve,{keyPath:["componentId","capturedAt"]});t.createIndex("byCapturedAt","capturedAt"),t.createIndex("byComponentId","componentId")}}})),_a}async function Uc(e){const t=Zt(e),a=new TextEncoder().encode(t),n=await crypto.subtle.digest("SHA-256",a);return Array.from(new Uint8Array(n)).map(o=>o.toString(16).padStart(2,"0")).join("")}async function Gr(e,t,a){if(e.length===0)return null;const n=e[0],o=n.componentId,r=n.pageUrl??n.scope,i=n.startedAt,c={componentId:o,pageUrl:r,scope:n.scope,grade:t.letter,totals:{critical:t.totals.critical,serious:t.totals.serious,moderate:t.totals.moderate,minor:t.totals.minor,unique:t.totals.unique},axeVersion:n.axeVersion,capturedAt:i,statesAudited:e.length},l={hash:await Uc(c),componentId:o,pageUrl:r,scope:n.scope,grade:t.letter,totals:c.totals,axeVersion:n.axeVersion,capturedAt:i,statesAudited:e.length,durationMs:a};try{await(await Ca()).put(Ve,l)}catch(p){return console.warn("[forensic-log] failed to record audit",p),l}return l}async function Vr(e,t,a){try{const n=await Ca(),o=await n.get(Ve,[e,t]);if(!o){console.warn("[forensic-log] no entry to attach receipt to",{componentId:e,capturedAt:t});return}o.receipt=a,await n.put(Ve,o)}catch(n){console.warn("[forensic-log] failed to attach receipt",n)}}async function Tt(){try{return(await(await Ca()).getAll(Ve)).sort((a,n)=>a.capturedAt<n.capturedAt?1:-1)}catch(e){return console.warn("[forensic-log] listAudits failed",e),[]}}async function Wc(e){try{return(await(await Ca()).transaction(Ve).objectStore(Ve).index("byComponentId").getAll(IDBKeyRange.only(e))).sort((o,r)=>o.capturedAt<r.capturedAt?1:-1)}catch(t){return console.warn("[forensic-log] listAuditsForComponent failed",t),[]}}const Gc={"color-contrast":{whatsWrong:"Some text on this page is too light to read against its background.",whyItMatters:"People with low vision, older eyes, or anyone reading on a sunny phone screen can't make out faint text. Color blindness affects about 1 in 12 men and 1 in 200 women. Insufficient contrast is the #1 most-common cause of accessibility lawsuits — every Domino's-style ADA suit cites it.",howToFix:"If you use a website builder (Squarespace, Wix, Shopify, WordPress with a theme), open your site styles or theme color settings and pick darker text colors or lighter backgrounds. Aim for very dark text on white, or very light text on dark. Free check at webaim.org/resources/contrastchecker. If you have a developer, ask them to ensure all text meets WCAG AA contrast (4.5:1 for normal text, 3:1 for large text)."},"color-contrast-enhanced":{whatsWrong:"Text on this page doesn't meet the higher (AAA) contrast level — important for people with significant low vision.",whyItMatters:"AAA is a stricter standard than the legal-minimum AA. If your audience includes older adults, people with cataracts or macular degeneration, or government-related contracts that require AAA conformance, this matters.",howToFix:"Same as basic contrast — go darker on text or lighter on backgrounds. AAA requires 7:1 for normal text and 4.5:1 for large text."},"link-in-text-block":{whatsWrong:"Links inside paragraphs are distinguishable from the surrounding text by color alone.",whyItMatters:"If a user is colorblind or sees in grayscale, they can't tell which words are clickable. WCAG requires another visual cue (underline, bold, icon) in addition to color.",howToFix:"Add an underline to links inside paragraphs. In most theme editors there's a 'link style' setting — turn underlines back on. The underline is the standard convention; it's also more discoverable for everyone."},"image-alt":{whatsWrong:"Some images on this page have no description for screen readers.",whyItMatters:"Blind and low-vision users rely on screen readers to read pages aloud. When an image has no description, the screen reader either skips it entirely or reads out the file name — useless. Missing alt text is the #2 lawsuit-magnet category.",howToFix:"In your site editor, click each image and look for an 'Alt text' or 'Image description' field. Write a short sentence describing what the image shows or what it links to. For purely decorative images (dividers, background flourishes), enter empty alt text or check a 'decorative' box if the platform offers one."},"input-image-alt":{whatsWrong:"Image-based form buttons (submit buttons that are images) have no description.",whyItMatters:"Without a description, screen-reader users can't tell what the button does — they hear 'button, submit.png' or just 'graphic'.",howToFix:"Replace image submit buttons with regular text buttons (most platforms let you do this in form settings), OR if you must use an image, ensure it has alt text describing the action ('Subscribe', 'Send message', 'Submit form')."},"area-alt":{whatsWrong:"An image map (clickable regions inside a single image) has unlabeled regions.",whyItMatters:"Each clickable area is a link — without a description, screen-reader users don't know what it points to.",howToFix:"Image maps are uncommon today. If you need one, your developer should add an alt attribute to each clickable area. The simpler modern approach: use multiple separate linked images or buttons instead."},"svg-img-alt":{whatsWrong:"Some SVG icons that act as images don't have a description.",whyItMatters:"SVG icons used decoratively are fine to leave silent, but icons that convey meaning (status indicators, social-media icons that link out) need a description so screen readers know what they represent.",howToFix:"In most platforms this requires a developer. Tell them: 'meaningful SVG icons need a title or aria-label; decorative SVGs need aria-hidden=true'. Examples: an envelope icon linking to 'Contact us' should be labeled 'Contact us', not silent."},"object-alt":{whatsWrong:"Embedded content (PDFs, videos, embedded apps) has no fallback description.",whyItMatters:"If the embed fails to load — or the user's screen reader can't navigate it — they need a text description of what the embed is.",howToFix:"Replace embedded objects with native modern alternatives where possible (HTML5 video instead of Flash, linked PDF with a description). If you must use an <object> embed, ask your developer to add fallback content describing what it is."},"role-img-alt":{whatsWrong:"An element marked as an image is missing its description.",whyItMatters:"Same as missing image alt — screen-reader users get nothing where they should hear a description.",howToFix:"Ask your developer: 'elements with role=img need aria-label or aria-labelledby'."},"image-redundant-alt":{whatsWrong:"Some images have alt text that duplicates nearby visible text.",whyItMatters:"Screen-reader users hear the same content twice — the surrounding text and then the alt text. Annoying, not blocking. Lower-priority fix.",howToFix:"Find images where the caption or nearby paragraph already says what the image shows, and either change the alt text to add new info or set the alt to empty (so the screen reader skips it as decorative)."},label:{whatsWrong:"Some form fields have no label that screen readers can announce.",whyItMatters:"Visually you can see what each field is for (next to a 'Name:' or 'Email:' text), but screen-reader users hear only 'edit text' if there's no programmatic label connecting the text to the field. The result: blind users can't tell what to type where.",howToFix:"In a website builder, every form field has a 'Label' field or similar. Make sure every input has one filled in. Placeholder text (the gray hint inside the field) does NOT count as a label — it has to be a real label."},"select-name":{whatsWrong:"A dropdown menu has no description.",whyItMatters:"Screen-reader users hear 'combo box, has popup' but not what the dropdown is for. They can't fill out forms reliably.",howToFix:"In your form editor, check that each dropdown has a visible label above or beside it. If your platform has separate label/field settings, both must be filled."},"aria-input-field-name":{whatsWrong:"A custom input control (search box, range slider, custom date picker) has no name.",whyItMatters:"Custom controls are common in modern site builders — they look fancy but break for screen-reader users when not labeled.",howToFix:"This is a developer fix. Ask: 'custom inputs with role=textbox / searchbox / slider / spinbutton need an aria-label or aria-labelledby'."},"aria-toggle-field-name":{whatsWrong:"A toggle switch (on/off control) has no description.",whyItMatters:"User can see the switch but can't tell what it controls without a label.",howToFix:"Developer fix: 'switches and checkboxes with role=switch need an accessible name'."},"form-field-multiple-labels":{whatsWrong:"A form field has multiple labels, which confuses screen readers.",whyItMatters:"Screen readers will read all labels — sometimes saying conflicting things — leaving users unsure what to enter.",howToFix:"Developer fix: 'this field has multiple <label> associations; pick one and remove the others'."},"label-content-name-mismatch":{whatsWrong:"A button's visible text doesn't match the description it announces to screen readers.",whyItMatters:"A user dictating 'click Submit' to voice-control software may see 'Submit' on screen but the announce-name is something else, so the voice command fails.",howToFix:"Developer fix: 'aria-label should start with or fully contain the visible button text'."},"autocomplete-valid":{whatsWrong:"A field that should auto-fill (name, email, phone, address) is missing the right autocomplete hint.",whyItMatters:"Browsers use these hints to auto-fill personal info, helping users with motor disabilities and saving everyone time. Older adults appreciate this most.",howToFix:"In your form builder, look for an 'Autocomplete' or 'Field type' setting per field. Set them to 'email', 'name', 'tel', 'street-address' etc. as appropriate."},"button-name":{whatsWrong:"Some buttons have no description that screen readers can announce.",whyItMatters:"Visual users see an icon (a heart, a trash can, an X), but screen-reader users hear only 'button' — they don't know if it'll favorite a post, delete it, or close a dialog. This is a critical accessibility blocker and a top lawsuit driver.",howToFix:"Every button needs a text label. If you can't put text inside the button (icon-only design), in your platform's button settings look for an 'Aria label' or 'Screen reader text' field — fill it with what the button does ('Close', 'Add to cart', 'Open menu')."},"input-button-name":{whatsWrong:"An <input type=submit> or similar button has no description.",whyItMatters:"Same as button-name — screen-reader users don't know what the button does.",howToFix:"Set the button's 'value' or 'label' to text describing the action ('Submit', 'Subscribe', 'Send')."},"link-name":{whatsWrong:"Some links have no readable text — usually icon-only links.",whyItMatters:"Screen-reader users hear 'link' but not where the link goes. Users navigate by listening to a list of links — useless if every entry is just 'link, link, link'.",howToFix:"In your site editor, for icon-only links (social-media icons, navigation arrows), set an 'Aria label' or 'Title' field describing where it goes ('Follow us on Twitter', 'Next page')."},"empty-button":{whatsWrong:"A button is completely empty — no text, no icon, no description.",whyItMatters:"Both visual and screen-reader users can't tell what an empty button does.",howToFix:"Find empty buttons in your editor (often a leftover from a deleted icon) and either delete them or add text/an icon + description."},"empty-link":{whatsWrong:"A link has no readable text or description.",whyItMatters:"Screen-reader users hear nothing useful; visual users see an empty space they can't tell is clickable.",howToFix:"Same as link-name — add link text or an aria-label describing the destination."},"empty-heading":{whatsWrong:"A heading on this page has no text.",whyItMatters:"Screen-reader users navigate pages by jumping between headings. An empty heading is dead air — they don't know what the section is about.",howToFix:"Find the empty heading in your editor (often a section title that was deleted but the heading element remained) and either delete it or add real heading text."},"document-title":{whatsWrong:"This page's title (in the browser tab) is missing or generic.",whyItMatters:"Tab titles help users who have many tabs open know which one is which. Screen readers also announce the page title when the page loads.",howToFix:"In your platform's page settings or SEO panel, find the 'Page title' field and set it to something descriptive ('Contact Us — YourBusiness', not 'Untitled' or just your business name)."},"html-has-lang":{whatsWrong:"The page doesn't declare what language it's in.",whyItMatters:"Screen readers use this to pick the right voice and pronunciation rules. Without it, English content might be read with a French accent (or vice versa).",howToFix:"In your platform's site settings, find a 'Site language' option and set it (English = en, Spanish = es, etc.). Most modern platforms do this automatically — if it's missing, ask your developer to add lang='en' to the html element."},"html-lang-valid":{whatsWrong:"The page declares a language code that isn't valid.",whyItMatters:"Same as missing language — screen readers can't pick the right voice.",howToFix:"Use a standard ISO language code: en, es, fr, de, ja, zh, pt, etc. Avoid full strings like 'english' — use the two-letter code."},"valid-lang":{whatsWrong:"Text marked as a different language uses an invalid code.",whyItMatters:"Same as above — screen readers can't switch voices for the foreign-language passage.",howToFix:"Use ISO codes (en, fr, es) for the lang attribute on individual elements. This typically requires a developer."},"page-has-heading-one":{whatsWrong:"This page is missing a main heading (H1).",whyItMatters:"Screen-reader users use the H1 to confirm they landed on the right page. The H1 is also a strong SEO signal — Google uses it to understand what the page is about.",howToFix:"Add a clear, descriptive H1 to the top of the page. In website builders, this is usually the 'Title' or 'Page heading' setting. Each page should have exactly one H1."},"heading-order":{whatsWrong:"Heading levels skip — for example, an H2 jumps directly to an H4 with no H3 between them.",whyItMatters:"Screen-reader users navigate by heading levels. Skipped levels suggest missing content and break the page's outline. Confusing for everyone.",howToFix:"In your editor, find the headings that skip levels and either bump them up to the right level (H4 → H3 if no H2 exists between) or insert the missing level. Headings should follow a logical outline like a document outline."},"landmark-one-main":{whatsWrong:"This page has no 'main content' region marked.",whyItMatters:"Screen-reader users press a single shortcut to jump to the main content, skipping menus and headers. Without a marked main region, they have to tab through everything.",howToFix:"Most modern platforms add this automatically. If yours doesn't, ask your developer to wrap the main content in a <main> element."},"landmark-no-duplicate-banner":{whatsWrong:"There are multiple top banners (headers) on the page when there should be one.",whyItMatters:"Screen-reader users get confused by duplicate landmarks — they don't know which is the 'real' header.",howToFix:"Developer fix: 'only one element should have role=banner or be a top-level <header>; remove duplicates'."},"landmark-no-duplicate-contentinfo":{whatsWrong:"There are multiple footer regions on the page when there should be one.",whyItMatters:"Same as duplicate banner — confusing landmark navigation.",howToFix:"Developer fix: 'only one element should have role=contentinfo or be a top-level <footer>; remove duplicates'."},region:{whatsWrong:"Some content on the page isn't inside a recognized region (header, nav, main, footer, aside).",whyItMatters:"Screen-reader users navigate by these regions. Content outside any region can be missed when skimming.",howToFix:"Developer fix: 'wrap orphaned page content in semantic landmark elements (<main>, <aside>, <nav>) so it's reachable via landmark nav'."},bypass:{whatsWrong:"There's no 'skip to main content' link at the top of the page.",whyItMatters:"Keyboard-only users (and some screen-reader users) have to tab through every menu item on every page before reaching the main content. A skip link gives them a single keystroke to jump past the menu.",howToFix:"Most modern themes include a skip link automatically — it's only visible when keyboard-focused. If yours doesn't, ask your developer to add one as the first focusable element on every page."},"aria-required-attr":{whatsWrong:"An element uses a screen-reader role but is missing required information for that role.",whyItMatters:"Custom controls (sliders, tabs, dialogs) need specific extra info to work properly with screen readers. Missing pieces = broken behavior.",howToFix:"Developer fix: 'this role requires additional aria-* attributes; check axe-core docs for the specific role'."},"aria-allowed-attr":{whatsWrong:"An element has a screen-reader hint that doesn't apply to its role.",whyItMatters:"Inappropriate hints can confuse or break screen-reader announcements.",howToFix:"Developer fix: 'this aria-* attribute isn't allowed on this role; remove it or change the role'."},"aria-roles":{whatsWrong:"An element claims to be a type that isn't valid (typo or misuse of a role).",whyItMatters:"Screen readers ignore invalid roles, leaving the element unannounced.",howToFix:"Developer fix: 'this role value isn't recognized; check spelling or use a valid role'."},"aria-valid-attr":{whatsWrong:"An element uses a screen-reader hint that doesn't exist (typo).",whyItMatters:"Same as invalid roles — typos are silently ignored.",howToFix:"Developer fix: 'this aria-* attribute name isn't recognized; check spelling'."},"presentation-role-conflict":{whatsWrong:"An element is marked as decorative but is also focusable or has a description — contradictory.",whyItMatters:"Screen readers handle this inconsistently; some announce it anyway, some don't, leaving users with mixed experiences.",howToFix:"Developer fix: 'role=presentation/none cannot coexist with focusability or aria-label'."},"duplicate-id":{whatsWrong:"Two or more elements on the page share the same internal ID.",whyItMatters:"Some screen reader and skip-link behaviors rely on unique IDs. Duplicates can break label associations and skip-link targets.",howToFix:"Developer fix: 'rename duplicate IDs so each is unique'. Often arises when copying components."},"duplicate-id-aria":{whatsWrong:"An ID referenced by a screen-reader hint exists multiple times.",whyItMatters:"Screen readers may pick the wrong one and announce wrong information.",howToFix:"Developer fix: 'IDs used in aria-labelledby / aria-describedby must be unique'."},"meta-viewport":{whatsWrong:"The page tells mobile browsers users can't zoom in.",whyItMatters:"People with low vision rely on pinch-zoom to read small text. Disabling zoom is a real WCAG failure and a common lawsuit target.",howToFix:"In platform site settings, look for a 'Mobile zoom' or 'Pinch zoom' option and enable it. If your developer added user-scalable=no in a viewport meta tag, ask them to remove it."},"nested-interactive":{whatsWrong:"An interactive element (link, button) is inside another interactive element.",whyItMatters:"Keyboard and screen-reader users can't reliably reach the inner element. Common in 'card-as-link' designs where a button sits inside a clickable card.",howToFix:"Developer fix: 'avoid putting buttons inside links or links inside buttons; use a single interactive element with internal helpers'."},tabindex:{whatsWrong:"Some elements have positive tabindex values, forcing a custom focus order.",whyItMatters:"Custom focus orders are nearly always confusing — focus jumps around unexpectedly. Best practice is to never use positive tabindex.",howToFix:"Developer fix: 'replace tabindex=1/2/3... with tabindex=0 (or remove entirely); use DOM order to control focus order'."},"frame-focusable-content":{whatsWrong:"A frame on the page can't be navigated by keyboard.",whyItMatters:"Keyboard users get stuck — they can't reach interactive content inside the frame.",howToFix:"Developer fix: 'iframes containing interactive content shouldn't have tabindex=-1 on their parent or content'."},"no-focusable-content":{whatsWrong:"An element looks interactive but can't actually receive keyboard focus.",whyItMatters:"Keyboard-only users can't activate the element.",howToFix:"Developer fix: 'replace divs/spans-with-onClick with a real <button>, or add tabindex=0'."},"target-size":{whatsWrong:"Some buttons or links are too small to tap reliably on touch screens.",whyItMatters:"WCAG 2.2 requires touch targets to be at least 24×24 pixels. Small targets are a problem for users with hand tremors, large fingers, or motor disabilities. They're also frustrating for everyone.",howToFix:"In your editor, increase padding around small icon buttons. If your developer is involved, ask them to ensure all interactive elements have minimum 24×24 CSS pixels of clickable area."},"frame-title":{whatsWrong:"An iframe (embedded content) has no title.",whyItMatters:"Screen readers announce frame titles when entering them. Without one, users hear 'frame' with no context.",howToFix:"If using a third-party embed (YouTube, Vimeo, Stripe), most platforms set the title automatically. If yours doesn't, look for a 'Title' or 'Description' field on the embed block."}};function Vc(e){const t=Gc[e];return t||{whatsWrong:"An accessibility rule failed on this page.",whyItMatters:"Some users — particularly those using screen readers, keyboard-only navigation, or who have low vision or motor disabilities — may have trouble using this part of the page.",howToFix:"Share the technical rule ID with your developer. They can look up the full fix at https://dequeuniversity.com/rules/axe."}}const jc={whatsWrong:"This element appears to have a contrast issue, but it's actually transparent — its parent (or itself) has an opacity below 100%.",whyItMatters:"When something is partially transparent, the colors blend with whatever's behind it, which automated tools see as poor contrast. Most often this happens because the element is part of an entry animation (fade-in on scroll) that hasn't completed when the audit ran. The element will likely look correct once the animation finishes.",howToFix:"Check whether this element is inside a scroll-reveal animation (common patterns: classes like .fade-in, .reveal, .animate-on-scroll; or JavaScript using IntersectionObserver to add a .visible class). If so, this is NOT a real contrast problem — the element will become opaque once the user scrolls past it. If you want the audit to skip these false positives, ensure your site honors prefers-reduced-motion (which serves the final state immediately) or remove the opacity:0 default."};function Mn(e,t){return e==="color-contrast"&&t&&(t.self<.99||t.ancestor<.99)?jc:Vc(e)}const ea="inflight:audit";async function $a(){return(await chrome.storage.local.get(ea))[ea]??null}async function at(e){await chrome.storage.local.set({[ea]:e})}async function dt(){await chrome.storage.local.remove(ea)}async function Bp(){const e=await $a();e&&e.state==="running"&&await at({...e,state:"interrupted",lastProgressAt:new Date().toISOString()})}async function Lt(e){const t=await $a();t&&await at({...t,...e,lastProgressAt:new Date().toISOString()})}function Pt(){return crypto.randomUUID()}const jr="https://api.wcagcheckr.com/verify";function Hr(e){return`${jr}?hash=${encodeURIComponent(e)}`}function qp(e){return`${jr}#entry=${encodeURIComponent(JSON.stringify(e))}`}function Hc(e){return e.length<2?[]:[...qc(e),...Kc(e),...Yc(e),...Jc(e)]}function Bc(e){const t=new Set;if(e.length<2)return t;for(const o of["primary","footer","utility"]){const r=e.map(i=>i.navLinks.filter(c=>c.region===o&&c.href).map(c=>c.href)).filter(i=>i.length>0);for(let i=0;i<r.length-1;i++)if(r[i].filter(d=>r[i+1].includes(d)).length>=2){t.add("3.2.3");break}if(t.has("3.2.3"))break}const a=new Map;e.forEach((o,r)=>{for(const i of o.sharedComponents)a.has(i.fingerprint)||a.set(i.fingerprint,new Set),a.get(i.fingerprint).add(r)}),[...a.values()].some(o=>o.size>=2)&&t.add("3.2.4");const n=new Map;return e.forEach((o,r)=>{for(const i of o.helpMechanisms){const c=Ta(i.label);c&&(n.has(c)||n.set(c,new Set),n.get(c).add(r))}}),[...n.values()].some(o=>o.size>=2)&&t.add("3.2.6"),t}function qc(e){const t=[];for(const a of["primary","footer","utility"]){const n=e.map(o=>({snapshot:o,hrefs:o.navLinks.filter(r=>r.region===a&&r.href).map(r=>r.href)})).filter(o=>o.hrefs.length>0);if(!(n.length<2))for(let o=0;o<n.length-1;o++){const r=n[o],i=n[o+1],c=r.hrefs.filter(h=>i.hrefs.includes(h));if(c.length<2)continue;const d=c.map(h=>r.hrefs.indexOf(h)),l=c.map(h=>i.hrefs.indexOf(h)),p=[...d].sort((h,f)=>h-f),m=[...l].sort((h,f)=>h-f),u=p.map(h=>c[d.indexOf(h)]),g=m.map(h=>c[l.indexOf(h)]);zc(u,g)||t.push({ruleId:"wcc-consistent-navigation",wcagCriterion:"3.2.3",wcagLevel:"AA",impact:"moderate",description:`${a}-region nav links appear in a different order on these pages.`,details:[`Page A: ${r.snapshot.url}`,` order: [${u.join(", ")}]`,`Page B: ${i.snapshot.url}`,` order: [${g.join(", ")}]`,"WCAG 3.2.3 requires that components that appear on multiple pages occur in the same relative order. Additions are fine; reorders are not."].join(`
|
|
375
|
-
`),pages:[{url:r.snapshot.url,evidence:u.join(" → ")},{url:i.snapshot.url,evidence:g.join(" → ")}]})}}return t}function zc(e,t){if(e.length!==t.length)return!1;for(let a=0;a<e.length;a++)if(e[a]!==t[a])return!1;return!0}function Kc(e){const t=[],a=new Map;for(const n of e)for(const o of n.sharedComponents)a.has(o.fingerprint)||a.set(o.fingerprint,[]),a.get(o.fingerprint).push({url:n.url,accessibleName:o.accessibleName,role:o.role});for(const[n,o]of a.entries()){if(o.length<2)continue;const r=new Set(o.map(i=>Ta(i.accessibleName)));r.size<=1||t.push({ruleId:"wcc-consistent-identification",wcagCriterion:"3.2.4",wcagLevel:"AA",impact:"moderate",description:`Same component (${n}) has different accessible names across pages.`,details:[`Fingerprint: ${n}`,`Role: ${o[0].role}`,`Distinct names observed: ${[...r].map(i=>`"${i}"`).join(", ")}`,...o.map(i=>` ${i.url} → "${i.accessibleName}"`),"WCAG 3.2.4 requires components with identical functionality to have identical accessible names across pages."].join(`
|
|
376
|
-
`),pages:o.map(i=>({url:i.url,evidence:i.accessibleName}))})}return t}function Ta(e){return e.toLowerCase().replace(/\s+/g," ").trim()}function Yc(e){const t=[],a=new Map;for(const n of e)for(const o of n.helpMechanisms){const r=Ta(o.label);r&&(a.has(r)||a.set(r,[]),a.get(r).push({url:n.url,landmark:o.landmark,rawLabel:o.label}))}for(const[n,o]of a.entries()){if(o.length<2)continue;const r=new Set(o.map(i=>i.landmark));r.size<=1||t.push({ruleId:"wcc-consistent-help",wcagCriterion:"3.2.6",wcagLevel:"A",impact:"minor",description:`Help mechanism "${n}" appears in different landmarks across pages.`,details:[`Help label: "${n}"`,`Distinct landmarks observed: ${[...r].join(", ")}`,...o.map(i=>` ${i.url} → ${i.landmark}`),"WCAG 3.2.6 (AA, WCAG 2.2): when a help mechanism exists on multiple pages, it must appear in the same relative order. Same-label help that moves between header/footer/nav is a divergence."].join(`
|
|
377
|
-
`),pages:o.map(i=>({url:i.url,evidence:`${i.rawLabel} in ${i.landmark}`}))})}return t}function Jc(e){const t=[],a=e.filter(o=>o.isMultiStep);if(a.length<2)return t;const n=new Map;for(const o of a)for(const r of o.formFields){const i=Ta(r.name);i&&(n.has(i)||n.set(i,[]),n.get(i).push({url:o.url,label:r.label,type:r.type,isSecurityEssential:r.isSecurityEssential}))}for(const[o,r]of n.entries())r.length<2||r.some(i=>i.isSecurityEssential)||t.push({ruleId:"wcc-redundant-entry",wcagCriterion:"3.3.7",wcagLevel:"A",impact:"minor",description:`Field "${o}" is requested on multiple steps of a multi-step flow.`,details:[`Field name: "${o}"`,"Occurrences:",...r.map(i=>` ${i.url} → label "${i.label}", type ${i.type}`),"WCAG 3.3.7 (A, WCAG 2.2): information previously entered in a multi-step process must be auto-populated or selectable, unless re-entry is essential (e.g., password confirmation, security questions)."].join(`
|
|
378
|
-
`),pages:r.map(i=>({url:i.url,evidence:`${i.label} (${i.type})`}))});return t}const Xc=Sa.length;function Qc(e,t){const a=Sa.map(c=>c.id),n=a.length,o=a.filter(c=>!t.has(c)),r=n-o.length;return e!=="A"?{status:"none",displayGrade:e,automationGrade:e,answered:r,total:n,unanswered:o,reason:null}:o.length>0?{status:"pending",displayGrade:"pending",automationGrade:e,answered:r,total:n,unanswered:o,reason:null}:a.some(c=>t.get(c)==="not-addressed")?{status:"resolved",displayGrade:"B",automationGrade:e,answered:r,total:n,unanswered:o,reason:"spot-checks-not-addressed"}:{status:"resolved",displayGrade:"A",automationGrade:e,answered:r,total:n,unanswered:o,reason:null}}function Zc(e){const t=new Set(Sa.map(n=>n.id)),a=new Map;for(const n of e)t.has(n.criterionId)&&a.set(n.criterionId,n.kind);return a}function Br(e,t){if(e==="pending"){const a=(t==null?void 0:t.answered)??0,n=(t==null?void 0:t.total)??Xc;return`Pending — ${a} of ${n} content-meaning spot-checks answered; no letter grade is issued until all are`}return e}const ta={A:0,B:1,C:2,D:3,F:4},io={low:0,moderate:1,high:2,critical:3};function el(e,t){return ta[e]>ta[t]?e:t}function tl(e,t){return io[e]>io[t]?e:t}function al(e){const t=[];for(const a of e)for(const n of a.results)for(const o of n.violations)o.needsReview||t.push({url:a.url,componentId:a.componentId,v:o});return t}function nl(e,t){const a=[];for(const n of e){const o=t==null?void 0:t.get(n.componentId);for(const r of n.results)for(const i of r.violations)i.needsReview&&(o!=null&&o.has(i.matchKey)||a.push({url:n.url,v:i}))}return a}function qr(e,t,a,n,o,r,i,c,d,l,p){const m=t.filter(y=>!y.error).length,u=t.filter(y=>!!y.error&&!y.skipped).length,g=t.filter(y=>!!y.skipped).length,h=t.length<=1?"single-page":"site-crawl",f=t.map(y=>y.snapshot).filter(y=>!!y),w=Hc(f),b=new Set(w.map(y=>y.wcagCriterion)),k=[...Bc(f)].filter(y=>!b.has(y)),v=()=>k.map(y=>({criterionId:y,verdict:"pass",note:`[auto] cross-page consistency comparator found no divergence across ${f.length} crawled pages`,verifiedAt:n})),s=t.filter(y=>!y.skipped).map(y=>{if(y.error)return{url:y.url,grade:"F",risk:"critical",uniqueViolations:0,totals:{critical:0,serious:0,moderate:0,minor:0},durationMs:y.durationMs,error:y.error,walkthroughFindings:[],walkthroughAcknowledgements:[]};const D=y.results.flatMap(Re=>Re.violations),R=r==null?void 0:r.get(y.componentId),G=i==null?void 0:i.get(y.componentId),Y=c==null?void 0:c.get(y.componentId),H=[...(d==null?void 0:d.get(y.componentId))??[],...v()],K=l==null?void 0:l.get(y.componentId),Q=et(D,{runs:[],workflows:De},y.results,Y?new Set(Y):void 0,R?[...R]:void 0,G?[...G]:void 0,H?[...H]:void 0,h,K?[...K]:void 0),ae=Q.score;return{url:y.url,grade:Q.combined,risk:ae.risk,uniqueViolations:ae.totals.unique,totals:{critical:ae.totals.critical,serious:ae.totals.serious,moderate:ae.totals.moderate,minor:ae.totals.minor},durationMs:y.durationMs,walkthroughFindings:ae.walkthroughFindings,walkthroughAcknowledgements:ae.walkthroughAcknowledgements}});s.sort((y,D)=>{const R=ta[D.grade]-ta[y.grade];return R!==0?R:D.uniqueViolations-y.uniqueViolations});let x="A",$="low";for(const y of s)x=el(x,y.grade),$=tl($,y.risk);const A=s.length>0?Qc(x,Zc(p??[])):void 0,S=(A==null?void 0:A.displayGrade)??x,F=al(t).filter(({v:y,componentId:D})=>{const R=c==null?void 0:c.get(D);return!(R!=null&&R.has(y.matchKey))}),T=y=>`${y.ruleId}::${y.target.selector}`,I=new Map;for(const{v:y}of F){const D=T(y);I.has(D)||I.set(D,y)}const N=I.size,C={critical:0,serious:0,moderate:0,minor:0};for(const y of I.values())C[y.impact]++;const _=new Map;for(const{url:y,v:D}of F){let R=_.get(D.ruleId);R||(R={ruleId:D.ruleId,description:D.description,impact:D.impact,urls:new Set,totalOccurrences:0},_.set(D.ruleId,R)),R.urls.add(y),R.totalOccurrences++}const M=Array.from(_.values()).map(y=>({ruleId:y.ruleId,description:y.description,impact:y.impact,urlsAffected:y.urls.size,totalOccurrences:y.totalOccurrences,sampleUrls:Array.from(y.urls).slice(0,5)})).sort((y,D)=>D.urlsAffected!==y.urlsAffected?D.urlsAffected-y.urlsAffected:D.totalOccurrences-y.totalOccurrences),L=nl(t,c),W=new Map,P=new Set;for(const{url:y,v:D}of L){P.add(`${D.ruleId}::${D.target.selector}`);let R=W.get(D.ruleId);R||(R={ruleId:D.ruleId,description:D.description,impact:D.impact,urls:new Set,totalOccurrences:0},W.set(D.ruleId,R)),R.urls.add(y),R.totalOccurrences++}const V=Array.from(W.values()).map(y=>({ruleId:y.ruleId,description:y.description,impact:y.impact,urlsAffected:y.urls.size,totalOccurrences:y.totalOccurrences,sampleUrls:Array.from(y.urls).slice(0,5)})).sort((y,D)=>D.urlsAffected!==y.urlsAffected?D.urlsAffected-y.urlsAffected:D.totalOccurrences-y.totalOccurrences),O=P.size,j={minor:0,moderate:1,serious:2,critical:3},q=new Map;for(const y of t){if(y.error)continue;const D=c==null?void 0:c.get(y.componentId);for(const R of y.results)for(const G of R.violations){if(D!=null&&D.has(G.matchKey))continue;const Y=`${Ne(G.ruleId,G.target.selector)}::${G.needsReview?"NR":"ACT"}`;let H=q.get(Y);H||(H={ruleId:G.ruleId,representativeSelector:G.target.selector,representativeOuterHTML:G.target.outerHTML??"",impact:G.impact,wcagCriterion:G.wcagCriterion??null,axeDescription:G.description??"",pages:new Map,totalOccurrences:0,needsReview:G.needsReview??!1,failureSummary:G.target.failureSummary??null},q.set(Y,H)),j[G.impact]>j[H.impact]&&(H.impact=G.impact),H.totalOccurrences++,H.pages.has(y.url)||H.pages.set(y.url,{selector:G.target.selector,outerHTML:G.target.outerHTML??""}),!H.failureSummary&&G.target.failureSummary&&(H.failureSummary=G.target.failureSummary)}}const z=Array.from(q.values()).map(y=>({ruleId:y.ruleId,representativeSelector:y.representativeSelector,representativeOuterHTML:y.representativeOuterHTML,impact:y.impact,wcagCriterion:y.wcagCriterion,axeDescription:y.axeDescription,pages:Array.from(y.pages.entries()).map(([D,R])=>({url:D,selector:R.selector,outerHTML:R.outerHTML})),totalOccurrences:y.totalOccurrences,isShared:y.pages.size>=2,needsReview:y.needsReview,failureSummary:y.failureSummary})).sort((y,D)=>j[D.impact]!==j[y.impact]?j[D.impact]-j[y.impact]:D.pages.length-y.pages.length),X={A:0,B:0,C:0,D:0,F:0};for(const y of s)X[y.grade]++;const ge=new Set,ue=[],we=[];for(const y of s){for(const D of y.walkthroughFindings){const R=`${D.criterionId}::${D.pageUrl}`;ge.has(R)||(ge.add(R),ue.push(D))}for(const D of y.walkthroughAcknowledgements){const R=`${D.criterionId}::${D.pageUrl}::ack`;ge.has(R)||(ge.add(R),we.push(D))}}const ve=new Map,Ie={untested:0,inconclusive:1,failing:2},Be=new Map;for(const y of fa("2.1","AA"))Be.set(y.id,y.title);const qe=new Map;function $e(y,D){const R=`${D.pageUrl}::${D.selector??""}::${D.source}::${D.ruleOrStepId}`;let G=qe.get(y);if(G||(G=new Set,qe.set(y,G)),G.has(R))return;G.add(R);const Y=ve.get(y);Y&&(Y.evidence.length>=6||Y.evidence.push(D))}for(const y of t){if(y.error)continue;const D=y.results.flatMap(be=>be.violations),R=r==null?void 0:r.get(y.componentId),G=i==null?void 0:i.get(y.componentId),Y=c==null?void 0:c.get(y.componentId),H=[...(d==null?void 0:d.get(y.componentId))??[],...v()],K=l==null?void 0:l.get(y.componentId),Q=xa(D,void 0,y.results,Y?new Set(Y):void 0,R?[...R]:void 0,G?[...G]:void 0,H?[...H]:void 0,h,K?[...K]:void 0);if(!Q.coverage)continue;const ae=(be,Se)=>{for(const ne of be){const Pe=Be.get(ne)??ne,Fe=ve.get(ne);Fe?Ie[Se]>Ie[Fe.state]&&ve.set(ne,{state:Se,title:Pe,evidence:Fe.evidence}):ve.set(ne,{state:Se,title:Pe,evidence:[]})}};ae(Q.coverage.untestedCriteria,"untested"),ae(Q.coverage.inconclusiveCriteria,"inconclusive"),ae(Q.coverage.failingCriteria,"failing");const Re=Q.coverage.inconclusiveReasons??{};for(const[be,Se]of Object.entries(Re))for(const ne of Se)if(ne.elements&&ne.elements.length>0)for(const Pe of ne.elements)$e(be,{pageUrl:ne.pageUrl??y.url,selector:Pe.selector,ruleOrStepId:ne.ruleOrStepId,source:ne.source,notes:Pe.failureSummary});else $e(be,{pageUrl:ne.pageUrl??y.url,ruleOrStepId:ne.ruleOrStepId,source:ne.source,notes:ne.resolutionHint});const ee=Q.coverage.failingReasons??{};for(const[be,Se]of Object.entries(ee))for(const ne of Se)$e(be,{pageUrl:ne.pageUrl??y.url,selector:ne.selector,ruleOrStepId:ne.ruleOrStepId,source:ne.source,notes:ne.notes})}const ie=Array.from(ve.entries()).map(([y,D])=>({criterionId:y,title:D.title,state:D.state,evidence:D.evidence})).sort((y,D)=>y.criterionId.localeCompare(D.criterionId,void 0,{numeric:!0}));return{startedAt:a,finishedAt:n,startUrl:e,pagesAudited:m,pagesFailed:u,pagesSkipped:g,totalDurationMs:o,siteGrade:S,spotCheckGate:A,siteRisk:$,totalUniqueViolations:N,totals:C,topViolations:M,needsReviewItems:V,totalNeedsReview:O,findingGroups:z,pages:s,pagesByGrade:X,consistencyFindings:w,walkthroughFindings:ue,walkthroughAcknowledgements:we,totalWalkthroughFindings:ue.length,blockingCriteria:ie}}const ol="offscreen/offscreen.html";let Wt=null;async function rl(){return Wt||(Wt=(async()=>{if(!chrome.offscreen)throw new Error("chrome.offscreen API is unavailable. Update Chrome to 109+.");await chrome.offscreen.hasDocument()||await chrome.offscreen.createDocument({url:ol,reasons:[chrome.offscreen.Reason.DOM_PARSER],justification:"Parse PDF accessibility structure tree with pdf.js (no DOM access available in service worker)."})})().catch(e=>{throw Wt=null,e})),Wt}async function il(e,t){try{await rl()}catch(o){return{ok:!1,source:e,findings:[],pageCount:null,fatalError:`Could not create offscreen document for PDF audit: ${o instanceof Error?o.message:String(o)}`}}const a={type:"OFFSCREEN_PDF_AUDIT_REQUEST",url:e,...(t==null?void 0:t.maxBytes)!==void 0?{maxBytes:t.maxBytes}:{}};let n;try{n=await chrome.runtime.sendMessage(a)}catch(o){return{ok:!1,source:e,findings:[],pageCount:null,fatalError:`Offscreen audit messaging failed: ${o instanceof Error?o.message:String(o)}`}}return!n||n.type!=="OFFSCREEN_PDF_AUDIT_RESPONSE"?{ok:!1,source:e,findings:[],pageCount:null,fatalError:"Offscreen audit returned an unexpected response shape."}:{ok:n.ok,source:n.source,findings:[...n.findings],pageCount:n.pageCount,...n.fatalError?{fatalError:n.fatalError}:{},...n._perPageContrast?{_perPageContrast:n._perPageContrast}:{},...n.aiCandidates?{aiCandidates:n.aiCandidates}:{}}}async function sl(e,t){var a;try{return((a=(await chrome.scripting.executeScript({target:{tabId:e},func:cl,args:[t]}))[0])==null?void 0:a.result)??null}catch{return null}}function cl(e){var k;function t(v){var S,F;const s=v.getAttribute("aria-label");if(s&&s.trim())return s.trim();const x=v.getAttribute("aria-labelledby");if(x){const T=x.split(/\s+/).map(I=>{var N,C;return((C=(N=document.getElementById(I))==null?void 0:N.textContent)==null?void 0:C.trim())??""}).filter(Boolean);if(T.length)return T.join(" ")}if(v instanceof HTMLInputElement||v instanceof HTMLTextAreaElement||v instanceof HTMLSelectElement){if(v.id){const I=document.querySelector(`label[for="${CSS.escape(v.id)}"]`);if((S=I==null?void 0:I.textContent)!=null&&S.trim())return I.textContent.trim()}const T=v.closest("label");if((F=T==null?void 0:T.textContent)!=null&&F.trim())return T.textContent.trim();if(v instanceof HTMLInputElement&&v.placeholder)return v.placeholder}const $=(v.textContent??"").replace(/\s+/g," ").trim();if($)return $.slice(0,120);const A=v.getAttribute("title");return A?A.trim():""}function a(v){if(!v||v.startsWith("javascript:")||v.startsWith("#"))return null;try{return new URL(v,document.baseURI).href}catch{return v}}function n(v){let s=v;for(;s&&s!==document.body;){if(s.tagName==="HEADER")return"header";if(s.tagName==="FOOTER")return"footer";if(s.tagName==="NAV")return"nav";if(s.tagName==="MAIN"||s.getAttribute("role")==="main")return"main";if(s.tagName==="ASIDE"||s.getAttribute("role")==="complementary")return"aside";const x=s.getAttribute("role");if(x==="banner")return"header";if(x==="contentinfo")return"footer";if(x==="navigation")return"nav";s=s.parentElement}return"none"}function o(v){let s=v;for(;s&&s!==document.body;){if(s.tagName==="FOOTER"||s.getAttribute("role")==="contentinfo")return"footer";if(s.tagName==="HEADER"||s.getAttribute("role")==="banner")return"primary";s=s.parentElement}return"utility"}const r=[],i=document.querySelectorAll('nav, [role="navigation"]');for(const v of Array.from(i)){const s=o(v),x=v.querySelectorAll("a[href]");let $=0;for(const A of Array.from(x)){const S=a(A.getAttribute("href")),F=t(A);!F&&!S||r.push({href:S,accessibleName:F,index:$++,region:s})}}const c=[],d=new Set;for(const v of r)v.href&&(d.has(v.href)||(d.add(v.href),c.push({fingerprint:`href:${v.href}`,accessibleName:v.accessibleName,role:"link"})));const l=[{sel:'[data-test-id="logo"]',alias:"logo",role:"link"},{sel:".logo",alias:"logo",role:"link"},{sel:'[aria-label*="menu" i]',alias:"menu-toggle",role:"button"},{sel:'[data-test-id="menu-toggle"]',alias:"menu-toggle",role:"button"},{sel:'[aria-label*="search" i]',alias:"search-toggle",role:"button"},{sel:'[data-test-id="search"]',alias:"search-toggle",role:"button"}];for(const{sel:v,alias:s,role:x}of l){const $=document.querySelector(v);if(!$)continue;const A=`alias:${s}`;d.has(A)||(d.add(A),c.push({fingerprint:A,accessibleName:t($),role:x}))}const p=[],m=/(help|support|contact|faq|chat|live\s*chat)\b/i,u=document.querySelectorAll('a, button, [role="button"], [role="link"]'),g=new Set;for(const v of Array.from(u)){const s=t(v),x=v.getAttribute("aria-label")??"",$=s||x;if(!m.test($))continue;const A=`${$.toLowerCase().slice(0,32)}::${v.tagName}`;g.has(A)||(g.add(A),p.push({label:$.slice(0,80),href:v instanceof HTMLAnchorElement?a(v.getAttribute("href")):null,landmark:n(v)}))}const h=[],f=/(password|cvc|cvv|otp|one[-_\s]?time|security[-_\s]?answer|secret|2fa|mfa)/i,w=document.querySelectorAll("input, textarea, select");for(const v of Array.from(w)){if(v instanceof HTMLInputElement){const S=(v.type||"text").toLowerCase();if(S==="hidden"||S==="submit"||S==="button"||S==="image"||S==="reset")continue}const s=v.getAttribute("name")||v.id||"";if(!s)continue;const x=v instanceof HTMLInputElement?v.type||"text":v instanceof HTMLTextAreaElement?"textarea":"select",$=t(v),A=f.test(s)||f.test($)||x==="password";h.push({name:s,type:x,label:$,isSecurityEssential:A})}let b=!1;if(document.querySelector('[role="progressbar"]')&&(b=!0),!b){const v=document.querySelectorAll('button, a, [role="button"]');let s=0,x=0;for(const $ of Array.from(v)){const A=($.textContent??"").trim();if(/step\s*\d+\s*of\s*\d+/i.test(A)){b=!0;break}/^(next|continue)$/i.test(A)&&s++,/^(previous|prev|back)$/i.test(A)&&x++}s>0&&x>0&&(b=!0)}return{url:e,title:(document.title||(((k=document.querySelector("h1"))==null?void 0:k.textContent)??"")).trim(),navLinks:r,sharedComponents:c,helpMechanisms:p,formFields:h,isMultiStep:b}}const ll=`Respond ONLY with a JSON object — no markdown, no preamble:
|
|
378
|
+
$1`)}function ea(e){return e===null||typeof e!="object"?JSON.stringify(e):Array.isArray(e)?"["+e.map(n=>ea(n)).join(",")+"]":"{"+Object.keys(e).sort().map(n=>{const o=e[n];return JSON.stringify(n)+":"+ea(o)}).join(",")+"}"}const Xc="wcag-forensic-log",Qc=1,Ve="audits";let _a=null;function $a(){return _a||(_a=vn(Xc,Qc,{upgrade(e){if(!e.objectStoreNames.contains(Ve)){const t=e.createObjectStore(Ve,{keyPath:["componentId","capturedAt"]});t.createIndex("byCapturedAt","capturedAt"),t.createIndex("byComponentId","componentId")}}})),_a}async function Zc(e){const t=ea(e),a=new TextEncoder().encode(t),n=await crypto.subtle.digest("SHA-256",a);return Array.from(new Uint8Array(n)).map(o=>o.toString(16).padStart(2,"0")).join("")}async function jr(e,t,a){if(e.length===0)return null;const n=e[0],o=n.componentId,r=n.pageUrl??n.scope,i=n.startedAt,c={componentId:o,pageUrl:r,scope:n.scope,grade:t.letter,totals:{critical:t.totals.critical,serious:t.totals.serious,moderate:t.totals.moderate,minor:t.totals.minor,unique:t.totals.unique},axeVersion:n.axeVersion,capturedAt:i,statesAudited:e.length},l={hash:await Zc(c),componentId:o,pageUrl:r,scope:n.scope,grade:t.letter,totals:c.totals,axeVersion:n.axeVersion,capturedAt:i,statesAudited:e.length,durationMs:a};try{await(await $a()).put(Ve,l)}catch(p){return console.warn("[forensic-log] failed to record audit",p),l}return l}async function Hr(e,t,a){try{const n=await $a(),o=await n.get(Ve,[e,t]);if(!o){console.warn("[forensic-log] no entry to attach receipt to",{componentId:e,capturedAt:t});return}o.receipt=a,await n.put(Ve,o)}catch(n){console.warn("[forensic-log] failed to attach receipt",n)}}async function Tt(){try{return(await(await $a()).getAll(Ve)).sort((a,n)=>a.capturedAt<n.capturedAt?1:-1)}catch(e){return console.warn("[forensic-log] listAudits failed",e),[]}}async function el(e){try{return(await(await $a()).transaction(Ve).objectStore(Ve).index("byComponentId").getAll(IDBKeyRange.only(e))).sort((o,r)=>o.capturedAt<r.capturedAt?1:-1)}catch(t){return console.warn("[forensic-log] listAuditsForComponent failed",t),[]}}const tl={"color-contrast":{whatsWrong:"Some text on this page is too light to read against its background.",whyItMatters:"People with low vision, older eyes, or anyone reading on a sunny phone screen can't make out faint text. Color blindness affects about 1 in 12 men and 1 in 200 women. Insufficient contrast is the #1 most-common cause of accessibility lawsuits — every Domino's-style ADA suit cites it.",howToFix:"If you use a website builder (Squarespace, Wix, Shopify, WordPress with a theme), open your site styles or theme color settings and pick darker text colors or lighter backgrounds. Aim for very dark text on white, or very light text on dark. Free check at webaim.org/resources/contrastchecker. If you have a developer, ask them to ensure all text meets WCAG AA contrast (4.5:1 for normal text, 3:1 for large text)."},"color-contrast-enhanced":{whatsWrong:"Text on this page doesn't meet the higher (AAA) contrast level — important for people with significant low vision.",whyItMatters:"AAA is a stricter standard than the legal-minimum AA. If your audience includes older adults, people with cataracts or macular degeneration, or government-related contracts that require AAA conformance, this matters.",howToFix:"Same as basic contrast — go darker on text or lighter on backgrounds. AAA requires 7:1 for normal text and 4.5:1 for large text."},"link-in-text-block":{whatsWrong:"Links inside paragraphs are distinguishable from the surrounding text by color alone.",whyItMatters:"If a user is colorblind or sees in grayscale, they can't tell which words are clickable. WCAG requires another visual cue (underline, bold, icon) in addition to color.",howToFix:"Add an underline to links inside paragraphs. In most theme editors there's a 'link style' setting — turn underlines back on. The underline is the standard convention; it's also more discoverable for everyone."},"image-alt":{whatsWrong:"Some images on this page have no description for screen readers.",whyItMatters:"Blind and low-vision users rely on screen readers to read pages aloud. When an image has no description, the screen reader either skips it entirely or reads out the file name — useless. Missing alt text is the #2 lawsuit-magnet category.",howToFix:"In your site editor, click each image and look for an 'Alt text' or 'Image description' field. Write a short sentence describing what the image shows or what it links to. For purely decorative images (dividers, background flourishes), enter empty alt text or check a 'decorative' box if the platform offers one."},"input-image-alt":{whatsWrong:"Image-based form buttons (submit buttons that are images) have no description.",whyItMatters:"Without a description, screen-reader users can't tell what the button does — they hear 'button, submit.png' or just 'graphic'.",howToFix:"Replace image submit buttons with regular text buttons (most platforms let you do this in form settings), OR if you must use an image, ensure it has alt text describing the action ('Subscribe', 'Send message', 'Submit form')."},"area-alt":{whatsWrong:"An image map (clickable regions inside a single image) has unlabeled regions.",whyItMatters:"Each clickable area is a link — without a description, screen-reader users don't know what it points to.",howToFix:"Image maps are uncommon today. If you need one, your developer should add an alt attribute to each clickable area. The simpler modern approach: use multiple separate linked images or buttons instead."},"svg-img-alt":{whatsWrong:"Some SVG icons that act as images don't have a description.",whyItMatters:"SVG icons used decoratively are fine to leave silent, but icons that convey meaning (status indicators, social-media icons that link out) need a description so screen readers know what they represent.",howToFix:"In most platforms this requires a developer. Tell them: 'meaningful SVG icons need a title or aria-label; decorative SVGs need aria-hidden=true'. Examples: an envelope icon linking to 'Contact us' should be labeled 'Contact us', not silent."},"object-alt":{whatsWrong:"Embedded content (PDFs, videos, embedded apps) has no fallback description.",whyItMatters:"If the embed fails to load — or the user's screen reader can't navigate it — they need a text description of what the embed is.",howToFix:"Replace embedded objects with native modern alternatives where possible (HTML5 video instead of Flash, linked PDF with a description). If you must use an <object> embed, ask your developer to add fallback content describing what it is."},"role-img-alt":{whatsWrong:"An element marked as an image is missing its description.",whyItMatters:"Same as missing image alt — screen-reader users get nothing where they should hear a description.",howToFix:"Ask your developer: 'elements with role=img need aria-label or aria-labelledby'."},"image-redundant-alt":{whatsWrong:"Some images have alt text that duplicates nearby visible text.",whyItMatters:"Screen-reader users hear the same content twice — the surrounding text and then the alt text. Annoying, not blocking. Lower-priority fix.",howToFix:"Find images where the caption or nearby paragraph already says what the image shows, and either change the alt text to add new info or set the alt to empty (so the screen reader skips it as decorative)."},label:{whatsWrong:"Some form fields have no label that screen readers can announce.",whyItMatters:"Visually you can see what each field is for (next to a 'Name:' or 'Email:' text), but screen-reader users hear only 'edit text' if there's no programmatic label connecting the text to the field. The result: blind users can't tell what to type where.",howToFix:"In a website builder, every form field has a 'Label' field or similar. Make sure every input has one filled in. Placeholder text (the gray hint inside the field) does NOT count as a label — it has to be a real label."},"select-name":{whatsWrong:"A dropdown menu has no description.",whyItMatters:"Screen-reader users hear 'combo box, has popup' but not what the dropdown is for. They can't fill out forms reliably.",howToFix:"In your form editor, check that each dropdown has a visible label above or beside it. If your platform has separate label/field settings, both must be filled."},"aria-input-field-name":{whatsWrong:"A custom input control (search box, range slider, custom date picker) has no name.",whyItMatters:"Custom controls are common in modern site builders — they look fancy but break for screen-reader users when not labeled.",howToFix:"This is a developer fix. Ask: 'custom inputs with role=textbox / searchbox / slider / spinbutton need an aria-label or aria-labelledby'."},"aria-toggle-field-name":{whatsWrong:"A toggle switch (on/off control) has no description.",whyItMatters:"User can see the switch but can't tell what it controls without a label.",howToFix:"Developer fix: 'switches and checkboxes with role=switch need an accessible name'."},"form-field-multiple-labels":{whatsWrong:"A form field has multiple labels, which confuses screen readers.",whyItMatters:"Screen readers will read all labels — sometimes saying conflicting things — leaving users unsure what to enter.",howToFix:"Developer fix: 'this field has multiple <label> associations; pick one and remove the others'."},"label-content-name-mismatch":{whatsWrong:"A button's visible text doesn't match the description it announces to screen readers.",whyItMatters:"A user dictating 'click Submit' to voice-control software may see 'Submit' on screen but the announce-name is something else, so the voice command fails.",howToFix:"Developer fix: 'aria-label should start with or fully contain the visible button text'."},"autocomplete-valid":{whatsWrong:"A field that should auto-fill (name, email, phone, address) is missing the right autocomplete hint.",whyItMatters:"Browsers use these hints to auto-fill personal info, helping users with motor disabilities and saving everyone time. Older adults appreciate this most.",howToFix:"In your form builder, look for an 'Autocomplete' or 'Field type' setting per field. Set them to 'email', 'name', 'tel', 'street-address' etc. as appropriate."},"button-name":{whatsWrong:"Some buttons have no description that screen readers can announce.",whyItMatters:"Visual users see an icon (a heart, a trash can, an X), but screen-reader users hear only 'button' — they don't know if it'll favorite a post, delete it, or close a dialog. This is a critical accessibility blocker and a top lawsuit driver.",howToFix:"Every button needs a text label. If you can't put text inside the button (icon-only design), in your platform's button settings look for an 'Aria label' or 'Screen reader text' field — fill it with what the button does ('Close', 'Add to cart', 'Open menu')."},"input-button-name":{whatsWrong:"An <input type=submit> or similar button has no description.",whyItMatters:"Same as button-name — screen-reader users don't know what the button does.",howToFix:"Set the button's 'value' or 'label' to text describing the action ('Submit', 'Subscribe', 'Send')."},"link-name":{whatsWrong:"Some links have no readable text — usually icon-only links.",whyItMatters:"Screen-reader users hear 'link' but not where the link goes. Users navigate by listening to a list of links — useless if every entry is just 'link, link, link'.",howToFix:"In your site editor, for icon-only links (social-media icons, navigation arrows), set an 'Aria label' or 'Title' field describing where it goes ('Follow us on Twitter', 'Next page')."},"empty-button":{whatsWrong:"A button is completely empty — no text, no icon, no description.",whyItMatters:"Both visual and screen-reader users can't tell what an empty button does.",howToFix:"Find empty buttons in your editor (often a leftover from a deleted icon) and either delete them or add text/an icon + description."},"empty-link":{whatsWrong:"A link has no readable text or description.",whyItMatters:"Screen-reader users hear nothing useful; visual users see an empty space they can't tell is clickable.",howToFix:"Same as link-name — add link text or an aria-label describing the destination."},"empty-heading":{whatsWrong:"A heading on this page has no text.",whyItMatters:"Screen-reader users navigate pages by jumping between headings. An empty heading is dead air — they don't know what the section is about.",howToFix:"Find the empty heading in your editor (often a section title that was deleted but the heading element remained) and either delete it or add real heading text."},"document-title":{whatsWrong:"This page's title (in the browser tab) is missing or generic.",whyItMatters:"Tab titles help users who have many tabs open know which one is which. Screen readers also announce the page title when the page loads.",howToFix:"In your platform's page settings or SEO panel, find the 'Page title' field and set it to something descriptive ('Contact Us — YourBusiness', not 'Untitled' or just your business name)."},"html-has-lang":{whatsWrong:"The page doesn't declare what language it's in.",whyItMatters:"Screen readers use this to pick the right voice and pronunciation rules. Without it, English content might be read with a French accent (or vice versa).",howToFix:"In your platform's site settings, find a 'Site language' option and set it (English = en, Spanish = es, etc.). Most modern platforms do this automatically — if it's missing, ask your developer to add lang='en' to the html element."},"html-lang-valid":{whatsWrong:"The page declares a language code that isn't valid.",whyItMatters:"Same as missing language — screen readers can't pick the right voice.",howToFix:"Use a standard ISO language code: en, es, fr, de, ja, zh, pt, etc. Avoid full strings like 'english' — use the two-letter code."},"valid-lang":{whatsWrong:"Text marked as a different language uses an invalid code.",whyItMatters:"Same as above — screen readers can't switch voices for the foreign-language passage.",howToFix:"Use ISO codes (en, fr, es) for the lang attribute on individual elements. This typically requires a developer."},"page-has-heading-one":{whatsWrong:"This page is missing a main heading (H1).",whyItMatters:"Screen-reader users use the H1 to confirm they landed on the right page. The H1 is also a strong SEO signal — Google uses it to understand what the page is about.",howToFix:"Add a clear, descriptive H1 to the top of the page. In website builders, this is usually the 'Title' or 'Page heading' setting. Each page should have exactly one H1."},"heading-order":{whatsWrong:"Heading levels skip — for example, an H2 jumps directly to an H4 with no H3 between them.",whyItMatters:"Screen-reader users navigate by heading levels. Skipped levels suggest missing content and break the page's outline. Confusing for everyone.",howToFix:"In your editor, find the headings that skip levels and either bump them up to the right level (H4 → H3 if no H2 exists between) or insert the missing level. Headings should follow a logical outline like a document outline."},"landmark-one-main":{whatsWrong:"This page has no 'main content' region marked.",whyItMatters:"Screen-reader users press a single shortcut to jump to the main content, skipping menus and headers. Without a marked main region, they have to tab through everything.",howToFix:"Most modern platforms add this automatically. If yours doesn't, ask your developer to wrap the main content in a <main> element."},"landmark-no-duplicate-banner":{whatsWrong:"There are multiple top banners (headers) on the page when there should be one.",whyItMatters:"Screen-reader users get confused by duplicate landmarks — they don't know which is the 'real' header.",howToFix:"Developer fix: 'only one element should have role=banner or be a top-level <header>; remove duplicates'."},"landmark-no-duplicate-contentinfo":{whatsWrong:"There are multiple footer regions on the page when there should be one.",whyItMatters:"Same as duplicate banner — confusing landmark navigation.",howToFix:"Developer fix: 'only one element should have role=contentinfo or be a top-level <footer>; remove duplicates'."},region:{whatsWrong:"Some content on the page isn't inside a recognized region (header, nav, main, footer, aside).",whyItMatters:"Screen-reader users navigate by these regions. Content outside any region can be missed when skimming.",howToFix:"Developer fix: 'wrap orphaned page content in semantic landmark elements (<main>, <aside>, <nav>) so it's reachable via landmark nav'."},bypass:{whatsWrong:"There's no 'skip to main content' link at the top of the page.",whyItMatters:"Keyboard-only users (and some screen-reader users) have to tab through every menu item on every page before reaching the main content. A skip link gives them a single keystroke to jump past the menu.",howToFix:"Most modern themes include a skip link automatically — it's only visible when keyboard-focused. If yours doesn't, ask your developer to add one as the first focusable element on every page."},"aria-required-attr":{whatsWrong:"An element uses a screen-reader role but is missing required information for that role.",whyItMatters:"Custom controls (sliders, tabs, dialogs) need specific extra info to work properly with screen readers. Missing pieces = broken behavior.",howToFix:"Developer fix: 'this role requires additional aria-* attributes; check axe-core docs for the specific role'."},"aria-allowed-attr":{whatsWrong:"An element has a screen-reader hint that doesn't apply to its role.",whyItMatters:"Inappropriate hints can confuse or break screen-reader announcements.",howToFix:"Developer fix: 'this aria-* attribute isn't allowed on this role; remove it or change the role'."},"aria-roles":{whatsWrong:"An element claims to be a type that isn't valid (typo or misuse of a role).",whyItMatters:"Screen readers ignore invalid roles, leaving the element unannounced.",howToFix:"Developer fix: 'this role value isn't recognized; check spelling or use a valid role'."},"aria-valid-attr":{whatsWrong:"An element uses a screen-reader hint that doesn't exist (typo).",whyItMatters:"Same as invalid roles — typos are silently ignored.",howToFix:"Developer fix: 'this aria-* attribute name isn't recognized; check spelling'."},"presentation-role-conflict":{whatsWrong:"An element is marked as decorative but is also focusable or has a description — contradictory.",whyItMatters:"Screen readers handle this inconsistently; some announce it anyway, some don't, leaving users with mixed experiences.",howToFix:"Developer fix: 'role=presentation/none cannot coexist with focusability or aria-label'."},"duplicate-id":{whatsWrong:"Two or more elements on the page share the same internal ID.",whyItMatters:"Some screen reader and skip-link behaviors rely on unique IDs. Duplicates can break label associations and skip-link targets.",howToFix:"Developer fix: 'rename duplicate IDs so each is unique'. Often arises when copying components."},"duplicate-id-aria":{whatsWrong:"An ID referenced by a screen-reader hint exists multiple times.",whyItMatters:"Screen readers may pick the wrong one and announce wrong information.",howToFix:"Developer fix: 'IDs used in aria-labelledby / aria-describedby must be unique'."},"meta-viewport":{whatsWrong:"The page tells mobile browsers users can't zoom in.",whyItMatters:"People with low vision rely on pinch-zoom to read small text. Disabling zoom is a real WCAG failure and a common lawsuit target.",howToFix:"In platform site settings, look for a 'Mobile zoom' or 'Pinch zoom' option and enable it. If your developer added user-scalable=no in a viewport meta tag, ask them to remove it."},"nested-interactive":{whatsWrong:"An interactive element (link, button) is inside another interactive element.",whyItMatters:"Keyboard and screen-reader users can't reliably reach the inner element. Common in 'card-as-link' designs where a button sits inside a clickable card.",howToFix:"Developer fix: 'avoid putting buttons inside links or links inside buttons; use a single interactive element with internal helpers'."},tabindex:{whatsWrong:"Some elements have positive tabindex values, forcing a custom focus order.",whyItMatters:"Custom focus orders are nearly always confusing — focus jumps around unexpectedly. Best practice is to never use positive tabindex.",howToFix:"Developer fix: 'replace tabindex=1/2/3... with tabindex=0 (or remove entirely); use DOM order to control focus order'."},"frame-focusable-content":{whatsWrong:"A frame on the page can't be navigated by keyboard.",whyItMatters:"Keyboard users get stuck — they can't reach interactive content inside the frame.",howToFix:"Developer fix: 'iframes containing interactive content shouldn't have tabindex=-1 on their parent or content'."},"no-focusable-content":{whatsWrong:"An element looks interactive but can't actually receive keyboard focus.",whyItMatters:"Keyboard-only users can't activate the element.",howToFix:"Developer fix: 'replace divs/spans-with-onClick with a real <button>, or add tabindex=0'."},"target-size":{whatsWrong:"Some buttons or links are too small to tap reliably on touch screens.",whyItMatters:"WCAG 2.2 requires touch targets to be at least 24×24 pixels. Small targets are a problem for users with hand tremors, large fingers, or motor disabilities. They're also frustrating for everyone.",howToFix:"In your editor, increase padding around small icon buttons. If your developer is involved, ask them to ensure all interactive elements have minimum 24×24 CSS pixels of clickable area."},"frame-title":{whatsWrong:"An iframe (embedded content) has no title.",whyItMatters:"Screen readers announce frame titles when entering them. Without one, users hear 'frame' with no context.",howToFix:"If using a third-party embed (YouTube, Vimeo, Stripe), most platforms set the title automatically. If yours doesn't, look for a 'Title' or 'Description' field on the embed block."}};function al(e){const t=tl[e];return t||{whatsWrong:"An accessibility rule failed on this page.",whyItMatters:"Some users — particularly those using screen readers, keyboard-only navigation, or who have low vision or motor disabilities — may have trouble using this part of the page.",howToFix:"Share the technical rule ID with your developer. They can look up the full fix at https://dequeuniversity.com/rules/axe."}}const nl={whatsWrong:"This element appears to have a contrast issue, but it's actually transparent — its parent (or itself) has an opacity below 100%.",whyItMatters:"When something is partially transparent, the colors blend with whatever's behind it, which automated tools see as poor contrast. Most often this happens because the element is part of an entry animation (fade-in on scroll) that hasn't completed when the audit ran. The element will likely look correct once the animation finishes.",howToFix:"Check whether this element is inside a scroll-reveal animation (common patterns: classes like .fade-in, .reveal, .animate-on-scroll; or JavaScript using IntersectionObserver to add a .visible class). If so, this is NOT a real contrast problem — the element will become opaque once the user scrolls past it. If you want the audit to skip these false positives, ensure your site honors prefers-reduced-motion (which serves the final state immediately) or remove the opacity:0 default."};function Mn(e,t){return e==="color-contrast"&&t&&(t.self<.99||t.ancestor<.99)?nl:al(e)}const ta="inflight:audit";async function Ta(){return(await chrome.storage.local.get(ta))[ta]??null}async function at(e){await chrome.storage.local.set({[ta]:e})}async function dt(){await chrome.storage.local.remove(ta)}async function Bp(){const e=await Ta();e&&e.state==="running"&&await at({...e,state:"interrupted",lastProgressAt:new Date().toISOString()})}async function Lt(e){const t=await Ta();t&&await at({...t,...e,lastProgressAt:new Date().toISOString()})}function Pt(){return crypto.randomUUID()}const Br="https://api.wcagcheckr.com/verify";function qr(e){return`${Br}?hash=${encodeURIComponent(e)}`}function qp(e){return`${Br}#entry=${encodeURIComponent(JSON.stringify(e))}`}const ol="offscreen/offscreen.html";let Wt=null;async function rl(){return Wt||(Wt=(async()=>{if(!chrome.offscreen)throw new Error("chrome.offscreen API is unavailable. Update Chrome to 109+.");await chrome.offscreen.hasDocument()||await chrome.offscreen.createDocument({url:ol,reasons:[chrome.offscreen.Reason.DOM_PARSER],justification:"Parse PDF accessibility structure tree with pdf.js (no DOM access available in service worker)."})})().catch(e=>{throw Wt=null,e})),Wt}async function il(e,t){try{await rl()}catch(o){return{ok:!1,source:e,findings:[],pageCount:null,fatalError:`Could not create offscreen document for PDF audit: ${o instanceof Error?o.message:String(o)}`}}const a={type:"OFFSCREEN_PDF_AUDIT_REQUEST",url:e,...(t==null?void 0:t.maxBytes)!==void 0?{maxBytes:t.maxBytes}:{}};let n;try{n=await chrome.runtime.sendMessage(a)}catch(o){return{ok:!1,source:e,findings:[],pageCount:null,fatalError:`Offscreen audit messaging failed: ${o instanceof Error?o.message:String(o)}`}}return!n||n.type!=="OFFSCREEN_PDF_AUDIT_RESPONSE"?{ok:!1,source:e,findings:[],pageCount:null,fatalError:"Offscreen audit returned an unexpected response shape."}:{ok:n.ok,source:n.source,findings:[...n.findings],pageCount:n.pageCount,...n.fatalError?{fatalError:n.fatalError}:{},...n._perPageContrast?{_perPageContrast:n._perPageContrast}:{},...n.aiCandidates?{aiCandidates:n.aiCandidates}:{}}}async function sl(e,t){var a;try{return((a=(await chrome.scripting.executeScript({target:{tabId:e},func:cl,args:[t]}))[0])==null?void 0:a.result)??null}catch{return null}}function cl(e){var k;function t(v){var S,F;const s=v.getAttribute("aria-label");if(s&&s.trim())return s.trim();const x=v.getAttribute("aria-labelledby");if(x){const T=x.split(/\s+/).map(I=>{var N,C;return((C=(N=document.getElementById(I))==null?void 0:N.textContent)==null?void 0:C.trim())??""}).filter(Boolean);if(T.length)return T.join(" ")}if(v instanceof HTMLInputElement||v instanceof HTMLTextAreaElement||v instanceof HTMLSelectElement){if(v.id){const I=document.querySelector(`label[for="${CSS.escape(v.id)}"]`);if((S=I==null?void 0:I.textContent)!=null&&S.trim())return I.textContent.trim()}const T=v.closest("label");if((F=T==null?void 0:T.textContent)!=null&&F.trim())return T.textContent.trim();if(v instanceof HTMLInputElement&&v.placeholder)return v.placeholder}const $=(v.textContent??"").replace(/\s+/g," ").trim();if($)return $.slice(0,120);const A=v.getAttribute("title");return A?A.trim():""}function a(v){if(!v||v.startsWith("javascript:")||v.startsWith("#"))return null;try{return new URL(v,document.baseURI).href}catch{return v}}function n(v){let s=v;for(;s&&s!==document.body;){if(s.tagName==="HEADER")return"header";if(s.tagName==="FOOTER")return"footer";if(s.tagName==="NAV")return"nav";if(s.tagName==="MAIN"||s.getAttribute("role")==="main")return"main";if(s.tagName==="ASIDE"||s.getAttribute("role")==="complementary")return"aside";const x=s.getAttribute("role");if(x==="banner")return"header";if(x==="contentinfo")return"footer";if(x==="navigation")return"nav";s=s.parentElement}return"none"}function o(v){let s=v;for(;s&&s!==document.body;){if(s.tagName==="FOOTER"||s.getAttribute("role")==="contentinfo")return"footer";if(s.tagName==="HEADER"||s.getAttribute("role")==="banner")return"primary";s=s.parentElement}return"utility"}const r=[],i=document.querySelectorAll('nav, [role="navigation"]');for(const v of Array.from(i)){const s=o(v),x=v.querySelectorAll("a[href]");let $=0;for(const A of Array.from(x)){const S=a(A.getAttribute("href")),F=t(A);!F&&!S||r.push({href:S,accessibleName:F,index:$++,region:s})}}const c=[],d=new Set;for(const v of r)v.href&&(d.has(v.href)||(d.add(v.href),c.push({fingerprint:`href:${v.href}`,accessibleName:v.accessibleName,role:"link"})));const l=[{sel:'[data-test-id="logo"]',alias:"logo",role:"link"},{sel:".logo",alias:"logo",role:"link"},{sel:'[aria-label*="menu" i]',alias:"menu-toggle",role:"button"},{sel:'[data-test-id="menu-toggle"]',alias:"menu-toggle",role:"button"},{sel:'[aria-label*="search" i]',alias:"search-toggle",role:"button"},{sel:'[data-test-id="search"]',alias:"search-toggle",role:"button"}];for(const{sel:v,alias:s,role:x}of l){const $=document.querySelector(v);if(!$)continue;const A=`alias:${s}`;d.has(A)||(d.add(A),c.push({fingerprint:A,accessibleName:t($),role:x}))}const p=[],m=/(help|support|contact|faq|chat|live\s*chat)\b/i,u=document.querySelectorAll('a, button, [role="button"], [role="link"]'),g=new Set;for(const v of Array.from(u)){const s=t(v),x=v.getAttribute("aria-label")??"",$=s||x;if(!m.test($))continue;const A=`${$.toLowerCase().slice(0,32)}::${v.tagName}`;g.has(A)||(g.add(A),p.push({label:$.slice(0,80),href:v instanceof HTMLAnchorElement?a(v.getAttribute("href")):null,landmark:n(v)}))}const h=[],f=/(password|cvc|cvv|otp|one[-_\s]?time|security[-_\s]?answer|secret|2fa|mfa)/i,w=document.querySelectorAll("input, textarea, select");for(const v of Array.from(w)){if(v instanceof HTMLInputElement){const S=(v.type||"text").toLowerCase();if(S==="hidden"||S==="submit"||S==="button"||S==="image"||S==="reset")continue}const s=v.getAttribute("name")||v.id||"";if(!s)continue;const x=v instanceof HTMLInputElement?v.type||"text":v instanceof HTMLTextAreaElement?"textarea":"select",$=t(v),A=f.test(s)||f.test($)||x==="password";h.push({name:s,type:x,label:$,isSecurityEssential:A})}let b=!1;if(document.querySelector('[role="progressbar"]')&&(b=!0),!b){const v=document.querySelectorAll('button, a, [role="button"]');let s=0,x=0;for(const $ of Array.from(v)){const A=($.textContent??"").trim();if(/step\s*\d+\s*of\s*\d+/i.test(A)){b=!0;break}/^(next|continue)$/i.test(A)&&s++,/^(previous|prev|back)$/i.test(A)&&x++}s>0&&x>0&&(b=!0)}return{url:e,title:(document.title||(((k=document.querySelector("h1"))==null?void 0:k.textContent)??"")).trim(),navLinks:r,sharedComponents:c,helpMechanisms:p,formFields:h,isMultiStep:b}}const ll=`Respond ONLY with a JSON object — no markdown, no preamble:
|
|
379
379
|
{
|
|
380
380
|
"verdict": "pass" | "fail" | "uncertain",
|
|
381
381
|
"reasoning": "one-sentence explanation",
|
|
@@ -988,10 +988,10 @@ Primary AI verdict was uncertain. Programmatic multi-column detector found no ma
|
|
|
988
988
|
document.body.offsetHeight;
|
|
989
989
|
return true;
|
|
990
990
|
})()
|
|
991
|
-
`;await chrome.debugger.sendCommand({tabId:e},"Runtime.evaluate",{expression:t})}async function Eo(e,t){let a;try{a=await chrome.debugger.sendCommand({tabId:e},"Page.captureScreenshot",{format:"png",captureBeyondViewport:!0,clip:{x:t.x,y:t.y,width:t.width,height:t.height,scale:1}})}catch(n){return Xe.warn(`captureElementClip failed: ${n instanceof Error?n.message:String(n)}`),null}if(!(a!=null&&a.data))return null;try{const n=`data:image/png;base64,${a.data}`,o=await fetch(n).then(d=>d.blob()),r=await createImageBitmap(o),c=new OffscreenCanvas(r.width,r.height).getContext("2d");return c?(c.drawImage(r,0,0),c.getImageData(0,0,r.width,r.height)):null}catch(n){return Xe.warn(`clip decode failed: ${n instanceof Error?n.message:String(n)}`),null}}const Qd=25,hi=8,Zd=150,eu=.95,tu=4,au=400;function nu(e,t,a){if(e.width!==t.width||e.height!==t.height)return null;const n=e.width,o=e.height;let r=0,i=0;for(let v=0;v<o;v++)for(let s=0;s<n;s++){const x=(v*n+s)*4,$=t.data[x],A=t.data[x+1],S=t.data[x+2],F=e.data[x],T=e.data[x+1],I=e.data[x+2],N=Math.abs(F-$)+Math.abs(T-A)+Math.abs(I-S);N>i&&(i=N),N>=Qd&&r++}if(r<hi)return null;const c=i*eu;let d=0,l=0,p=0,m=0;if(i>=au)for(let v=0;v<o;v++)for(let s=0;s<n;s++){const x=(v*n+s)*4,$=t.data[x],A=t.data[x+1],S=t.data[x+2],F=e.data[x],T=e.data[x+1],I=e.data[x+2];Math.abs(F-$)+Math.abs(T-A)+Math.abs(I-S)>=c&&(d+=$,l+=A,p+=S,m++)}const u=m>=tu?{r:Math.round(d/m),g:Math.round(l/m),b:Math.round(p/m)}:a,g=new Map;let h=0;for(let v=0;v<o;v++)for(let s=0;s<n;s++){const x=(v*n+s)*4,$=t.data[x],A=t.data[x+1],S=t.data[x+2];if(Math.abs($-u.r)+Math.abs(A-u.g)+Math.abs(S-u.b)>=Zd){h++;const T=$>>4<<8|A>>4<<4|S>>4,I=g.get(T);I?(I.r+=$,I.g+=A,I.b+=S,I.count++):g.set(T,{r:$,g:A,b:S,count:1})}}const f=n*o;if(h<f*.2){g.clear();for(let v=0;v<o;v++)for(let s=0;s<n;s++){const x=(v*n+s)*4,$=e.data[x],A=e.data[x+1],S=e.data[x+2],F=$>>4<<8|A>>4<<4|S>>4,T=g.get(F);T?(T.r+=$,T.g+=A,T.b+=S,T.count++):g.set(F,{r:$,g:A,b:S,count:1})}}let w={r:0,g:0,b:0,count:0};for(const v of g.values())v.count>w.count&&(w=v);const b={r:Math.round(w.r/w.count),g:Math.round(w.g/w.count),b:Math.round(w.b/w.count)},k=Un(u,b);return{textPixelCount:r,worstRatio:k,worstBackground:b,worstForeground:u}}async function ou(e,t,a){var d,l,p,m,u,g;const n=new Map;for(const h of t){const f=h.pageUrl??h.scope??"";for(const w of((d=h.axeRulesEvaluated)==null?void 0:d.incomplete)??[])if(w.ruleId==="color-contrast")for(const b of w.elements??[]){const k=`${f}::${w.ruleId}::${b.selector}`;n.has(k)||n.set(k,{pageUrl:f,ruleId:w.ruleId,selector:b.selector,wcagCriterion:w.wcagCriterion??"1.4.3",foreground:(l=b.styles)==null?void 0:l.foreground,fontSize:(p=b.styles)==null?void 0:p.fontSize,fontWeight:(m=b.styles)==null?void 0:m.fontWeight,failureSummary:b.failureSummary})}}if(n.size===0)return{resolved:0,skipped:0};let o=!1,r=null;for(let h=1;h<=6;h++)try{await chrome.debugger.attach({tabId:e},jd),o=!0;break}catch(f){r=f;const w=f instanceof Error?f.message:String(f);if(/already attached/i.test(w)){await new Promise(b=>setTimeout(b,500));continue}break}if(!o)return Xe.warn("debugger attach failed after retries; skipping pixel sampling",r),{resolved:0,skipped:n.size};let i=0,c=0;try{const h=Array.from(n.values()),f=h.map(I=>I.selector),w=await Yd(e,f);if(a!=null&&a.aborted)return{resolved:0,skipped:n.size};const b=I=>{const N=w.get(I);return N&&!("decorative"in N)?N:null},k=new Set;for(const[I,N]of w)N&&"decorative"in N&&k.add(I);for(const I of h){if(!k.has(I.selector))continue;const N={pageUrl:I.pageUrl,ruleId:I.ruleId,selector:I.selector,verdict:"pass",reasoning:'Not applicable to WCAG 1.4.3 (contrast minimum): this element is decorative / removed from the accessibility tree — it sits inside an aria-hidden="true" subtree, or is visually hidden (display:none, sr-only clip, off-screen, or zero opacity). Decorative and incidental content is exempt from the contrast-minimum requirement, so it is recorded as not-applicable and does not count as a violation.',resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:I.wcagCriterion,inputHash:await $o({...I,bgFingerprint:null})};await We(N),i++}const v=h.filter(I=>!k.has(I.selector));if(v.length===0)return Xe.info(`pixel-contrast sampler: all ${h.length} targets decorative/not-applicable (1.4.3)`),{resolved:i,skipped:c};const s=((u=h[0])==null?void 0:u.pageUrl)??"",x=s?await va(s):[],$=new Map;for(const I of x)$.set(`${I.ruleId}::${I.selector}`,I);const A=new Map,S=[];for(const I of v){const N=await $o({...I,bgFingerprint:((g=b(I.selector))==null?void 0:g.bgFingerprint)??null});A.set(I.selector,N);const C=$.get(`${I.ruleId}::${I.selector}`);if(C&&C.inputHash===N&&C.verdict!=="uncertain"){const _={...C,resolvedAt:new Date().toISOString(),reasoning:C.reasoning.includes("(cached, hash-match)")?C.reasoning:`${C.reasoning} (cached, hash-match)`};await We(_),i++;continue}S.push(I)}if(S.length===0)return Xe.info(`pixel-contrast sampler: all ${h.length} targets reused from hash-cache`),{resolved:i,skipped:c};await Jd(e);const F=new Map;for(const I of S){if(a!=null&&a.aborted)break;const N=b(I.selector);if(!N)continue;const C=await Eo(e,N);C&&F.set(I.selector,C)}if(await Xd(e),a!=null&&a.aborted)return{resolved:0,skipped:n.size};const T=new Map;for(const I of S){if(a!=null&&a.aborted)break;const N=b(I.selector);if(!N)continue;const C=await Eo(e,N);C&&T.set(I.selector,C)}for(const I of S){if(a!=null&&a.aborted)break;const N=A.get(I.selector),C=b(I.selector);if(!C){c++;const X={pageUrl:I.pageUrl,ruleId:I.ruleId,selector:I.selector,verdict:"uncertain",reasoning:"Hide-text observation: element's bounding rect could not be resolved at audit time (element may be display:none, detached from DOM, or zero-size). Cannot sample contrast empirically. Manual verification required if you believe this element is visible.",resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:I.wcagCriterion,inputHash:N};await We(X);continue}const _=F.get(I.selector),M=T.get(I.selector);if(!_||!M){c++;const X={pageUrl:I.pageUrl,ruleId:I.ruleId,selector:I.selector,verdict:"uncertain",reasoning:"Hide-text observation: per-element clip capture failed via chrome.debugger Page.captureScreenshot. Manual verification required.",resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:I.wcagCriterion,inputHash:N};await We(X);continue}const L=zd(I.foreground);if(!L){c++;const X={pageUrl:I.pageUrl,ruleId:I.ruleId,selector:I.selector,verdict:"uncertain",reasoning:`Hide-text observation: element's computed CSS foreground color could not be parsed (got ${JSON.stringify(I.foreground)}). Cannot compute contrast against rendered background without an intended foreground color. Manual verification required.`,resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:I.wcagCriterion,inputHash:N};await We(X);continue}const W=nu(_,M,L);if(!W){c++;const X={pageUrl:I.pageUrl,ruleId:I.ruleId,selector:I.selector,verdict:"uncertain",reasoning:`Hide-text observation: too few text pixels detected (under ${hi} after diffing hidden vs visible captures). Element may be canvas-rendered, SVG with non-standard rendering, or carry styles that prevent color: transparent from hiding the text. Manual verification required.`,resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:I.wcagCriterion,inputHash:N};await We(X);continue}const P=qd(I.fontSize,I.fontWeight),V=P?Bd:Hd,O=X=>`rgb(${X.r},${X.g},${X.b})`;let j=W.worstRatio>=V,q="";if(Kd(W.worstRatio,L,C.resolvedBg,V)){const X=Un(L,C.resolvedBg);j=!0,q=` Cross-check (rc.356): computed CSS contrast ${O(L)} on resolved background ${O(C.resolvedBg)} = ${X.toFixed(2)}:1, which PASSES ${V}:1 — the sub-threshold pixel reading is an anti-aliasing/sampling artifact, not a real failure. Suppressed.`}const z={pageUrl:I.pageUrl,ruleId:I.ruleId,selector:I.selector,verdict:j?"pass":"fail",reasoning:`Hide-text observation: ${W.textPixelCount} text-core pixels detected by diffing hidden-vs-visible viewport screenshots. Worst per-pixel contrast: ${W.worstRatio.toFixed(2)}:1 against AA threshold ${V}:1 (${P?"large text":"normal text"}). At worst pixel: rendered foreground ${O(W.worstForeground)} on rendered background ${O(W.worstBackground)}. ${j&&q?"Passes (computed cross-check).":j?"Passes threshold.":"Falls below threshold — at least one text pixel reads against a background that fails contrast."}`+q,resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:I.wcagCriterion,inputHash:N};await We(z),i++}}finally{try{await chrome.debugger.sendCommand({tabId:e},"Runtime.evaluate",{expression:`(() => { const el = document.getElementById(${JSON.stringify(Wn)}); if (el) el.remove(); return true; })()`})}catch{}try{await chrome.debugger.detach({tabId:e})}catch{}}return Xe.info(`pixel-contrast sampler: resolved ${i}, skipped ${c}/${n.size}`),{resolved:i,skipped:c}}const mt=Z("ai-text");function bt(e){if(e!=null&&e.aborted){const t=new Error("aborted");throw t.name="AbortError",t}}function yt(e){return e instanceof Error&&(e.name==="AbortError"||e.name==="ExternalAbortError"||e.message==="aborted")}async function ru(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.headings)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=de(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Le(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),p=e.slice(0,l),m=p.length;for(let g=0;g<p.length;g++){const h=p[g];if(!i.canCharge())break;try{bt(n.signal);const f=await r.judgeHeading({headingText:h.text,sectionContent:h.sectionContent,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.6&&c.push(await wt("ai-heading-not-descriptive","2.4.6","AA","moderate","AI judged this heading does not describe its section content.",h.selector,h.outerHTML,h.text,f,t))}catch(f){if(yt(f)){d.push(`canceled after ${g}/${m} candidates`);break}const w=f instanceof Error?f.message:String(f);d.push(`${h.selector}: ${w}`),mt.warn("heading judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function iu(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.sensory)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=de(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Le(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),p=e.slice(0,l),m=p.length;for(let g=0;g<p.length;g++){const h=p[g];if(!i.canCharge())break;try{bt(n.signal);const f=await r.judgeSensoryLanguage({instructionText:h.text,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.6&&c.push(await wt("ai-sensory-instruction","1.3.3","A","serious","AI flagged this instruction as relying on sensory characteristics (shape, color, location) without a programmatic identifier.",h.selector,h.outerHTML,h.text.slice(0,100),f,t))}catch(f){if(yt(f)){d.push(`canceled after ${g}/${m} candidates`);break}const w=f instanceof Error?f.message:String(f);d.push(`${h.selector}: ${w}`),mt.warn("sensory judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function su(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.aria)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=de(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Le(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),p=e.slice(0,l),m=p.length;for(let g=0;g<p.length;g++){const h=p[g];if(!i.canCharge())break;try{bt(n.signal);const f=await r.judgeAriaSemantics({elementHtml:h.outerHTML,computedRole:h.role,surroundingHtml:h.surroundingHtml,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.65&&c.push(await wt("ai-aria-misuse","4.1.2","A","serious",`AI judged role="${h.role}" is being used inappropriately on this element.`,h.selector,h.outerHTML,h.role,f,t))}catch(f){if(yt(f)){d.push(`canceled after ${g}/${m} candidates`);break}const w=f instanceof Error?f.message:String(f);d.push(`${h.selector}: ${w}`),mt.warn("aria judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function cu(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.genericLinkText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=de(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Le(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),p=e.slice(0,l),m=p.length;for(let g=0;g<p.length;g++){const h=p[g];if(!i.canCharge())break;try{bt(n.signal);const f=await r.judgeGenericLinkText({linkText:h.linkText,surroundingText:h.surroundingText,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.6&&c.push(await wt("ai-generic-link-text","2.4.4","A","serious","AI judged this link's purpose is not clear from its text or surrounding context.",h.selector,h.outerHTML,h.linkText,f,t))}catch(f){if(yt(f)){d.push(`canceled after ${g}/${m} candidates`);break}const w=f instanceof Error?f.message:String(f);d.push(`${h.selector}: ${w}`),mt.warn("generic-link judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function lu(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.wallOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=de(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Le(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),p=e.slice(0,l),m=p.length;for(let g=0;g<p.length;g++){const h=p[g];if(!i.canCharge())break;try{bt(n.signal);const f=await r.judgeWallOfText({blockText:h.blockText,structuralChildrenCount:h.structuralChildrenCount,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.6&&c.push(await wt("ai-wall-of-text","3.1.5","AAA","moderate","AI flagged this block as a wall of text with insufficient structural breaks (subheadings, lists, paragraphs).",h.selector,h.outerHTML,h.blockText.slice(0,80),f,t))}catch(f){if(yt(f)){d.push(`canceled after ${g}/${m} candidates`);break}const w=f instanceof Error?f.message:String(f);d.push(`${h.selector}: ${w}`),mt.warn("wall-of-text judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function du(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.languageMismatch)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=de(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Le(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),p=e.slice(0,l),m=p.length;for(let g=0;g<p.length;g++){const h=p[g];if(!i.canCharge())break;try{bt(n.signal);const f=await r.judgeLanguageMismatch({declaredLang:h.declaredLang,bodyTextSample:h.bodyTextSample,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.7&&c.push(await wt("ai-language-mismatch","3.1.1","A","serious",`AI judged the page's content does not match the declared lang="${h.declaredLang||"(none)"}".`,h.selector,h.outerHTML,h.bodyTextSample.slice(0,80),f,t))}catch(f){if(yt(f)){d.push(`canceled after ${g}/${m} candidates`);break}const w=f instanceof Error?f.message:String(f);d.push(`${h.selector}: ${w}`),mt.warn("language-mismatch judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function wt(e,t,a,n,o,r,i,c,d,l){const p={selector:r,outerHTML:i.slice(0,500),failureSummary:`AI assessment: ${d.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:c.slice(0,100)||null,attrId:null,attrTestid:null},m=await yn({ruleId:e,componentId:l.componentId,currentState:l.currentState,target:p});return{ruleId:e,wcagCriterion:t,wcagLevel:a,impact:n,description:o,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:p,componentId:l.componentId,currentState:l.currentState,axeVersion:l.axeVersion,detectedAt:new Date().toISOString(),matchKey:m,ai:{reasoning:d.reasoning,confidence:d.confidence,model:d.model,costUsd:d.costUsd}}}const pi=Z("ai-vision");function fi(e){if(e!=null&&e.aborted){const t=new Error("aborted");throw t.name="AbortError",t}}function gi(e){return e instanceof Error&&(e.name==="AbortError"||e.name==="ExternalAbortError"||e.message==="aborted")}async function uu(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.imageOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=de(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Le(a.costCapUsd),c=Math.max(1,a.imageOfTextMaxImages??5),d=[...e].sort((g,h)=>h.pixelArea-g.pixelArea).slice(0,c),l=d.length,p=[],m=[];for(let g=0;g<d.length;g++){const h=d[g];if(!i.canCharge())break;try{fi(n.signal);const f=await r.judgeImageOfText({imageUrl:h.imageUrl,accessibleName:h.accessibleName,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.65&&p.push(await mi("ai-image-of-text","1.4.5","AA","moderate","AI judged this image bakes substantial text into the raster — should be presented as actual HTML text instead.",h.selector,h.outerHTML,h.accessibleName??"",f,t))}catch(f){if(gi(f)){m.push(`canceled after ${g}/${l} candidates`);break}const w=f instanceof Error?f.message:String(f);m.push(`${h.selector}: ${w}`),pi.warn("image-of-text judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,l)}return{findings:p,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:m}}async function hu(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.colorOnlyMeaning)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=de(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[o.reason]};const r=o.client,i=Le(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),p=e.slice(0,l),m=p.length;for(let g=0;g<p.length;g++){const h=p[g];if(!i.canCharge())break;try{fi(n.signal);const f=await r.judgeColorOnlyMeaning({elementHtml:h.elementHtml,contextDescription:h.contextDescription,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.65&&c.push(await mi("ai-color-only-meaning","1.4.1","A","serious","AI judged this region conveys information only through color (no text label, icon glyph, or shape difference).",h.selector,h.outerHTML,h.contextDescription,f,t))}catch(f){if(gi(f)){d.push(`canceled after ${g}/${m} candidates`);break}const w=f instanceof Error?f.message:String(f);d.push(w),pi.warn("color-only judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function mi(e,t,a,n,o,r,i,c,d,l){const p={selector:r,outerHTML:i.slice(0,500),failureSummary:`AI assessment: ${d.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:c.slice(0,100)||null,attrId:null,attrTestid:null},m=await yn({ruleId:e,componentId:l.componentId,currentState:l.currentState,target:p});return{ruleId:e,wcagCriterion:t,wcagLevel:a,impact:n,description:o,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:p,componentId:l.componentId,currentState:l.currentState,axeVersion:l.axeVersion,detectedAt:new Date().toISOString(),matchKey:m,ai:{reasoning:d.reasoning,confidence:d.confidence,model:d.model,costUsd:d.costUsd}}}const fn=Z("ensure-content-script"),pu=1500,fu=300;async function Gn(e,t){if(await Ro(e,t))return{ok:!0};const a=chrome.runtime.getManifest(),n=[];for(const o of a.content_scripts??[])Array.isArray(o.js)&&n.push(...o.js);if(n.length===0)return fn.error("manifest declares no content_scripts"),{ok:!1,reason:"inject-failed",detail:"no content_scripts declared in manifest"};try{const o=t!==void 0?{tabId:e,frameIds:[t]}:{tabId:e,allFrames:!0};await chrome.scripting.executeScript({target:o,files:n})}catch(o){return gu(o)}return await mu(fu),await Ro(e,t)?(fn.info("content script injected on demand",{tabId:e,frameId:t}),{ok:!0}):{ok:!1,reason:"unresponsive-after-inject",detail:"content script injected but did not register handlers — likely Content Security Policy on this page is blocking it"}}async function Ro(e,t){try{const a=t!==void 0?{frameId:t}:void 0,n=await Promise.race([chrome.tabs.sendMessage(e,{type:"PING_REQUEST"},a),new Promise((o,r)=>setTimeout(()=>r(new Error("ping timeout")),pu))]);return(n==null?void 0:n.type)==="PING_RESPONSE"}catch{return!1}}function gu(e){const t=e instanceof Error?e.message:String(e);return/cannot access contents of (?:the page|url)/i.test(t)||/chrome:\/\//i.test(t)||/chrome-extension:\/\//i.test(t)||/devtools:\/\//i.test(t)||/chrome\.google\.com\/webstore/i.test(t)||/the extensions gallery/i.test(t)?{ok:!1,reason:"restricted-url",detail:t}:/no tab with id/i.test(t)||/tab .* was closed/i.test(t)?{ok:!1,reason:"tab-gone",detail:t}:(fn.warn("content-script injection failed",e),{ok:!1,reason:"inject-failed",detail:t})}function mu(e){return new Promise(t=>setTimeout(t,e))}function gn(e){switch(e.reason){case"restricted-url":return"Chrome blocks extensions on this page (chrome:// pages, the Chrome Web Store, view-source: pages, and a few others). Open a regular https:// page and try again.";case"tab-gone":return"The tab was closed or navigated away while the audit was starting. Click into the tab you want audited and click Scan again.";case"unresponsive-after-inject":return"We loaded our auditor onto the page, but the page's Content Security Policy appears to be blocking it. Some sites (banks, gov, strict CSPs) prevent extensions from running. Try a different page on the same site, or open DevTools → Console to confirm a CSP error.";case"inject-failed":return`Could not load our auditor onto the page. Try a hard refresh (Ctrl+Shift+R) on the page and click Scan again. If it persists, the page may be using an unusual document type or origin restriction.${e.detail?` (Detail: ${e.detail})`:""}`;case"unknown":default:return"Could not establish connection with the page. Hard-refresh (Ctrl+Shift+R) the tab you want audited and try again."}}const qa=Z("forensic-anchor-client"),bu="https://api.wcagcheckr.com",yu="wcagcheckr",wu=15e3;function vu(e){if(typeof e!="object"||e===null)return!1;const t=e;return t.schemaVersion!==1&&t.schemaVersion!==2||!(typeof t.anchoredAt=="string"&&typeof t.tsaName=="string"&&typeof t.rfc3161TokenBase64=="string"&&t.rfc3161TokenBase64.length>0&&typeof t.serverSignatureBase64=="string"&&t.serverSignatureBase64.length>0&&typeof t.serverKeyFingerprint=="string"&&t.serverKeyFingerprint.length>0)?!1:t.schemaVersion===2?typeof t.prevAuditHash=="string"&&/^[0-9a-f]{64}$/.test(t.prevAuditHash):!0}async function bi(e,t){return yi(e.hash,e.pageUrl,e.capturedAt,t)}async function yi(e,t,a,n){const o={auditHash:e,pageUrl:t,capturedAt:a,...n?{licenseId:n}:{}},r=`${bu}/v1/products/${yu}/forensic/anchor`;try{const i=await fetch(r,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(o),signal:AbortSignal.timeout(wu)});if(!i.ok)return qa.warn(`anchor HTTP ${i.status}; entry stays local-only`),null;const c=await i.json();return vu(c)?c:(qa.warn("anchor returned malformed receipt; entry stays local-only",c),null)}catch(i){return qa.warn("anchor request failed; entry stays local-only",i),null}}async function Au(e){const t=Zt(e),a=new TextEncoder().encode(t),n=await crypto.subtle.digest("SHA-256",a);return Array.from(new Uint8Array(n)).map(o=>o.toString(16).padStart(2,"0")).join("")}function ku(e){try{return new URL(e).hostname}catch{return e}}const xu="wcag-site-claims",Iu=1,sa="claims";let za=null;function wi(){return za||(za=vn(xu,Iu,{upgrade(e){e.objectStoreNames.contains(sa)||e.createObjectStore(sa,{keyPath:"domain"})}})),za}async function Su(e){await(await wi()).put(sa,e)}async function ef(e){return await(await wi()).get(sa,e)??null}const Oo="4.11.4";async function Cu(e){try{return(await chrome.tabs.get(e)).url??""}catch{return""}}const U=Z("flows");async function vt(){const e=await dr();if(!e)throw new Error("no audit target tab found");return e}async function Vn(){var a,n,o,r,i;const e=await Ye("stateMatrix",Qa);return((a=e.pseudoStates)==null?void 0:a.length)>0&&((n=e.themes)==null?void 0:n.length)>0&&((o=e.directions)==null?void 0:o.length)>0&&((r=e.breakpoints)==null?void 0:r.length)>0&&((i=e.breakpointPresets)==null?void 0:i.length)>0?e:(U.warn("stored stateMatrix is invalid; using defaults"),Qa)}async function $u(e,t,a){const n=t.themes.includes("light"),o=t.themes.includes("dark");if(!n||!o)return t;let r=null;try{r=await Ae(e,{type:"THEME_AWARENESS_REQUEST"},a)}catch{return t}return!r||r.hasUnreadableSheets?t:r.hasDarkModeCss&&!r.hasLightModeCss?(U.info("theme-axis prune: site is dark-only, skipping light pass"),{...t,themes:t.themes.filter(i=>i!=="light")}):r.hasLightModeCss&&!r.hasDarkModeCss?(U.info("theme-axis prune: site is light-only, skipping dark pass"),{...t,themes:t.themes.filter(i=>i!=="dark")}):t}const Tu=200,Ka=3,Mo=8;async function Eu(e,t){const a=[];let n=null,o=null;for(let i=0;i<Mo&&!t();i++){const c=await e();if(c.error&&(o=c.error),!c.result)break;if(n=c.result,a.push(c.violations),i===1&&a[0].length===0&&a[1].length===0)return{violations:[],lastResult:n,sampleCount:2,stabilized:!0,lastError:o};if(a.length>=Ka){const d=a.slice(-Ka);if(Ru(d))return{violations:d[d.length-1],lastResult:n,sampleCount:a.length,stabilized:!0,lastError:o}}i<Mo-1&&await new Promise(d=>setTimeout(d,Tu))}const r=a.slice(-Ka);return{violations:Ou(r),lastResult:n,sampleCount:a.length,stabilized:!1,lastError:o}}function Ru(e){if(e.length<2)return!0;const t=Do(e[0]);for(let a=1;a<e.length;a++){const n=Do(e[a]);if(n.size!==t.size)return!1;for(const o of t)if(!n.has(o))return!1}return!0}function Do(e){return new Set(e.map(t=>`${t.ruleId}::${t.target.selector}::${t.matchKey}`))}function Ou(e){const t=new Map,a="Audit detected this finding intermittently across multiple samples — likely an animation or transitional state was in progress. Verify manually.";for(const n of e)for(const o of n){const r=`${o.ruleId}::${o.target.selector}::${o.matchKey}`;t.has(r)||t.set(r,{...o,needsReview:!0,flakyAcrossRuns:!0,target:{...o.target,failureSummary:o.target.failureSummary?`${o.target.failureSummary} ${a}`:a}})}return Array.from(t.values())}async function No(e,t,a,n,o){await Promise.all([(async()=>{var r;try{const i=await Ae(e,{type:"READING_ORDER_REQUEST",selector:t},a);n[0]&&(i.issues.length>0?(n[0].readingOrderIssues=i.issues,n[0].positionAnalysisCapturedAt=o):(r=n[0].axeRulesEvaluated)==null||r.passed.push({ruleId:"wcc-reading-order-clean",wcagCriterion:"1.3.2"}))}catch(i){U.debug("reading-order analysis skipped",i)}})(),(async()=>{var r;try{const i=await Ae(e,{type:"TAB_ORDER_ANALYZE_REQUEST"},a);n[0]&&(i.issues.length>0?(n[0].tabOrderIssues=i.issues,n[0].positionAnalysisCapturedAt=o):(r=n[0].axeRulesEvaluated)==null||r.passed.push({ruleId:"wcc-tab-order-clean",wcagCriterion:"2.4.3"}))}catch(i){U.debug("tab-order analysis skipped",i)}})(),(async()=>{var r;try{const i=await Ae(e,{type:"TYPOGRAPHY_ANALYZE_REQUEST",selector:t},a);i.issues.length>0&&n[0]?n[0].typographyIssues=i.issues:n[0]&&((r=n[0].axeRulesEvaluated)==null||r.passed.push({ruleId:"wcc-typography-clean",wcagCriterion:"1.4.12"}))}catch(i){U.debug("typography analysis skipped",i)}})(),(async()=>{try{const r=await Ae(e,{type:"CUSTOM_PROPERTY_REQUEST"},a);r.undefined.length>0&&n[0]&&(n[0].undefinedCustomProperties=r.undefined)}catch(r){U.debug("custom-property analysis skipped",r)}})()])}async function pt(e,t,a,n,o={}){var I,N,C,_,M;const r=o.asSubroutine===!0;if(await je())return r||B({type:"AUDIT_FAILED_EVENT",error:{code:Dt,message:Ze,recoverable:!1}}),{ok:!1,results:[],error:Ze};const i=o.tabIdOverride??await vt(),c=await Gn(i,a);if(!c.ok)return r||B({type:"AUDIT_FAILED_EVENT",error:{code:c.reason==="restricted-url"?"RESTRICTED_URL":"CONTENT_SCRIPT_UNREACHABLE",message:gn(c),recoverable:c.reason!=="restricted-url"}}),{ok:!1,results:[],error:gn(c)};try{const[L]=await chrome.scripting.executeScript({target:{tabId:i,frameIds:a!==void 0?[a]:void 0},func:()=>document.contentType}),W=String((L==null?void 0:L.result)??"").toLowerCase();if(!(W===""||W==="text/html"||W==="application/xhtml+xml")){const V=`This page is not an HTML document (Content-Type: ${W||"unknown"}). WCAG HTML checks don't apply to XML sitemaps, RSS/Atom feeds, JSON, or plain text — skipped.`;return r||B({type:"AUDIT_FAILED_EVENT",error:{code:"NOT_HTML_DOCUMENT",message:V,recoverable:!1}}),{ok:!1,results:[],error:V,skipped:!0}}}catch(L){U.debug("content-type probe failed (non-fatal); proceeding with audit",L)}try{await chrome.scripting.executeScript({target:{tabId:i,frameIds:a!==void 0?[a]:void 0},world:"MAIN",func:()=>{try{let L=document.getElementById("__wcagcheckr_audit_freeze");L||(L=document.createElement("style"),L.id="__wcagcheckr_audit_freeze",L.textContent="*, *::before, *::after { animation-duration: 0s !important; animation-delay: 0s !important; transition-duration: 0s !important; transition-delay: 0s !important; } html, body { scroll-behavior: auto !important; }",document.documentElement.appendChild(L)),window.scrollTo(0,0);const W=document.activeElement;W&&W instanceof HTMLElement&&W!==document.body&&W.blur()}catch{}}}),await chrome.scripting.executeScript({target:{tabId:i,frameIds:a!==void 0?[a]:void 0},world:"MAIN",func:async()=>{try{await document.fonts.ready}catch{}}})}catch(L){U.debug("audit-start state stabilization failed (non-fatal)",L)}const d=n??await Vn(),l=await $u(i,d,a),p=Ll(l),m=await ss(`${await Cu(i)}|${e}|${a??0}`),u=await cs(l),g=await Ye("axeDisabledRules",[]),h=await ls(g);let f=null;try{f=(await Ae(i,{type:"SCOPE_FINGERPRINT_REQUEST",selector:e},a)).fingerprint}catch(L){U.debug("scope fingerprint failed; skipping cache fast-path",L)}if(f){const L=await ds(m);if(L&&L.fingerprint===f&&L.axeVersion===Oo&&L.matrixConfigHash===u&&L.disabledRulesHash===h)return U.info(`audit-cache HIT for ${L.componentId} — skipping matrix iteration`),r||(B({type:"AUDIT_COMPLETE_EVENT",componentId:L.componentId,results:L.results,delta:L.delta}),B({type:"SCORECARD_UPDATED_EVENT"})),{ok:!0,results:L.results}}r||await at({sessionId:Pt(),mode:"single-element",scope:e,startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"}),await $t(i,{type:"LIVE_REGIONS_ARM_REQUEST"},a).catch(()=>{}),await $t(i,{type:"FOCUS_TRACE_ARM_REQUEST"},a).catch(()=>{}),await Ae(i,{type:"WARMUP_REQUEST"},a).catch(()=>{});const w=[],b=[];let k="",v,s=!1,x=null,$=null,A=null,S=null,F=!1,T=null;try{for(let L=0;L<p.length;L++){if(t.isCanceled()){U.info("single audit canceled by user");break}const W=p[L];try{const P=await hn(i,W,e,a);if(!P.success){v=(I=P.error)==null?void 0:I.message,U.warn(`state ${L+1}/${p.length} drive failed`,v);continue}W.breakpoint.id!==x&&(await Ae(i,{type:"WARMUP_REQUEST"},a).catch(()=>{}),x=W.breakpoint.id);const V=L===0?void 0:[...ai],O=await Eu(async()=>{var z;const q=await Ae(i,{type:"AUDIT_REQUEST",selector:e,currentState:W,rulesToRun:V},a);return q.success&&q.result?{result:q.result,violations:q.result.violations,error:null}:{result:null,violations:[],error:((z=q.error)==null?void 0:z.message)??null}},()=>t.isCanceled());O.lastError&&(v=O.lastError,U.warn(`state ${L+1}/${p.length} sampling: ${O.lastError}`));const j=O.lastResult;if(!O.stabilized&&O.sampleCount>0&&U.info(`state ${L+1}/${p.length} did not stabilize after ${O.sampleCount} samples — ${O.violations.length} finding(s) routed to needs-review as flickering`),j){s=!0;const q=O.violations;U.info(`[adaptive-sample] state ${L+1}/${p.length} ${W.pseudoState}·${W.theme}·${W.direction}·${W.breakpoint.id}: ${O.sampleCount} sample(s), ${O.stabilized?"stabilized":"flickering"}, ${q.length} finding(s)`);const z=await ed(i,{quality:75})??void 0;let X,ge;try{X=(await Ae(i,{type:"LIVE_REGIONS_DRAIN_REQUEST"},a)).announcements}catch{}try{ge=(await Ae(i,{type:"FOCUS_TRACE_DRAIN_REQUEST"},a)).focusEvents}catch{}if(b.push({...j,violations:q,frameId:a,announcements:X,focusEvents:ge,screenshotDataUrl:z}),w.push(...q),k=j.componentId,L===0&&$===null)try{const ue=await Ye("aiConfig",{}),we=ye(ue);if(we.enabled&&we.apiKey&&k){$=ua(i,e,a,we,b,k,t).catch(Ie=>{U.warn("ai augmentation (parallel) failed",Ie)});const ve=((N=b[0])==null?void 0:N.pageUrl)??((C=b[0])==null?void 0:C.scope)??"";A=Gd({tabId:i,componentId:k,pageUrl:ve,results:b,verifyFixesOnly:o.verifyFixesOnly}).catch(Ie=>(U.warn("1.4.11 walkthrough (parallel) failed",Ie),{criterionId:"1.4.11",ran:!0,ok:!1,error:String(Ie)}))}}catch(ue){U.debug("ai augmentation (parallel) gate-read failed",ue)}!F&&W.pseudoState==="default"&&W.theme==="light"&&W.direction==="ltr"&&W.breakpoint.id==="desktop"&&b[0]&&(F=!0,T=No(i,e,a,b,W).catch(we=>{U.debug("lightweight deep analyzers (parallel) failed",we)}))}}catch(P){v=P instanceof Error?P.message:String(P),U.warn(`state ${L+1}/${p.length} threw`,P)}B({type:"AUDIT_PROGRESS_EVENT",current:L+1,total:p.length,currentState:W,componentId:k||void 0}),r||await Lt({})}if(s&&k){const L={pseudoState:"default",ariaVariation:null,theme:"light",direction:"ltr",breakpoint:{id:"desktop",label:"Desktop",width:1280,height:800,deviceScaleFactor:1,mobile:!1}};if(F){if(T)try{await T}catch{}}else{try{await hn(i,L,e,a),await new Promise(O=>setTimeout(O,200))}catch(O){U.debug("reference-state drive failed; analyzers will use last matrix state",O)}await No(i,e,a,b,L).catch(O=>{U.debug("lightweight deep analyzers (post-matrix fallback) failed",O)})}try{const O=await Ye("aiConfig",{}),j=ye(O);if(j.enabled&&j.apiKey&&k&&b[0]){const q=b[0].pageUrl??b[0].scope??"";S=Wd({tabId:i,componentId:k,pageUrl:q,results:b,verifyFixesOnly:o.verifyFixesOnly}).catch(z=>(U.warn("1.3.2 walkthrough (parallel) failed",z),{criterionId:"1.3.2",ran:!0,ok:!1,error:String(z)}))}}catch(O){U.debug("1.3.2 walkthrough (parallel) gate-read failed",O)}try{if($)await $;else{const O=await Ye("aiConfig",{}),j=ye(O);j.enabled&&j.apiKey&&await ua(i,e,a,j,b,k,t)}}catch(O){U.warn("ai augmentation skipped",O)}try{const O=await Ye("aiConfig",{}),j=ye(O);if(j.enabled&&j.apiKey&&k&&b[0]){await zt(i,e,a).catch(ue=>U.warn("pre-walkthrough resetState failed",ue));const q=b[0].pageUrl??b[0].scope??"",z=new AbortController,X=setInterval(()=>{t.isCanceled()&&z.abort()},250),ge=await Ud({tabId:i,componentId:k,pageUrl:q,results:b,signal:z.signal,verifyFixesOnly:o.verifyFixesOnly}).finally(()=>clearInterval(X));U.info("keyboard walkthroughs complete",ge)}}catch(O){U.warn("keyboard walkthroughs failed",O)}try{const O=[];A&&O.push(await A),S&&O.push(await S),O.length>0&&U.info("parallel AI walkthroughs joined",O)}catch(O){U.warn("parallel AI walkthrough join failed",O)}try{if(k&&b[0]){const O=b[0].pageUrl??b[0].scope??"",j=new AbortController,q=setInterval(()=>{t.isCanceled()&&j.abort()},250),z=await Vd({tabId:i,componentId:k,pageUrl:O,results:b,signal:j.signal,verifyFixesOnly:o.verifyFixesOnly}).finally(()=>clearInterval(q));U.info("2.5.3 label-in-name walkthrough complete",z)}}catch(O){U.warn("2.5.3 walkthrough failed",O)}try{if(k&&b[0]){await zt(i,e,a).catch(X=>U.warn("pre-sampler resetState failed",X));const O=new AbortController,j=setInterval(()=>{t.isCanceled()&&O.abort()},250),{resolved:q,skipped:z}=await ou(i,b,O.signal).finally(()=>clearInterval(j));U.info(`pixel contrast sampler: resolved=${q} skipped=${z}`)}}catch(O){U.warn("pixel contrast sampler failed",O)}const W=b.flatMap(O=>O.announcements??[]),P=b.flatMap(O=>O.focusEvents??[]),V=await Pn(k,w,{announcements:W,focusEvents:P},d);try{const O=await Aa(b.map(ie=>ie.componentId)),j=await xr(b.map(ie=>ie.componentId)),q=new Set;for(const ie of new Set(b.map(y=>y.componentId))){const y=await ya(ie);for(const D of y)q.add(D)}const z=[...new Set(b.map(ie=>ie.componentId))],X=await Dr(z),ge=((_=b[0])==null?void 0:_.pageUrl)??((M=b[0])==null?void 0:M.scope)??"",ue=ge?await va(ge):[],we=new Set(b.map(ie=>ie.pageUrl??ie.scope??"").filter(Boolean)).size>1?"site-crawl":"single-page",ve=et(w,{runs:[],workflows:De},b,q,O,j,X,we,ue),Ie={...ve.score,letter:ve.combined},Be=b.reduce((ie,y)=>ie+y.durationMs,0),qe=await le(),$e=ce(qe,"forensicLog")?await Gr(b,Ie,Be):null;if($e){const ie=await le();if(ce(ie,"forensicAnchoring"))try{const y=await pa(),D=await bi($e,y??void 0);D&&await Vr($e.componentId,$e.capturedAt,D)}catch(y){U.warn("forensic anchoring failed (entry stays local-only)",y)}}}catch(O){U.warn("forensic log recording failed",O)}if(f)try{await us({lookupKey:m,fingerprint:f,axeVersion:Oo,matrixConfigHash:u,disabledRulesHash:h,results:b,delta:V,componentId:k,cachedAt:Date.now()})}catch(O){U.warn("audit-cache write failed",O)}try{await chrome.scripting.executeScript({target:{tabId:i,frameIds:a!==void 0?[a]:void 0},world:"MAIN",func:()=>{const O=document.getElementById("__wcagcheckr_audit_freeze");O&&O.remove()}})}catch(O){U.debug("audit-end state-freeze cleanup failed",O)}r||B({type:"AUDIT_COMPLETE_EVENT",componentId:k,results:b,delta:V})}else r||B({type:"AUDIT_FAILED_EVENT",error:{code:"AXE_FAILED",message:v?`All audits failed. Last error: ${v}`:"No state produced results. Selector may not exist in the audit target frame.",recoverable:!0}});r||await dt()}catch(L){U.error("single audit failed",L),r||B({type:"AUDIT_FAILED_EVENT",componentId:k||void 0,error:Kn(L)?L.payload:{code:"UNKNOWN",message:String(L),recoverable:!1}}),v=Kn(L)?L.payload.message:String(L)}finally{await $t(i,{type:"LIVE_REGIONS_DISARM_REQUEST"},a).catch(()=>{}),await $t(i,{type:"FOCUS_TRACE_DISARM_REQUEST"},a).catch(()=>{}),await zt(i,e,a)}return{ok:s,results:b,error:s?void 0:v}}async function vi(e){var t;try{const n=(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{var r;const o=window;return{detected:typeof o.__STORYBOOK_PREVIEW__=="object",version:(r=o.__STORYBOOK_PREVIEW__)==null?void 0:r.version}}})).find(o=>{var r;return(r=o.result)==null?void 0:r.detected});if(n)return{detected:!0,version:(t=n.result)==null?void 0:t.version,frameId:n.frameId}}catch(a){U.warn("storybook detection scripting failed",a)}return{detected:!1}}async function Mu(e){if(await je()){B({type:"AUDIT_FAILED_EVENT",error:{code:Dt,message:Ze,recoverable:!1}});return}const t=await vt(),a=await xi(t);if(await chrome.storage.local.set({"storybook:lastDetected":{detected:a.kind==="storybook"&&a.detected,version:a.version,detectedAt:new Date().toISOString()},"playground:lastDetected":{kind:a.kind,detected:a.detected,version:a.version,detectedAt:new Date().toISOString()}}),!a.detected){B({type:"AUDIT_FAILED_EVENT",error:{code:"STORYBOOK_NOT_DETECTED",message:"No supported component playground detected on this tab. Open a Storybook (iframe.html / storybook-static), Ladle, or Bit page and try again.",recoverable:!0}});return}if(a.kind==="bit"){B({type:"AUDIT_FAILED_EVENT",error:{code:"STORYBOOK_NOT_DETECTED",message:"Bit detected. Auto-iterate is not yet supported for Bit (compositions are iframe-per-component without a unified listing API). Use the element picker to audit individual compositions one at a time.",recoverable:!0}});return}const n=a.frameId??0,o=a.kind==="storybook"?await Lu(t,n):await Fu(t,n);if(!o.length){U.warn(`${a.kind} detected but no stories returned`),B({type:"AUDIT_FAILED_EVENT",error:{code:"STORYBOOK_NOT_DETECTED",message:`${a.kind==="ladle"?"Ladle":"Storybook"} detected but no stories were returned. Try reloading the page.`,recoverable:!0}});return}await at({sessionId:Pt(),mode:"storybook-all",totalStories:o.length,completedStories:[],startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});for(const r of o){if(e.isCanceled()){U.info(`${a.kind} iteration canceled by user`);break}try{if(a.kind==="storybook"){await Pu(t,n,r.id);const c=await $t(t,{type:"STORYBOOK_GET_STORY_TARGET_REQUEST",tabId:t},n);await pt(c.selector,e,n)}else await _u(t,n,r.id),await pt("#ladle-root",e,n)}catch(c){U.warn(`story ${r.id} failed; continuing with next`,c)}const i=await $a();if((i==null?void 0:i.state)==="interrupted"){U.info("storybook iteration interrupted");break}await Lt({completedStories:[...(i==null?void 0:i.completedStories)??[],r.id]})}B({type:"SCORECARD_UPDATED_EVENT"}),await dt()}async function Du(e){if(await je()){B({type:"AUDIT_FAILED_EVENT",error:{code:Dt,message:Ze,recoverable:!1}});return}const t=await le();if(!ce(t,"allFramesAudit")){B({type:"AUDIT_FAILED_EVENT",error:{code:"FEATURE_NOT_AVAILABLE",message:"All-frames audit is not available on your current plan.",recoverable:!1}});return}const a=await vt();let n=[];try{n=(await chrome.scripting.executeScript({target:{tabId:a,allFrames:!0},func:()=>({url:window.location.href})})).map(r=>{var i;return{frameId:r.frameId,url:((i=r.result)==null?void 0:i.url)??""}}).filter(r=>/^https?:/i.test(r.url))}catch(o){U.warn("multi-frame discovery failed",o)}if(n.length===0){B({type:"AUDIT_FAILED_EVENT",error:{code:"CONTENT_SCRIPT_UNREACHABLE",message:"No auditable frames found on this tab. The page may use only chrome-extension:// or data: frames.",recoverable:!0}});return}await at({sessionId:Pt(),mode:"all-frames",totalFrames:n.length,completedFrames:[],startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});for(const o of n){if(e.isCanceled()){U.info("all-frames iteration canceled");break}try{await pt("html",e,o.frameId)}catch(i){U.warn(`frame ${o.frameId} (${o.url.slice(0,80)}) audit failed; continuing`,i)}const r=await $a();if((r==null?void 0:r.state)==="interrupted"){U.info("all-frames iteration interrupted");break}await Lt({completedFrames:[...(r==null?void 0:r.completedFrames)??[],o.frameId]})}B({type:"SCORECARD_UPDATED_EVENT"}),await dt()}async function Nu(e){var o;const t=await le();if(!ce(t,"parallelScan")){B({type:"AUDIT_FAILED_EVENT",error:{code:"FEATURE_NOT_AVAILABLE",message:"Parallel scan is not available on your current plan.",recoverable:!1}});return}const a=await dr();let n;if(a!==null)try{n=(await chrome.tabs.get(a)).url}catch(r){U.warn("failed to get target tab url",r)}if(!n){B({type:"AUDIT_FAILED_EVENT",error:{code:"NETWORK",message:"Could not determine target URL for parallel scan.",recoverable:!1}});return}await at({sessionId:Pt(),mode:"single-element",scope:"html",startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});try{const r=await Vn(),{runParallelTabAudit:i}=await bn(async()=>{const{runParallelTabAudit:m}=await import("./parallel-tab-flow-BmRaOzXY.js");return{runParallelTabAudit:m}},__vite__mapDeps([0,1,2,3,4])),c=await i({targetUrl:n,scope:"html",matrix:r,parallelism:4,cancel:e});if(U.info(`parallel scan complete: ${c.results.length} results across ${c.tabsCompleted} tabs (${c.tabsFailed} failed) in ${Math.round(c.parallelDurationMs)}ms`),c.results.length===0){B({type:"AUDIT_FAILED_EVENT",error:{code:"NETWORK",message:`All ${c.tabsFailed} worker tabs failed. Falling back to Full Page scan is recommended.`,recoverable:!0}});return}const d=((o=c.results[0])==null?void 0:o.componentId)??"parallel-scan";if(a!==null)try{const m=await Ye("aiConfig",{}),u=ye(m);u.enabled&&u.apiKey&&await ua(a,"html",void 0,u,c.results,d,e)}catch(m){U.warn("parallel scan: ai augmentation skipped",m)}const l=c.results.flatMap(m=>m.violations);let p;try{p=await Pn(d,l)}catch(m){U.warn("parallel scan: baseline compare failed",m),p={new:[],persistent:[],fixed:[],newCount:0,persistentCount:0,fixedCount:0,baselineSnapshotMeta:null,comparedAt:new Date().toISOString()}}B({type:"AUDIT_COMPLETE_EVENT",componentId:d,results:c.results,delta:p}),B({type:"SCORECARD_UPDATED_EVENT"})}catch(r){U.warn("parallel scan failed",r),B({type:"AUDIT_FAILED_EVENT",error:{code:"UNKNOWN",message:r instanceof Error?r.message:String(r),recoverable:!0}})}finally{await dt()}}async function Lu(e,t){var a;try{return((a=(await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",func:async()=>{var d,l;const o=window.__STORYBOOK_PREVIEW__;if(!o)return[];const r=o.storyStoreValue??o.storyStore;if(!r)return[];typeof r.cacheAllCSFFiles=="function"&&await r.cacheAllCSFFiles();const i=((d=r.extract)==null?void 0:d.call(r))??((l=r.cachedCSFFiles)==null?void 0:l.call(r));if(!i)return[];const c=[];if(i instanceof Map)for(const[p,m]of i){const u=m;c.push({id:String(p),name:u.name??String(p),kind:u.title??u.kind??""})}else if(typeof i=="object"&&i!==null)for(const[p,m]of Object.entries(i)){const u=m;c.push({id:p,name:u.name??p,kind:u.title??u.kind??""})}return c}}))[0])==null?void 0:a.result)??[]}catch(n){return U.warn("listStoriesViaMainWorld failed",n),[]}}async function Pu(e,t,a){try{await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",args:[a,3e3],func:async(n,o)=>{const r=new URL(window.location.href);r.searchParams.set("id",n),r.searchParams.set("viewMode","story"),window.location.href!==r.href&&(window.history.pushState({},"",r.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(i=>{const c=setTimeout(()=>i(),o),d=()=>{clearTimeout(c),i()};window.addEventListener("storyrendered",d,{once:!0})})}})}catch(n){U.warn(`navigateToStoryViaMainWorld(${a}) failed`,n)}}async function Ai(e){var t;try{const n=(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{const o=document.getElementById("ladle-root")!==null,r=document.title==="Ladle",i=!!document.querySelector('link[href*="ladle.css"]');return{detected:o&&(r||i),version:void 0}}})).find(o=>{var r;return(r=o.result)==null?void 0:r.detected});if(n)return{detected:!0,version:(t=n.result)==null?void 0:t.version,frameId:n.frameId}}catch(a){U.warn("ladle detection scripting failed",a)}return{detected:!1}}async function Fu(e,t){var a;try{return((a=(await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",func:async()=>{try{const o=await fetch(new URL("/meta.json",window.location.href).href,{credentials:"same-origin"});if(!o.ok)return[];const i=(await o.json()).stories??{};return Object.entries(i).map(([c,d])=>({id:c,name:d.name??c,kind:(d.levels??[]).join(" / ")}))}catch{return[]}}}))[0])==null?void 0:a.result)??[]}catch(n){return U.warn("listLadleStoriesViaMainWorld failed",n),[]}}async function _u(e,t,a){try{await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",args:[a,3e3],func:async(n,o)=>{const r=new URL(window.location.href);r.searchParams.set("story",n),window.location.href!==r.href&&(window.history.pushState({},"",r.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(i=>setTimeout(i,Math.min(o,500)))}})}catch(n){U.warn(`navigateToLadleStoryViaMainWorld(${a}) failed`,n)}}async function ki(e){try{return(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{const a=window.location.href;return/bit\.dev|bit-dev|bitsrc|\/~aspect|\/composition/i.test(a)}})).some(a=>a.result===!0)}catch{return!1}}async function xi(e){const t=await vi(e);if(t.detected)return{kind:"storybook",...t};const a=await Ai(e);return a.detected?{kind:"ladle",...a}:await ki(e)?{kind:"bit",detected:!0}:{kind:null,detected:!1}}const ca=200,Ii=6e4;function la(e,t){try{return new URL(e).origin===new URL(t).origin}catch{return!1}}function Si(e,t){try{const a=new URL(e,t);return a.hash="",a.toString()}catch{return null}}function Ci(e){try{return/\.(xml|rss|atom|json|txt|csv|ics|rdf|xsl|xslt)$/i.test(new URL(e).pathname)}catch{return!1}}async function $i(e,t,a){await chrome.tabs.update(e,{url:t}),await new Promise((n,o)=>{const r=setTimeout(()=>{chrome.tabs.onUpdated.removeListener(i),o(new Error(`navigation timeout (${a}ms): ${t}`))},a);function i(c,d){c===e&&d.status==="complete"&&(clearTimeout(r),chrome.tabs.onUpdated.removeListener(i),n())}chrome.tabs.onUpdated.addListener(i)}),await new Promise(n=>setTimeout(n,400))}async function Ti(e){var t;try{return((t=(await chrome.scripting.executeScript({target:{tabId:e},func:()=>Array.from(document.querySelectorAll("a[href]")).map(n=>n.getAttribute("href")).filter(n=>typeof n=="string"&&!n.startsWith("javascript:"))}))[0])==null?void 0:t.result)??[]}catch(a){return U.debug("link discovery failed",a),[]}}async function Ei(e,t){var a,n;try{const r=new URL(t).pathname.toLowerCase().replace(/\/+$/,"");if(/(?:^|\/)(?:404|not-found|page-not-found|error|server-error)$/.test(r))return`URL path "${r||"/"}" matches a common error-route pattern.`}catch{}try{const o=await chrome.scripting.executeScript({target:{tabId:e},func:()=>document.title||""}),r=((n=(a=o==null?void 0:o[0])==null?void 0:a.result)==null?void 0:n.trim())??"";if(!r)return null;const i=[[/^4(?:0[0-46-9]|[1-9]\d)\b/,"title starts with a 4xx error code"],[/^5\d{2}\b/,"title starts with a 5xx error code"],[/^(?:page\s+)?not\s+found\b/i,'title starts with "Not Found"'],[/^error\s*[-:]?\s*\d{3}\b/i,'title starts with "Error" + status code'],[/^(?:oops|whoops)[\s,!:.-]/i,'title leads with "Oops" — common error-template pattern']];for(const[c,d]of i)if(c.test(r))return`${d} (title: "${r.slice(0,80)}").`;return null}catch(o){return U.debug("error-page detection failed; proceeding with audit",o),null}}const Lo=/Linked PDF at (\S+)\./;async function Ri(e,t){if(!ce(t,"pdfAudit")){U.info("PDF audit disabled for tier; skipping crawl PDF pass",{tier:t});return}const a=new Map;for(const n of e)if(!n.error)for(const o of n.results)for(const r of o.violations){if(r.ruleId!=="pdf-needs-cli-audit")continue;const i=Lo.exec(r.description);if(!i)continue;const c=i[1];let d;try{d=new URL(c,n.url).href}catch{continue}if(!/^https?:\/\//i.test(d))continue;let l=a.get(d);l||(l=new Set,a.set(d,l)),l.add(n)}if(a.size!==0){U.info(`PDF audit pass: ${a.size} unique PDF(s) across the crawl`);for(const[n,o]of a){let r;try{r=await il(n)}catch(i){U.warn(`PDF audit threw for ${n}`,i);continue}for(const i of o){const c=i.results[0];if(!c)continue;const d=c.componentId,l=c.state,p=r.findings.map(m=>Po(m,n,d,l));c.violations=c.violations.filter(m=>{if(m.ruleId!=="pdf-needs-cli-audit")return!0;const u=Lo.exec(m.description);if(!u)return!0;try{return new URL(u[1],i.url).href!==n}catch{return!0}}),c.violations.push(...p),r.fatalError&&c.violations.push(Po({ruleId:"pdf-audit-skipped",impact:"minor",wcagCriterion:"1.1.1",description:`PDF audit skipped: ${r.fatalError}`,failureSummary:"The PDF could not be audited automatically. Run `wcagcheckr-ci pdf <url>` against this PDF on the command line for a manual audit."},n,d,l))}}}}function Po(e,t,a,n){return{ruleId:e.ruleId,wcagCriterion:e.wcagCriterion,wcagLevel:"AA",impact:e.impact,description:e.description,helpUrl:"https://www.w3.org/WAI/standards-guidelines/wcag/",target:{selector:t,outerHTML:`<a href="${t}">[PDF]</a>`,failureSummary:e.failureSummary,tagName:"PDF",role:null,accessibleName:null,textSnippet:null,attrId:null,attrTestid:null},componentId:a,currentState:n,axeVersion:"pdf-audit",detectedAt:new Date().toISOString(),matchKey:`pdf::${e.ruleId}::${t}`}}async function Uu(e,t,a){if(await je())return B({type:"CRAWL_DISCOVERY_FAILED_EVENT",error:{code:Dt,message:Ze,recoverable:!1}}),[];const n=await le();if(!ce(n,"siteCrawl"))return B({type:"CRAWL_DISCOVERY_FAILED_EVENT",error:{code:"FEATURE_NOT_AVAILABLE",message:"Site crawl is not available on your current plan.",recoverable:!1}}),[];const o=await vt(),r=Xo(n).siteCrawlMaxPages,i=Number.isFinite(r)?Math.min(r,ca):ca,c=Math.min(t.maxPages??25,i),d=t.includeRegex?da(t.includeRegex):null,l=t.excludeRegex?da(t.excludeRegex):null,p=[e],m=new Set,u=[];try{for(;p.length>0&&u.length<c;){if(a.isCanceled()){U.info("crawl discovery canceled by user");break}const g=p.shift();if(!(!g||m.has(g))&&la(g,e)&&!(d&&!d.test(g))&&!(l&&l.test(g))){m.add(g);try{if(await $i(o,g,Ii),!(await Gn(o,0)).ok||await Ei(o,g))continue;u.push(g),B({type:"CRAWL_DISCOVERY_PROGRESS_EVENT",found:u.length,url:g});const w=await Ti(o);for(const b of w){const k=Si(b,g);k&&la(k,e)&&!Ci(k)&&!m.has(k)&&!p.includes(k)&&p.push(k)}}catch(h){U.debug("discovery: page navigation/link-read failed; skipping",h);continue}await Lt({})}}return B({type:"CRAWL_DISCOVERY_COMPLETE_EVENT",urls:u}),u}catch(g){return U.error("crawl discovery failed",g),B({type:"CRAWL_DISCOVERY_FAILED_EVENT",error:{code:"CRAWL_DISCOVERY_FAILED",message:String(g),recoverable:!0}}),[]}}async function Wu(e,t,a){if(await je()){B({type:"SITE_CRAWL_FAILED_EVENT",error:{code:Dt,message:Ze,recoverable:!1}});return}const n=await le();if(!ce(n,"siteCrawl")){B({type:"SITE_CRAWL_FAILED_EVENT",error:{code:"FEATURE_NOT_AVAILABLE",message:"Site crawl is not available on your current plan.",recoverable:!1}});return}const o=await vt(),r=Xo(n).siteCrawlMaxPages,i=Number.isFinite(r)?Math.min(r,ca):ca,c=Array.isArray(t.urls)&&t.urls.length>0,d=Math.min(c?t.urls.length:t.maxPages??25,i),l=t.includeRegex?da(t.includeRegex):null,p=t.excludeRegex?da(t.excludeRegex):null,m=new Date().toISOString(),u=Date.now(),g=c?[...t.urls]:[e],h=new Set,f=[];await at({sessionId:Pt(),mode:"storybook-all",scope:e,startedAt:m,lastProgressAt:new Date().toISOString(),state:"running"});try{for(;g.length>0&&h.size<d;){if(a.isCanceled()){U.info("site crawl canceled by user");break}const x=g.shift();if(!x||h.has(x)||!la(x,e)||l&&!l.test(x)||p&&p.test(x))continue;h.add(x);const $=h.size,A=Date.now();B({type:"SITE_CRAWL_PROGRESS_EVENT",current:$,total:Math.min(d,h.size+g.length),url:x,status:"auditing"});try{await $i(o,x,Ii);const S=await Gn(o,0);if(!S.ok)throw new Error(gn(S));const F=await Ei(o,x);if(F){const _=`Skipped — page appears to be an error response. ${F}`;f.push({url:x,results:[],delta:null,componentId:x,durationMs:Date.now()-A,error:_}),B({type:"SITE_CRAWL_PROGRESS_EVENT",current:$,total:Math.min(d,h.size+g.length),url:x,status:"failed",error:_});continue}await Ae(o,{type:"WARMUP_REQUEST"},0).catch(()=>{});const T=await pt("html",a,0,void 0,{asSubroutine:!0,tabIdOverride:o,verifyFixesOnly:t.verifyFixesOnly}),I=T.results,N=T.ok?void 0:T.error,C=Date.now()-A;if(!T.ok&&T.skipped)f.push({url:x,results:[],delta:null,componentId:x,durationMs:C,error:T.error,skipped:!0}),B({type:"SITE_CRAWL_PROGRESS_EVENT",current:$,total:Math.min(d,h.size+g.length),url:x,status:"skipped",error:T.error});else if(I.length>0){const _=await sl(o,x).catch(()=>null),M=I.reduce((L,W)=>L+W.violations.length,0);f.push({url:x,results:I,delta:null,componentId:I[0].componentId,durationMs:C,snapshot:_??void 0}),B({type:"SITE_CRAWL_PROGRESS_EVENT",current:$,total:Math.min(d,h.size+g.length),url:x,status:"completed",violations:M})}else{const _=N??"audit returned no result";f.push({url:x,results:[],delta:null,componentId:x,durationMs:C,error:_}),B({type:"SITE_CRAWL_PROGRESS_EVENT",current:$,total:Math.min(d,h.size+g.length),url:x,status:"failed",error:_})}if(!c){const _=await Ti(o);for(const M of _){const L=Si(M,x);L&&la(L,e)&&!Ci(L)&&!h.has(L)&&!g.includes(L)&&g.push(L)}}}catch(S){const F=Date.now()-A,T=S instanceof Error?S.message:String(S);f.push({url:x,results:[],delta:null,componentId:x,durationMs:F,error:T}),B({type:"SITE_CRAWL_PROGRESS_EVENT",current:$,total:Math.min(d,h.size+g.length),url:x,status:"failed",error:T})}await Lt({})}try{const x=await le();await Ri(f,x)}catch(x){U.warn("PDF audit pass failed",x)}const w=new Date().toISOString(),b=Date.now()-u,k=f.flatMap(x=>x.results),v=await Me(k,[e]),s=qr(e,f,m,w,b,v.walkthroughsByComponent,v.walkthroughAcksByComponent,v.ackedKeysByComponent,v.humanVerdictsByComponent,v.incompleteResolutionsByComponent,v.attestations);try{s.auditSettings=await Dn(w)}catch(x){U.warn("audit-settings capture failed",x)}if(s.consistencyFindings.length>0)try{const x=await Dl(s.consistencyFindings);s.consistencyFindings=x.emitted}catch(x){U.warn("consistency AI suppression failed; emitting heuristic findings",x)}try{const x=f.map($=>({...$,results:$.results.map(({screenshotDataUrl:A,...S})=>S)}));await lr(x,{startUrl:e,finishedAt:w})}catch(x){const $=x instanceof Error?x.message:String(x);U.error("persisting siteCrawlPerUrlResults failed",x),B({type:"SITE_CRAWL_FAILED_EVENT",error:{code:"CRAWL_PERSIST_FAILED",message:`Crawl finished but persistence failed (${$}). The AI fix prompt export won't have per-page data.`,recoverable:!1}})}B({type:"SITE_CRAWL_COMPLETE_EVENT",report:s}),await Oi(s),await dt()}catch(w){U.error("site crawl failed",w),B({type:"SITE_CRAWL_FAILED_EVENT",error:{code:"UNKNOWN",message:String(w),recoverable:!1}}),await dt()}}function da(e){try{return new RegExp(e)}catch{return null}}async function Gu(){const[e,t]=await Promise.all([In(),Jt()]);if(!e)return{ok:!1,reason:"No crawl report cached. Run a site crawl first."};if(!t||t.length===0)return{ok:!1,reason:"Crawl report exists but per-URL audit data was not retained. Re-run the crawl once; future regrades will work."};const a=t.flatMap(i=>i.results),n=await Me(a,[e.startUrl]),o=new Date().toISOString(),r=qr(e.startUrl,t,e.startedAt,o,e.totalDurationMs,n.walkthroughsByComponent,n.walkthroughAcksByComponent,n.ackedKeysByComponent,n.humanVerdictsByComponent,n.incompleteResolutionsByComponent,n.attestations);return r.auditSettings=e.auditSettings??await Dn(o),await cr(r),B({type:"SITE_CRAWL_COMPLETE_EVENT",report:r}),await Oi(r),{ok:!0}}async function Oi(e){try{if(e.siteGrade==="pending")return;const t=await le();if(!ce(t,"forensicAnchoring"))return;const a={domain:ku(e.startUrl),grade:e.siteGrade,scope:"site",pageCount:e.pagesAudited,wcagLevel:`${Mt} ${wn}`,auditedAt:e.finishedAt,productSlug:"wcagcheckr"},n=await Au(a),o=await pa(),r=await yi(n,e.startUrl,e.finishedAt,o??void 0);await Su({domain:a.domain,claim:a,hash:n,receipt:r}),B({type:"SITE_CLAIM_SEALED_EVENT",domain:a.domain})}catch(t){U.warn("site-claim seal/anchor failed (badge unavailable for this crawl)",t)}}const Ya=5*6e4,Fo=9e4,_o={altText:"Verifying alt text…",headings:"Judging heading quality…",sensory:"Scanning for sensory-only instructions…",aria:"Reviewing ARIA semantics…",imageOfText:"Detecting baked-in text in images…",colorOnlyMeaning:"Checking for color-only meaning…",genericLinkText:"Reviewing link clarity…",wallOfText:"Detecting walls of text…",languageMismatch:"Verifying page language…"},Vu={altText:["1.1.1"],headings:["2.4.6"],sensory:["1.3.3"],aria:["4.1.2"],imageOfText:["1.4.5"],colorOnlyMeaning:["1.4.1"],genericLinkText:["2.4.4"],wallOfText:[],languageMismatch:["3.1.1","3.1.2"]};async function ua(e,t,a,n,o,r,i){const c=await le();if(!ce(c,"aiJudgment")){U.debug("aiJudgment not allowed on current tier — skipping AI augmentation");return}let d;try{d=await Ae(e,{type:"AI_CANDIDATES_REQUEST",selector:t},a)}catch(C){U.debug("ai candidates fetch failed",C);return}const l=o[0];if(!l)return;const p={componentId:r,currentState:l.state,axeVersion:l.axeVersion},m={at:new Date().toISOString(),componentId:r,pageUrl:l.pageUrl??t,enabledChecks:n.enabledChecks,candidateCounts:{images:d.images.length,headings:d.headings.length,instructions:d.instructions.length,ariaElements:d.ariaElements.length,links:d.links.length,longTextBlocks:d.longTextBlocks.length,colorOnlyRegions:d.colorOnlyRegions.length,languageInfo:d.languageInfo?"present":"null"}};U.info("ai augmentation gate snapshot",JSON.stringify(m));try{await chrome.storage.local.set({aiDiagnosticLatest:m})}catch(C){U.warn("failed to persist ai diagnostic to storage.local",C)}const u=[];if(n.enabledChecks.altText&&u.push({key:"altText",run:C=>{const _=d.images.map(M=>({imageUrl:M.imageUrl,alt:M.alt,selector:M.selector,outerHTML:M.outerHTML,surroundingContext:M.surroundingContext,...p}));return hd(_,n,C)}}),n.enabledChecks.headings&&u.push({key:"headings",run:C=>ru(d.headings,p,n,C)}),n.enabledChecks.sensory&&u.push({key:"sensory",run:C=>iu(d.instructions,p,n,C)}),n.enabledChecks.aria&&u.push({key:"aria",run:C=>su(d.ariaElements,p,n,C)}),n.enabledChecks.imageOfText&&u.push({key:"imageOfText",run:C=>{const _=d.images.map(M=>({imageUrl:M.imageUrl,accessibleName:M.alt||void 0,selector:M.selector,outerHTML:M.outerHTML,pixelArea:M.pixelArea}));return uu(_,p,n,C)}}),n.enabledChecks.colorOnlyMeaning&&u.push({key:"colorOnlyMeaning",run:C=>hu(d.colorOnlyRegions,p,n,C)}),n.enabledChecks.genericLinkText&&u.push({key:"genericLinkText",run:C=>cu(d.links,p,n,C)}),n.enabledChecks.wallOfText&&u.push({key:"wallOfText",run:C=>lu(d.longTextBlocks,p,n,C)}),n.enabledChecks.languageMismatch){const C=d.languageInfo;u.push({key:"languageMismatch",run:_=>du(C?[C]:[],p,n,_)})}if(u.length===0)return;let g=0,h=0,f=!1;const w={};let b=0,k=0,v=0;const s=[];let x=!1,$=!1;function A(C,_){if(w[C]=(w[C]??0)+_.totalCostUsd,g+=_.totalCostUsd,h+=_.findings.length,f=f||_.capExceeded,b++,_.errors.length>0){v++;for(const M of _.errors)s.includes(M)||s.push(M)}else k++;U.info(`ai ${C}: ${_.findings.length} findings, $${_.totalCostUsd.toFixed(4)}, errs=${_.errors.length}`),_.capExceeded&&U.warn(`ai cost cap exceeded after $${_.totalCostUsd.toFixed(4)} during ${C}`)}const S=(async()=>{for(let C=0;C<u.length;C++){if(i!=null&&i.isCanceled()){$=!0;break}const _=u[C];B({type:"AI_AUGMENTATION_PROGRESS_EVENT",componentId:r,currentCheck:_.key,currentCheckLabel:_o[_.key],current:C+1,total:u.length});const M=new AbortController,L=setTimeout(()=>M.abort(),Fo),W=i?setInterval(()=>{i.isCanceled()&&M.abort()},500):null;let P;try{P=await _.run({signal:M.signal,onProgress:(V,O)=>{O>0&&B({type:"AI_AUGMENTATION_PROGRESS_EVENT",componentId:r,currentCheck:_.key,currentCheckLabel:_o[_.key],current:C+1,total:u.length,candidatesDone:V,candidatesTotal:O})}}),M.signal.aborted&&(i!=null&&i.isCanceled())?P.errors=[...P.errors,`${_.key} canceled by user`]:M.signal.aborted}catch(V){const O=V instanceof Error?V.message:String(V),j=M.signal.aborted,q=j&&(i!=null&&i.isCanceled())?`${_.key} canceled by user`:j?`${_.key} hit per-check ${Fo/1e3}s cap — analyzer aborted`:`${_.key} threw: ${O}`;P={findings:[],totalCostUsd:0,capExceeded:!1,errors:[q]}}finally{clearTimeout(L),W&&clearInterval(W)}if(P.findings.length>0&&(l.violations=[...l.violations,...P.findings]),P.errors.length===0){const V=Vu[_.key];if(V&&V.length>0&&l.axeRulesEvaluated)for(const O of V)l.axeRulesEvaluated.passed.push({ruleId:`ai-${_.key}-coverage`,wcagCriterion:O})}A(_.key,P)}})(),F=new Promise(C=>{setTimeout(()=>C("timeout"),Ya)});await Promise.race([S.then(()=>"done"),F])==="timeout"&&(x=!0,U.warn(`ai augmentation hit global ${Ya/1e3}s timeout after ${b}/${u.length} checks`));let I;if(v>0||x||$){x?I=`AI augmentation timed out after ${Ya/1e3}s`:$?I="AI augmentation canceled":I=s[0]??"AI analyzer error";const C=k===0?"total":"partial";B({type:"AI_AUGMENTATION_FAILED_EVENT",componentId:r,severity:C,reason:I,checksAttempted:b,checksSucceeded:k,checksErrored:v,errorDetails:x||$?[I,...s.slice(0,4)]:s.slice(0,5)}),U.warn(`ai augmentation ${C} failure: ${v}/${b} checks errored — ${I}`)}(g>0||h>0||I)&&await hs({at:new Date().toISOString(),pageUrl:l.pageUrl??t,componentId:r,totalCostUsd:g,byCheck:w,findingsCount:h,capExceeded:f,failureReason:I})}const tf=Object.freeze(Object.defineProperty({__proto__:null,auditCrawlPdfs:Ri,detectBit:ki,detectLadleAcrossFrames:Ai,detectPlayground:xi,detectStorybookAcrossFrames:vi,discoverCrawlUrls:Uu,getActiveTabId:vt,getMatrixSettings:Vn,regradeSiteCrawl:Gu,runAiAugmentation:ua,runAllFramesAudit:Du,runParallelScan:Nu,runSingleElementAudit:pt,runSiteCrawl:Wu,runStorybookAllAudit:Mu},Symbol.toStringTag,{value:"Module"})),St=Z("alert-sender"),ju="wcagcheckr",Hu="https://api.wcagcheckr.com",Bu=`${Hu}/v1/products/${ju}/alerts/send`,qu=1e4,mn="alertConfig:v1",zu={emailEnabled:!1,webhookEnabled:!1,webhookUrl:"",minImpact:"serious"},Bt={minor:1,moderate:2,serious:3,critical:4};async function Ku(){const t=(await chrome.storage.local.get(mn))[mn];return{...zu,...t??{}}}async function af(e){await chrome.storage.local.set({[mn]:e})}async function Yu(e){let t;try{t=await Ku()}catch(h){return St.warn("failed to read alert config",h),{fired:!1,reason:"config_read_failed"}}if(!t.emailEnabled&&!t.webhookEnabled)return{fired:!1,reason:"no_channel_enabled"};const a=await le(),n=ce(a,"emailAlerts"),o=ce(a,"webhookAlerts"),r=t.emailEnabled&&n,i=t.webhookEnabled&&o;if(!r&&!i)return{fired:!1,reason:"no_channel_enabled"};const c=[];for(const h of e.results){const f=h.delta;f!=null&&f.new&&c.push(...f.new)}const d=Bt[t.minImpact],l=c.filter(h=>Bt[h.impact]>=d);if(l.length===0)return{fired:!1,reason:"below_impact_threshold"};const p=await pa();if(!p)return St.debug("no license token; alert skipped"),{fired:!1,reason:"no_license_token"};l.sort((h,f)=>Bt[f.impact]-Bt[h.impact]);const m=l.slice(0,10).map(h=>({wcag:h.wcagCriterion,impact:h.impact,selector:h.target.selector,help:h.description.slice(0,480),helpUrl:h.helpUrl})),u=e.results.reduce((h,f)=>h+f.violations.length,0),g={token:p,scheduleId:e.scheduleId,auditedUrl:e.auditedUrl,totalViolations:u,newViolationCount:l.length,topViolations:m,webhookUrl:i&&t.webhookUrl?t.webhookUrl:void 0,emailEnabled:r,ranAt:e.ranAt};try{const h=await fetch(Bu,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(g),signal:AbortSignal.timeout(qu)}),f=await h.json().catch(()=>null);return h.ok?(St.info("alert delivered",{emailSent:f==null?void 0:f.emailSent,webhookSent:f==null?void 0:f.webhookSent}),{fired:!0,emailSent:(f==null?void 0:f.emailSent)??!1,webhookSent:(f==null?void 0:f.webhookSent)??!1,serverStatus:h.status}):(St.warn(`alert server returned ${h.status}`,f),{fired:!1,reason:`server_${h.status}`,serverStatus:h.status})}catch(h){return St.warn("alert send failed",h),{fired:!1,reason:h instanceof Error?h.message:"unknown_error"}}}const Ju="#dc2626",Xu="#f59e0b",Qu="#059669",Zu="#94a3b8";function eh(e){const t=e.filter(c=>c.enabled);let a=0,n=0,o=0,r=0;for(const c of t){const d=c.lastNewViolationCount??0;c.lastResult==="failed"?n++:c.lastResult==="ok"?d>0?a+=d:o++:r++}let i;return t.length===0?i="no-schedules":a>0?i="new-violations":n>0?i="failed":o>0?i="all-clear":i="pending-first-run",{state:i,newViolationsTotal:a,failingCount:n,okCount:o,pendingCount:r,totalEnabled:t.length}}async function th(e){switch(e.state){case"new-violations":{const t=e.newViolationsTotal>99?"99+":String(e.newViolationsTotal);await chrome.action.setBadgeText({text:t}),await chrome.action.setBadgeBackgroundColor({color:Ju}),await chrome.action.setTitle({title:`wcagcheckr — ${e.newViolationsTotal} new WCAG violation${e.newViolationsTotal===1?"":"s"} vs baseline across ${e.totalEnabled} scheduled audit${e.totalEnabled===1?"":"s"}`});break}case"failed":{await chrome.action.setBadgeText({text:"!"}),await chrome.action.setBadgeBackgroundColor({color:Xu}),await chrome.action.setTitle({title:`wcagcheckr — ${e.failingCount} scheduled audit${e.failingCount===1?"":"s"} failed to run`});break}case"all-clear":{await chrome.action.setBadgeText({text:"✓"}),await chrome.action.setBadgeBackgroundColor({color:Qu}),await chrome.action.setTitle({title:`wcagcheckr — all ${e.okCount} scheduled audit${e.okCount===1?"":"s"} passing`});break}case"pending-first-run":{await chrome.action.setBadgeText({text:""}),await chrome.action.setBadgeBackgroundColor({color:Zu}),await chrome.action.setTitle({title:`wcagcheckr — ${e.pendingCount} scheduled audit${e.pendingCount===1?"":"s"} awaiting first run`});break}case"no-schedules":default:{await chrome.action.setBadgeText({text:""}),await chrome.action.setTitle({title:"wcagcheckr"});break}}}async function ah(){const e=await le();if(!ce(e,"statusBadge")){try{await chrome.action.setBadgeText({text:""})}catch{}return{state:"no-schedules",newViolationsTotal:0,failingCount:0,okCount:0,pendingCount:0,totalEnabled:0}}const t=await or(),a=eh(t);try{await th(a)}catch(n){console.warn("[status-badge] failed to apply",n)}return a}const Uo=Z("deposition-packet");function J(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}function nh(e,t,a){if(!e)return`<section class="dp-cover dp-cover-error">
|
|
991
|
+
`;await chrome.debugger.sendCommand({tabId:e},"Runtime.evaluate",{expression:t})}async function Eo(e,t){let a;try{a=await chrome.debugger.sendCommand({tabId:e},"Page.captureScreenshot",{format:"png",captureBeyondViewport:!0,clip:{x:t.x,y:t.y,width:t.width,height:t.height,scale:1}})}catch(n){return Xe.warn(`captureElementClip failed: ${n instanceof Error?n.message:String(n)}`),null}if(!(a!=null&&a.data))return null;try{const n=`data:image/png;base64,${a.data}`,o=await fetch(n).then(d=>d.blob()),r=await createImageBitmap(o),c=new OffscreenCanvas(r.width,r.height).getContext("2d");return c?(c.drawImage(r,0,0),c.getImageData(0,0,r.width,r.height)):null}catch(n){return Xe.warn(`clip decode failed: ${n instanceof Error?n.message:String(n)}`),null}}const Qd=25,hi=8,Zd=150,eu=.95,tu=4,au=400;function nu(e,t,a){if(e.width!==t.width||e.height!==t.height)return null;const n=e.width,o=e.height;let r=0,i=0;for(let v=0;v<o;v++)for(let s=0;s<n;s++){const x=(v*n+s)*4,$=t.data[x],A=t.data[x+1],S=t.data[x+2],F=e.data[x],T=e.data[x+1],I=e.data[x+2],N=Math.abs(F-$)+Math.abs(T-A)+Math.abs(I-S);N>i&&(i=N),N>=Qd&&r++}if(r<hi)return null;const c=i*eu;let d=0,l=0,p=0,m=0;if(i>=au)for(let v=0;v<o;v++)for(let s=0;s<n;s++){const x=(v*n+s)*4,$=t.data[x],A=t.data[x+1],S=t.data[x+2],F=e.data[x],T=e.data[x+1],I=e.data[x+2];Math.abs(F-$)+Math.abs(T-A)+Math.abs(I-S)>=c&&(d+=$,l+=A,p+=S,m++)}const u=m>=tu?{r:Math.round(d/m),g:Math.round(l/m),b:Math.round(p/m)}:a,g=new Map;let h=0;for(let v=0;v<o;v++)for(let s=0;s<n;s++){const x=(v*n+s)*4,$=t.data[x],A=t.data[x+1],S=t.data[x+2];if(Math.abs($-u.r)+Math.abs(A-u.g)+Math.abs(S-u.b)>=Zd){h++;const T=$>>4<<8|A>>4<<4|S>>4,I=g.get(T);I?(I.r+=$,I.g+=A,I.b+=S,I.count++):g.set(T,{r:$,g:A,b:S,count:1})}}const f=n*o;if(h<f*.2){g.clear();for(let v=0;v<o;v++)for(let s=0;s<n;s++){const x=(v*n+s)*4,$=e.data[x],A=e.data[x+1],S=e.data[x+2],F=$>>4<<8|A>>4<<4|S>>4,T=g.get(F);T?(T.r+=$,T.g+=A,T.b+=S,T.count++):g.set(F,{r:$,g:A,b:S,count:1})}}let w={r:0,g:0,b:0,count:0};for(const v of g.values())v.count>w.count&&(w=v);const b={r:Math.round(w.r/w.count),g:Math.round(w.g/w.count),b:Math.round(w.b/w.count)},k=Un(u,b);return{textPixelCount:r,worstRatio:k,worstBackground:b,worstForeground:u}}async function ou(e,t,a){var d,l,p,m,u,g;const n=new Map;for(const h of t){const f=h.pageUrl??h.scope??"";for(const w of((d=h.axeRulesEvaluated)==null?void 0:d.incomplete)??[])if(w.ruleId==="color-contrast")for(const b of w.elements??[]){const k=`${f}::${w.ruleId}::${b.selector}`;n.has(k)||n.set(k,{pageUrl:f,ruleId:w.ruleId,selector:b.selector,wcagCriterion:w.wcagCriterion??"1.4.3",foreground:(l=b.styles)==null?void 0:l.foreground,fontSize:(p=b.styles)==null?void 0:p.fontSize,fontWeight:(m=b.styles)==null?void 0:m.fontWeight,failureSummary:b.failureSummary})}}if(n.size===0)return{resolved:0,skipped:0};let o=!1,r=null;for(let h=1;h<=6;h++)try{await chrome.debugger.attach({tabId:e},jd),o=!0;break}catch(f){r=f;const w=f instanceof Error?f.message:String(f);if(/already attached/i.test(w)){await new Promise(b=>setTimeout(b,500));continue}break}if(!o)return Xe.warn("debugger attach failed after retries; skipping pixel sampling",r),{resolved:0,skipped:n.size};let i=0,c=0;try{const h=Array.from(n.values()),f=h.map(I=>I.selector),w=await Yd(e,f);if(a!=null&&a.aborted)return{resolved:0,skipped:n.size};const b=I=>{const N=w.get(I);return N&&!("decorative"in N)?N:null},k=new Set;for(const[I,N]of w)N&&"decorative"in N&&k.add(I);for(const I of h){if(!k.has(I.selector))continue;const N={pageUrl:I.pageUrl,ruleId:I.ruleId,selector:I.selector,verdict:"pass",reasoning:'Not applicable to WCAG 1.4.3 (contrast minimum): this element is decorative / removed from the accessibility tree — it sits inside an aria-hidden="true" subtree, or is visually hidden (display:none, sr-only clip, off-screen, or zero opacity). Decorative and incidental content is exempt from the contrast-minimum requirement, so it is recorded as not-applicable and does not count as a violation.',resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:I.wcagCriterion,inputHash:await $o({...I,bgFingerprint:null})};await We(N),i++}const v=h.filter(I=>!k.has(I.selector));if(v.length===0)return Xe.info(`pixel-contrast sampler: all ${h.length} targets decorative/not-applicable (1.4.3)`),{resolved:i,skipped:c};const s=((u=h[0])==null?void 0:u.pageUrl)??"",x=s?await va(s):[],$=new Map;for(const I of x)$.set(`${I.ruleId}::${I.selector}`,I);const A=new Map,S=[];for(const I of v){const N=await $o({...I,bgFingerprint:((g=b(I.selector))==null?void 0:g.bgFingerprint)??null});A.set(I.selector,N);const C=$.get(`${I.ruleId}::${I.selector}`);if(C&&C.inputHash===N&&C.verdict!=="uncertain"){const _={...C,resolvedAt:new Date().toISOString(),reasoning:C.reasoning.includes("(cached, hash-match)")?C.reasoning:`${C.reasoning} (cached, hash-match)`};await We(_),i++;continue}S.push(I)}if(S.length===0)return Xe.info(`pixel-contrast sampler: all ${h.length} targets reused from hash-cache`),{resolved:i,skipped:c};await Jd(e);const F=new Map;for(const I of S){if(a!=null&&a.aborted)break;const N=b(I.selector);if(!N)continue;const C=await Eo(e,N);C&&F.set(I.selector,C)}if(await Xd(e),a!=null&&a.aborted)return{resolved:0,skipped:n.size};const T=new Map;for(const I of S){if(a!=null&&a.aborted)break;const N=b(I.selector);if(!N)continue;const C=await Eo(e,N);C&&T.set(I.selector,C)}for(const I of S){if(a!=null&&a.aborted)break;const N=A.get(I.selector),C=b(I.selector);if(!C){c++;const X={pageUrl:I.pageUrl,ruleId:I.ruleId,selector:I.selector,verdict:"uncertain",reasoning:"Hide-text observation: element's bounding rect could not be resolved at audit time (element may be display:none, detached from DOM, or zero-size). Cannot sample contrast empirically. Manual verification required if you believe this element is visible.",resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:I.wcagCriterion,inputHash:N};await We(X);continue}const _=F.get(I.selector),M=T.get(I.selector);if(!_||!M){c++;const X={pageUrl:I.pageUrl,ruleId:I.ruleId,selector:I.selector,verdict:"uncertain",reasoning:"Hide-text observation: per-element clip capture failed via chrome.debugger Page.captureScreenshot. Manual verification required.",resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:I.wcagCriterion,inputHash:N};await We(X);continue}const L=zd(I.foreground);if(!L){c++;const X={pageUrl:I.pageUrl,ruleId:I.ruleId,selector:I.selector,verdict:"uncertain",reasoning:`Hide-text observation: element's computed CSS foreground color could not be parsed (got ${JSON.stringify(I.foreground)}). Cannot compute contrast against rendered background without an intended foreground color. Manual verification required.`,resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:I.wcagCriterion,inputHash:N};await We(X);continue}const W=nu(_,M,L);if(!W){c++;const X={pageUrl:I.pageUrl,ruleId:I.ruleId,selector:I.selector,verdict:"uncertain",reasoning:`Hide-text observation: too few text pixels detected (under ${hi} after diffing hidden vs visible captures). Element may be canvas-rendered, SVG with non-standard rendering, or carry styles that prevent color: transparent from hiding the text. Manual verification required.`,resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:I.wcagCriterion,inputHash:N};await We(X);continue}const P=qd(I.fontSize,I.fontWeight),V=P?Bd:Hd,O=X=>`rgb(${X.r},${X.g},${X.b})`;let j=W.worstRatio>=V,q="";if(Kd(W.worstRatio,L,C.resolvedBg,V)){const X=Un(L,C.resolvedBg);j=!0,q=` Cross-check (rc.356): computed CSS contrast ${O(L)} on resolved background ${O(C.resolvedBg)} = ${X.toFixed(2)}:1, which PASSES ${V}:1 — the sub-threshold pixel reading is an anti-aliasing/sampling artifact, not a real failure. Suppressed.`}const z={pageUrl:I.pageUrl,ruleId:I.ruleId,selector:I.selector,verdict:j?"pass":"fail",reasoning:`Hide-text observation: ${W.textPixelCount} text-core pixels detected by diffing hidden-vs-visible viewport screenshots. Worst per-pixel contrast: ${W.worstRatio.toFixed(2)}:1 against AA threshold ${V}:1 (${P?"large text":"normal text"}). At worst pixel: rendered foreground ${O(W.worstForeground)} on rendered background ${O(W.worstBackground)}. ${j&&q?"Passes (computed cross-check).":j?"Passes threshold.":"Falls below threshold — at least one text pixel reads against a background that fails contrast."}`+q,resolvedAt:new Date().toISOString(),costUsd:0,wcagCriterion:I.wcagCriterion,inputHash:N};await We(z),i++}}finally{try{await chrome.debugger.sendCommand({tabId:e},"Runtime.evaluate",{expression:`(() => { const el = document.getElementById(${JSON.stringify(Wn)}); if (el) el.remove(); return true; })()`})}catch{}try{await chrome.debugger.detach({tabId:e})}catch{}}return Xe.info(`pixel-contrast sampler: resolved ${i}, skipped ${c}/${n.size}`),{resolved:i,skipped:c}}const mt=Z("ai-text");function bt(e){if(e!=null&&e.aborted){const t=new Error("aborted");throw t.name="AbortError",t}}function yt(e){return e instanceof Error&&(e.name==="AbortError"||e.name==="ExternalAbortError"||e.message==="aborted")}async function ru(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.headings)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=de(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Le(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),p=e.slice(0,l),m=p.length;for(let g=0;g<p.length;g++){const h=p[g];if(!i.canCharge())break;try{bt(n.signal);const f=await r.judgeHeading({headingText:h.text,sectionContent:h.sectionContent,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.6&&c.push(await wt("ai-heading-not-descriptive","2.4.6","AA","moderate","AI judged this heading does not describe its section content.",h.selector,h.outerHTML,h.text,f,t))}catch(f){if(yt(f)){d.push(`canceled after ${g}/${m} candidates`);break}const w=f instanceof Error?f.message:String(f);d.push(`${h.selector}: ${w}`),mt.warn("heading judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function iu(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.sensory)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=de(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Le(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),p=e.slice(0,l),m=p.length;for(let g=0;g<p.length;g++){const h=p[g];if(!i.canCharge())break;try{bt(n.signal);const f=await r.judgeSensoryLanguage({instructionText:h.text,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.6&&c.push(await wt("ai-sensory-instruction","1.3.3","A","serious","AI flagged this instruction as relying on sensory characteristics (shape, color, location) without a programmatic identifier.",h.selector,h.outerHTML,h.text.slice(0,100),f,t))}catch(f){if(yt(f)){d.push(`canceled after ${g}/${m} candidates`);break}const w=f instanceof Error?f.message:String(f);d.push(`${h.selector}: ${w}`),mt.warn("sensory judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function su(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.aria)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=de(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Le(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),p=e.slice(0,l),m=p.length;for(let g=0;g<p.length;g++){const h=p[g];if(!i.canCharge())break;try{bt(n.signal);const f=await r.judgeAriaSemantics({elementHtml:h.outerHTML,computedRole:h.role,surroundingHtml:h.surroundingHtml,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.65&&c.push(await wt("ai-aria-misuse","4.1.2","A","serious",`AI judged role="${h.role}" is being used inappropriately on this element.`,h.selector,h.outerHTML,h.role,f,t))}catch(f){if(yt(f)){d.push(`canceled after ${g}/${m} candidates`);break}const w=f instanceof Error?f.message:String(f);d.push(`${h.selector}: ${w}`),mt.warn("aria judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function cu(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.genericLinkText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=de(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Le(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),p=e.slice(0,l),m=p.length;for(let g=0;g<p.length;g++){const h=p[g];if(!i.canCharge())break;try{bt(n.signal);const f=await r.judgeGenericLinkText({linkText:h.linkText,surroundingText:h.surroundingText,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.6&&c.push(await wt("ai-generic-link-text","2.4.4","A","serious","AI judged this link's purpose is not clear from its text or surrounding context.",h.selector,h.outerHTML,h.linkText,f,t))}catch(f){if(yt(f)){d.push(`canceled after ${g}/${m} candidates`);break}const w=f instanceof Error?f.message:String(f);d.push(`${h.selector}: ${w}`),mt.warn("generic-link judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function lu(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.wallOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=de(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Le(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),p=e.slice(0,l),m=p.length;for(let g=0;g<p.length;g++){const h=p[g];if(!i.canCharge())break;try{bt(n.signal);const f=await r.judgeWallOfText({blockText:h.blockText,structuralChildrenCount:h.structuralChildrenCount,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.6&&c.push(await wt("ai-wall-of-text","3.1.5","AAA","moderate","AI flagged this block as a wall of text with insufficient structural breaks (subheadings, lists, paragraphs).",h.selector,h.outerHTML,h.blockText.slice(0,80),f,t))}catch(f){if(yt(f)){d.push(`canceled after ${g}/${m} candidates`);break}const w=f instanceof Error?f.message:String(f);d.push(`${h.selector}: ${w}`),mt.warn("wall-of-text judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function du(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.languageMismatch)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=de(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Le(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),p=e.slice(0,l),m=p.length;for(let g=0;g<p.length;g++){const h=p[g];if(!i.canCharge())break;try{bt(n.signal);const f=await r.judgeLanguageMismatch({declaredLang:h.declaredLang,bodyTextSample:h.bodyTextSample,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.7&&c.push(await wt("ai-language-mismatch","3.1.1","A","serious",`AI judged the page's content does not match the declared lang="${h.declaredLang||"(none)"}".`,h.selector,h.outerHTML,h.bodyTextSample.slice(0,80),f,t))}catch(f){if(yt(f)){d.push(`canceled after ${g}/${m} candidates`);break}const w=f instanceof Error?f.message:String(f);d.push(`${h.selector}: ${w}`),mt.warn("language-mismatch judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function wt(e,t,a,n,o,r,i,c,d,l){const p={selector:r,outerHTML:i.slice(0,500),failureSummary:`AI assessment: ${d.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:c.slice(0,100)||null,attrId:null,attrTestid:null},m=await yn({ruleId:e,componentId:l.componentId,currentState:l.currentState,target:p});return{ruleId:e,wcagCriterion:t,wcagLevel:a,impact:n,description:o,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:p,componentId:l.componentId,currentState:l.currentState,axeVersion:l.axeVersion,detectedAt:new Date().toISOString(),matchKey:m,ai:{reasoning:d.reasoning,confidence:d.confidence,model:d.model,costUsd:d.costUsd}}}const pi=Z("ai-vision");function fi(e){if(e!=null&&e.aborted){const t=new Error("aborted");throw t.name="AbortError",t}}function gi(e){return e instanceof Error&&(e.name==="AbortError"||e.name==="ExternalAbortError"||e.message==="aborted")}async function uu(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.imageOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=de(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const r=o.client,i=Le(a.costCapUsd),c=Math.max(1,a.imageOfTextMaxImages??5),d=[...e].sort((g,h)=>h.pixelArea-g.pixelArea).slice(0,c),l=d.length,p=[],m=[];for(let g=0;g<d.length;g++){const h=d[g];if(!i.canCharge())break;try{fi(n.signal);const f=await r.judgeImageOfText({imageUrl:h.imageUrl,accessibleName:h.accessibleName,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.65&&p.push(await mi("ai-image-of-text","1.4.5","AA","moderate","AI judged this image bakes substantial text into the raster — should be presented as actual HTML text instead.",h.selector,h.outerHTML,h.accessibleName??"",f,t))}catch(f){if(gi(f)){m.push(`canceled after ${g}/${l} candidates`);break}const w=f instanceof Error?f.message:String(f);m.push(`${h.selector}: ${w}`),pi.warn("image-of-text judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,l)}return{findings:p,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:m}}async function hu(e,t,a,n={}){var u;if(!a.enabled||!a.enabledChecks.colorOnlyMeaning)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=de(a);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[o.reason]};const r=o.client,i=Le(a.costCapUsd),c=[],d=[],l=Math.max(1,a.maxCandidatesPerCheck??10),p=e.slice(0,l),m=p.length;for(let g=0;g<p.length;g++){const h=p[g];if(!i.canCharge())break;try{fi(n.signal);const f=await r.judgeColorOnlyMeaning({elementHtml:h.elementHtml,contextDescription:h.contextDescription,signal:n.signal});i.recordCharge(f.costUsd),f.verdict==="fail"&&f.confidence>=.65&&c.push(await mi("ai-color-only-meaning","1.4.1","A","serious","AI judged this region conveys information only through color (no text label, icon glyph, or shape difference).",h.selector,h.outerHTML,h.contextDescription,f,t))}catch(f){if(gi(f)){d.push(`canceled after ${g}/${m} candidates`);break}const w=f instanceof Error?f.message:String(f);d.push(w),pi.warn("color-only judgment failed",f)}(u=n.onProgress)==null||u.call(n,g+1,m)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:d}}async function mi(e,t,a,n,o,r,i,c,d,l){const p={selector:r,outerHTML:i.slice(0,500),failureSummary:`AI assessment: ${d.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:c.slice(0,100)||null,attrId:null,attrTestid:null},m=await yn({ruleId:e,componentId:l.componentId,currentState:l.currentState,target:p});return{ruleId:e,wcagCriterion:t,wcagLevel:a,impact:n,description:o,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:p,componentId:l.componentId,currentState:l.currentState,axeVersion:l.axeVersion,detectedAt:new Date().toISOString(),matchKey:m,ai:{reasoning:d.reasoning,confidence:d.confidence,model:d.model,costUsd:d.costUsd}}}const fn=Z("ensure-content-script"),pu=1500,fu=300;async function Gn(e,t){if(await Ro(e,t))return{ok:!0};const a=chrome.runtime.getManifest(),n=[];for(const o of a.content_scripts??[])Array.isArray(o.js)&&n.push(...o.js);if(n.length===0)return fn.error("manifest declares no content_scripts"),{ok:!1,reason:"inject-failed",detail:"no content_scripts declared in manifest"};try{const o=t!==void 0?{tabId:e,frameIds:[t]}:{tabId:e,allFrames:!0};await chrome.scripting.executeScript({target:o,files:n})}catch(o){return gu(o)}return await mu(fu),await Ro(e,t)?(fn.info("content script injected on demand",{tabId:e,frameId:t}),{ok:!0}):{ok:!1,reason:"unresponsive-after-inject",detail:"content script injected but did not register handlers — likely Content Security Policy on this page is blocking it"}}async function Ro(e,t){try{const a=t!==void 0?{frameId:t}:void 0,n=await Promise.race([chrome.tabs.sendMessage(e,{type:"PING_REQUEST"},a),new Promise((o,r)=>setTimeout(()=>r(new Error("ping timeout")),pu))]);return(n==null?void 0:n.type)==="PING_RESPONSE"}catch{return!1}}function gu(e){const t=e instanceof Error?e.message:String(e);return/cannot access contents of (?:the page|url)/i.test(t)||/chrome:\/\//i.test(t)||/chrome-extension:\/\//i.test(t)||/devtools:\/\//i.test(t)||/chrome\.google\.com\/webstore/i.test(t)||/the extensions gallery/i.test(t)?{ok:!1,reason:"restricted-url",detail:t}:/no tab with id/i.test(t)||/tab .* was closed/i.test(t)?{ok:!1,reason:"tab-gone",detail:t}:(fn.warn("content-script injection failed",e),{ok:!1,reason:"inject-failed",detail:t})}function mu(e){return new Promise(t=>setTimeout(t,e))}function gn(e){switch(e.reason){case"restricted-url":return"Chrome blocks extensions on this page (chrome:// pages, the Chrome Web Store, view-source: pages, and a few others). Open a regular https:// page and try again.";case"tab-gone":return"The tab was closed or navigated away while the audit was starting. Click into the tab you want audited and click Scan again.";case"unresponsive-after-inject":return"We loaded our auditor onto the page, but the page's Content Security Policy appears to be blocking it. Some sites (banks, gov, strict CSPs) prevent extensions from running. Try a different page on the same site, or open DevTools → Console to confirm a CSP error.";case"inject-failed":return`Could not load our auditor onto the page. Try a hard refresh (Ctrl+Shift+R) on the page and click Scan again. If it persists, the page may be using an unusual document type or origin restriction.${e.detail?` (Detail: ${e.detail})`:""}`;case"unknown":default:return"Could not establish connection with the page. Hard-refresh (Ctrl+Shift+R) the tab you want audited and try again."}}const qa=Z("forensic-anchor-client"),bu="https://api.wcagcheckr.com",yu="wcagcheckr",wu=15e3;function vu(e){if(typeof e!="object"||e===null)return!1;const t=e;return t.schemaVersion!==1&&t.schemaVersion!==2||!(typeof t.anchoredAt=="string"&&typeof t.tsaName=="string"&&typeof t.rfc3161TokenBase64=="string"&&t.rfc3161TokenBase64.length>0&&typeof t.serverSignatureBase64=="string"&&t.serverSignatureBase64.length>0&&typeof t.serverKeyFingerprint=="string"&&t.serverKeyFingerprint.length>0)?!1:t.schemaVersion===2?typeof t.prevAuditHash=="string"&&/^[0-9a-f]{64}$/.test(t.prevAuditHash):!0}async function bi(e,t){return yi(e.hash,e.pageUrl,e.capturedAt,t)}async function yi(e,t,a,n){const o={auditHash:e,pageUrl:t,capturedAt:a,...n?{licenseId:n}:{}},r=`${bu}/v1/products/${yu}/forensic/anchor`;try{const i=await fetch(r,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(o),signal:AbortSignal.timeout(wu)});if(!i.ok)return qa.warn(`anchor HTTP ${i.status}; entry stays local-only`),null;const c=await i.json();return vu(c)?c:(qa.warn("anchor returned malformed receipt; entry stays local-only",c),null)}catch(i){return qa.warn("anchor request failed; entry stays local-only",i),null}}async function Au(e){const t=ea(e),a=new TextEncoder().encode(t),n=await crypto.subtle.digest("SHA-256",a);return Array.from(new Uint8Array(n)).map(o=>o.toString(16).padStart(2,"0")).join("")}function ku(e){try{return new URL(e).hostname}catch{return e}}const xu="wcag-site-claims",Iu=1,sa="claims";let za=null;function wi(){return za||(za=vn(xu,Iu,{upgrade(e){e.objectStoreNames.contains(sa)||e.createObjectStore(sa,{keyPath:"domain"})}})),za}async function Su(e){await(await wi()).put(sa,e)}async function ef(e){return await(await wi()).get(sa,e)??null}const Oo="4.11.4";async function Cu(e){try{return(await chrome.tabs.get(e)).url??""}catch{return""}}const U=Z("flows");async function vt(){const e=await dr();if(!e)throw new Error("no audit target tab found");return e}async function Vn(){var a,n,o,r,i;const e=await Ye("stateMatrix",Qa);return((a=e.pseudoStates)==null?void 0:a.length)>0&&((n=e.themes)==null?void 0:n.length)>0&&((o=e.directions)==null?void 0:o.length)>0&&((r=e.breakpoints)==null?void 0:r.length)>0&&((i=e.breakpointPresets)==null?void 0:i.length)>0?e:(U.warn("stored stateMatrix is invalid; using defaults"),Qa)}async function $u(e,t,a){const n=t.themes.includes("light"),o=t.themes.includes("dark");if(!n||!o)return t;let r=null;try{r=await Ae(e,{type:"THEME_AWARENESS_REQUEST"},a)}catch{return t}return!r||r.hasUnreadableSheets?t:r.hasDarkModeCss&&!r.hasLightModeCss?(U.info("theme-axis prune: site is dark-only, skipping light pass"),{...t,themes:t.themes.filter(i=>i!=="light")}):r.hasLightModeCss&&!r.hasDarkModeCss?(U.info("theme-axis prune: site is light-only, skipping dark pass"),{...t,themes:t.themes.filter(i=>i!=="dark")}):t}const Tu=200,Ka=3,Mo=8;async function Eu(e,t){const a=[];let n=null,o=null;for(let i=0;i<Mo&&!t();i++){const c=await e();if(c.error&&(o=c.error),!c.result)break;if(n=c.result,a.push(c.violations),i===1&&a[0].length===0&&a[1].length===0)return{violations:[],lastResult:n,sampleCount:2,stabilized:!0,lastError:o};if(a.length>=Ka){const d=a.slice(-Ka);if(Ru(d))return{violations:d[d.length-1],lastResult:n,sampleCount:a.length,stabilized:!0,lastError:o}}i<Mo-1&&await new Promise(d=>setTimeout(d,Tu))}const r=a.slice(-Ka);return{violations:Ou(r),lastResult:n,sampleCount:a.length,stabilized:!1,lastError:o}}function Ru(e){if(e.length<2)return!0;const t=Do(e[0]);for(let a=1;a<e.length;a++){const n=Do(e[a]);if(n.size!==t.size)return!1;for(const o of t)if(!n.has(o))return!1}return!0}function Do(e){return new Set(e.map(t=>`${t.ruleId}::${t.target.selector}::${t.matchKey}`))}function Ou(e){const t=new Map,a="Audit detected this finding intermittently across multiple samples — likely an animation or transitional state was in progress. Verify manually.";for(const n of e)for(const o of n){const r=`${o.ruleId}::${o.target.selector}::${o.matchKey}`;t.has(r)||t.set(r,{...o,needsReview:!0,flakyAcrossRuns:!0,target:{...o.target,failureSummary:o.target.failureSummary?`${o.target.failureSummary} ${a}`:a}})}return Array.from(t.values())}async function No(e,t,a,n,o){await Promise.all([(async()=>{var r;try{const i=await Ae(e,{type:"READING_ORDER_REQUEST",selector:t},a);n[0]&&(i.issues.length>0?(n[0].readingOrderIssues=i.issues,n[0].positionAnalysisCapturedAt=o):(r=n[0].axeRulesEvaluated)==null||r.passed.push({ruleId:"wcc-reading-order-clean",wcagCriterion:"1.3.2"}))}catch(i){U.debug("reading-order analysis skipped",i)}})(),(async()=>{var r;try{const i=await Ae(e,{type:"TAB_ORDER_ANALYZE_REQUEST"},a);n[0]&&(i.issues.length>0?(n[0].tabOrderIssues=i.issues,n[0].positionAnalysisCapturedAt=o):(r=n[0].axeRulesEvaluated)==null||r.passed.push({ruleId:"wcc-tab-order-clean",wcagCriterion:"2.4.3"}))}catch(i){U.debug("tab-order analysis skipped",i)}})(),(async()=>{var r;try{const i=await Ae(e,{type:"TYPOGRAPHY_ANALYZE_REQUEST",selector:t},a);i.issues.length>0&&n[0]?n[0].typographyIssues=i.issues:n[0]&&((r=n[0].axeRulesEvaluated)==null||r.passed.push({ruleId:"wcc-typography-clean",wcagCriterion:"1.4.12"}))}catch(i){U.debug("typography analysis skipped",i)}})(),(async()=>{try{const r=await Ae(e,{type:"CUSTOM_PROPERTY_REQUEST"},a);r.undefined.length>0&&n[0]&&(n[0].undefinedCustomProperties=r.undefined)}catch(r){U.debug("custom-property analysis skipped",r)}})()])}async function pt(e,t,a,n,o={}){var I,N,C,_,M;const r=o.asSubroutine===!0;if(await je())return r||B({type:"AUDIT_FAILED_EVENT",error:{code:Dt,message:Ze,recoverable:!1}}),{ok:!1,results:[],error:Ze};const i=o.tabIdOverride??await vt(),c=await Gn(i,a);if(!c.ok)return r||B({type:"AUDIT_FAILED_EVENT",error:{code:c.reason==="restricted-url"?"RESTRICTED_URL":"CONTENT_SCRIPT_UNREACHABLE",message:gn(c),recoverable:c.reason!=="restricted-url"}}),{ok:!1,results:[],error:gn(c)};try{const[L]=await chrome.scripting.executeScript({target:{tabId:i,frameIds:a!==void 0?[a]:void 0},func:()=>document.contentType}),W=String((L==null?void 0:L.result)??"").toLowerCase();if(!(W===""||W==="text/html"||W==="application/xhtml+xml")){const V=`This page is not an HTML document (Content-Type: ${W||"unknown"}). WCAG HTML checks don't apply to XML sitemaps, RSS/Atom feeds, JSON, or plain text — skipped.`;return r||B({type:"AUDIT_FAILED_EVENT",error:{code:"NOT_HTML_DOCUMENT",message:V,recoverable:!1}}),{ok:!1,results:[],error:V,skipped:!0}}}catch(L){U.debug("content-type probe failed (non-fatal); proceeding with audit",L)}try{await chrome.scripting.executeScript({target:{tabId:i,frameIds:a!==void 0?[a]:void 0},world:"MAIN",func:()=>{try{let L=document.getElementById("__wcagcheckr_audit_freeze");L||(L=document.createElement("style"),L.id="__wcagcheckr_audit_freeze",L.textContent="*, *::before, *::after { animation-duration: 0s !important; animation-delay: 0s !important; transition-duration: 0s !important; transition-delay: 0s !important; } html, body { scroll-behavior: auto !important; }",document.documentElement.appendChild(L)),window.scrollTo(0,0);const W=document.activeElement;W&&W instanceof HTMLElement&&W!==document.body&&W.blur()}catch{}}}),await chrome.scripting.executeScript({target:{tabId:i,frameIds:a!==void 0?[a]:void 0},world:"MAIN",func:async()=>{try{await document.fonts.ready}catch{}}})}catch(L){U.debug("audit-start state stabilization failed (non-fatal)",L)}const d=n??await Vn(),l=await $u(i,d,a),p=Ll(l),m=await ss(`${await Cu(i)}|${e}|${a??0}`),u=await cs(l),g=await Ye("axeDisabledRules",[]),h=await ls(g);let f=null;try{f=(await Ae(i,{type:"SCOPE_FINGERPRINT_REQUEST",selector:e},a)).fingerprint}catch(L){U.debug("scope fingerprint failed; skipping cache fast-path",L)}if(f){const L=await ds(m);if(L&&L.fingerprint===f&&L.axeVersion===Oo&&L.matrixConfigHash===u&&L.disabledRulesHash===h)return U.info(`audit-cache HIT for ${L.componentId} — skipping matrix iteration`),r||(B({type:"AUDIT_COMPLETE_EVENT",componentId:L.componentId,results:L.results,delta:L.delta}),B({type:"SCORECARD_UPDATED_EVENT"})),{ok:!0,results:L.results}}r||await at({sessionId:Pt(),mode:"single-element",scope:e,startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"}),await $t(i,{type:"LIVE_REGIONS_ARM_REQUEST"},a).catch(()=>{}),await $t(i,{type:"FOCUS_TRACE_ARM_REQUEST"},a).catch(()=>{}),await Ae(i,{type:"WARMUP_REQUEST"},a).catch(()=>{});const w=[],b=[];let k="",v,s=!1,x=null,$=null,A=null,S=null,F=!1,T=null;try{for(let L=0;L<p.length;L++){if(t.isCanceled()){U.info("single audit canceled by user");break}const W=p[L];try{const P=await hn(i,W,e,a);if(!P.success){v=(I=P.error)==null?void 0:I.message,U.warn(`state ${L+1}/${p.length} drive failed`,v);continue}W.breakpoint.id!==x&&(await Ae(i,{type:"WARMUP_REQUEST"},a).catch(()=>{}),x=W.breakpoint.id);const V=L===0?void 0:[...ai],O=await Eu(async()=>{var z;const q=await Ae(i,{type:"AUDIT_REQUEST",selector:e,currentState:W,rulesToRun:V},a);return q.success&&q.result?{result:q.result,violations:q.result.violations,error:null}:{result:null,violations:[],error:((z=q.error)==null?void 0:z.message)??null}},()=>t.isCanceled());O.lastError&&(v=O.lastError,U.warn(`state ${L+1}/${p.length} sampling: ${O.lastError}`));const j=O.lastResult;if(!O.stabilized&&O.sampleCount>0&&U.info(`state ${L+1}/${p.length} did not stabilize after ${O.sampleCount} samples — ${O.violations.length} finding(s) routed to needs-review as flickering`),j){s=!0;const q=O.violations;U.info(`[adaptive-sample] state ${L+1}/${p.length} ${W.pseudoState}·${W.theme}·${W.direction}·${W.breakpoint.id}: ${O.sampleCount} sample(s), ${O.stabilized?"stabilized":"flickering"}, ${q.length} finding(s)`);const z=await ed(i,{quality:75})??void 0;let X,ge;try{X=(await Ae(i,{type:"LIVE_REGIONS_DRAIN_REQUEST"},a)).announcements}catch{}try{ge=(await Ae(i,{type:"FOCUS_TRACE_DRAIN_REQUEST"},a)).focusEvents}catch{}if(b.push({...j,violations:q,frameId:a,announcements:X,focusEvents:ge,screenshotDataUrl:z}),w.push(...q),k=j.componentId,L===0&&$===null)try{const ue=await Ye("aiConfig",{}),we=ye(ue);if(we.enabled&&we.apiKey&&k){$=ua(i,e,a,we,b,k,t).catch(Ie=>{U.warn("ai augmentation (parallel) failed",Ie)});const ve=((N=b[0])==null?void 0:N.pageUrl)??((C=b[0])==null?void 0:C.scope)??"";A=Gd({tabId:i,componentId:k,pageUrl:ve,results:b,verifyFixesOnly:o.verifyFixesOnly}).catch(Ie=>(U.warn("1.4.11 walkthrough (parallel) failed",Ie),{criterionId:"1.4.11",ran:!0,ok:!1,error:String(Ie)}))}}catch(ue){U.debug("ai augmentation (parallel) gate-read failed",ue)}!F&&W.pseudoState==="default"&&W.theme==="light"&&W.direction==="ltr"&&W.breakpoint.id==="desktop"&&b[0]&&(F=!0,T=No(i,e,a,b,W).catch(we=>{U.debug("lightweight deep analyzers (parallel) failed",we)}))}}catch(P){v=P instanceof Error?P.message:String(P),U.warn(`state ${L+1}/${p.length} threw`,P)}B({type:"AUDIT_PROGRESS_EVENT",current:L+1,total:p.length,currentState:W,componentId:k||void 0}),r||await Lt({})}if(s&&k){const L={pseudoState:"default",ariaVariation:null,theme:"light",direction:"ltr",breakpoint:{id:"desktop",label:"Desktop",width:1280,height:800,deviceScaleFactor:1,mobile:!1}};if(F){if(T)try{await T}catch{}}else{try{await hn(i,L,e,a),await new Promise(O=>setTimeout(O,200))}catch(O){U.debug("reference-state drive failed; analyzers will use last matrix state",O)}await No(i,e,a,b,L).catch(O=>{U.debug("lightweight deep analyzers (post-matrix fallback) failed",O)})}try{const O=await Ye("aiConfig",{}),j=ye(O);if(j.enabled&&j.apiKey&&k&&b[0]){const q=b[0].pageUrl??b[0].scope??"";S=Wd({tabId:i,componentId:k,pageUrl:q,results:b,verifyFixesOnly:o.verifyFixesOnly}).catch(z=>(U.warn("1.3.2 walkthrough (parallel) failed",z),{criterionId:"1.3.2",ran:!0,ok:!1,error:String(z)}))}}catch(O){U.debug("1.3.2 walkthrough (parallel) gate-read failed",O)}try{if($)await $;else{const O=await Ye("aiConfig",{}),j=ye(O);j.enabled&&j.apiKey&&await ua(i,e,a,j,b,k,t)}}catch(O){U.warn("ai augmentation skipped",O)}try{const O=await Ye("aiConfig",{}),j=ye(O);if(j.enabled&&j.apiKey&&k&&b[0]){await zt(i,e,a).catch(ue=>U.warn("pre-walkthrough resetState failed",ue));const q=b[0].pageUrl??b[0].scope??"",z=new AbortController,X=setInterval(()=>{t.isCanceled()&&z.abort()},250),ge=await Ud({tabId:i,componentId:k,pageUrl:q,results:b,signal:z.signal,verifyFixesOnly:o.verifyFixesOnly}).finally(()=>clearInterval(X));U.info("keyboard walkthroughs complete",ge)}}catch(O){U.warn("keyboard walkthroughs failed",O)}try{const O=[];A&&O.push(await A),S&&O.push(await S),O.length>0&&U.info("parallel AI walkthroughs joined",O)}catch(O){U.warn("parallel AI walkthrough join failed",O)}try{if(k&&b[0]){const O=b[0].pageUrl??b[0].scope??"",j=new AbortController,q=setInterval(()=>{t.isCanceled()&&j.abort()},250),z=await Vd({tabId:i,componentId:k,pageUrl:O,results:b,signal:j.signal,verifyFixesOnly:o.verifyFixesOnly}).finally(()=>clearInterval(q));U.info("2.5.3 label-in-name walkthrough complete",z)}}catch(O){U.warn("2.5.3 walkthrough failed",O)}try{if(k&&b[0]){await zt(i,e,a).catch(X=>U.warn("pre-sampler resetState failed",X));const O=new AbortController,j=setInterval(()=>{t.isCanceled()&&O.abort()},250),{resolved:q,skipped:z}=await ou(i,b,O.signal).finally(()=>clearInterval(j));U.info(`pixel contrast sampler: resolved=${q} skipped=${z}`)}}catch(O){U.warn("pixel contrast sampler failed",O)}const W=b.flatMap(O=>O.announcements??[]),P=b.flatMap(O=>O.focusEvents??[]),V=await Pn(k,w,{announcements:W,focusEvents:P},d);try{const O=await Aa(b.map(ie=>ie.componentId)),j=await xr(b.map(ie=>ie.componentId)),q=new Set;for(const ie of new Set(b.map(y=>y.componentId))){const y=await ya(ie);for(const D of y)q.add(D)}const z=[...new Set(b.map(ie=>ie.componentId))],X=await Dr(z),ge=((_=b[0])==null?void 0:_.pageUrl)??((M=b[0])==null?void 0:M.scope)??"",ue=ge?await va(ge):[],we=new Set(b.map(ie=>ie.pageUrl??ie.scope??"").filter(Boolean)).size>1?"site-crawl":"single-page",ve=et(w,{runs:[],workflows:De},b,q,O,j,X,we,ue),Ie={...ve.score,letter:ve.combined},Be=b.reduce((ie,y)=>ie+y.durationMs,0),qe=await le(),$e=ce(qe,"forensicLog")?await jr(b,Ie,Be):null;if($e){const ie=await le();if(ce(ie,"forensicAnchoring"))try{const y=await pa(),D=await bi($e,y??void 0);D&&await Hr($e.componentId,$e.capturedAt,D)}catch(y){U.warn("forensic anchoring failed (entry stays local-only)",y)}}}catch(O){U.warn("forensic log recording failed",O)}if(f)try{await us({lookupKey:m,fingerprint:f,axeVersion:Oo,matrixConfigHash:u,disabledRulesHash:h,results:b,delta:V,componentId:k,cachedAt:Date.now()})}catch(O){U.warn("audit-cache write failed",O)}try{await chrome.scripting.executeScript({target:{tabId:i,frameIds:a!==void 0?[a]:void 0},world:"MAIN",func:()=>{const O=document.getElementById("__wcagcheckr_audit_freeze");O&&O.remove()}})}catch(O){U.debug("audit-end state-freeze cleanup failed",O)}r||B({type:"AUDIT_COMPLETE_EVENT",componentId:k,results:b,delta:V})}else r||B({type:"AUDIT_FAILED_EVENT",error:{code:"AXE_FAILED",message:v?`All audits failed. Last error: ${v}`:"No state produced results. Selector may not exist in the audit target frame.",recoverable:!0}});r||await dt()}catch(L){U.error("single audit failed",L),r||B({type:"AUDIT_FAILED_EVENT",componentId:k||void 0,error:Kn(L)?L.payload:{code:"UNKNOWN",message:String(L),recoverable:!1}}),v=Kn(L)?L.payload.message:String(L)}finally{await $t(i,{type:"LIVE_REGIONS_DISARM_REQUEST"},a).catch(()=>{}),await $t(i,{type:"FOCUS_TRACE_DISARM_REQUEST"},a).catch(()=>{}),await zt(i,e,a)}return{ok:s,results:b,error:s?void 0:v}}async function vi(e){var t;try{const n=(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{var r;const o=window;return{detected:typeof o.__STORYBOOK_PREVIEW__=="object",version:(r=o.__STORYBOOK_PREVIEW__)==null?void 0:r.version}}})).find(o=>{var r;return(r=o.result)==null?void 0:r.detected});if(n)return{detected:!0,version:(t=n.result)==null?void 0:t.version,frameId:n.frameId}}catch(a){U.warn("storybook detection scripting failed",a)}return{detected:!1}}async function Mu(e){if(await je()){B({type:"AUDIT_FAILED_EVENT",error:{code:Dt,message:Ze,recoverable:!1}});return}const t=await vt(),a=await xi(t);if(await chrome.storage.local.set({"storybook:lastDetected":{detected:a.kind==="storybook"&&a.detected,version:a.version,detectedAt:new Date().toISOString()},"playground:lastDetected":{kind:a.kind,detected:a.detected,version:a.version,detectedAt:new Date().toISOString()}}),!a.detected){B({type:"AUDIT_FAILED_EVENT",error:{code:"STORYBOOK_NOT_DETECTED",message:"No supported component playground detected on this tab. Open a Storybook (iframe.html / storybook-static), Ladle, or Bit page and try again.",recoverable:!0}});return}if(a.kind==="bit"){B({type:"AUDIT_FAILED_EVENT",error:{code:"STORYBOOK_NOT_DETECTED",message:"Bit detected. Auto-iterate is not yet supported for Bit (compositions are iframe-per-component without a unified listing API). Use the element picker to audit individual compositions one at a time.",recoverable:!0}});return}const n=a.frameId??0,o=a.kind==="storybook"?await Lu(t,n):await Fu(t,n);if(!o.length){U.warn(`${a.kind} detected but no stories returned`),B({type:"AUDIT_FAILED_EVENT",error:{code:"STORYBOOK_NOT_DETECTED",message:`${a.kind==="ladle"?"Ladle":"Storybook"} detected but no stories were returned. Try reloading the page.`,recoverable:!0}});return}await at({sessionId:Pt(),mode:"storybook-all",totalStories:o.length,completedStories:[],startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});for(const r of o){if(e.isCanceled()){U.info(`${a.kind} iteration canceled by user`);break}try{if(a.kind==="storybook"){await Pu(t,n,r.id);const c=await $t(t,{type:"STORYBOOK_GET_STORY_TARGET_REQUEST",tabId:t},n);await pt(c.selector,e,n)}else await _u(t,n,r.id),await pt("#ladle-root",e,n)}catch(c){U.warn(`story ${r.id} failed; continuing with next`,c)}const i=await Ta();if((i==null?void 0:i.state)==="interrupted"){U.info("storybook iteration interrupted");break}await Lt({completedStories:[...(i==null?void 0:i.completedStories)??[],r.id]})}B({type:"SCORECARD_UPDATED_EVENT"}),await dt()}async function Du(e){if(await je()){B({type:"AUDIT_FAILED_EVENT",error:{code:Dt,message:Ze,recoverable:!1}});return}const t=await le();if(!ce(t,"allFramesAudit")){B({type:"AUDIT_FAILED_EVENT",error:{code:"FEATURE_NOT_AVAILABLE",message:"All-frames audit is not available on your current plan.",recoverable:!1}});return}const a=await vt();let n=[];try{n=(await chrome.scripting.executeScript({target:{tabId:a,allFrames:!0},func:()=>({url:window.location.href})})).map(r=>{var i;return{frameId:r.frameId,url:((i=r.result)==null?void 0:i.url)??""}}).filter(r=>/^https?:/i.test(r.url))}catch(o){U.warn("multi-frame discovery failed",o)}if(n.length===0){B({type:"AUDIT_FAILED_EVENT",error:{code:"CONTENT_SCRIPT_UNREACHABLE",message:"No auditable frames found on this tab. The page may use only chrome-extension:// or data: frames.",recoverable:!0}});return}await at({sessionId:Pt(),mode:"all-frames",totalFrames:n.length,completedFrames:[],startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});for(const o of n){if(e.isCanceled()){U.info("all-frames iteration canceled");break}try{await pt("html",e,o.frameId)}catch(i){U.warn(`frame ${o.frameId} (${o.url.slice(0,80)}) audit failed; continuing`,i)}const r=await Ta();if((r==null?void 0:r.state)==="interrupted"){U.info("all-frames iteration interrupted");break}await Lt({completedFrames:[...(r==null?void 0:r.completedFrames)??[],o.frameId]})}B({type:"SCORECARD_UPDATED_EVENT"}),await dt()}async function Nu(e){var o;const t=await le();if(!ce(t,"parallelScan")){B({type:"AUDIT_FAILED_EVENT",error:{code:"FEATURE_NOT_AVAILABLE",message:"Parallel scan is not available on your current plan.",recoverable:!1}});return}const a=await dr();let n;if(a!==null)try{n=(await chrome.tabs.get(a)).url}catch(r){U.warn("failed to get target tab url",r)}if(!n){B({type:"AUDIT_FAILED_EVENT",error:{code:"NETWORK",message:"Could not determine target URL for parallel scan.",recoverable:!1}});return}await at({sessionId:Pt(),mode:"single-element",scope:"html",startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});try{const r=await Vn(),{runParallelTabAudit:i}=await bn(async()=>{const{runParallelTabAudit:m}=await import("./parallel-tab-flow-f0rYsTcq.js");return{runParallelTabAudit:m}},__vite__mapDeps([0,1,2,3,4])),c=await i({targetUrl:n,scope:"html",matrix:r,parallelism:4,cancel:e});if(U.info(`parallel scan complete: ${c.results.length} results across ${c.tabsCompleted} tabs (${c.tabsFailed} failed) in ${Math.round(c.parallelDurationMs)}ms`),c.results.length===0){B({type:"AUDIT_FAILED_EVENT",error:{code:"NETWORK",message:`All ${c.tabsFailed} worker tabs failed. Falling back to Full Page scan is recommended.`,recoverable:!0}});return}const d=((o=c.results[0])==null?void 0:o.componentId)??"parallel-scan";if(a!==null)try{const m=await Ye("aiConfig",{}),u=ye(m);u.enabled&&u.apiKey&&await ua(a,"html",void 0,u,c.results,d,e)}catch(m){U.warn("parallel scan: ai augmentation skipped",m)}const l=c.results.flatMap(m=>m.violations);let p;try{p=await Pn(d,l)}catch(m){U.warn("parallel scan: baseline compare failed",m),p={new:[],persistent:[],fixed:[],newCount:0,persistentCount:0,fixedCount:0,baselineSnapshotMeta:null,comparedAt:new Date().toISOString()}}B({type:"AUDIT_COMPLETE_EVENT",componentId:d,results:c.results,delta:p}),B({type:"SCORECARD_UPDATED_EVENT"})}catch(r){U.warn("parallel scan failed",r),B({type:"AUDIT_FAILED_EVENT",error:{code:"UNKNOWN",message:r instanceof Error?r.message:String(r),recoverable:!0}})}finally{await dt()}}async function Lu(e,t){var a;try{return((a=(await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",func:async()=>{var d,l;const o=window.__STORYBOOK_PREVIEW__;if(!o)return[];const r=o.storyStoreValue??o.storyStore;if(!r)return[];typeof r.cacheAllCSFFiles=="function"&&await r.cacheAllCSFFiles();const i=((d=r.extract)==null?void 0:d.call(r))??((l=r.cachedCSFFiles)==null?void 0:l.call(r));if(!i)return[];const c=[];if(i instanceof Map)for(const[p,m]of i){const u=m;c.push({id:String(p),name:u.name??String(p),kind:u.title??u.kind??""})}else if(typeof i=="object"&&i!==null)for(const[p,m]of Object.entries(i)){const u=m;c.push({id:p,name:u.name??p,kind:u.title??u.kind??""})}return c}}))[0])==null?void 0:a.result)??[]}catch(n){return U.warn("listStoriesViaMainWorld failed",n),[]}}async function Pu(e,t,a){try{await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",args:[a,3e3],func:async(n,o)=>{const r=new URL(window.location.href);r.searchParams.set("id",n),r.searchParams.set("viewMode","story"),window.location.href!==r.href&&(window.history.pushState({},"",r.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(i=>{const c=setTimeout(()=>i(),o),d=()=>{clearTimeout(c),i()};window.addEventListener("storyrendered",d,{once:!0})})}})}catch(n){U.warn(`navigateToStoryViaMainWorld(${a}) failed`,n)}}async function Ai(e){var t;try{const n=(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{const o=document.getElementById("ladle-root")!==null,r=document.title==="Ladle",i=!!document.querySelector('link[href*="ladle.css"]');return{detected:o&&(r||i),version:void 0}}})).find(o=>{var r;return(r=o.result)==null?void 0:r.detected});if(n)return{detected:!0,version:(t=n.result)==null?void 0:t.version,frameId:n.frameId}}catch(a){U.warn("ladle detection scripting failed",a)}return{detected:!1}}async function Fu(e,t){var a;try{return((a=(await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",func:async()=>{try{const o=await fetch(new URL("/meta.json",window.location.href).href,{credentials:"same-origin"});if(!o.ok)return[];const i=(await o.json()).stories??{};return Object.entries(i).map(([c,d])=>({id:c,name:d.name??c,kind:(d.levels??[]).join(" / ")}))}catch{return[]}}}))[0])==null?void 0:a.result)??[]}catch(n){return U.warn("listLadleStoriesViaMainWorld failed",n),[]}}async function _u(e,t,a){try{await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",args:[a,3e3],func:async(n,o)=>{const r=new URL(window.location.href);r.searchParams.set("story",n),window.location.href!==r.href&&(window.history.pushState({},"",r.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(i=>setTimeout(i,Math.min(o,500)))}})}catch(n){U.warn(`navigateToLadleStoryViaMainWorld(${a}) failed`,n)}}async function ki(e){try{return(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{const a=window.location.href;return/bit\.dev|bit-dev|bitsrc|\/~aspect|\/composition/i.test(a)}})).some(a=>a.result===!0)}catch{return!1}}async function xi(e){const t=await vi(e);if(t.detected)return{kind:"storybook",...t};const a=await Ai(e);return a.detected?{kind:"ladle",...a}:await ki(e)?{kind:"bit",detected:!0}:{kind:null,detected:!1}}const ca=200,Ii=6e4;function la(e,t){try{return new URL(e).origin===new URL(t).origin}catch{return!1}}function Si(e,t){try{const a=new URL(e,t);return a.hash="",a.toString()}catch{return null}}function Ci(e){try{return/\.(xml|rss|atom|json|txt|csv|ics|rdf|xsl|xslt)$/i.test(new URL(e).pathname)}catch{return!1}}async function $i(e,t,a){await chrome.tabs.update(e,{url:t}),await new Promise((n,o)=>{const r=setTimeout(()=>{chrome.tabs.onUpdated.removeListener(i),o(new Error(`navigation timeout (${a}ms): ${t}`))},a);function i(c,d){c===e&&d.status==="complete"&&(clearTimeout(r),chrome.tabs.onUpdated.removeListener(i),n())}chrome.tabs.onUpdated.addListener(i)}),await new Promise(n=>setTimeout(n,400))}async function Ti(e){var t;try{return((t=(await chrome.scripting.executeScript({target:{tabId:e},func:()=>Array.from(document.querySelectorAll("a[href]")).map(n=>n.getAttribute("href")).filter(n=>typeof n=="string"&&!n.startsWith("javascript:"))}))[0])==null?void 0:t.result)??[]}catch(a){return U.debug("link discovery failed",a),[]}}async function Ei(e,t){var a,n;try{const r=new URL(t).pathname.toLowerCase().replace(/\/+$/,"");if(/(?:^|\/)(?:404|not-found|page-not-found|error|server-error)$/.test(r))return`URL path "${r||"/"}" matches a common error-route pattern.`}catch{}try{const o=await chrome.scripting.executeScript({target:{tabId:e},func:()=>document.title||""}),r=((n=(a=o==null?void 0:o[0])==null?void 0:a.result)==null?void 0:n.trim())??"";if(!r)return null;const i=[[/^4(?:0[0-46-9]|[1-9]\d)\b/,"title starts with a 4xx error code"],[/^5\d{2}\b/,"title starts with a 5xx error code"],[/^(?:page\s+)?not\s+found\b/i,'title starts with "Not Found"'],[/^error\s*[-:]?\s*\d{3}\b/i,'title starts with "Error" + status code'],[/^(?:oops|whoops)[\s,!:.-]/i,'title leads with "Oops" — common error-template pattern']];for(const[c,d]of i)if(c.test(r))return`${d} (title: "${r.slice(0,80)}").`;return null}catch(o){return U.debug("error-page detection failed; proceeding with audit",o),null}}const Lo=/Linked PDF at (\S+)\./;async function Ri(e,t){if(!ce(t,"pdfAudit")){U.info("PDF audit disabled for tier; skipping crawl PDF pass",{tier:t});return}const a=new Map;for(const n of e)if(!n.error)for(const o of n.results)for(const r of o.violations){if(r.ruleId!=="pdf-needs-cli-audit")continue;const i=Lo.exec(r.description);if(!i)continue;const c=i[1];let d;try{d=new URL(c,n.url).href}catch{continue}if(!/^https?:\/\//i.test(d))continue;let l=a.get(d);l||(l=new Set,a.set(d,l)),l.add(n)}if(a.size!==0){U.info(`PDF audit pass: ${a.size} unique PDF(s) across the crawl`);for(const[n,o]of a){let r;try{r=await il(n)}catch(i){U.warn(`PDF audit threw for ${n}`,i);continue}for(const i of o){const c=i.results[0];if(!c)continue;const d=c.componentId,l=c.state,p=r.findings.map(m=>Po(m,n,d,l));c.violations=c.violations.filter(m=>{if(m.ruleId!=="pdf-needs-cli-audit")return!0;const u=Lo.exec(m.description);if(!u)return!0;try{return new URL(u[1],i.url).href!==n}catch{return!0}}),c.violations.push(...p),r.fatalError&&c.violations.push(Po({ruleId:"pdf-audit-skipped",impact:"minor",wcagCriterion:"1.1.1",description:`PDF audit skipped: ${r.fatalError}`,failureSummary:"The PDF could not be audited automatically. Run `wcagcheckr-ci pdf <url>` against this PDF on the command line for a manual audit."},n,d,l))}}}}function Po(e,t,a,n){return{ruleId:e.ruleId,wcagCriterion:e.wcagCriterion,wcagLevel:"AA",impact:e.impact,description:e.description,helpUrl:"https://www.w3.org/WAI/standards-guidelines/wcag/",target:{selector:t,outerHTML:`<a href="${t}">[PDF]</a>`,failureSummary:e.failureSummary,tagName:"PDF",role:null,accessibleName:null,textSnippet:null,attrId:null,attrTestid:null},componentId:a,currentState:n,axeVersion:"pdf-audit",detectedAt:new Date().toISOString(),matchKey:`pdf::${e.ruleId}::${t}`}}async function Uu(e,t,a){if(await je())return B({type:"CRAWL_DISCOVERY_FAILED_EVENT",error:{code:Dt,message:Ze,recoverable:!1}}),[];const n=await le();if(!ce(n,"siteCrawl"))return B({type:"CRAWL_DISCOVERY_FAILED_EVENT",error:{code:"FEATURE_NOT_AVAILABLE",message:"Site crawl is not available on your current plan.",recoverable:!1}}),[];const o=await vt(),r=Xo(n).siteCrawlMaxPages,i=Number.isFinite(r)?Math.min(r,ca):ca,c=Math.min(t.maxPages??25,i),d=t.includeRegex?da(t.includeRegex):null,l=t.excludeRegex?da(t.excludeRegex):null,p=[e],m=new Set,u=[];try{for(;p.length>0&&u.length<c;){if(a.isCanceled()){U.info("crawl discovery canceled by user");break}const g=p.shift();if(!(!g||m.has(g))&&la(g,e)&&!(d&&!d.test(g))&&!(l&&l.test(g))){m.add(g);try{if(await $i(o,g,Ii),!(await Gn(o,0)).ok||await Ei(o,g))continue;u.push(g),B({type:"CRAWL_DISCOVERY_PROGRESS_EVENT",found:u.length,url:g});const w=await Ti(o);for(const b of w){const k=Si(b,g);k&&la(k,e)&&!Ci(k)&&!m.has(k)&&!p.includes(k)&&p.push(k)}}catch(h){U.debug("discovery: page navigation/link-read failed; skipping",h);continue}await Lt({})}}return B({type:"CRAWL_DISCOVERY_COMPLETE_EVENT",urls:u}),u}catch(g){return U.error("crawl discovery failed",g),B({type:"CRAWL_DISCOVERY_FAILED_EVENT",error:{code:"CRAWL_DISCOVERY_FAILED",message:String(g),recoverable:!0}}),[]}}async function Wu(e,t,a){if(await je()){B({type:"SITE_CRAWL_FAILED_EVENT",error:{code:Dt,message:Ze,recoverable:!1}});return}const n=await le();if(!ce(n,"siteCrawl")){B({type:"SITE_CRAWL_FAILED_EVENT",error:{code:"FEATURE_NOT_AVAILABLE",message:"Site crawl is not available on your current plan.",recoverable:!1}});return}const o=await vt(),r=Xo(n).siteCrawlMaxPages,i=Number.isFinite(r)?Math.min(r,ca):ca,c=Array.isArray(t.urls)&&t.urls.length>0,d=Math.min(c?t.urls.length:t.maxPages??25,i),l=t.includeRegex?da(t.includeRegex):null,p=t.excludeRegex?da(t.excludeRegex):null,m=new Date().toISOString(),u=Date.now(),g=c?[...t.urls]:[e],h=new Set,f=[];await at({sessionId:Pt(),mode:"storybook-all",scope:e,startedAt:m,lastProgressAt:new Date().toISOString(),state:"running"});try{for(;g.length>0&&h.size<d;){if(a.isCanceled()){U.info("site crawl canceled by user");break}const x=g.shift();if(!x||h.has(x)||!la(x,e)||l&&!l.test(x)||p&&p.test(x))continue;h.add(x);const $=h.size,A=Date.now();B({type:"SITE_CRAWL_PROGRESS_EVENT",current:$,total:Math.min(d,h.size+g.length),url:x,status:"auditing"});try{await $i(o,x,Ii);const S=await Gn(o,0);if(!S.ok)throw new Error(gn(S));const F=await Ei(o,x);if(F){const _=`Skipped — page appears to be an error response. ${F}`;f.push({url:x,results:[],delta:null,componentId:x,durationMs:Date.now()-A,error:_}),B({type:"SITE_CRAWL_PROGRESS_EVENT",current:$,total:Math.min(d,h.size+g.length),url:x,status:"failed",error:_});continue}await Ae(o,{type:"WARMUP_REQUEST"},0).catch(()=>{});const T=await pt("html",a,0,void 0,{asSubroutine:!0,tabIdOverride:o,verifyFixesOnly:t.verifyFixesOnly}),I=T.results,N=T.ok?void 0:T.error,C=Date.now()-A;if(!T.ok&&T.skipped)f.push({url:x,results:[],delta:null,componentId:x,durationMs:C,error:T.error,skipped:!0}),B({type:"SITE_CRAWL_PROGRESS_EVENT",current:$,total:Math.min(d,h.size+g.length),url:x,status:"skipped",error:T.error});else if(I.length>0){const _=await sl(o,x).catch(()=>null),M=I.reduce((L,W)=>L+W.violations.length,0);f.push({url:x,results:I,delta:null,componentId:I[0].componentId,durationMs:C,snapshot:_??void 0}),B({type:"SITE_CRAWL_PROGRESS_EVENT",current:$,total:Math.min(d,h.size+g.length),url:x,status:"completed",violations:M})}else{const _=N??"audit returned no result";f.push({url:x,results:[],delta:null,componentId:x,durationMs:C,error:_}),B({type:"SITE_CRAWL_PROGRESS_EVENT",current:$,total:Math.min(d,h.size+g.length),url:x,status:"failed",error:_})}if(!c){const _=await Ti(o);for(const M of _){const L=Si(M,x);L&&la(L,e)&&!Ci(L)&&!h.has(L)&&!g.includes(L)&&g.push(L)}}}catch(S){const F=Date.now()-A,T=S instanceof Error?S.message:String(S);f.push({url:x,results:[],delta:null,componentId:x,durationMs:F,error:T}),B({type:"SITE_CRAWL_PROGRESS_EVENT",current:$,total:Math.min(d,h.size+g.length),url:x,status:"failed",error:T})}await Lt({})}try{const x=await le();await Ri(f,x)}catch(x){U.warn("PDF audit pass failed",x)}const w=new Date().toISOString(),b=Date.now()-u,k=f.flatMap(x=>x.results),v=await Me(k,[e]),s=Vr(e,f,m,w,b,v.walkthroughsByComponent,v.walkthroughAcksByComponent,v.ackedKeysByComponent,v.humanVerdictsByComponent,v.incompleteResolutionsByComponent,v.attestations);try{s.auditSettings=await Dn(w)}catch(x){U.warn("audit-settings capture failed",x)}if(s.consistencyFindings.length>0)try{const x=await Dl(s.consistencyFindings);s.consistencyFindings=x.emitted}catch(x){U.warn("consistency AI suppression failed; emitting heuristic findings",x)}try{const x=f.map($=>({...$,results:$.results.map(({screenshotDataUrl:A,...S})=>S)}));await lr(x,{startUrl:e,finishedAt:w})}catch(x){const $=x instanceof Error?x.message:String(x);U.error("persisting siteCrawlPerUrlResults failed",x),B({type:"SITE_CRAWL_FAILED_EVENT",error:{code:"CRAWL_PERSIST_FAILED",message:`Crawl finished but persistence failed (${$}). The AI fix prompt export won't have per-page data.`,recoverable:!1}})}B({type:"SITE_CRAWL_COMPLETE_EVENT",report:s}),await Oi(s),await dt()}catch(w){U.error("site crawl failed",w),B({type:"SITE_CRAWL_FAILED_EVENT",error:{code:"UNKNOWN",message:String(w),recoverable:!1}}),await dt()}}function da(e){try{return new RegExp(e)}catch{return null}}async function Gu(){const[e,t]=await Promise.all([In(),Jt()]);if(!e)return{ok:!1,reason:"No crawl report cached. Run a site crawl first."};if(!t||t.length===0)return{ok:!1,reason:"Crawl report exists but per-URL audit data was not retained. Re-run the crawl once; future regrades will work."};const a=t.flatMap(i=>i.results),n=await Me(a,[e.startUrl]),o=new Date().toISOString(),r=Vr(e.startUrl,t,e.startedAt,o,e.totalDurationMs,n.walkthroughsByComponent,n.walkthroughAcksByComponent,n.ackedKeysByComponent,n.humanVerdictsByComponent,n.incompleteResolutionsByComponent,n.attestations);return r.auditSettings=e.auditSettings??await Dn(o),await cr(r),B({type:"SITE_CRAWL_COMPLETE_EVENT",report:r}),await Oi(r),{ok:!0}}async function Oi(e){try{if(e.siteGrade==="pending")return;const t=await le();if(!ce(t,"forensicAnchoring"))return;const a={domain:ku(e.startUrl),grade:e.siteGrade,scope:"site",pageCount:e.pagesAudited,wcagLevel:`${Mt} ${wn}`,auditedAt:e.finishedAt,productSlug:"wcagcheckr"},n=await Au(a),o=await pa(),r=await yi(n,e.startUrl,e.finishedAt,o??void 0);await Su({domain:a.domain,claim:a,hash:n,receipt:r}),B({type:"SITE_CLAIM_SEALED_EVENT",domain:a.domain})}catch(t){U.warn("site-claim seal/anchor failed (badge unavailable for this crawl)",t)}}const Ya=5*6e4,Fo=9e4,_o={altText:"Verifying alt text…",headings:"Judging heading quality…",sensory:"Scanning for sensory-only instructions…",aria:"Reviewing ARIA semantics…",imageOfText:"Detecting baked-in text in images…",colorOnlyMeaning:"Checking for color-only meaning…",genericLinkText:"Reviewing link clarity…",wallOfText:"Detecting walls of text…",languageMismatch:"Verifying page language…"},Vu={altText:["1.1.1"],headings:["2.4.6"],sensory:["1.3.3"],aria:["4.1.2"],imageOfText:["1.4.5"],colorOnlyMeaning:["1.4.1"],genericLinkText:["2.4.4"],wallOfText:[],languageMismatch:["3.1.1","3.1.2"]};async function ua(e,t,a,n,o,r,i){const c=await le();if(!ce(c,"aiJudgment")){U.debug("aiJudgment not allowed on current tier — skipping AI augmentation");return}let d;try{d=await Ae(e,{type:"AI_CANDIDATES_REQUEST",selector:t},a)}catch(C){U.debug("ai candidates fetch failed",C);return}const l=o[0];if(!l)return;const p={componentId:r,currentState:l.state,axeVersion:l.axeVersion},m={at:new Date().toISOString(),componentId:r,pageUrl:l.pageUrl??t,enabledChecks:n.enabledChecks,candidateCounts:{images:d.images.length,headings:d.headings.length,instructions:d.instructions.length,ariaElements:d.ariaElements.length,links:d.links.length,longTextBlocks:d.longTextBlocks.length,colorOnlyRegions:d.colorOnlyRegions.length,languageInfo:d.languageInfo?"present":"null"}};U.info("ai augmentation gate snapshot",JSON.stringify(m));try{await chrome.storage.local.set({aiDiagnosticLatest:m})}catch(C){U.warn("failed to persist ai diagnostic to storage.local",C)}const u=[];if(n.enabledChecks.altText&&u.push({key:"altText",run:C=>{const _=d.images.map(M=>({imageUrl:M.imageUrl,alt:M.alt,selector:M.selector,outerHTML:M.outerHTML,surroundingContext:M.surroundingContext,...p}));return hd(_,n,C)}}),n.enabledChecks.headings&&u.push({key:"headings",run:C=>ru(d.headings,p,n,C)}),n.enabledChecks.sensory&&u.push({key:"sensory",run:C=>iu(d.instructions,p,n,C)}),n.enabledChecks.aria&&u.push({key:"aria",run:C=>su(d.ariaElements,p,n,C)}),n.enabledChecks.imageOfText&&u.push({key:"imageOfText",run:C=>{const _=d.images.map(M=>({imageUrl:M.imageUrl,accessibleName:M.alt||void 0,selector:M.selector,outerHTML:M.outerHTML,pixelArea:M.pixelArea}));return uu(_,p,n,C)}}),n.enabledChecks.colorOnlyMeaning&&u.push({key:"colorOnlyMeaning",run:C=>hu(d.colorOnlyRegions,p,n,C)}),n.enabledChecks.genericLinkText&&u.push({key:"genericLinkText",run:C=>cu(d.links,p,n,C)}),n.enabledChecks.wallOfText&&u.push({key:"wallOfText",run:C=>lu(d.longTextBlocks,p,n,C)}),n.enabledChecks.languageMismatch){const C=d.languageInfo;u.push({key:"languageMismatch",run:_=>du(C?[C]:[],p,n,_)})}if(u.length===0)return;let g=0,h=0,f=!1;const w={};let b=0,k=0,v=0;const s=[];let x=!1,$=!1;function A(C,_){if(w[C]=(w[C]??0)+_.totalCostUsd,g+=_.totalCostUsd,h+=_.findings.length,f=f||_.capExceeded,b++,_.errors.length>0){v++;for(const M of _.errors)s.includes(M)||s.push(M)}else k++;U.info(`ai ${C}: ${_.findings.length} findings, $${_.totalCostUsd.toFixed(4)}, errs=${_.errors.length}`),_.capExceeded&&U.warn(`ai cost cap exceeded after $${_.totalCostUsd.toFixed(4)} during ${C}`)}const S=(async()=>{for(let C=0;C<u.length;C++){if(i!=null&&i.isCanceled()){$=!0;break}const _=u[C];B({type:"AI_AUGMENTATION_PROGRESS_EVENT",componentId:r,currentCheck:_.key,currentCheckLabel:_o[_.key],current:C+1,total:u.length});const M=new AbortController,L=setTimeout(()=>M.abort(),Fo),W=i?setInterval(()=>{i.isCanceled()&&M.abort()},500):null;let P;try{P=await _.run({signal:M.signal,onProgress:(V,O)=>{O>0&&B({type:"AI_AUGMENTATION_PROGRESS_EVENT",componentId:r,currentCheck:_.key,currentCheckLabel:_o[_.key],current:C+1,total:u.length,candidatesDone:V,candidatesTotal:O})}}),M.signal.aborted&&(i!=null&&i.isCanceled())?P.errors=[...P.errors,`${_.key} canceled by user`]:M.signal.aborted}catch(V){const O=V instanceof Error?V.message:String(V),j=M.signal.aborted,q=j&&(i!=null&&i.isCanceled())?`${_.key} canceled by user`:j?`${_.key} hit per-check ${Fo/1e3}s cap — analyzer aborted`:`${_.key} threw: ${O}`;P={findings:[],totalCostUsd:0,capExceeded:!1,errors:[q]}}finally{clearTimeout(L),W&&clearInterval(W)}if(P.findings.length>0&&(l.violations=[...l.violations,...P.findings]),P.errors.length===0){const V=Vu[_.key];if(V&&V.length>0&&l.axeRulesEvaluated)for(const O of V)l.axeRulesEvaluated.passed.push({ruleId:`ai-${_.key}-coverage`,wcagCriterion:O})}A(_.key,P)}})(),F=new Promise(C=>{setTimeout(()=>C("timeout"),Ya)});await Promise.race([S.then(()=>"done"),F])==="timeout"&&(x=!0,U.warn(`ai augmentation hit global ${Ya/1e3}s timeout after ${b}/${u.length} checks`));let I;if(v>0||x||$){x?I=`AI augmentation timed out after ${Ya/1e3}s`:$?I="AI augmentation canceled":I=s[0]??"AI analyzer error";const C=k===0?"total":"partial";B({type:"AI_AUGMENTATION_FAILED_EVENT",componentId:r,severity:C,reason:I,checksAttempted:b,checksSucceeded:k,checksErrored:v,errorDetails:x||$?[I,...s.slice(0,4)]:s.slice(0,5)}),U.warn(`ai augmentation ${C} failure: ${v}/${b} checks errored — ${I}`)}(g>0||h>0||I)&&await hs({at:new Date().toISOString(),pageUrl:l.pageUrl??t,componentId:r,totalCostUsd:g,byCheck:w,findingsCount:h,capExceeded:f,failureReason:I})}const tf=Object.freeze(Object.defineProperty({__proto__:null,auditCrawlPdfs:Ri,detectBit:ki,detectLadleAcrossFrames:Ai,detectPlayground:xi,detectStorybookAcrossFrames:vi,discoverCrawlUrls:Uu,getActiveTabId:vt,getMatrixSettings:Vn,regradeSiteCrawl:Gu,runAiAugmentation:ua,runAllFramesAudit:Du,runParallelScan:Nu,runSingleElementAudit:pt,runSiteCrawl:Wu,runStorybookAllAudit:Mu},Symbol.toStringTag,{value:"Module"})),St=Z("alert-sender"),ju="wcagcheckr",Hu="https://api.wcagcheckr.com",Bu=`${Hu}/v1/products/${ju}/alerts/send`,qu=1e4,mn="alertConfig:v1",zu={emailEnabled:!1,webhookEnabled:!1,webhookUrl:"",minImpact:"serious"},Bt={minor:1,moderate:2,serious:3,critical:4};async function Ku(){const t=(await chrome.storage.local.get(mn))[mn];return{...zu,...t??{}}}async function af(e){await chrome.storage.local.set({[mn]:e})}async function Yu(e){let t;try{t=await Ku()}catch(h){return St.warn("failed to read alert config",h),{fired:!1,reason:"config_read_failed"}}if(!t.emailEnabled&&!t.webhookEnabled)return{fired:!1,reason:"no_channel_enabled"};const a=await le(),n=ce(a,"emailAlerts"),o=ce(a,"webhookAlerts"),r=t.emailEnabled&&n,i=t.webhookEnabled&&o;if(!r&&!i)return{fired:!1,reason:"no_channel_enabled"};const c=[];for(const h of e.results){const f=h.delta;f!=null&&f.new&&c.push(...f.new)}const d=Bt[t.minImpact],l=c.filter(h=>Bt[h.impact]>=d);if(l.length===0)return{fired:!1,reason:"below_impact_threshold"};const p=await pa();if(!p)return St.debug("no license token; alert skipped"),{fired:!1,reason:"no_license_token"};l.sort((h,f)=>Bt[f.impact]-Bt[h.impact]);const m=l.slice(0,10).map(h=>({wcag:h.wcagCriterion,impact:h.impact,selector:h.target.selector,help:h.description.slice(0,480),helpUrl:h.helpUrl})),u=e.results.reduce((h,f)=>h+f.violations.length,0),g={token:p,scheduleId:e.scheduleId,auditedUrl:e.auditedUrl,totalViolations:u,newViolationCount:l.length,topViolations:m,webhookUrl:i&&t.webhookUrl?t.webhookUrl:void 0,emailEnabled:r,ranAt:e.ranAt};try{const h=await fetch(Bu,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(g),signal:AbortSignal.timeout(qu)}),f=await h.json().catch(()=>null);return h.ok?(St.info("alert delivered",{emailSent:f==null?void 0:f.emailSent,webhookSent:f==null?void 0:f.webhookSent}),{fired:!0,emailSent:(f==null?void 0:f.emailSent)??!1,webhookSent:(f==null?void 0:f.webhookSent)??!1,serverStatus:h.status}):(St.warn(`alert server returned ${h.status}`,f),{fired:!1,reason:`server_${h.status}`,serverStatus:h.status})}catch(h){return St.warn("alert send failed",h),{fired:!1,reason:h instanceof Error?h.message:"unknown_error"}}}const Ju="#dc2626",Xu="#f59e0b",Qu="#059669",Zu="#94a3b8";function eh(e){const t=e.filter(c=>c.enabled);let a=0,n=0,o=0,r=0;for(const c of t){const d=c.lastNewViolationCount??0;c.lastResult==="failed"?n++:c.lastResult==="ok"?d>0?a+=d:o++:r++}let i;return t.length===0?i="no-schedules":a>0?i="new-violations":n>0?i="failed":o>0?i="all-clear":i="pending-first-run",{state:i,newViolationsTotal:a,failingCount:n,okCount:o,pendingCount:r,totalEnabled:t.length}}async function th(e){switch(e.state){case"new-violations":{const t=e.newViolationsTotal>99?"99+":String(e.newViolationsTotal);await chrome.action.setBadgeText({text:t}),await chrome.action.setBadgeBackgroundColor({color:Ju}),await chrome.action.setTitle({title:`wcagcheckr — ${e.newViolationsTotal} new WCAG violation${e.newViolationsTotal===1?"":"s"} vs baseline across ${e.totalEnabled} scheduled audit${e.totalEnabled===1?"":"s"}`});break}case"failed":{await chrome.action.setBadgeText({text:"!"}),await chrome.action.setBadgeBackgroundColor({color:Xu}),await chrome.action.setTitle({title:`wcagcheckr — ${e.failingCount} scheduled audit${e.failingCount===1?"":"s"} failed to run`});break}case"all-clear":{await chrome.action.setBadgeText({text:"✓"}),await chrome.action.setBadgeBackgroundColor({color:Qu}),await chrome.action.setTitle({title:`wcagcheckr — all ${e.okCount} scheduled audit${e.okCount===1?"":"s"} passing`});break}case"pending-first-run":{await chrome.action.setBadgeText({text:""}),await chrome.action.setBadgeBackgroundColor({color:Zu}),await chrome.action.setTitle({title:`wcagcheckr — ${e.pendingCount} scheduled audit${e.pendingCount===1?"":"s"} awaiting first run`});break}case"no-schedules":default:{await chrome.action.setBadgeText({text:""}),await chrome.action.setTitle({title:"wcagcheckr"});break}}}async function ah(){const e=await le();if(!ce(e,"statusBadge")){try{await chrome.action.setBadgeText({text:""})}catch{}return{state:"no-schedules",newViolationsTotal:0,failingCount:0,okCount:0,pendingCount:0,totalEnabled:0}}const t=await or(),a=eh(t);try{await th(a)}catch(n){console.warn("[status-badge] failed to apply",n)}return a}const Uo=Z("deposition-packet");function J(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}function nh(e,t,a){if(!e)return`<section class="dp-cover dp-cover-error">
|
|
992
992
|
<h2 style="margin:0 0 8pt 0; color:#b91c1c">⚠ Chain-of-Custody: UNAVAILABLE</h2>
|
|
993
993
|
<p>This packet could not be sealed because the underlying audit record was empty. Provenance cannot be verified.</p>
|
|
994
|
-
</section>`;const n=
|
|
994
|
+
</section>`;const n=qr(e.hash);return t?`<section class="dp-cover dp-cover-sealed" style="border:2pt solid #059669; background:#ecfdf5; padding:14pt; border-radius:4pt; margin:0 0 14pt 0">
|
|
995
995
|
<h2 style="margin:0 0 8pt 0; color:#064e3b">🔒 Chain-of-Custody: SEALED (RFC 3161 + ed25519)</h2>
|
|
996
996
|
<p style="margin:0 0 10pt 0">This audit's content hash was independently witnessed by a public RFC 3161 timestamp authority and counter-signed by the wcagcheckr server. The seal can be verified offline using only published keys.</p>
|
|
997
997
|
<dl style="margin:0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size:9pt">
|
|
@@ -1028,7 +1028,7 @@ Primary AI verdict was uncertain. Programmatic multi-column detector found no ma
|
|
|
1028
1028
|
<dd style="margin:0 0 6pt 16pt">${J(e.grade)}</dd>
|
|
1029
1029
|
</dl>
|
|
1030
1030
|
<p style="margin:8pt 0 0 0; font-size:10pt"><strong>To convert to anchored status:</strong> re-run this export with an online connection to api.wcagcheckr.com.</p>
|
|
1031
|
-
</section>`}function oh(e,t){var n;if(!e)return"";const a=
|
|
1031
|
+
</section>`}function oh(e,t){var n;if(!e)return"";const a=qr(e.hash);return`<section class="dp-verification" style="border:1pt solid #cbd5e1; padding:14pt; margin:24pt 0; border-radius:4pt; background:#f8fafc">
|
|
1032
1032
|
<h2 style="margin:0 0 8pt 0">How to verify this audit's integrity</h2>
|
|
1033
1033
|
<p style="margin:0 0 8pt 0">This packet is the output of an automated WCAG accessibility audit conducted by the wcagcheckr Chrome extension. The audit's content has been cryptographically sealed so any modification to this document — including changes to the violation list, the screenshots, or the grade — would invalidate the seal.</p>
|
|
1034
1034
|
|
|
@@ -1080,7 +1080,7 @@ Primary AI verdict was uncertain. Programmatic multi-column detector found no ma
|
|
|
1080
1080
|
<h2>Raw axe-core JSON (machine-readable)</h2>
|
|
1081
1081
|
<p>The block below is the unfiltered axe-core output included for machine re-verification. View HTML source to extract.</p>
|
|
1082
1082
|
<pre id="wcagcheckr-axe-raw-json" data-product="wcagcheckr" data-format="deposition-packet-v1">${J(JSON.stringify(t,null,2))}</pre>
|
|
1083
|
-
</section>`}async function sh(e,t,a,n,o,r){if(e.length===0)return null;const i=e[0],d=(await
|
|
1083
|
+
</section>`}async function sh(e,t,a,n,o,r){if(e.length===0)return null;const i=e[0],d=(await el(i.componentId)).find(u=>u.capturedAt===i.startedAt);if(d)return d;const l=e.flatMap(u=>u.violations),p=et(l,{runs:[],workflows:De},e,n,t,a,o,ft(e),r),m=e.reduce((u,g)=>u+g.durationMs,0);return jr(e,{...p.score,letter:p.combined},m)}async function Mi(e){let t;try{t=await sh(e.results,e.interactiveAuditResults,e.walkthroughAcks,e.acknowledgedMatchKeys,e.humanCriterionVerdicts,e.incompleteResolutions)}catch(f){Uo.warn("ensureForensicEntry failed",f),t=null}let a;if(t&&!t.receipt)try{const f=await bi(t,e.licenseId);f?(await Hr(t.componentId,t.capturedAt,f),t={...t,receipt:f}):a="server returned null or malformed receipt"}catch(f){a=f instanceof Error?f.message:String(f),Uo.warn("anchor failed",f)}const n=await Tt().catch(()=>[]),o=Ft(e.results,e.delta,"defense",e.manualRuns,n,e.aiSummary??void 0,e.wallClockMs,e.interactiveAuditResults,e.walkthroughAcks,e.acknowledgedMatchKeys,e.humanCriterionVerdicts,e.incompleteResolutions),r=await Dn(new Date().toISOString()).catch(()=>{}),i=nh(t,(t==null?void 0:t.receipt)??null,a)+`<section class="dp-grade-scope" style="border:1pt solid #cbd5e1; background:#f8fafc; padding:12pt; border-radius:4pt; margin:0 0 14pt 0"><h3 style="margin:0 0 6pt 0; font-size:11pt;">About this grade — basis and limits</h3><p style="margin:0 0 6pt 0; font-size:9.5pt; line-height:1.5; color:#334155">${J(_r)}</p><p style="margin:0; font-size:9.5pt; line-height:1.5; color:#334155"><strong>Audit rigor:</strong> ${J(ei(r))}`+(()=>{const f=ti(r);return f?` <strong style="color:#b45309">[${J(f)}]</strong>`:' <strong style="color:#047857">[Full 2.1 AA audit — settings cleared the conformance bar.]</strong>'})()+"</p></section>",c=oh(t,(t==null?void 0:t.receipt)??null),d=rh(t),l=ih(e.results);let p=o;const m=p.indexOf("<h1");m>-1?p=p.slice(0,m)+i+p.slice(m):p=i+p;const u=p.lastIndexOf("</main>"),g=u>-1?u:p.lastIndexOf("</body>"),h=c+d+l;return p=g>-1?p.slice(0,g)+h+p.slice(g):p+h,{html:p,entry:t,anchored:!!(t!=null&&t.receipt),anchorError:a}}function xe(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t])}function ch(e){const t=new Set;for(const a of e){const n=a.axeRulesEvaluated;if(n){for(const o of n.passed??[])t.add(o.ruleId);for(const o of n.inapplicable??[])t.add(o.ruleId);for(const o of n.incomplete??[])t.add(o.ruleId);for(const o of a.violations)t.add(o.ruleId)}}return t}const lh=new Set(["1.1.1","1.2.2","1.3.1","1.3.4","1.3.5","1.4.1","1.4.2","1.4.3","1.4.4","2.1.1","2.2.2","2.4.1","2.4.2","2.4.4","2.4.6","2.5.3","3.1.1","3.1.2","3.2.2","3.3.1","3.3.2","4.1.1","4.1.2","4.1.3"]);function dh(e,t,a,n){return`<section>
|
|
1084
1084
|
<h2>1. Tool identification</h2>
|
|
1085
1085
|
<table style="border-collapse:collapse; margin-top:8pt; font-size:10pt">
|
|
1086
1086
|
<tr><td style="padding:2pt 12pt 2pt 0; color:#475569">Tool</td><td><strong>wcagcheckr</strong> — Chrome MV3 accessibility audit extension</td></tr>
|
|
@@ -1670,7 +1670,7 @@ ${t.length===0?"":`<h3 style="margin-top:12pt">Human verified (${t.length})</h3>
|
|
|
1670
1670
|
Generated by ${ke} v${Ee()}. Conformance assessed against all violations detected during the audit (not delta-filtered).
|
|
1671
1671
|
</footer>
|
|
1672
1672
|
</body>
|
|
1673
|
-
</html>`)}function Bh(e){const t=[],a=e.target.outerHTML;if(e.ruleId==="image-alt"||e.ruleId==="input-image-alt"||e.ruleId==="area-alt"||e.ruleId==="svg-img-alt"||e.ruleId==="object-alt"){const n=/\bsrc\s*=\s*"([^"]+)"|\bsrc\s*=\s*'([^']+)'/i.exec(a);n&&t.push({label:"Image source",value:`\`${n[1]??n[2]}\` — if you have vision capability, view this image and propose alt text. Use \`alt=""\` if it's decorative.`});const o=/<title>([^<]+)<\/title>/i.exec(a);o&&t.push({label:"Inline SVG <title>",value:o[1]})}if(e.ruleId==="button-name"||e.ruleId==="empty-button"||e.ruleId==="input-button-name"){const n=/\baria-label\s*=\s*"([^"]+)"/i.exec(a);n&&t.push({label:"Existing aria-label",value:n[1]});const o=/<svg[^>]*>[\s\S]*?<title>([^<]+)<\/title>/i.exec(a);o&&t.push({label:"SVG <title> inside",value:o[1]});const r=/<(?:i|svg|span)[^>]*class\s*=\s*"([^"]*(?:icon|fa-|material-icon)[^"]*)"/i.exec(a);r&&t.push({label:"Icon class hint",value:`\`${r[1]}\` — name suggests intent`})}if(e.ruleId==="link-name"||e.ruleId==="empty-link"){const n=/\bhref\s*=\s*"([^"]+)"|\bhref\s*=\s*'([^']+)'/i.exec(a);n&&t.push({label:"Link href",value:`\`${n[1]??n[2]}\` — destination may suggest the right link text`});const o=/\baria-label\s*=\s*"([^"]+)"/i.exec(a);o&&t.push({label:"Existing aria-label",value:o[1]})}if(e.ruleId==="label"||e.ruleId==="select-name"||e.ruleId==="aria-input-field-name"){const n=/\bname\s*=\s*"([^"]+)"|\bname\s*=\s*'([^']+)'/i.exec(a);n&&t.push({label:"Field name attribute",value:`\`${n[1]??n[2]}\` — often hints at the intended label`});const o=/\bplaceholder\s*=\s*"([^"]+)"|\bplaceholder\s*=\s*'([^']+)'/i.exec(a);o&&t.push({label:"Placeholder text (visible)",value:o[1]??o[2]});const r=/\btype\s*=\s*"([^"]+)"|\btype\s*=\s*'([^']+)'/i.exec(a);r&&t.push({label:"Input type",value:`\`${r[1]??r[2]}\``});const i=/\bautocomplete\s*=\s*"([^"]+)"|\bautocomplete\s*=\s*'([^']+)'/i.exec(a);i&&t.push({label:"Autocomplete",value:i[1]??i[2]})}return t}function qh(e,t){var i;const a=new Set((t??[]).filter(c=>c.verdict==="pass"||c.verdict==="fail").map(c=>`${c.pageUrl}::${c.ruleId}::${c.selector}`)),n=new Map;for(const c of t??[])c.verdict==="uncertain"&&n.set(`${c.pageUrl}::${c.ruleId}::${c.selector}`,c.reasoning);const o=new Set,r=[];for(const c of e){const d=c.pageUrl??c.scope,l=((i=c.axeRulesEvaluated)==null?void 0:i.incomplete)??[];for(const p of l)for(const m of p.elements??[]){const u=`${d}::${p.ruleId}::${m.selector}`;o.has(u)||a.has(u)||(o.add(u),r.push({pageUrl:d,ruleId:p.ruleId,wcagCriterion:p.wcagCriterion,selector:m.selector,failureSummary:m.failureSummary,styles:m.styles,aiAttemptReasoning:n.get(u)}))}}return r}function Bi(e,t,a,n,o,r,i,c,d){var z,X,ge,ue,we,ve,Ie,Be,qe,$e,ie;const l=new Set(o??[]),p=new Set(d??[]),m=((z=e[0])==null?void 0:z.componentId)??"unknown",u=He(e),g=!!(t&&t.baselineSnapshotMeta),h=e.flatMap(y=>y.violations),f=p.size>0?h.filter(y=>p.has(y.matchKey)):[],w=y=>l.has(`violation::${y}`),b=h.filter(y=>!p.has(y.matchKey)&&!w(y.matchKey)),k=new Map;for(const y of b){const D=y.ruleId.startsWith("ai-")?`${y.ruleId}::${y.target.selector}::${(y.target.outerHTML??"").slice(0,200)}`:Ne(y.ruleId,y.target.selector),R=`${y.currentState.pseudoState} · ${y.currentState.theme} · ${y.currentState.direction} · ${y.currentState.breakpoint.id}`,G=k.get(D);if(G){G._states.includes(R)||G._states.push(R),G._instanceSelectors.includes(y.target.selector)||G._instanceSelectors.push(y.target.selector);continue}k.set(D,{...y,_states:[R],_instanceSelectors:[y.target.selector]})}const v=Array.from(k.values()).sort((y,D)=>{const R={critical:0,serious:1,moderate:2,minor:3};return(R[y.impact]??99)-(R[D.impact]??99)}),s=[];s.push("# Accessibility Fix Request"),s.push(""),s.push("> **Your job is to APPLY the fixes in this report — not to evaluate, second-guess, or re-litigate whether the findings are correct.** This report is the output of a real automated audit (axe-core run in a live browser across many UI states) plus AI verification; the triage already happened upstream. Do not debate severity or skip items you personally doubt. Read each finding, apply the canonical fix, and report the outcome per the Work contract below. A genuine blocker or a real user-decision is the only acceptable non-fix — and those are reported explicitly per the contract, never silently dropped."),s.push(""),s.push(`You are a senior accessibility engineer pairing with the user on their codebase. The component below has ${v.length} unique WCAG violation${v.length===1?"":"s"} detected by an automated audit running axe-core ${((X=e[0])==null?void 0:X.axeVersion)??"4.x"} across ${e.length} state combinations (hover, focus, focus-visible, active, disabled × dark / forced-colors / RTL / breakpoints). Your task: locate each violation in the codebase and apply the canonical fix — carefully, atomically, and asking before guessing.`),s.push(""),s.push("## [!] Work contract — read before doing anything else"),s.push(""),s.push(`**You will receive ${v.length} numbered finding${v.length===1?"":"s"} below.** Each one needs an outcome BEFORE you report completion. Do not stop early. Do not batch by impact level. Do not declare done without addressing every numbered item.`),s.push(""),s.push("**For each numbered finding (1 through "+v.length+"), produce one of:**"),s.push(""),s.push("- [DONE] **Applied** — the canonical fix from this prompt was applied to the source. Cite the file + line."),s.push("- [BLOCKED] **Blocked** — explicitly state WHY you cannot apply the fix (missing info, ambiguous user content, dependency change required, etc.). One sentence per blocked finding."),s.push("- [NEEDS-DECISION] **Needs user decision** — you found options but need the user to pick. List the options inline."),s.push(""),s.push(`**The single answer you cannot use: "skipped" or "didn't get to it."** If you ran out of context or time, stop and tell the user — do not silently omit items.`),s.push(""),s.push("**Required final response format** — at the end of your work, produce a completion table:"),s.push(""),s.push("```"),s.push("| # | Rule ID | Status | Notes |"),s.push("|---|---------|--------|-------|"),s.push("| 1 | <rule> | DONE / BLOCKED / NEEDS-DECISION | <file:line OR reason> |"),s.push("| 2 | ... | ... | ... |"),s.push(`| ${v.length} | ... | ... | ... |`),s.push("```"),s.push(""),s.push("**Every row must be present.** If status is BLOCKED or NEEDS-DECISION, the Notes column explains specifically what the blocker is so the user can address it on the next pass. The point is that ONE pass through this prompt should produce either a fix or a clear next action for every finding — not a partial sweep that leaves the user re-running the loop."),s.push(""),s.push("---"),s.push("");const x=a&&a.length>0?{runs:a,workflows:De}:void 0,$=c==null?void 0:c.map(y=>({criterionId:y.criterionId,ruleId:`ai-interactive::${y.criterionId}`,pageUrl:y.pageUrl,verdict:y.verdict,reasoning:y.reasoning})),A=et(h,x,e,p,$,void 0,void 0,ft(e),r),S=A.score;if(s.push("## Context"),s.push(""),s.push(`- **Component / scope:** \`${m}\``),s.push(`- **Audited URL:** ${u}`),s.push(`- **WCAG target:** WCAG ${Mt} ${wn} (this audit's goal; the receiving fix should bring the page into compliance with this level)`),s.push("- **Source filter:** Full audit — every detected violation"),s.push(`- **Compliance posture:** Grade **${A.combined}** · **${S.risk.toUpperCase()} risk** (${S.totals.critical} critical · ${S.totals.serious} serious · ${S.totals.moderate} moderate · ${S.totals.minor} minor). ${S.risk==="critical"||S.risk==="high"?"Treat as triage priority — these violations represent meaningful lawsuit exposure under ADA Title III, EAA, and EN 301 549.":S.risk==="moderate"?"Should be addressed in the current sprint cycle; not lawsuit-emergency but real exposure.":"Lower exposure; still worth fixing but not blocking other work."}`),S.coverage){const y=S.coverage.untestedCriteria.length;y>0?s.push(`- **WCAG ${S.coverage.targetVersion} ${S.coverage.targetLevel} coverage:** ${S.coverage.evaluated} of ${S.coverage.totalApplicable} criteria evaluated. **Cannot claim ${S.coverage.targetLevel} conformance** until ${y} untested criteria are evaluated via Guided Tests (Pass / Fail / N/A). Untested: ${S.coverage.untestedCriteria.join(", ")}.`):s.push(`- **WCAG ${S.coverage.targetVersion} ${S.coverage.targetLevel} coverage:** ${S.coverage.evaluated} of ${S.coverage.totalApplicable} criteria evaluated. Full coverage — defensible conformance claim possible once all violations below are fixed.`)}g&&t&&s.push(`- **Trend vs your saved baseline:** ${t.newCount} new · ${t.persistentCount} carried over · ${t.fixedCount} resolved. The fix list below is the FULL current violation set, not just the delta.`),s.push(""),s.push("## Before you start (safety)"),s.push(""),s.push("Do these in order before making any code changes:"),s.push(""),s.push("1. **Verify a clean git working tree** (`git status`). If there are uncommitted changes, ask the user whether to commit, stash, or proceed."),s.push("2. **Switch to a feature branch** dedicated to this work — e.g., `a11y/fix-component-name`. Don't commit directly to `main` / `master` / `develop`."),s.push("3. **Plan one commit per fix.** Each violation gets its own atomic commit so any individual change can be reverted without untangling the rest. Commit messages should follow the pattern: `a11y: fix <ruleId> on <selector or component>`."),s.push("4. **Show diffs before applying** when your tooling supports it (Cursor / Copilot Chat agents). Never silently overwrite files."),s.push("5. **Do not bump dependencies** to resolve accessibility issues — fixes should be code changes, not package upgrades."),s.push(""),s.push("## Ask me these questions first"),s.push(""),s.push("Before applying any fix, ask the user the questions below. Don't proceed until you have answers — guessing here produces fixes that don't match the codebase's conventions and will be rejected on review."),s.push(""),s.push("1. **Framework + style approach?** React / Vue / Svelte / Angular / Solid / vanilla — and CSS Modules / Tailwind / styled-components / Emotion / scoped `<style>` / global CSS / design-tokens system. This determines *how* every fix is written."),s.push("2. **Existing design tokens or raw values?** If the codebase uses `var(--color-text-on-surface)` or a `tokens.ts` / `theme.json`, fixes must use tokens — never introduce new hex codes. Where can you read the available tokens?"),s.push('3. **Existing utility classes or component library?** e.g., does `.text-slate-700` or `<Button variant="primary">` already exist? Use what\'s there before inventing new selectors or components.'),s.push("4. **Files or directories that are off-limits?** Generated code, vendored libraries, third-party widgets, lockfiles — confirm what you must NOT touch."),s.push("5. **For color-contrast fixes: prefer adjusting foreground or background?** Brand color preservation is usually a stakeholder call, not a developer call."),s.push("6. **How should I verify fixes are done?** Re-run this auditor (preferred), run the existing test suite, manual screen-reader pass, or all three?"),s.push(""),s.push("## Decision rules (when ambiguity surfaces during fixes)"),s.push(""),s.push('- **Native HTML beats ARIA.** First rule of ARIA: don\'t use ARIA when a native element works. Prefer `<button>` over `<div role="button">`, `<nav>` over `<div role="navigation">`, etc.'),s.push("- **State-specific failures get state-specific fixes.** A `:hover`-only contrast failure goes in the `:hover` rule, not the default selector."),s.push("- **Where multiple valid fixes exist, surface options — don't pick silently.** E.g., adding an accessible name can be done via visible text, `aria-label`, or `aria-labelledby` referencing existing copy. Present the options with tradeoffs and let the user choose."),s.push("- **When the fix would alter visible design** (color shifts, layout changes, content additions), pause and confirm with the user before applying."),s.push(""),s.push("## [STOP] Do not dismiss findings without empirical verification"),s.push(""),s.push('**Default stance: every finding in this report is real and needs a fix.** Your job is to apply fixes, not to triage which ones "probably aren\'t real." That triage already happened — the auditor ran on a real browser, captured real DOM, real `getBoundingClientRect()` coordinates, real WCAG rule failures from axe-core. You are downstream of all that.'),s.push(""),s.push('Some categories (reading-order, tab-order) are explicitly heuristic and DO have legitimate false-positive cases. Those cases are listed inline in each section. **But "this looks like it might be a false positive based on the pattern" is not verification.** Pattern-matching from the report alone is inference, not evidence.'),s.push(""),s.push("**To dismiss any finding as a false positive, you must do all of the following:**"),s.push(""),s.push("1. **Load the deployed page in a real browser** at the viewport listed in the finding's capture-state header (e.g., 1280×800 LTR for the position-based analyses)."),s.push("2. **Inspect the flagged elements directly** — DevTools, real `getBoundingClientRect()` coordinates, real DOM source order."),s.push("3. **Compare your empirical observation to the auditor's claim.** Does the visual layout actually contradict the DOM order, or not?"),s.push("4. **If you conclude false positive, document why** — include the empirical evidence (coordinates, screenshots, DOM excerpt) so the user can audit your reasoning."),s.push(`5. **Even after empirical verification, ASK THE USER before dismissing.** They have context you don't (e.g., "yes that's an intentional skip-link / multi-column layout / RTL-only behavior").`),s.push(""),s.push("**What does NOT count as verification:**"),s.push(""),s.push("- Looking at the CSS file and not seeing `flex-direction: row-reverse` (the layout could be from `order:`, `grid-area`, absolute positioning, or RTL emulation)"),s.push('- Pattern-matching the finding shape ("this looks like a perfect arithmetic flip therefore it must be an RTL artifact")'),s.push(`- Reading the auditor's false-positive list and concluding your case "looks like one of those"`),s.push(`- Deciding it's "probably fine" because the structure looks reasonable on a quick read`),s.push(""),s.push("**If you cannot run a real browser** (e.g., environment doesn't allow headless Chromium), then you cannot verify, which means you cannot dismiss. In that case: surface the finding to the user, propose the fix, and let them decide."),s.push(""),s.push("**Skipping a finding without verification is worse than fixing one that turns out to be a false positive.** Over-fix is reversible (delete the commit). Under-fix is silent — the user thinks they're compliant when they aren't."),s.push(""),s.push("**When you DO verify a finding is a false positive** (per the empirical rules above), the right action is to recommend the user click **dismiss** on that finding in the auditor's Activity tab. The auditor records the dismissal with your reason, suppresses the finding from displayed counts, the Matrix banner, and future AI prompts. **Don't modify code to silence a verified false positive** — that produces brittle workarounds and pollutes the codebase with comments explaining heuristic quirks. Dismissals persist per-URL and survive re-audits cleanly."),s.push(""),s.push("## Content-decision rules (alt text, button labels, link text)"),s.push(""),s.push("Some violations require copy that the audit can't generate. Don't invent copy out of nowhere, but DON'T just punt to the user either — use the capabilities you have:"),s.push(""),s.push('1. **For images missing alt text** (`image-alt`, `input-image-alt`, `area-alt`, `svg-img-alt`): if you have vision capability, **fetch or view the image** at the `src` URL listed in the violation and PROPOSE descriptive alt text based on what the image actually contains. Mark obviously-decorative images (icons inside buttons that already have text labels, dividers, background flourishes) for `alt=""`.'),s.push('2. **For unlabeled buttons / links** (`button-name`, `link-name`, `empty-button`, `empty-link`): look at the surrounding code context — nearby text, the button\'s icon (often a known SVG name like "close" / "search" / "menu"), the route the link points at — and PROPOSE an accessible name. Show your inference reasoning.'),s.push("3. **For unlabeled form fields** (`label`, `select-name`, `aria-input-field-name`): inspect the field's `name`, `placeholder`, `id`, and any nearby visible text in the parent container. PROPOSE a label."),s.push("4. **For ambiguous content** (alt text where you genuinely can't tell what an image shows; labels where there's no surrounding context), ask the user — don't guess."),s.push("5. **Always show your proposed copy before applying** — let the user approve, edit, or reject."),s.push(""),s.push("## [VERIFY] Verify with Playwright before asking the user"),s.push(""),s.push(`Many WCAG criteria that look like "needs human verification" are actually scriptable — you can write a short Playwright test, run it, observe the result, and resolve the question without bothering the user. **Default to this path.** Only ask the user when Playwright can't reach the answer (subjective copy decisions, content judgment, business-context questions).`),s.push(""),s.push("### Step 1: Check if Playwright is available"),s.push(""),s.push('Look in the project root for `playwright.config.ts` / `playwright.config.js` / a `tests/` or `e2e/` directory using `@playwright/test`. If yes — use it directly. If no — ask the user once: "Want me to add Playwright (`npm install -D @playwright/test && npx playwright install`) so I can verify a few WCAG criteria automatically?" If they decline, fall back to asking per-item.'),s.push(""),s.push("### Step 2: Run targeted smoke tests instead of asking"),s.push(""),s.push("Here are the manual-checklist items below that Playwright CAN verify on the CURRENT PAGE (no multi-page setup needed). For each, write the test, run it, and only escalate to the user if the test result is ambiguous:"),s.push(""),s.push("**Scope disambiguation:** the criteria below are all **single-page** — they describe behavior of the current page. They are NOT multi-page concerns. Cross-page criteria (3.2.3 Consistent Navigation, 3.2.4 Consistent Identification, 3.2.6 Consistent Help) require comparing multiple pages and are handled by wcagcheckr's Site Crawl flow, NOT by Playwright tests here. Don't skip 3.2.1 / 3.2.2 thinking they're multi-page — they're single-page and Playwright-testable."),s.push(""),s.push("| Criterion | Scope | Playwright test |"),s.push("|---|---|---|"),s.push('| **1.4.4 Resize Text (AA)** | single-page | `await page.evaluate(() => document.body.style.zoom = "200%"); await page.screenshot(...); assert no horizontal scroll on critical content blocks` |'),s.push("| **1.4.10 Reflow (AA)** | single-page | `await page.setViewportSize({ width: 320, height: 800 }); await expect(page).toHaveScreenshot(...); assert documentElement.scrollWidth ≤ clientWidth + 10` |"),s.push("| **1.4.12 Text Spacing (AA)** | single-page | inject `* { line-height: 1.5 !important; letter-spacing: 0.12em !important; word-spacing: 0.16em !important; }` via `addStyleTag`, screenshot, check no clipping/overlap |"),s.push('| **1.4.13 Content on Hover or Focus (AA)** | single-page | hover each tooltip-bearing element, then `await page.keyboard.press("Escape")` — verify tooltip closes (dismissible); move mouse INTO tooltip, verify it stays (hoverable); wait 5s without interaction, verify it stays (persistent) |'),s.push("| **2.1.4 Character Key Shortcuts (A)** | single-page | enumerate document keydown handlers; press each single-key shortcut + verify it only fires when expected (focus-dependent or toggleable) |"),s.push("| **2.2.1 Timing Adjustable (A)** | single-page | scan code for setTimeout/setInterval with user-facing impact; assert each has a pause/extend/disable affordance |"),s.push("| **2.2.2 Pause, Stop, Hide (A)** | single-page | find elements with CSS animation / transition / autoplay video; assert each has a visible pause/stop control |"),s.push('| **2.4.5 Multiple Ways (AA)** | single-page | `await page.locator("nav").count() + page.locator("[role=search]").count() + page.locator("a[href*=sitemap]").count() ≥ 2` — checks the current page exposes at least two ways to reach other pages (nav + search, or nav + sitemap link, etc.). |'),s.push("| **2.5.2 Pointer Cancellation (A)** | single-page | for each `<button>` / `[role=button]`: `page.mouse.down()` on it, `page.mouse.move(elsewhere)`, `page.mouse.up()` — assert no action fired (no URL change, no submitted form, no state mutation) |"),s.push("| **3.2.1 On Focus (A)** | **single-page (per-page test, NOT multi-page)** | iterate every focusable on the current page; `.focus()` on each; assert URL unchanged + no modal opened + no form submitted + no scroll jump. This is about each FOCUS event on this page, not about page-to-page navigation. |"),s.push('| **3.2.2 On Input (A)** | **single-page (per-page test, NOT multi-page)** | for each `<select>` / `<input>` / `<textarea>` / `[contenteditable]` on the current page: change value via `.fill()` or `.selectOption()`; assert no URL change, no auto-submit, no scroll jump UNLESS the form clearly warns ("submits on change" UI affordance). |'),s.push('| **4.1.3 Status Messages (AA)** | single-page | trigger a form-validation error or async update; assert the live region announces (`role="status"` or `role="alert"` exists + has text content) |'),s.push(""),s.push("### Step 3: Vision-capable items"),s.push(""),s.push('For items where the test result is "a screenshot to evaluate":'),s.push("- **Color contrast on a gradient/image background:** Playwright screenshot of the element + 50px context, sample foreground + background pixels at the text's bounding box, compute WCAG contrast ratio, compare to 4.5:1 (normal) or 3:1 (large)."),s.push('- **Alt text accuracy:** fetch the `src=` URL, view the image, propose alt text. If the image is a logo/wordmark/decorative, recommend `alt=""`.'),s.push("- **1.4.5 Images of Text:** screenshot the hero / heading / banner; check whether typography is rendered as text (selectable + DOM-visible) or as a raster image."),s.push(""),s.push("### Step 4: When to fall back to asking the user"),s.push(""),s.push("Ask only when:"),s.push(`1. The verification requires subjective judgment Playwright can't make (e.g., "is this alt text accurate to the brand's voice?")`),s.push('2. The criterion needs production-data context (e.g., "do users hit this timeout in practice?")'),s.push("3. The Playwright test itself is ambiguous and the user has context that resolves it"),s.push(""),s.push('**Report Playwright results inline with your fix proposals.** "I ran a smoke test for 1.4.10 at 320px — no horizontal scroll detected; checking this off" beats "Could you verify 1.4.10 manually?" every time.'),s.push(""),s.push("## Determinism + reproducibility"),s.push(""),s.push("**AI judgments are not reproducible by default.** Anthropic, OpenAI, and most LLM APIs run at non-zero sampling temperature unless you explicitly request otherwise — meaning the same prompt + same context can produce different answers on different runs. wcagcheckr learned this the hard way (rc.190 → rc.192): consecutive audits on an unchanged page produced different verdicts because the audit's own AI calls ran at default temperature. The fix was `temperature: 0` on every judgment call."),s.push(""),s.push("You face the same risk for any decision you make based on AI judgment rather than empirical observation. Mitigate by:"),s.push(""),s.push('1. **Prefer empirical verification over AI judgment.** If a question can be answered by running Playwright, running axe-core, sampling pixel values, or grepping the codebase — do that, not "I think the screenshot shows…". The Playwright section above lists 11 criteria that can be verified deterministically.'),s.push("2. **For any auxiliary AI calls you make** (vision tasks, classification, summarization), pass `temperature: 0` (Anthropic / OpenAI) or `top_p: 0.01` (other providers). Same screenshot + same prompt should produce the same answer twice in a row — verify before trusting the answer."),s.push('3. **Document AI-judgment decisions in commit messages.** When you DO rely on AI for a subjective call ("I chose `aria-label=\\"Search the catalog\\"` over `\\"Search\\"` because…"), record the reasoning in the commit so the next session — yours or a human\'s — can audit the call.'),s.push("4. **Treat any single AI verdict as one data point, not gospel.** If you ask AI the same question twice and get different answers, that's the variance showing. Stick with the FIRST answer you committed to OR cross-check with Playwright; don't flip-flop based on subsequent re-rolls."),s.push("5. **Re-run the auditor (`wcagcheckr`) after every commit** rather than asking AI \"is this fixed?\" — the auditor's output is the source of truth for whether a fix landed, not the fixer AI's self-assessment."),s.push(""),s.push("Sampling variance is the silent enemy of compliance automation. Treat it as a constraint to design around, not noise to ignore."),s.push(""),s.push("## Constraints"),s.push(""),s.push("- Don't fix violations that aren't in this list (no scope creep)."),s.push(`- Don't reformat or reorder unrelated code. No "while I'm here" cleanup.`),s.push(`- Match existing code style. Don't invoke Prettier / ESLint to "fix everything" — focus your diff strictly on the violation's element + relevant style rule.`),s.push("- Don't add new dependencies, configs, or build steps."),s.push("- Add inline comments only where the fix's rationale is non-obvious; otherwise skip comments."),s.push("- Preserve existing functionality and visual design wherever possible."),s.push(""),s.push("## How to use this prompt"),s.push(""),s.push("Paste this into Claude / Cursor / Copilot Chat with your codebase open. The AI should:"),s.push('1. **First**: walk through the "Before you start" + "Ask me these questions" sections with the user.'),s.push("2. **Then**: identify the source files containing each violating element (use the selectors + outerHTML below)."),s.push("3. **Then**: for each violation, propose the fix using the recipe + answers gathered, surface ambiguity per the decision rules, and apply only after the user confirms."),s.push("4. **Finally**: produce one atomic commit per fix, with a clear message."),s.push(""),s.push("## Locating findings in source"),s.push(""),s.push("For each finding below, the most reliable search strategies (try in this order):"),s.push(""),s.push('1. **The accessible name / text content** — works for JSX, Vue templates, Svelte, server-rendered HTML. Search for the visible text in `"quotes"` or as a string literal in component files.'),s.push("2. **A distinctive class name** — e.g., `.cta-primary` → grep `cta-primary` in CSS + component files. BEM-style and Tailwind utility classes are the most stable identifiers."),s.push('3. **A unique data attribute** — `data-testid="checkout-button"`, `data-qa=`, `data-cy=` — these are usually authored deliberately and survive minification.'),s.push("4. **A stable ID** — `#hero-search` → grep `hero-search`. Watch out for IDs that look generated (`#field_3f8a92`)."),s.push('5. **The outerHTML pattern** — fall back when class/id/testid don\'t help. Search for distinctive attribute combinations (e.g., `aria-label="…"` + `class="…"`).'),s.push(""),s.push("**Do not search on these — they're runtime artifacts:**"),s.push("- `:nth-child(N)`, `:nth-of-type(N)` — these are positional, never appear in source"),s.push("- Numeric suffixes that look generated (`-1234`, `__abc123`) — usually from CSS Modules / styled-components hashing"),s.push("- Selector paths with `>` chains — describes a DOM structure, not a source location"),s.push(""),s.push("**For pages built with a framework**, also check:"),s.push("- Storybook stories (`*.stories.tsx`, `*.stories.mdx`) — often the canonical component definition"),s.push("- Shared layout components (`Header.tsx`, `Footer.tsx`, `Layout.tsx`) — most page-level violations live here"),s.push("- Theme / token files (`theme.ts`, `tokens.json`) — color-contrast fixes often patch tokens, not consumers"),s.push(""),s.push("---"),s.push("");const F=(((ge=e[0])==null?void 0:ge.readingOrderIssues)??[]).filter(y=>!l.has(`reading-order::${y.selector}`)),T=(ue=e[0])==null?void 0:ue.positionAnalysisCapturedAt,I=T?`${T.breakpoint.width}×${T.breakpoint.height} (${T.breakpoint.label}), ${T.direction.toUpperCase()}, ${T.theme} theme, ${T.pseudoState} pseudo-state`:"(reference state unavailable — findings reflect last matrix state)",N=c==null?void 0:c.some(y=>y.criterionId==="1.3.2");if(F.length>0&&!N){s.push("## Reading-order concerns (DOM ≠ visual order)"),s.push(""),s.push(`_Positions captured at: **${I}**. Multi-column layouts, RTL pages, and mobile-stacked sections may have legitimately different reading orders at other viewports — verify each finding against the layout the user actually sees._`),s.push(""),s.push(`Screen readers read the DOM in source order. CSS that visually rearranges things (\`flex-direction: row-reverse\`, \`order:\`, \`grid-area\`, absolute positioning) does NOT reorder what the SR sees. ${F.length} element${F.length===1?" has":"s have"} a notable gap between their DOM position and their visual position:`),s.push(""),s.push("| DOM index | Visual index | Selector | Text |"),s.push("|---|---|---|---|");for(const y of F){const D=y.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");s.push(`| ${y.domIndex} | ${y.visualIndex} | \`${y.selector}\` | ${D} |`)}s.push(""),s.push("**How to fix:** match DOM order to intended reading order — reorder the JSX/HTML, NOT the CSS. CSS visual rearrangement is a code smell only because it diverges from DOM order; if the DOM order is what the user should hear, no fix is needed."),s.push(""),s.push("**False-positive cases — these are the ONLY legitimate reasons to skip a flagged element:**"),s.push(""),s.push('1. **Multi-column / newspaper layouts** — DOM order is "column 1 top to bottom, then column 2", which is correct for SR but reads as "out of order" to this analyzer.'),s.push('2. **Skip-links** ("Skip to main content") — visually positioned late but DOM-early on purpose.'),s.push("3. **Caption-first patterns** — captions placed before their image in DOM so SR users get context before the alt text."),s.push("4. **Modal / popover triggers** — the trigger button is DOM-near the modal's portal mount point, which is often `<body>` end."),s.push(""),s.push('**Per the verification rules above (§ "Do not dismiss findings without empirical verification"): even when a finding looks like one of these false-positive cases, you must verify in a real browser before skipping. "Looks like a multi-column layout" is inference, not proof.** Read the DOM. Get real coordinates. Then ask the user. Then skip if confirmed.'),s.push(""),s.push("Surface the candidates above to the user, ask which (if any) are intended, and only reorder the rest. **If in doubt, fix it.** Reordering JSX is reversible; failing a Level A criterion is not."),s.push(""),s.push("---"),s.push("")}const C=(((we=e[0])==null?void 0:we.tabOrderIssues)??[]).filter(y=>!l.has(`tab-order::${y.selector}`)),_=c==null?void 0:c.some(y=>y.criterionId==="2.1.2"&&y.verdict==="pass");if(C.length>0&&!_){const y=C.filter(R=>R.flag==="visual"||R.flag==="both").length,D=C.filter(R=>R.flag==="tabindex"||R.flag==="both").length;s.push("## Tab-order concerns (keyboard focus sequence ≠ visual order) — WCAG 2.4.3 Focus Order (A)"),s.push(""),s.push(`_Positions captured at: **${I}**. Tab-position-vs-visual-position divergence is direction- and breakpoint-sensitive — what looks wrong here may be correct on RTL or mobile (and vice versa). Verify each finding against the layout the user actually sees._`),s.push(""),s.push(`Keyboard users tab through the page in one order; sighted users scan in another. axe-core does not detect this. ${C.length} focusable element${C.length===1?" has":"s have"} a notable divergence. Of these: ${y} ${y===1?"has":"have"} a visual-vs-tab mismatch (focus jumps around the page); ${D} ${D===1?"is":"are"} caused by positive \`tabindex\` reordering DOM source.`),s.push(""),s.push("| Tab pos | Visual pos | DOM pos | Flag | Selector | Text |"),s.push("|---|---|---|---|---|---|");for(const R of C){const G=R.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");s.push(`| ${R.tabPosition} | ${R.visualPosition} | ${R.domPosition} | ${R.flag} | \`${R.selector}\` | ${G} |`)}s.push(""),s.push("**How to fix:**"),s.push(""),s.push("- **`flag: visual`** — tab order doesn't match visual layout. Reorder the JSX/HTML so the DOM source order matches the layout order users see. Don't use `tabindex` to \"patch\" this — that creates the next class of bug."),s.push('- **`flag: tabindex`** — a positive `tabindex` value (`tabindex="3"`) is reordering DOM. Remove the positive `tabindex` and rely on DOM source order. Use `tabindex="0"` only to make a non-focusable element focusable; use `tabindex="-1"` only to remove from the tab order.'),s.push("- **`flag: both`** — both conditions present. Fix the DOM order first, then remove the positive `tabindex`."),s.push(""),s.push("**False-positive cases — these are the ONLY legitimate reasons to skip a flagged element:**"),s.push(""),s.push('1. **Skip-links** ("Skip to main content") — visually-late, DOM-early on purpose. Their tab position should be near 1; their visual position is wherever their `:focus` styling places them.'),s.push("2. **Modal triggers + portal-rendered modals** — the trigger button is DOM-near the modal's portal mount point (often `<body>` end), so tab/visual positions can legitimately diverge."),s.push("3. **Dropdown menus / popovers** — a button DOM-far from its portal-rendered menu may produce false positives when the menu is open."),s.push(""),s.push(`**Per the verification rules above (§ "Do not dismiss findings without empirical verification"): even when a finding looks like one of these cases, you must verify in a real browser before skipping. Tabbing through the actual deployed page is empirical evidence; reading the auditor's table and pattern-matching is not.** If in doubt, fix it. Reordering JSX is reversible; failing a Level A criterion is not.`),s.push(""),s.push("Surface candidates to the user; ask which are intended; only fix the rest."),s.push(""),s.push("---"),s.push("")}const M=e.flatMap(y=>y.announcements??[]),W=e.flatMap(y=>y.focusEvents??[]).filter(y=>y.isFocusReset);if(M.length>0||W.length>0){if(s.push("## Runtime behavioral observations"),s.push(""),M.length>0){const y=M.filter(R=>R.politeness==="assertive").length;s.push(`**Live-region announcements** (${M.length} total, ${y} assertive): screen-reader announcements captured during state-driving. WCAG 4.1.3 Status Messages (AA) requires that non-essential changes use polite announcements; only blocking errors warrant assertive. Review:`),s.push("");const D=M.slice(0,8);for(const R of D){const G=R.text.replace(/\|/g,"\\|").replace(/\n/g," ").slice(0,120);s.push(`- \`[${R.politeness}${R.role?`/${R.role}`:""}]\` ${G}`)}M.length>8&&s.push(`- (and ${M.length-8} more)`),s.push(""),s.push("Verify each is appropriate. Common bug: error messages firing as `polite` (SR may not interrupt), or status updates firing as `assertive` (interrupts user mid-sentence)."),s.push("")}if(W.length>0){s.push(`**Focus resets to body** (${W.length} occurrence${W.length===1?"":"s"}): during the audit, focus jumped to \`<body>\` or \`<html>\` unexpectedly. This is almost always a bug — focus should move to a logical landing place (modal, alert, next field), never disappear.`),s.push("");for(const y of W.slice(0,5))s.push(`- \`${y.fromSelector??"(none)"}\` → \`${y.toSelector}\``);W.length>5&&s.push(`- (and ${W.length-5} more)`),s.push(""),s.push("Common cause: an element is removed from the DOM (e.g., modal closes) without `.focus()` being called on a sensible destination."),s.push("")}s.push("---"),s.push("")}const P=((ve=e[0])==null?void 0:ve.undefinedCustomProperties)??[];if(P.length>0){s.push("## Undefined CSS custom properties (cascade root cause)"),s.push(""),s.push(`${P.length} CSS custom propert${P.length===1?"y is":"ies are"} referenced via \`var(--name)\` but never declared. When a custom property is missing, the browser falls back to the property's initial value (often \`Canvas\` for color slots → resolves to white), which can produce a wave of false contrast failures. **Fix these missing tokens FIRST** — many of the contrast violations below may disappear once the cascade resolves correctly.`),s.push(""),s.push("| Custom property | References | First sample sites |"),s.push("|---|---|---|");for(const y of P){const D=y.sampleSites.map(R=>`\`${R.selector} { ${R.property} }\``).join("; ");s.push(`| \`${y.name}\` | ${y.referenceCount} | ${D} |`)}s.push(""),s.push("Each entry needs a definition in `:root` (or wherever the design-token layer lives). Surface to the user before applying contrast fixes — the missing token may be the root cause."),s.push(""),s.push("---"),s.push("")}if(a&&a.length>0){s.push("## Manual checks already completed (Guided Tests)"),s.push(""),s.push(`The consultant has run ${a.length} Intelligent Guided Test workflow${a.length===1?"":"s"} against this component covering the WCAG criteria automation cannot verify (keyboard navigation, screen-reader experience, focus management, forms, error prevention, etc.). Treat the verdicts below as ground truth — these came from a human running the actual flow.`),s.push(""),s.push("**Manual passes outrank heuristic findings.** When an IGT workflow has passed (e.g. Keyboard 7/7) and the heuristic analyzers below (Tab-order concerns, Reading-order concerns) flag findings on the same WCAG dimension, the manual pass is ground truth. A human ran real keyboard/SR navigation against the deployed page; the heuristic computes visual position from `getBoundingClientRect()` and can produce false positives on sticky / multi-column / grid layouts. **When the IGT and the heuristic disagree on the same WCAG criterion, treat the heuristic findings as suspect first**, run the empirical verification described above, and (if confirmed false positive) recommend dismissal via the Activity tab."),s.push("");const y=De;s.push("| Workflow | Status | Notes |"),s.push("|---|---|---|");for(const Y of a){const H=y.find(Pe=>Pe.id===Y.workflowId);if(!H)continue;let K=0,Q=0,ae=0,Re=0,ee=0;for(const Pe of H.steps){const Fe=Y.steps[Pe.id];if(!Fe){ee++;continue}Fe.status==="pass"?K++:Fe.status==="fail"?Q++:Fe.status==="skip"?ae++:Fe.status==="not-applicable"?Re++:ee++}const be=Q>0?`[FAIL] ${Q} failed`:ee>0?`[WARN] ${ee} unanswered`:`[PASS] all ${H.steps.length} answered`,Se=[`${K}p`,`${Q}f`];Re>0&&Se.push(`${Re} N/A`),ae>0&&Se.push(`${ae} skip`),ee>0&&Se.push(`${ee} unanswered`);const ne=`${Se.join(" · ")} of ${H.steps.length} steps`;s.push(`| ${H.name} | ${be} | ${ne} |`)}s.push("");const D=[];for(const Y of a){const H=y.find(K=>K.id===Y.workflowId);if(H)for(const K of H.steps){const Q=Y.steps[K.id];(Q==null?void 0:Q.status)==="fail"&&D.push({workflow:H.name,stepTitle:K.question,notes:Q.notes})}}if(D.length>0){s.push("**Failed manual checks** (these are real WCAG failures the consultant verified by hand):"),s.push("");for(const Y of D)s.push(`- **${Y.workflow} → ${Y.stepTitle}**${Y.notes?`: ${Y.notes}`:""}`);s.push(""),s.push("**Address these alongside the automated violations below.** They will not appear in the violations list because no automated rule catches them — but they are still real WCAG failures and must be fixed for compliance."),s.push("")}const R=new Set(a.map(Y=>Y.workflowId)),G=y.filter(Y=>!R.has(Y.id));G.length>0&&(s.push(`**Not yet manually verified** (${G.length} workflow${G.length===1?"":"s"}): `+G.map(Y=>Y.name).join(", ")+". These cover WCAG criteria automation cannot detect; ask the consultant whether to run them before declaring the audit complete."),s.push("")),s.push("---"),s.push("")}if(n&&n.pagesAudited>1){const y=(()=>{try{return new URL(u).origin}catch{return null}})(),D=(()=>{try{return new URL(n.startUrl).origin}catch{return null}})();if(y&&D&&y===D){const R=n.finishedAt,G=R?Date.now()-new Date(R).getTime():0,Y=G<6e4?"just now":G<36e5?`${Math.round(G/6e4)} minute${Math.round(G/6e4)===1?"":"s"} ago`:G<864e5?`${Math.round(G/36e5)} hour${Math.round(G/36e5)===1?"":"s"} ago`:`${Math.round(G/864e5)} day${Math.round(G/864e5)===1?"":"s"} ago`;if(s.push("## Site-wide context (from a recent site crawl)"),s.push(""),s.push(`A crawl ${R?`(${Y})`:""} of ${n.pagesAudited} page${n.pagesAudited===1?"":"s"} on this origin produced grade **${Br(n.siteGrade,n.spotCheckGate)}** site-wide with **${n.totalUniqueViolations}** unique violations. Use this context for WCAG 3.2.3 (Consistent Navigation) and 3.2.4 (Consistent Identification) which require cross-page comparison.`),s.push(""),n.topViolations.length>0){s.push("**Most-frequent violations across the site** (rules to prioritize because they affect multiple pages):"),s.push("");for(const K of n.topViolations.slice(0,6))s.push(`- \`${K.ruleId}\` — ${K.impact} — found on ${K.urlsAffected} page${K.urlsAffected===1?"":"s"} (${K.totalOccurrences} total instances)`);s.push(""),s.push("Fixing one of these rules in shared components / layouts is leverage — one fix resolves N pages."),s.push("")}const H=n.consistencyFindings??[];if(H.length===0&&n.topViolations.some(Q=>Q.ruleId==="wcc-consistency-needs-site-crawl"||Q.ruleId==="wcc-redundant-entry-needs-site-crawl")&&(s.push("**Cross-page consistency status:** the comparators ran across the crawled pages and detected **no real divergences** — primary navigation, brand link, footer help mechanism, and multi-step form fields are all consistent across the sample. The `wcc-consistency-needs-site-crawl` and `wcc-redundant-entry-needs-site-crawl` rule IDs in the violations list are placeholder findings emitted per-page by the single-page analyzer (which can't see other pages); the cross-page comparators downgrade them when the crawl confirms consistency. **Do not chase these on a per-page basis** — they're already cleared at the site level."),s.push("")),H.length>0){s.push("**Cross-page consistency divergences detected:**"),s.push("");for(const K of H){const Q=K.aiVerdict?K.aiVerdict.verdict==="uncertain"?" (AI: uncertain — human verify)":` (AI: confirmed real, confidence ${(K.aiVerdict.confidence*100).toFixed(0)}%)`:"";s.push(`- **${K.wcagCriterion} ${K.impact}**${Q} — ${K.description}`);for(const ae of K.details.split(`
|
|
1673
|
+
</html>`)}function Bh(e){const t=[],a=e.target.outerHTML;if(e.ruleId==="image-alt"||e.ruleId==="input-image-alt"||e.ruleId==="area-alt"||e.ruleId==="svg-img-alt"||e.ruleId==="object-alt"){const n=/\bsrc\s*=\s*"([^"]+)"|\bsrc\s*=\s*'([^']+)'/i.exec(a);n&&t.push({label:"Image source",value:`\`${n[1]??n[2]}\` — if you have vision capability, view this image and propose alt text. Use \`alt=""\` if it's decorative.`});const o=/<title>([^<]+)<\/title>/i.exec(a);o&&t.push({label:"Inline SVG <title>",value:o[1]})}if(e.ruleId==="button-name"||e.ruleId==="empty-button"||e.ruleId==="input-button-name"){const n=/\baria-label\s*=\s*"([^"]+)"/i.exec(a);n&&t.push({label:"Existing aria-label",value:n[1]});const o=/<svg[^>]*>[\s\S]*?<title>([^<]+)<\/title>/i.exec(a);o&&t.push({label:"SVG <title> inside",value:o[1]});const r=/<(?:i|svg|span)[^>]*class\s*=\s*"([^"]*(?:icon|fa-|material-icon)[^"]*)"/i.exec(a);r&&t.push({label:"Icon class hint",value:`\`${r[1]}\` — name suggests intent`})}if(e.ruleId==="link-name"||e.ruleId==="empty-link"){const n=/\bhref\s*=\s*"([^"]+)"|\bhref\s*=\s*'([^']+)'/i.exec(a);n&&t.push({label:"Link href",value:`\`${n[1]??n[2]}\` — destination may suggest the right link text`});const o=/\baria-label\s*=\s*"([^"]+)"/i.exec(a);o&&t.push({label:"Existing aria-label",value:o[1]})}if(e.ruleId==="label"||e.ruleId==="select-name"||e.ruleId==="aria-input-field-name"){const n=/\bname\s*=\s*"([^"]+)"|\bname\s*=\s*'([^']+)'/i.exec(a);n&&t.push({label:"Field name attribute",value:`\`${n[1]??n[2]}\` — often hints at the intended label`});const o=/\bplaceholder\s*=\s*"([^"]+)"|\bplaceholder\s*=\s*'([^']+)'/i.exec(a);o&&t.push({label:"Placeholder text (visible)",value:o[1]??o[2]});const r=/\btype\s*=\s*"([^"]+)"|\btype\s*=\s*'([^']+)'/i.exec(a);r&&t.push({label:"Input type",value:`\`${r[1]??r[2]}\``});const i=/\bautocomplete\s*=\s*"([^"]+)"|\bautocomplete\s*=\s*'([^']+)'/i.exec(a);i&&t.push({label:"Autocomplete",value:i[1]??i[2]})}return t}function qh(e,t){var i;const a=new Set((t??[]).filter(c=>c.verdict==="pass"||c.verdict==="fail").map(c=>`${c.pageUrl}::${c.ruleId}::${c.selector}`)),n=new Map;for(const c of t??[])c.verdict==="uncertain"&&n.set(`${c.pageUrl}::${c.ruleId}::${c.selector}`,c.reasoning);const o=new Set,r=[];for(const c of e){const d=c.pageUrl??c.scope,l=((i=c.axeRulesEvaluated)==null?void 0:i.incomplete)??[];for(const p of l)for(const m of p.elements??[]){const u=`${d}::${p.ruleId}::${m.selector}`;o.has(u)||a.has(u)||(o.add(u),r.push({pageUrl:d,ruleId:p.ruleId,wcagCriterion:p.wcagCriterion,selector:m.selector,failureSummary:m.failureSummary,styles:m.styles,aiAttemptReasoning:n.get(u)}))}}return r}function Bi(e,t,a,n,o,r,i,c,d){var z,X,ge,ue,we,ve,Ie,Be,qe,$e,ie;const l=new Set(o??[]),p=new Set(d??[]),m=((z=e[0])==null?void 0:z.componentId)??"unknown",u=He(e),g=!!(t&&t.baselineSnapshotMeta),h=e.flatMap(y=>y.violations),f=p.size>0?h.filter(y=>p.has(y.matchKey)):[],w=y=>l.has(`violation::${y}`),b=h.filter(y=>!p.has(y.matchKey)&&!w(y.matchKey)),k=new Map;for(const y of b){const D=y.ruleId.startsWith("ai-")?`${y.ruleId}::${y.target.selector}::${(y.target.outerHTML??"").slice(0,200)}`:Ne(y.ruleId,y.target.selector),R=`${y.currentState.pseudoState} · ${y.currentState.theme} · ${y.currentState.direction} · ${y.currentState.breakpoint.id}`,G=k.get(D);if(G){G._states.includes(R)||G._states.push(R),G._instanceSelectors.includes(y.target.selector)||G._instanceSelectors.push(y.target.selector);continue}k.set(D,{...y,_states:[R],_instanceSelectors:[y.target.selector]})}const v=Array.from(k.values()).sort((y,D)=>{const R={critical:0,serious:1,moderate:2,minor:3};return(R[y.impact]??99)-(R[D.impact]??99)}),s=[];s.push("# Accessibility Fix Request"),s.push(""),s.push("> **Your job is to APPLY the fixes in this report — not to evaluate, second-guess, or re-litigate whether the findings are correct.** This report is the output of a real automated audit (axe-core run in a live browser across many UI states) plus AI verification; the triage already happened upstream. Do not debate severity or skip items you personally doubt. Read each finding, apply the canonical fix, and report the outcome per the Work contract below. A genuine blocker or a real user-decision is the only acceptable non-fix — and those are reported explicitly per the contract, never silently dropped."),s.push(""),s.push(`You are a senior accessibility engineer pairing with the user on their codebase. The component below has ${v.length} unique WCAG violation${v.length===1?"":"s"} detected by an automated audit running axe-core ${((X=e[0])==null?void 0:X.axeVersion)??"4.x"} across ${e.length} state combinations (hover, focus, focus-visible, active, disabled × dark / forced-colors / RTL / breakpoints). Your task: locate each violation in the codebase and apply the canonical fix — carefully, atomically, and asking before guessing.`),s.push(""),s.push("## [!] Work contract — read before doing anything else"),s.push(""),s.push(`**You will receive ${v.length} numbered finding${v.length===1?"":"s"} below.** Each one needs an outcome BEFORE you report completion. Do not stop early. Do not batch by impact level. Do not declare done without addressing every numbered item.`),s.push(""),s.push("**For each numbered finding (1 through "+v.length+"), produce one of:**"),s.push(""),s.push("- [DONE] **Applied** — the canonical fix from this prompt was applied to the source. Cite the file + line."),s.push("- [BLOCKED] **Blocked** — explicitly state WHY you cannot apply the fix (missing info, ambiguous user content, dependency change required, etc.). One sentence per blocked finding."),s.push("- [NEEDS-DECISION] **Needs user decision** — you found options but need the user to pick. List the options inline."),s.push(""),s.push(`**The single answer you cannot use: "skipped" or "didn't get to it."** If you ran out of context or time, stop and tell the user — do not silently omit items.`),s.push(""),s.push("**Required final response format** — at the end of your work, produce a completion table:"),s.push(""),s.push("```"),s.push("| # | Rule ID | Status | Notes |"),s.push("|---|---------|--------|-------|"),s.push("| 1 | <rule> | DONE / BLOCKED / NEEDS-DECISION | <file:line OR reason> |"),s.push("| 2 | ... | ... | ... |"),s.push(`| ${v.length} | ... | ... | ... |`),s.push("```"),s.push(""),s.push("**Every row must be present.** If status is BLOCKED or NEEDS-DECISION, the Notes column explains specifically what the blocker is so the user can address it on the next pass. The point is that ONE pass through this prompt should produce either a fix or a clear next action for every finding — not a partial sweep that leaves the user re-running the loop."),s.push(""),s.push("---"),s.push("");const x=a&&a.length>0?{runs:a,workflows:De}:void 0,$=c==null?void 0:c.map(y=>({criterionId:y.criterionId,ruleId:`ai-interactive::${y.criterionId}`,pageUrl:y.pageUrl,verdict:y.verdict,reasoning:y.reasoning})),A=et(h,x,e,p,$,void 0,void 0,ft(e),r),S=A.score;if(s.push("## Context"),s.push(""),s.push(`- **Component / scope:** \`${m}\``),s.push(`- **Audited URL:** ${u}`),s.push(`- **WCAG target:** WCAG ${Mt} ${wn} (this audit's goal; the receiving fix should bring the page into compliance with this level)`),s.push("- **Source filter:** Full audit — every detected violation"),s.push(`- **Compliance posture:** Grade **${A.combined}** · **${S.risk.toUpperCase()} risk** (${S.totals.critical} critical · ${S.totals.serious} serious · ${S.totals.moderate} moderate · ${S.totals.minor} minor). ${S.risk==="critical"||S.risk==="high"?"Treat as triage priority — these violations represent meaningful lawsuit exposure under ADA Title III, EAA, and EN 301 549.":S.risk==="moderate"?"Should be addressed in the current sprint cycle; not lawsuit-emergency but real exposure.":"Lower exposure; still worth fixing but not blocking other work."}`),S.coverage){const y=S.coverage.untestedCriteria.length;y>0?s.push(`- **WCAG ${S.coverage.targetVersion} ${S.coverage.targetLevel} coverage:** ${S.coverage.evaluated} of ${S.coverage.totalApplicable} criteria evaluated. **Cannot claim ${S.coverage.targetLevel} conformance** until ${y} untested criteria are evaluated via Guided Tests (Pass / Fail / N/A). Untested: ${S.coverage.untestedCriteria.join(", ")}.`):s.push(`- **WCAG ${S.coverage.targetVersion} ${S.coverage.targetLevel} coverage:** ${S.coverage.evaluated} of ${S.coverage.totalApplicable} criteria evaluated. Full coverage — defensible conformance claim possible once all violations below are fixed.`)}g&&t&&s.push(`- **Trend vs your saved baseline:** ${t.newCount} new · ${t.persistentCount} carried over · ${t.fixedCount} resolved. The fix list below is the FULL current violation set, not just the delta.`),s.push(""),s.push("## Before you start (safety)"),s.push(""),s.push("Do these in order before making any code changes:"),s.push(""),s.push("1. **Verify a clean git working tree** (`git status`). If there are uncommitted changes, ask the user whether to commit, stash, or proceed."),s.push("2. **Switch to a feature branch** dedicated to this work — e.g., `a11y/fix-component-name`. Don't commit directly to `main` / `master` / `develop`."),s.push("3. **Plan one commit per fix.** Each violation gets its own atomic commit so any individual change can be reverted without untangling the rest. Commit messages should follow the pattern: `a11y: fix <ruleId> on <selector or component>`."),s.push("4. **Show diffs before applying** when your tooling supports it (Cursor / Copilot Chat agents). Never silently overwrite files."),s.push("5. **Do not bump dependencies** to resolve accessibility issues — fixes should be code changes, not package upgrades."),s.push(""),s.push("## Ask me these questions first"),s.push(""),s.push("Before applying any fix, ask the user the questions below. Don't proceed until you have answers — guessing here produces fixes that don't match the codebase's conventions and will be rejected on review."),s.push(""),s.push("1. **Framework + style approach?** React / Vue / Svelte / Angular / Solid / vanilla — and CSS Modules / Tailwind / styled-components / Emotion / scoped `<style>` / global CSS / design-tokens system. This determines *how* every fix is written."),s.push("2. **Existing design tokens or raw values?** If the codebase uses `var(--color-text-on-surface)` or a `tokens.ts` / `theme.json`, fixes must use tokens — never introduce new hex codes. Where can you read the available tokens?"),s.push('3. **Existing utility classes or component library?** e.g., does `.text-slate-700` or `<Button variant="primary">` already exist? Use what\'s there before inventing new selectors or components.'),s.push("4. **Files or directories that are off-limits?** Generated code, vendored libraries, third-party widgets, lockfiles — confirm what you must NOT touch."),s.push("5. **For color-contrast fixes: prefer adjusting foreground or background?** Brand color preservation is usually a stakeholder call, not a developer call."),s.push("6. **How should I verify fixes are done?** Re-run this auditor (preferred), run the existing test suite, manual screen-reader pass, or all three?"),s.push(""),s.push("## Decision rules (when ambiguity surfaces during fixes)"),s.push(""),s.push('- **Native HTML beats ARIA.** First rule of ARIA: don\'t use ARIA when a native element works. Prefer `<button>` over `<div role="button">`, `<nav>` over `<div role="navigation">`, etc.'),s.push("- **State-specific failures get state-specific fixes.** A `:hover`-only contrast failure goes in the `:hover` rule, not the default selector."),s.push("- **Where multiple valid fixes exist, surface options — don't pick silently.** E.g., adding an accessible name can be done via visible text, `aria-label`, or `aria-labelledby` referencing existing copy. Present the options with tradeoffs and let the user choose."),s.push("- **When the fix would alter visible design** (color shifts, layout changes, content additions), pause and confirm with the user before applying."),s.push(""),s.push("## [STOP] Do not dismiss findings without empirical verification"),s.push(""),s.push('**Default stance: every finding in this report is real and needs a fix.** Your job is to apply fixes, not to triage which ones "probably aren\'t real." That triage already happened — the auditor ran on a real browser, captured real DOM, real `getBoundingClientRect()` coordinates, real WCAG rule failures from axe-core. You are downstream of all that.'),s.push(""),s.push('Some categories (reading-order, tab-order) are explicitly heuristic and DO have legitimate false-positive cases. Those cases are listed inline in each section. **But "this looks like it might be a false positive based on the pattern" is not verification.** Pattern-matching from the report alone is inference, not evidence.'),s.push(""),s.push("**To dismiss any finding as a false positive, you must do all of the following:**"),s.push(""),s.push("1. **Load the deployed page in a real browser** at the viewport listed in the finding's capture-state header (e.g., 1280×800 LTR for the position-based analyses)."),s.push("2. **Inspect the flagged elements directly** — DevTools, real `getBoundingClientRect()` coordinates, real DOM source order."),s.push("3. **Compare your empirical observation to the auditor's claim.** Does the visual layout actually contradict the DOM order, or not?"),s.push("4. **If you conclude false positive, document why** — include the empirical evidence (coordinates, screenshots, DOM excerpt) so the user can audit your reasoning."),s.push(`5. **Even after empirical verification, ASK THE USER before dismissing.** They have context you don't (e.g., "yes that's an intentional skip-link / multi-column layout / RTL-only behavior").`),s.push(""),s.push("**What does NOT count as verification:**"),s.push(""),s.push("- Looking at the CSS file and not seeing `flex-direction: row-reverse` (the layout could be from `order:`, `grid-area`, absolute positioning, or RTL emulation)"),s.push('- Pattern-matching the finding shape ("this looks like a perfect arithmetic flip therefore it must be an RTL artifact")'),s.push(`- Reading the auditor's false-positive list and concluding your case "looks like one of those"`),s.push(`- Deciding it's "probably fine" because the structure looks reasonable on a quick read`),s.push(""),s.push("**If you cannot run a real browser** (e.g., environment doesn't allow headless Chromium), then you cannot verify, which means you cannot dismiss. In that case: surface the finding to the user, propose the fix, and let them decide."),s.push(""),s.push("**Skipping a finding without verification is worse than fixing one that turns out to be a false positive.** Over-fix is reversible (delete the commit). Under-fix is silent — the user thinks they're compliant when they aren't."),s.push(""),s.push("**When you DO verify a finding is a false positive** (per the empirical rules above), the right action is to recommend the user click **dismiss** on that finding in the auditor's Activity tab. The auditor records the dismissal with your reason, suppresses the finding from displayed counts, the Matrix banner, and future AI prompts. **Don't modify code to silence a verified false positive** — that produces brittle workarounds and pollutes the codebase with comments explaining heuristic quirks. Dismissals persist per-URL and survive re-audits cleanly."),s.push(""),s.push("## Content-decision rules (alt text, button labels, link text)"),s.push(""),s.push("Some violations require copy that the audit can't generate. Don't invent copy out of nowhere, but DON'T just punt to the user either — use the capabilities you have:"),s.push(""),s.push('1. **For images missing alt text** (`image-alt`, `input-image-alt`, `area-alt`, `svg-img-alt`): if you have vision capability, **fetch or view the image** at the `src` URL listed in the violation and PROPOSE descriptive alt text based on what the image actually contains. Mark obviously-decorative images (icons inside buttons that already have text labels, dividers, background flourishes) for `alt=""`.'),s.push('2. **For unlabeled buttons / links** (`button-name`, `link-name`, `empty-button`, `empty-link`): look at the surrounding code context — nearby text, the button\'s icon (often a known SVG name like "close" / "search" / "menu"), the route the link points at — and PROPOSE an accessible name. Show your inference reasoning.'),s.push("3. **For unlabeled form fields** (`label`, `select-name`, `aria-input-field-name`): inspect the field's `name`, `placeholder`, `id`, and any nearby visible text in the parent container. PROPOSE a label."),s.push("4. **For ambiguous content** (alt text where you genuinely can't tell what an image shows; labels where there's no surrounding context), ask the user — don't guess."),s.push("5. **Always show your proposed copy before applying** — let the user approve, edit, or reject."),s.push(""),s.push("## [VERIFY] Verify with Playwright before asking the user"),s.push(""),s.push(`Many WCAG criteria that look like "needs human verification" are actually scriptable — you can write a short Playwright test, run it, observe the result, and resolve the question without bothering the user. **Default to this path.** Only ask the user when Playwright can't reach the answer (subjective copy decisions, content judgment, business-context questions).`),s.push(""),s.push("### Step 1: Check if Playwright is available"),s.push(""),s.push('Look in the project root for `playwright.config.ts` / `playwright.config.js` / a `tests/` or `e2e/` directory using `@playwright/test`. If yes — use it directly. If no — ask the user once: "Want me to add Playwright (`npm install -D @playwright/test && npx playwright install`) so I can verify a few WCAG criteria automatically?" If they decline, fall back to asking per-item.'),s.push(""),s.push("### Step 2: Run targeted smoke tests instead of asking"),s.push(""),s.push("Here are the manual-checklist items below that Playwright CAN verify on the CURRENT PAGE (no multi-page setup needed). For each, write the test, run it, and only escalate to the user if the test result is ambiguous:"),s.push(""),s.push("**Scope disambiguation:** the criteria below are all **single-page** — they describe behavior of the current page. They are NOT multi-page concerns. Cross-page criteria (3.2.3 Consistent Navigation, 3.2.4 Consistent Identification, 3.2.6 Consistent Help) require comparing multiple pages and are handled by wcagcheckr's Site Crawl flow, NOT by Playwright tests here. Don't skip 3.2.1 / 3.2.2 thinking they're multi-page — they're single-page and Playwright-testable."),s.push(""),s.push("| Criterion | Scope | Playwright test |"),s.push("|---|---|---|"),s.push('| **1.4.4 Resize Text (AA)** | single-page | `await page.evaluate(() => document.body.style.zoom = "200%"); await page.screenshot(...); assert no horizontal scroll on critical content blocks` |'),s.push("| **1.4.10 Reflow (AA)** | single-page | `await page.setViewportSize({ width: 320, height: 800 }); await expect(page).toHaveScreenshot(...); assert documentElement.scrollWidth ≤ clientWidth + 10` |"),s.push("| **1.4.12 Text Spacing (AA)** | single-page | inject `* { line-height: 1.5 !important; letter-spacing: 0.12em !important; word-spacing: 0.16em !important; }` via `addStyleTag`, screenshot, check no clipping/overlap |"),s.push('| **1.4.13 Content on Hover or Focus (AA)** | single-page | hover each tooltip-bearing element, then `await page.keyboard.press("Escape")` — verify tooltip closes (dismissible); move mouse INTO tooltip, verify it stays (hoverable); wait 5s without interaction, verify it stays (persistent) |'),s.push("| **2.1.4 Character Key Shortcuts (A)** | single-page | enumerate document keydown handlers; press each single-key shortcut + verify it only fires when expected (focus-dependent or toggleable) |"),s.push("| **2.2.1 Timing Adjustable (A)** | single-page | scan code for setTimeout/setInterval with user-facing impact; assert each has a pause/extend/disable affordance |"),s.push("| **2.2.2 Pause, Stop, Hide (A)** | single-page | find elements with CSS animation / transition / autoplay video; assert each has a visible pause/stop control |"),s.push('| **2.4.5 Multiple Ways (AA)** | single-page | `await page.locator("nav").count() + page.locator("[role=search]").count() + page.locator("a[href*=sitemap]").count() ≥ 2` — checks the current page exposes at least two ways to reach other pages (nav + search, or nav + sitemap link, etc.). |'),s.push("| **2.5.2 Pointer Cancellation (A)** | single-page | for each `<button>` / `[role=button]`: `page.mouse.down()` on it, `page.mouse.move(elsewhere)`, `page.mouse.up()` — assert no action fired (no URL change, no submitted form, no state mutation) |"),s.push("| **3.2.1 On Focus (A)** | **single-page (per-page test, NOT multi-page)** | iterate every focusable on the current page; `.focus()` on each; assert URL unchanged + no modal opened + no form submitted + no scroll jump. This is about each FOCUS event on this page, not about page-to-page navigation. |"),s.push('| **3.2.2 On Input (A)** | **single-page (per-page test, NOT multi-page)** | for each `<select>` / `<input>` / `<textarea>` / `[contenteditable]` on the current page: change value via `.fill()` or `.selectOption()`; assert no URL change, no auto-submit, no scroll jump UNLESS the form clearly warns ("submits on change" UI affordance). |'),s.push('| **4.1.3 Status Messages (AA)** | single-page | trigger a form-validation error or async update; assert the live region announces (`role="status"` or `role="alert"` exists + has text content) |'),s.push(""),s.push("### Step 3: Vision-capable items"),s.push(""),s.push('For items where the test result is "a screenshot to evaluate":'),s.push("- **Color contrast on a gradient/image background:** Playwright screenshot of the element + 50px context, sample foreground + background pixels at the text's bounding box, compute WCAG contrast ratio, compare to 4.5:1 (normal) or 3:1 (large)."),s.push('- **Alt text accuracy:** fetch the `src=` URL, view the image, propose alt text. If the image is a logo/wordmark/decorative, recommend `alt=""`.'),s.push("- **1.4.5 Images of Text:** screenshot the hero / heading / banner; check whether typography is rendered as text (selectable + DOM-visible) or as a raster image."),s.push(""),s.push("### Step 4: When to fall back to asking the user"),s.push(""),s.push("Ask only when:"),s.push(`1. The verification requires subjective judgment Playwright can't make (e.g., "is this alt text accurate to the brand's voice?")`),s.push('2. The criterion needs production-data context (e.g., "do users hit this timeout in practice?")'),s.push("3. The Playwright test itself is ambiguous and the user has context that resolves it"),s.push(""),s.push('**Report Playwright results inline with your fix proposals.** "I ran a smoke test for 1.4.10 at 320px — no horizontal scroll detected; checking this off" beats "Could you verify 1.4.10 manually?" every time.'),s.push(""),s.push("## Determinism + reproducibility"),s.push(""),s.push("**AI judgments are not reproducible by default.** Anthropic, OpenAI, and most LLM APIs run at non-zero sampling temperature unless you explicitly request otherwise — meaning the same prompt + same context can produce different answers on different runs. wcagcheckr learned this the hard way (rc.190 → rc.192): consecutive audits on an unchanged page produced different verdicts because the audit's own AI calls ran at default temperature. The fix was `temperature: 0` on every judgment call."),s.push(""),s.push("You face the same risk for any decision you make based on AI judgment rather than empirical observation. Mitigate by:"),s.push(""),s.push('1. **Prefer empirical verification over AI judgment.** If a question can be answered by running Playwright, running axe-core, sampling pixel values, or grepping the codebase — do that, not "I think the screenshot shows…". The Playwright section above lists 11 criteria that can be verified deterministically.'),s.push("2. **For any auxiliary AI calls you make** (vision tasks, classification, summarization), pass `temperature: 0` (Anthropic / OpenAI) or `top_p: 0.01` (other providers). Same screenshot + same prompt should produce the same answer twice in a row — verify before trusting the answer."),s.push('3. **Document AI-judgment decisions in commit messages.** When you DO rely on AI for a subjective call ("I chose `aria-label=\\"Search the catalog\\"` over `\\"Search\\"` because…"), record the reasoning in the commit so the next session — yours or a human\'s — can audit the call.'),s.push("4. **Treat any single AI verdict as one data point, not gospel.** If you ask AI the same question twice and get different answers, that's the variance showing. Stick with the FIRST answer you committed to OR cross-check with Playwright; don't flip-flop based on subsequent re-rolls."),s.push("5. **Re-run the auditor (`wcagcheckr`) after every commit** rather than asking AI \"is this fixed?\" — the auditor's output is the source of truth for whether a fix landed, not the fixer AI's self-assessment."),s.push(""),s.push("Sampling variance is the silent enemy of compliance automation. Treat it as a constraint to design around, not noise to ignore."),s.push(""),s.push("## Constraints"),s.push(""),s.push("- Don't fix violations that aren't in this list (no scope creep)."),s.push(`- Don't reformat or reorder unrelated code. No "while I'm here" cleanup.`),s.push(`- Match existing code style. Don't invoke Prettier / ESLint to "fix everything" — focus your diff strictly on the violation's element + relevant style rule.`),s.push("- Don't add new dependencies, configs, or build steps."),s.push("- Add inline comments only where the fix's rationale is non-obvious; otherwise skip comments."),s.push("- Preserve existing functionality and visual design wherever possible."),s.push(""),s.push("## How to use this prompt"),s.push(""),s.push("Paste this into Claude / Cursor / Copilot Chat with your codebase open. The AI should:"),s.push('1. **First**: walk through the "Before you start" + "Ask me these questions" sections with the user.'),s.push("2. **Then**: identify the source files containing each violating element (use the selectors + outerHTML below)."),s.push("3. **Then**: for each violation, propose the fix using the recipe + answers gathered, surface ambiguity per the decision rules, and apply only after the user confirms."),s.push("4. **Finally**: produce one atomic commit per fix, with a clear message."),s.push(""),s.push("## Locating findings in source"),s.push(""),s.push("For each finding below, the most reliable search strategies (try in this order):"),s.push(""),s.push('1. **The accessible name / text content** — works for JSX, Vue templates, Svelte, server-rendered HTML. Search for the visible text in `"quotes"` or as a string literal in component files.'),s.push("2. **A distinctive class name** — e.g., `.cta-primary` → grep `cta-primary` in CSS + component files. BEM-style and Tailwind utility classes are the most stable identifiers."),s.push('3. **A unique data attribute** — `data-testid="checkout-button"`, `data-qa=`, `data-cy=` — these are usually authored deliberately and survive minification.'),s.push("4. **A stable ID** — `#hero-search` → grep `hero-search`. Watch out for IDs that look generated (`#field_3f8a92`)."),s.push('5. **The outerHTML pattern** — fall back when class/id/testid don\'t help. Search for distinctive attribute combinations (e.g., `aria-label="…"` + `class="…"`).'),s.push(""),s.push("**Do not search on these — they're runtime artifacts:**"),s.push("- `:nth-child(N)`, `:nth-of-type(N)` — these are positional, never appear in source"),s.push("- Numeric suffixes that look generated (`-1234`, `__abc123`) — usually from CSS Modules / styled-components hashing"),s.push("- Selector paths with `>` chains — describes a DOM structure, not a source location"),s.push(""),s.push("**For pages built with a framework**, also check:"),s.push("- Storybook stories (`*.stories.tsx`, `*.stories.mdx`) — often the canonical component definition"),s.push("- Shared layout components (`Header.tsx`, `Footer.tsx`, `Layout.tsx`) — most page-level violations live here"),s.push("- Theme / token files (`theme.ts`, `tokens.json`) — color-contrast fixes often patch tokens, not consumers"),s.push(""),s.push("---"),s.push("");const F=(((ge=e[0])==null?void 0:ge.readingOrderIssues)??[]).filter(y=>!l.has(`reading-order::${y.selector}`)),T=(ue=e[0])==null?void 0:ue.positionAnalysisCapturedAt,I=T?`${T.breakpoint.width}×${T.breakpoint.height} (${T.breakpoint.label}), ${T.direction.toUpperCase()}, ${T.theme} theme, ${T.pseudoState} pseudo-state`:"(reference state unavailable — findings reflect last matrix state)",N=c==null?void 0:c.some(y=>y.criterionId==="1.3.2");if(F.length>0&&!N){s.push("## Reading-order concerns (DOM ≠ visual order)"),s.push(""),s.push(`_Positions captured at: **${I}**. Multi-column layouts, RTL pages, and mobile-stacked sections may have legitimately different reading orders at other viewports — verify each finding against the layout the user actually sees._`),s.push(""),s.push(`Screen readers read the DOM in source order. CSS that visually rearranges things (\`flex-direction: row-reverse\`, \`order:\`, \`grid-area\`, absolute positioning) does NOT reorder what the SR sees. ${F.length} element${F.length===1?" has":"s have"} a notable gap between their DOM position and their visual position:`),s.push(""),s.push("| DOM index | Visual index | Selector | Text |"),s.push("|---|---|---|---|");for(const y of F){const D=y.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");s.push(`| ${y.domIndex} | ${y.visualIndex} | \`${y.selector}\` | ${D} |`)}s.push(""),s.push("**How to fix:** match DOM order to intended reading order — reorder the JSX/HTML, NOT the CSS. CSS visual rearrangement is a code smell only because it diverges from DOM order; if the DOM order is what the user should hear, no fix is needed."),s.push(""),s.push("**False-positive cases — these are the ONLY legitimate reasons to skip a flagged element:**"),s.push(""),s.push('1. **Multi-column / newspaper layouts** — DOM order is "column 1 top to bottom, then column 2", which is correct for SR but reads as "out of order" to this analyzer.'),s.push('2. **Skip-links** ("Skip to main content") — visually positioned late but DOM-early on purpose.'),s.push("3. **Caption-first patterns** — captions placed before their image in DOM so SR users get context before the alt text."),s.push("4. **Modal / popover triggers** — the trigger button is DOM-near the modal's portal mount point, which is often `<body>` end."),s.push(""),s.push('**Per the verification rules above (§ "Do not dismiss findings without empirical verification"): even when a finding looks like one of these false-positive cases, you must verify in a real browser before skipping. "Looks like a multi-column layout" is inference, not proof.** Read the DOM. Get real coordinates. Then ask the user. Then skip if confirmed.'),s.push(""),s.push("Surface the candidates above to the user, ask which (if any) are intended, and only reorder the rest. **If in doubt, fix it.** Reordering JSX is reversible; failing a Level A criterion is not."),s.push(""),s.push("---"),s.push("")}const C=(((we=e[0])==null?void 0:we.tabOrderIssues)??[]).filter(y=>!l.has(`tab-order::${y.selector}`)),_=c==null?void 0:c.some(y=>y.criterionId==="2.1.2"&&y.verdict==="pass");if(C.length>0&&!_){const y=C.filter(R=>R.flag==="visual"||R.flag==="both").length,D=C.filter(R=>R.flag==="tabindex"||R.flag==="both").length;s.push("## Tab-order concerns (keyboard focus sequence ≠ visual order) — WCAG 2.4.3 Focus Order (A)"),s.push(""),s.push(`_Positions captured at: **${I}**. Tab-position-vs-visual-position divergence is direction- and breakpoint-sensitive — what looks wrong here may be correct on RTL or mobile (and vice versa). Verify each finding against the layout the user actually sees._`),s.push(""),s.push(`Keyboard users tab through the page in one order; sighted users scan in another. axe-core does not detect this. ${C.length} focusable element${C.length===1?" has":"s have"} a notable divergence. Of these: ${y} ${y===1?"has":"have"} a visual-vs-tab mismatch (focus jumps around the page); ${D} ${D===1?"is":"are"} caused by positive \`tabindex\` reordering DOM source.`),s.push(""),s.push("| Tab pos | Visual pos | DOM pos | Flag | Selector | Text |"),s.push("|---|---|---|---|---|---|");for(const R of C){const G=R.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");s.push(`| ${R.tabPosition} | ${R.visualPosition} | ${R.domPosition} | ${R.flag} | \`${R.selector}\` | ${G} |`)}s.push(""),s.push("**How to fix:**"),s.push(""),s.push("- **`flag: visual`** — tab order doesn't match visual layout. Reorder the JSX/HTML so the DOM source order matches the layout order users see. Don't use `tabindex` to \"patch\" this — that creates the next class of bug."),s.push('- **`flag: tabindex`** — a positive `tabindex` value (`tabindex="3"`) is reordering DOM. Remove the positive `tabindex` and rely on DOM source order. Use `tabindex="0"` only to make a non-focusable element focusable; use `tabindex="-1"` only to remove from the tab order.'),s.push("- **`flag: both`** — both conditions present. Fix the DOM order first, then remove the positive `tabindex`."),s.push(""),s.push("**False-positive cases — these are the ONLY legitimate reasons to skip a flagged element:**"),s.push(""),s.push('1. **Skip-links** ("Skip to main content") — visually-late, DOM-early on purpose. Their tab position should be near 1; their visual position is wherever their `:focus` styling places them.'),s.push("2. **Modal triggers + portal-rendered modals** — the trigger button is DOM-near the modal's portal mount point (often `<body>` end), so tab/visual positions can legitimately diverge."),s.push("3. **Dropdown menus / popovers** — a button DOM-far from its portal-rendered menu may produce false positives when the menu is open."),s.push(""),s.push(`**Per the verification rules above (§ "Do not dismiss findings without empirical verification"): even when a finding looks like one of these cases, you must verify in a real browser before skipping. Tabbing through the actual deployed page is empirical evidence; reading the auditor's table and pattern-matching is not.** If in doubt, fix it. Reordering JSX is reversible; failing a Level A criterion is not.`),s.push(""),s.push("Surface candidates to the user; ask which are intended; only fix the rest."),s.push(""),s.push("---"),s.push("")}const M=e.flatMap(y=>y.announcements??[]),W=e.flatMap(y=>y.focusEvents??[]).filter(y=>y.isFocusReset);if(M.length>0||W.length>0){if(s.push("## Runtime behavioral observations"),s.push(""),M.length>0){const y=M.filter(R=>R.politeness==="assertive").length;s.push(`**Live-region announcements** (${M.length} total, ${y} assertive): screen-reader announcements captured during state-driving. WCAG 4.1.3 Status Messages (AA) requires that non-essential changes use polite announcements; only blocking errors warrant assertive. Review:`),s.push("");const D=M.slice(0,8);for(const R of D){const G=R.text.replace(/\|/g,"\\|").replace(/\n/g," ").slice(0,120);s.push(`- \`[${R.politeness}${R.role?`/${R.role}`:""}]\` ${G}`)}M.length>8&&s.push(`- (and ${M.length-8} more)`),s.push(""),s.push("Verify each is appropriate. Common bug: error messages firing as `polite` (SR may not interrupt), or status updates firing as `assertive` (interrupts user mid-sentence)."),s.push("")}if(W.length>0){s.push(`**Focus resets to body** (${W.length} occurrence${W.length===1?"":"s"}): during the audit, focus jumped to \`<body>\` or \`<html>\` unexpectedly. This is almost always a bug — focus should move to a logical landing place (modal, alert, next field), never disappear.`),s.push("");for(const y of W.slice(0,5))s.push(`- \`${y.fromSelector??"(none)"}\` → \`${y.toSelector}\``);W.length>5&&s.push(`- (and ${W.length-5} more)`),s.push(""),s.push("Common cause: an element is removed from the DOM (e.g., modal closes) without `.focus()` being called on a sensible destination."),s.push("")}s.push("---"),s.push("")}const P=((ve=e[0])==null?void 0:ve.undefinedCustomProperties)??[];if(P.length>0){s.push("## Undefined CSS custom properties (cascade root cause)"),s.push(""),s.push(`${P.length} CSS custom propert${P.length===1?"y is":"ies are"} referenced via \`var(--name)\` but never declared. When a custom property is missing, the browser falls back to the property's initial value (often \`Canvas\` for color slots → resolves to white), which can produce a wave of false contrast failures. **Fix these missing tokens FIRST** — many of the contrast violations below may disappear once the cascade resolves correctly.`),s.push(""),s.push("| Custom property | References | First sample sites |"),s.push("|---|---|---|");for(const y of P){const D=y.sampleSites.map(R=>`\`${R.selector} { ${R.property} }\``).join("; ");s.push(`| \`${y.name}\` | ${y.referenceCount} | ${D} |`)}s.push(""),s.push("Each entry needs a definition in `:root` (or wherever the design-token layer lives). Surface to the user before applying contrast fixes — the missing token may be the root cause."),s.push(""),s.push("---"),s.push("")}if(a&&a.length>0){s.push("## Manual checks already completed (Guided Tests)"),s.push(""),s.push(`The consultant has run ${a.length} Intelligent Guided Test workflow${a.length===1?"":"s"} against this component covering the WCAG criteria automation cannot verify (keyboard navigation, screen-reader experience, focus management, forms, error prevention, etc.). Treat the verdicts below as ground truth — these came from a human running the actual flow.`),s.push(""),s.push("**Manual passes outrank heuristic findings.** When an IGT workflow has passed (e.g. Keyboard 7/7) and the heuristic analyzers below (Tab-order concerns, Reading-order concerns) flag findings on the same WCAG dimension, the manual pass is ground truth. A human ran real keyboard/SR navigation against the deployed page; the heuristic computes visual position from `getBoundingClientRect()` and can produce false positives on sticky / multi-column / grid layouts. **When the IGT and the heuristic disagree on the same WCAG criterion, treat the heuristic findings as suspect first**, run the empirical verification described above, and (if confirmed false positive) recommend dismissal via the Activity tab."),s.push("");const y=De;s.push("| Workflow | Status | Notes |"),s.push("|---|---|---|");for(const Y of a){const H=y.find(Pe=>Pe.id===Y.workflowId);if(!H)continue;let K=0,Q=0,ae=0,Re=0,ee=0;for(const Pe of H.steps){const Fe=Y.steps[Pe.id];if(!Fe){ee++;continue}Fe.status==="pass"?K++:Fe.status==="fail"?Q++:Fe.status==="skip"?ae++:Fe.status==="not-applicable"?Re++:ee++}const be=Q>0?`[FAIL] ${Q} failed`:ee>0?`[WARN] ${ee} unanswered`:`[PASS] all ${H.steps.length} answered`,Se=[`${K}p`,`${Q}f`];Re>0&&Se.push(`${Re} N/A`),ae>0&&Se.push(`${ae} skip`),ee>0&&Se.push(`${ee} unanswered`);const ne=`${Se.join(" · ")} of ${H.steps.length} steps`;s.push(`| ${H.name} | ${be} | ${ne} |`)}s.push("");const D=[];for(const Y of a){const H=y.find(K=>K.id===Y.workflowId);if(H)for(const K of H.steps){const Q=Y.steps[K.id];(Q==null?void 0:Q.status)==="fail"&&D.push({workflow:H.name,stepTitle:K.question,notes:Q.notes})}}if(D.length>0){s.push("**Failed manual checks** (these are real WCAG failures the consultant verified by hand):"),s.push("");for(const Y of D)s.push(`- **${Y.workflow} → ${Y.stepTitle}**${Y.notes?`: ${Y.notes}`:""}`);s.push(""),s.push("**Address these alongside the automated violations below.** They will not appear in the violations list because no automated rule catches them — but they are still real WCAG failures and must be fixed for compliance."),s.push("")}const R=new Set(a.map(Y=>Y.workflowId)),G=y.filter(Y=>!R.has(Y.id));G.length>0&&(s.push(`**Not yet manually verified** (${G.length} workflow${G.length===1?"":"s"}): `+G.map(Y=>Y.name).join(", ")+". These cover WCAG criteria automation cannot detect; ask the consultant whether to run them before declaring the audit complete."),s.push("")),s.push("---"),s.push("")}if(n&&n.pagesAudited>1){const y=(()=>{try{return new URL(u).origin}catch{return null}})(),D=(()=>{try{return new URL(n.startUrl).origin}catch{return null}})();if(y&&D&&y===D){const R=n.finishedAt,G=R?Date.now()-new Date(R).getTime():0,Y=G<6e4?"just now":G<36e5?`${Math.round(G/6e4)} minute${Math.round(G/6e4)===1?"":"s"} ago`:G<864e5?`${Math.round(G/36e5)} hour${Math.round(G/36e5)===1?"":"s"} ago`:`${Math.round(G/864e5)} day${Math.round(G/864e5)===1?"":"s"} ago`;if(s.push("## Site-wide context (from a recent site crawl)"),s.push(""),s.push(`A crawl ${R?`(${Y})`:""} of ${n.pagesAudited} page${n.pagesAudited===1?"":"s"} on this origin produced grade **${Gr(n.siteGrade,n.spotCheckGate)}** site-wide with **${n.totalUniqueViolations}** unique violations. Use this context for WCAG 3.2.3 (Consistent Navigation) and 3.2.4 (Consistent Identification) which require cross-page comparison.`),s.push(""),n.topViolations.length>0){s.push("**Most-frequent violations across the site** (rules to prioritize because they affect multiple pages):"),s.push("");for(const K of n.topViolations.slice(0,6))s.push(`- \`${K.ruleId}\` — ${K.impact} — found on ${K.urlsAffected} page${K.urlsAffected===1?"":"s"} (${K.totalOccurrences} total instances)`);s.push(""),s.push("Fixing one of these rules in shared components / layouts is leverage — one fix resolves N pages."),s.push("")}const H=n.consistencyFindings??[];if(H.length===0&&n.topViolations.some(Q=>Q.ruleId==="wcc-consistency-needs-site-crawl"||Q.ruleId==="wcc-redundant-entry-needs-site-crawl")&&(s.push("**Cross-page consistency status:** the comparators ran across the crawled pages and detected **no real divergences** — primary navigation, brand link, footer help mechanism, and multi-step form fields are all consistent across the sample. The `wcc-consistency-needs-site-crawl` and `wcc-redundant-entry-needs-site-crawl` rule IDs in the violations list are placeholder findings emitted per-page by the single-page analyzer (which can't see other pages); the cross-page comparators downgrade them when the crawl confirms consistency. **Do not chase these on a per-page basis** — they're already cleared at the site level."),s.push("")),H.length>0){s.push("**Cross-page consistency divergences detected:**"),s.push("");for(const K of H){const Q=K.aiVerdict?K.aiVerdict.verdict==="uncertain"?" (AI: uncertain — human verify)":` (AI: confirmed real, confidence ${(K.aiVerdict.confidence*100).toFixed(0)}%)`:"";s.push(`- **${K.wcagCriterion} ${K.impact}**${Q} — ${K.description}`);for(const ae of K.details.split(`
|
|
1674
1674
|
`))s.push(` ${ae}`);(Ie=K.aiVerdict)!=null&&Ie.reasoning&&s.push(` AI reasoning: ${K.aiVerdict.reasoning}`),s.push("")}}s.push("**Manual cross-page checks the AI should still verify** (the heuristic comparators above can miss edge cases):"),s.push(""),s.push("- **3.2.3 Consistent Navigation** — primary nav DOM order across pages."),s.push("- **3.2.4 Consistent Identification** — same logo / menu / search / nav-link accessible names across pages."),s.push("- **3.2.6 Consistent Help (WCAG 2.2 A)** — help mechanism position consistency."),s.push("- **3.3.7 Redundant Entry (WCAG 2.2 A)** — multi-step forms re-asking for already-entered information."),s.push(""),s.push("---"),s.push("")}}if(f.length>0){const y=new Map;for(const R of f)y.has(R.matchKey)||y.set(R.matchKey,R);const D=Array.from(y.values());s.push("## Previously verified by human — DO NOT re-flag or attempt to fix"),s.push(""),s.push(`The user manually verified each of the following ${D.length} finding${D.length===1?"":"s"} in the wcagcheckr side panel. They WILL appear in raw axe output on every subsequent audit (axe re-detects them because the underlying DOM hasn't changed), but the user has dispositioned each one as acceptable — typically because an automated rule fired on a legitimate edge case (decorative image with empty alt, contrast that passes APCA but not WCAG 2.0 ratio, etc.). **Do not attempt to "fix" any item in this list.** Treat them as resolved. If you genuinely believe one is misjudged, note your reasoning in a comment for the user to review on their next audit — do NOT modify the markup.`),s.push(""),D.forEach((R,G)=>{s.push(`${G+1}. \`${R.ruleId}\` on \`${R.target.selector}\``),R.target.accessibleName&&s.push(` - Accessible name: "${R.target.accessibleName}"`),R.target.textSnippet&&s.push(` - Text: "${R.target.textSnippet.slice(0,120)}"`),s.push(` - WCAG ${R.wcagCriterion} ${R.wcagLevel} · ${R.impact}`)}),s.push(""),s.push("---"),s.push("")}const V=(r??[]).filter(y=>y.verdict==="fail").filter(y=>!p.has(`${y.ruleId}::${y.selector}`));if(V.length>0){const y=new Map;for(const R of V){const G=`${R.ruleId}::${R.selector}`;y.has(G)||y.set(G,R)}const D=Array.from(y.values());s.push("## AI-evaluated fails (axe couldn't determine — AI vision determined FAIL)"),s.push(""),s.push(`axe-core flagged each of the following ${D.length} element${D.length===1?"":"s"} as INCOMPLETE — its automated heuristics couldn't reach a verdict (typically because the background is a gradient, image, or semi-transparent overlay that axe can't pixel-sample). wcagcheckr's AI resolver then evaluated each element via vision + computed-style sampling and determined it is a real **FAIL**. Treat these the same as axe violations: locate the element in source, apply the canonical fix for the rule, and re-audit. The AI's reasoning is included per finding so you don't have to re-derive what was wrong.`),s.push(""),D.forEach((R,G)=>{const Y=R.wcagCriterion?` — WCAG ${R.wcagCriterion}`:"";s.push(`### ${G+1}. \`${R.ruleId}\`${Y}`),s.push(""),s.push(`**Selector:** \`${R.selector}\``),s.push(""),s.push(`**AI verdict (FAIL):** ${R.reasoning}`),s.push("");const H=lt[R.ruleId];H&&(s.push(`**Recipe:** ${H.summary}`),s.push(""),H.snippet&&(s.push("```"+(H.snippetLang??"")),s.push(H.snippet),s.push("```"),s.push("")))}),s.push("---"),s.push("")}s.push("## Violations to fix"),s.push(""),v.forEach((y,D)=>{var Re;const R=lt[y.ruleId],G=y._instanceSelectors.length;if(s.push(`### ${D+1}. \`${y.ruleId}\` — ${y.impact} — WCAG ${y.wcagCriterion} ${y.wcagLevel}${G>1?` (${G} instances)`:""}`),s.push(""),s.push(`**Description:** ${y.description}`),s.push(""),G>1){s.push(`**Affects ${G} spots — fix at the SOURCE (one CSS rule, one component, one shared class) — DO NOT patch each instance separately:**`);for(const ee of y._instanceSelectors.slice(0,8))s.push(`- \`${ee}\``);y._instanceSelectors.length>8&&s.push(`- (and ${y._instanceSelectors.length-8} more)`),s.push(""),s.push(`> **Find the common source.** ${G} matches means there's a single component / class / CSS rule producing all of them. Search the codebase for the shared class name OR the JSX/template that renders this element, then patch ONCE there. If you can't find a shared source (the elements are genuinely independent), apply the same fix to each — but flag that to the user as "code smell: this pattern is repeated."`)}else s.push(`**Selector:** \`${y.target.selector}\``);s.push(""),s.push(`**Found in state(s):** ${y._states.join(" / ")}`),s.push(""),s.push(Cr(y)),s.push("");const Y=Bh(y);if(Y.length>0){for(const ee of Y)s.push(`**${ee.label}:** ${ee.value}`);s.push("")}y.target.failureSummary&&(s.push(`**Diagnostic:** ${y.target.failureSummary.replace(/\n+/g," ")}`),s.push("")),y.ai&&(s.push(`**[AI] AI-verified finding** (model: ${y.ai.model}, confidence: ${(y.ai.confidence*100).toFixed(0)}%)`),s.push(`> ${y.ai.reasoning}`),s.push(""));const H=y.target.cascadeChain;if(H&&(H.color||H.backgroundColor)){if(s.push("**CSS cascade chain (authored → rendered):**"),H.color){const ee=H.color.vars.length>0?` (vars: ${H.color.vars.map(be=>`\`${be}\``).join(", ")})`:"";s.push(`- \`color\`: \`${H.color.authored}\` → \`${H.color.rendered}\`${ee}${H.color.ruleSelector?` — set in rule \`${H.color.ruleSelector}\``:""}`)}if(H.backgroundColor){const ee=H.backgroundColor.vars.length>0?` (vars: ${H.backgroundColor.vars.map(be=>`\`${be}\``).join(", ")})`:"";s.push(`- \`background-color\`: \`${H.backgroundColor.authored}\` → \`${H.backgroundColor.rendered}\`${ee}${H.backgroundColor.ruleSelector?` — set in rule \`${H.backgroundColor.ruleSelector}\``:""}`)}s.push("")}const K=i==null?void 0:i[y.matchKey];if(K&&K.suggestions.length>0){s.push(`**AI color suggestions** (cached from in-UI tooling — ${K.suggestions.length} candidates passing WCAG):`);for(const ee of K.suggestions)s.push(`- foreground \`${ee.foreground}\` on background \`${ee.background}\` → ${ee.contrast.toFixed(2)}:1 — ${ee.rationale}`);s.push(`> AI reasoning: ${K.reasoning}`),s.push("")}s.push("**Element HTML:**"),s.push("```html"),s.push(y.target.outerHTML),s.push("```"),s.push("");const Q=(Re=K==null?void 0:K.suggestions)==null?void 0:Re[0],ae=Pi(y,{suggestedColors:Q?{foreground:Q.foreground,background:Q.background}:void 0});if(ae){s.push("**Suggested fix for this element** (apply to the source, not the rendered DOM):"),s.push("```diff");for(const ee of ae.current.split(`
|
|
1675
1675
|
`))s.push(`- ${ee}`);for(const ee of ae.suggested.split(`
|
|
1676
1676
|
`))s.push(`+ ${ee}`);s.push("```"),ae.note&&s.push(`> ${ae.note}`),s.push("")}R?(s.push(`**Recipe:** ${R.summary}`),R.snippet&&(s.push(""),s.push("```"+(R.snippetLang??"")),s.push(R.snippet),s.push("```"))):s.push(`**Recipe:** No curated recipe for this rule — apply the standard WCAG ${y.wcagCriterion} guidance.`),y.helpUrl&&(s.push(""),s.push(`**More info:** ${y.helpUrl}`)),s.push(""),s.push("---"),s.push("")});const O=qh(e,r);if(O.length>0){s.push("## Items axe couldn't determine — needs human review"),s.push(""),s.push("axe-core ran these rules but couldn't conclude pass/fail (the most common cause is color-contrast on a gradient / image / semi-transparent background). For each element below: if you have vision capability, sample the rendered background and compute the contrast ratio; otherwise ask the user to verify visually."),s.push("");for(const y of O)s.push(`### \`${y.ruleId}\` on \`${y.selector}\``),s.push(""),s.push(`- **WCAG criterion:** ${y.wcagCriterion}`),s.push(`- **Page URL:** ${y.pageUrl}`),y.failureSummary&&s.push(`- **axe diagnostic:** ${y.failureSummary}`),(Be=y.styles)!=null&&Be.foreground&&s.push(`- **Foreground:** \`${y.styles.foreground}\``),(qe=y.styles)!=null&&qe.background&&s.push(`- **Background:** \`${y.styles.background}\``),($e=y.styles)!=null&&$e.fontSize&&s.push(`- **Font size:** ${y.styles.fontSize}px, weight ${y.styles.fontWeight??"unknown"}`),(ie=y.styles)!=null&&ie.textSample&&s.push(`- **Text sample:** ${JSON.stringify(y.styles.textSample)}`),y.aiAttemptReasoning&&(s.push(""),s.push("**Our AI resolver already attempted this element and returned uncertain.** Its reasoning:"),s.push(""),s.push(`> ${y.aiAttemptReasoning.replace(/\n/g,`
|
|
@@ -2058,8 +2058,8 @@ No crawl results found in storage. Run a site crawl from the Crawl tab and try a
|
|
|
2058
2058
|
> `)}`:"> (no reasoning recorded)"),t.push(""),n==="fail"?(r==="2.4.3"?(t.push("**Fix this:** Reorder DOM source so the natural tab sequence matches the visual reading order, or assign `tabindex` only where you intentionally diverge from source order (and document the reason). Avoid positive `tabindex` values. The tab sequence the AI walked is below — fix every element whose position is out of order vs. its visible position on the page."),t.push(""),t.push('**Anti-patterns to avoid:** adding `tabindex="1"`, `"2"`, etc. (positive values create a parallel focus order that\'s a maintenance nightmare); using `tabindex="-1"` to skip elements that should be reachable; adding JS focus() management to "fix" what DOM-order should solve directly.'),t.push(""),t.push("**Verify by re-auditing:** after the fix, the 2.4.3 walkthrough should return pass.")):r==="2.1.2"?(t.push("**Fix this:** Identify any region where focus gets stuck. Common offenders: modal dialogs that intercept Tab without an escape route (add Escape handler + restore focus on close), custom widgets that loop within their own focus trap without an exit, contenteditable elements consuming Tab as literal input. Ensure forward Tab AND backward Shift+Tab can always reach the rest of the page from any focused element."),t.push(""),t.push("**Anti-patterns to avoid:** suppressing Tab in keydown handlers (event.preventDefault on Tab without specific intent); using `inert` on the wrong container so users can't reach controls that should be focusable."),t.push(""),t.push("**Verify by re-auditing:** after the fix, the 2.1.2 walkthrough should return pass — Tab should cycle forward AND Shift+Tab should cycle backward through every focusable element on the page.")):r==="2.4.7"?(t.push("**Fix this:** Every focusable element must show a visible focus indicator. If you use `outline: none` (e.g., for design reasons), you MUST compensate with a `:focus-visible` box-shadow, border, or background change that has at least 3:1 contrast against the unfocused state. Check every selector in the recorded walk below."),t.push(""),t.push("**Anti-patterns to avoid:** `outline: 0 !important` in a global reset without a `:focus-visible` replacement; relying on `:focus` instead of `:focus-visible` (the latter is the correct hook for keyboard-only focus styling); using ONLY a background color change with insufficient contrast (3:1 minimum against the unfocused state)."),t.push(""),t.push("**Verify by re-auditing:** after the fix, the 2.4.7 walkthrough should return pass on every captured focus state.")):r==="1.3.2"?(t.push("**Fix this:** ensure the DOM source order matches the visual reading order. Screen readers read DOM order; CSS (`flex-direction: row-reverse`, `order:`, `grid-area`, absolute positioning) does NOT reorder what the SR hears. Reorder the HTML so the source flows in the same direction the eye reads, then use minimal CSS for visual placement. Where DOM-reorder is genuinely not feasible (legitimate multi-column footers, RTL emulation, sticky-banner overlays), document the decision and verify the SR experience is still coherent."),t.push(""),t.push('**Anti-patterns to avoid:** using `tabindex` to "fix" reading order (tabindex affects keyboard order, not screen-reader order); adding `aria-flowto` (broadly unsupported; not a real fix); leaving inline `display: none` content that holds the "logical" reading order (it won\'t reach the SR).'),t.push(""),t.push("**Verify by re-auditing:** after the fix, the 1.3.2 walkthrough should return pass — the AI judges whether DOM-flagged divergences are real reading-order breaks vs. legitimate layout patterns.")):r==="1.4.11"?(t.push("**Fix this:** every UI component (button border, form input border, focus ring, icon, status indicator, graphical content needed for understanding) must have at least 3:1 contrast against adjacent colors. For each failed element: raise the foreground/border color, add a darker/lighter background to increase contrast, or thicken the indicator (e.g., 2px instead of 1px border). The AI vision identified specific elements; fix the CSS for each."),t.push(""),t.push('**Anti-patterns to avoid:** ONLY changing opacity/transparency (the rendered contrast on a varied background still fails); using box-shadow as a "border" without ensuring the shadow color meets 3:1 against the surrounding pixels; relying on hover state for contrast (must be visible in default state too).'),t.push(""),t.push("**Verify by re-auditing:** after the fix, the 1.4.11 walkthrough should return pass. Re-run with a fresh screenshot; AI will re-judge the updated colors.")):r==="2.5.3"&&(t.push('**Fix this:** every interactive control\'s accessible name must contain its visible text (case-insensitive, allowing icons/punctuation to be ignored). The AI listed which elements failed. For each, choose ONE of: (a) ensure the visible text matches the accessible name (e.g., button text); (b) use `aria-label` or `aria-labelledby` that STARTS WITH the visible text (e.g., `aria-label="Search — search the catalog"` when visible text is "Search"); (c) remove the conflicting `aria-label` and let the textContent serve as the name. Test by saying "click [visible text]" with speech recognition (Voice Control / Dragon) — the correct fix activates the right control.'),t.push(""),t.push('**Anti-patterns to avoid:** using `aria-label` that REPLACES the visible text entirely (e.g., visible "Cancel" + `aria-label="Close form"` — speech-recognition users saying "click Cancel" will fail); using `title=` attribute alone (not all SRs respect it as the accessible name); using `aria-describedby` for naming (that\'s for descriptions, not names).'),t.push(""),t.push("**Verify by re-auditing:** after the fix, the 2.5.3 walkthrough should return pass — the DOM walker re-collects {visibleText, accessibleName} pairs and AI re-judges ambiguous cases.")),t.push("")):n==="pass"?(t.push("_AI walked the criterion and found no issues. No action needed; this is positive evidence the criterion is met._"),t.push("")):(t.push("_AI could not confidently decide. Manually verify or re-run the walkthrough after rendering changes settle._"),t.push(""));const m=(Array.isArray(a.steps)?a.steps:[]).filter(u=>u&&u.action!=="initial");if(m.length>0){t.push(`<details><summary>Recorded ${m.length}-step keyboard walkthrough</summary>`),t.push(""),t.push("| # | Action | Selector | Role | Accessible name |"),t.push("|---|---|---|---|---|");for(const u of m.slice(0,30)){const g=u.focused??{selector:"?",role:"",name:""},h=String(g.selector??"?"),f=String(g.role??"-")||"-",w=String(g.name??"(no name)").replace(/\|/g,"\\|").slice(0,60),b=String(u.action??""),k=typeof u.ordinal=="number"?u.ordinal:"?";t.push(`| ${k} | ${b} | \`${h}\` | ${f} | ${w} |`)}m.length>30&&t.push(`| … | ${m.length-30} more steps elided | | | |`),t.push(""),t.push("</details>"),t.push("")}}catch(n){console.warn("[report-exporter] walkthrough render skipped:",n)}return t.push("---"),t.join(`
|
|
2059
2059
|
`)}function ip(e){var a;const t=new Set;for(const n of e){const o=n.currentState;if(!o)continue;const r=[];o.pseudoState&&o.pseudoState!=="default"&&r.push(o.pseudoState),o.theme&&o.theme!=="light"&&r.push(o.theme),o.direction&&o.direction!=="ltr"&&r.push(o.direction),(a=o.breakpoint)!=null&&a.id&&o.breakpoint.id!=="desktop"&&r.push(o.breakpoint.id),o.ariaVariation&&o.ariaVariation.id!=="none"&&r.push(`aria=${o.ariaVariation.id}`),t.add(r.length===0?"default":r.join("/"))}return{stateCount:t.size,states:Array.from(t).sort()}}const sp=/-needs-site-crawl$/;function Hn(e){try{const t=new URL(e);t.hash="";let a=t.toString();return a.endsWith("/")&&t.pathname!=="/"&&(a=a.slice(0,-1)),a}catch{return e}}function Qi(e,t,a=new Set){var l;const n=new Map;for(const p of e){const m=p.results.flatMap(u=>u.violations).filter(u=>!u.needsReview&&!a.has(u.matchKey)&&Fr(u.ruleId));for(const u of m){const g=n.get(u.ruleId)??{vs:[],urls:new Set};g.vs.push(u),g.urls.add(p.url),n.set(u.ruleId,g)}}const o={critical:0,serious:1,moderate:2,minor:3},r=Array.from(n.entries()).sort(([,p],[,m])=>(o[p.vs[0].impact]??99)-(o[m.vs[0].impact]??99)),i=r.length,c=[];if(c.push("# Help me reduce my website’s lawsuit-target risk"),c.push(""),c.push("You are an accessibility advisor helping me — a non-technical site owner — lower my exposure to the automated tools that ADA demand-letter firms use to find targets. I crawled my whole site and the findings below are ONLY the issues those automated scanners commonly flag. Please explain in plain English."),c.push(""),c.push("> **Scope.** This is the lawsuit-target (lawfare) view, not a full WCAG conformance audit. It covers the whole crawled site. Fixing everything here gets me off the easy-target list; it is not the same as a formal WCAG 2.1 AA conformance claim (for that I’d use Power or Dev mode in the auditor)."),c.push(""),c.push("## What I need from you"),c.push(""),c.push("1. **First, ask what I use to manage my site** (Squarespace, Wix, Shopify, WordPress + theme, Webflow, custom, etc.) — your advice differs per platform."),c.push("2. **For each finding:** tell me whether **I can fix it myself** in my editor (colors, alt text, link/button labels, page titles/language) or whether **my developer needs to** — and give me a short paragraph I can forward."),c.push("3. **If a finding spans multiple pages, it’s probably a shared template/theme element** — fixing it once helps every page."),c.push(""),c.push("## Site-wide lawsuit-target summary"),c.push(""),c.push(`- **Pages crawled:** ${e.length}`),c.push(`- **Lawsuit-target grade:** ${t.site.letter} — ${t.site.tier} risk`),c.push(`- **${t.site.headline}**`),c.push(`- **Lawsuit-target findings site-wide:** ${i}`),c.push(""),c.push("## The findings, in plain language"),c.push(""),i===0)return c.push("No findings of the kind automated demand-letter scanners commonly flag (missing image alt text, low color contrast, unlabeled buttons/links, missing page language or title). That keeps the site off the easy-target list. This is not a guarantee — scanners add checks over time, and a full WCAG conformance review (Power/Dev mode) covers far more. Re-crawl as the site changes."),c.push(""),c.push("Since there’s nothing to fix here, help me understand: what should I keep an eye on as I add new pages or content, to stay off the radar?"),c.join(`
|
|
2060
2060
|
`);let d=1;for(const[p,{vs:m,urls:u}]of r){const g=(l=m.find(b=>b.target.opacityContext))==null?void 0:l.target.opacityContext,h=Mn(p,g),f=m[0].impact,w=Array.from(u);c.push(`### ${d}. [${f.toUpperCase()}] ${h.whatsWrong}`),c.push(""),c.push(`**Why this matters.** ${h.whyItMatters}`),c.push(""),c.push(`**Plain-language fix guidance.** ${h.howToFix}`),c.push(""),c.push(`**Where (${w.length} ${w.length===1?"page":"pages"}${w.length>1?" — likely a shared template element, fix once":""}):**`);for(const b of w.slice(0,8))c.push(`- ${b}`);w.length>8&&c.push(`- (and ${w.length-8} more pages)`),c.push(""),c.push(`*Technical reference for your developer:* axe-core rule \`${p}\` — https://dequeuniversity.com/rules/axe/4.11/${p}`),c.push(""),d++}return c.push("---"),c.push(""),c.push("## How to use this"),c.push(""),c.push("Walk through these with me one at a time. Start by asking which platform I use, then for each finding tell me if it’s a self-fix or a developer task, and help me knock out the shared-template items first since they clear multiple pages at once."),c.push(""),c.push(`_Generated by wcagcheckr v${Ee()} — site crawl (owner / lawsuit-target lens, ${e.length} pages)._`),c.join(`
|
|
2061
|
-
`)}const cp={"1.3.2":"Meaningful Sequence (A)","1.4.11":"Non-text Contrast (AA)","2.1.2":"No Keyboard Trap (A)","2.4.3":"Focus Order (A)","2.4.7":"Focus Visible (AA)","2.5.3":"Label in Name (A)"},lp={"1.3.2":"Make the DOM source order match the visual reading order — CSS (order / flex-direction / grid-area / absolute positioning) does NOT move what a screen reader announces. Reorder the HTML; use minimal CSS for visual placement.","1.4.11":"Bring the cited component/state to >= 3:1 contrast against its adjacent color, OR distinguish the state by a non-color means (weight, underline, icon, border) so a sub-3:1 color delta is not the only differentiator. The reasoning above names the exact element + measured ratio + colors.","2.1.2":"Remove the focus trap: Tab AND Shift+Tab must always be able to leave any focused element (modals add Escape-to-close + restore focus on close).","2.4.3":"Reorder DOM source so the tab sequence matches the visual reading order; avoid positive tabindex values.","2.4.7":"Give every focusable element a visible :focus-visible indicator with >= 3:1 contrast against the unfocused state (never outline:none with no replacement).","2.5.3":"Ensure each control's accessible name CONTAINS its visible text — e.g. aria-label should START WITH the visible label rather than replace it."};function Yo(e){const t=[];if(!e||e.length===0)return t;t.push("**AI-walkthrough findings to fix on this page** — the AI watched the live DOM/keyboard and identified a concrete failure; treat each as a required fix:"),t.push("");for(const a of e){const n=String(a.criterionId),o=cp[n]??n,r=typeof a.reasoning=="string"&&a.reasoning.trim()?a.reasoning.trim():"(no reasoning recorded — re-run the walkthrough on this page)";t.push(`- **WCAG ${n} — ${o} — [FAIL]:** ${r}`);const i=lp[n];i&&t.push(` - **Fix:** ${i}`),t.push(" - **Verify:** re-run the crawl at the same depth; this walkthrough should then return pass.")}return t.push(""),t}function Zi(e,t,a=new Map,n=new Map,o=[]){var m;const r=[];r.push("# Accessibility Fix Request — Site Crawl"),r.push(""),r.push("> **Your job is to APPLY the fixes in this report — not to evaluate, second-guess, or re-litigate whether the findings are correct.** This report is the output of a real automated crawl + audit (axe-core run in a live browser across many UI states) plus AI verification; the triage already happened upstream. Do not debate severity or skip items you personally doubt. Work page by page, apply the canonical fix for each finding, and report the outcome per the Work contract below. A genuine blocker or a real user-decision is the only acceptable non-fix — and those are reported explicitly per the contract, never silently dropped."),r.push(""),r.push(`You are a senior accessibility engineer pairing with the user on their codebase. The user has crawled ${t.pagesAudited} page${t.pagesAudited===1?"":"s"} on this site and below you'll find the per-page WCAG findings + the cross-page consistency findings. Your task: walk page by page, apply fixes, and produce one atomic commit per fix.`),r.push(""),r.push(`> **Note on this report's scope.** This is a triage + bulk-fix prompt covering all crawled pages in one document. The user can also drop into any single page in the wcagcheckr side panel for per-finding acknowledgements, guided manual checks, fix-preview overlays, and interactive walkthrough re-runs — those are intentionally NOT in this report; they live in the per-page UI surface. If you need that level of control for a specific finding, suggest the user click that page in the Crawl tab's "Pages, worst first" list.`),r.push("");let i=0;for(const u of n.values())i+=u.length;let c=0;for(const u of a.values())for(const g of u)g.verdict==="fail"&&(c+=1);const d=t.totalUniqueViolations+i+c;r.push("## [!] Work contract — read before doing anything else"),r.push(""),r.push(`**Below you have ${d} finding${d===1?"":"s"} across ${t.pagesAudited} page${t.pagesAudited===1?"":"s"}** (${t.totalUniqueViolations} axe violation${t.totalUniqueViolations===1?"":"s"}${i>0?` + ${i} pixel-contrast-sampler fail${i===1?"":"s"}`:""}${c>0?` + ${c} AI-walkthrough fail${c===1?"":"s"}`:""}). When you respond, every numbered finding needs an outcome. Do not stop early. Do not declare done without addressing every finding.`),r.push(""),r.push("**For each numbered finding, produce one of:**"),r.push(""),r.push("- [DONE] **Applied** — the canonical fix was applied. Cite the file + line. If one shared-component fix resolves N findings, group them under the SAME fix and list which findings it covers."),r.push("- [BLOCKED] **Blocked** — explicitly state WHY (missing info, ambiguous user content, dependency change required). One sentence per blocked finding."),r.push("- [NEEDS-DECISION] **Needs user decision** — list the options inline."),r.push(""),r.push(`**The single answer you cannot use: "skipped" or "didn't get to it."** Stop and tell the user if you ran out of context — do not silently omit items.`),r.push(""),r.push("**Required final response format** — completion table:"),r.push(""),r.push("```"),r.push("| # | Rule ID | Page(s) | Status | Notes |"),r.push("|---|---------|---------|--------|-------|"),r.push("| 1 | <rule> | <url> | DONE / BLOCKED / NEEDS-DECISION | <file:line OR reason> |"),r.push(`| ${d} | ... | ... | ... | ... |`),r.push("```"),r.push(""),r.push("**Every row must be present.** One pass through this prompt should produce a fix or a clear next action for every finding — not a partial sweep that forces the user to re-run."),r.push(""),r.push("---"),r.push(""),r.push(_h),r.push("---"),r.push(""),r.push("## Site-wide summary"),r.push(""),r.push(`- **Start URL:** ${t.startUrl}`),r.push(`- **Pages audited:** ${t.pagesAudited}${t.pagesFailed>0?` (${t.pagesFailed} failed)`:""}`),r.push(`- **Site grade:** ${
|
|
2061
|
+
`)}const cp={"1.3.2":"Meaningful Sequence (A)","1.4.11":"Non-text Contrast (AA)","2.1.2":"No Keyboard Trap (A)","2.4.3":"Focus Order (A)","2.4.7":"Focus Visible (AA)","2.5.3":"Label in Name (A)"},lp={"1.3.2":"Make the DOM source order match the visual reading order — CSS (order / flex-direction / grid-area / absolute positioning) does NOT move what a screen reader announces. Reorder the HTML; use minimal CSS for visual placement.","1.4.11":"Bring the cited component/state to >= 3:1 contrast against its adjacent color, OR distinguish the state by a non-color means (weight, underline, icon, border) so a sub-3:1 color delta is not the only differentiator. The reasoning above names the exact element + measured ratio + colors.","2.1.2":"Remove the focus trap: Tab AND Shift+Tab must always be able to leave any focused element (modals add Escape-to-close + restore focus on close).","2.4.3":"Reorder DOM source so the tab sequence matches the visual reading order; avoid positive tabindex values.","2.4.7":"Give every focusable element a visible :focus-visible indicator with >= 3:1 contrast against the unfocused state (never outline:none with no replacement).","2.5.3":"Ensure each control's accessible name CONTAINS its visible text — e.g. aria-label should START WITH the visible label rather than replace it."};function Yo(e){const t=[];if(!e||e.length===0)return t;t.push("**AI-walkthrough findings to fix on this page** — the AI watched the live DOM/keyboard and identified a concrete failure; treat each as a required fix:"),t.push("");for(const a of e){const n=String(a.criterionId),o=cp[n]??n,r=typeof a.reasoning=="string"&&a.reasoning.trim()?a.reasoning.trim():"(no reasoning recorded — re-run the walkthrough on this page)";t.push(`- **WCAG ${n} — ${o} — [FAIL]:** ${r}`);const i=lp[n];i&&t.push(` - **Fix:** ${i}`),t.push(" - **Verify:** re-run the crawl at the same depth; this walkthrough should then return pass.")}return t.push(""),t}function Zi(e,t,a=new Map,n=new Map,o=[]){var m;const r=[];r.push("# Accessibility Fix Request — Site Crawl"),r.push(""),r.push("> **Your job is to APPLY the fixes in this report — not to evaluate, second-guess, or re-litigate whether the findings are correct.** This report is the output of a real automated crawl + audit (axe-core run in a live browser across many UI states) plus AI verification; the triage already happened upstream. Do not debate severity or skip items you personally doubt. Work page by page, apply the canonical fix for each finding, and report the outcome per the Work contract below. A genuine blocker or a real user-decision is the only acceptable non-fix — and those are reported explicitly per the contract, never silently dropped."),r.push(""),r.push(`You are a senior accessibility engineer pairing with the user on their codebase. The user has crawled ${t.pagesAudited} page${t.pagesAudited===1?"":"s"} on this site and below you'll find the per-page WCAG findings + the cross-page consistency findings. Your task: walk page by page, apply fixes, and produce one atomic commit per fix.`),r.push(""),r.push(`> **Note on this report's scope.** This is a triage + bulk-fix prompt covering all crawled pages in one document. The user can also drop into any single page in the wcagcheckr side panel for per-finding acknowledgements, guided manual checks, fix-preview overlays, and interactive walkthrough re-runs — those are intentionally NOT in this report; they live in the per-page UI surface. If you need that level of control for a specific finding, suggest the user click that page in the Crawl tab's "Pages, worst first" list.`),r.push("");let i=0;for(const u of n.values())i+=u.length;let c=0;for(const u of a.values())for(const g of u)g.verdict==="fail"&&(c+=1);const d=t.totalUniqueViolations+i+c;r.push("## [!] Work contract — read before doing anything else"),r.push(""),r.push(`**Below you have ${d} finding${d===1?"":"s"} across ${t.pagesAudited} page${t.pagesAudited===1?"":"s"}** (${t.totalUniqueViolations} axe violation${t.totalUniqueViolations===1?"":"s"}${i>0?` + ${i} pixel-contrast-sampler fail${i===1?"":"s"}`:""}${c>0?` + ${c} AI-walkthrough fail${c===1?"":"s"}`:""}). When you respond, every numbered finding needs an outcome. Do not stop early. Do not declare done without addressing every finding.`),r.push(""),r.push("**For each numbered finding, produce one of:**"),r.push(""),r.push("- [DONE] **Applied** — the canonical fix was applied. Cite the file + line. If one shared-component fix resolves N findings, group them under the SAME fix and list which findings it covers."),r.push("- [BLOCKED] **Blocked** — explicitly state WHY (missing info, ambiguous user content, dependency change required). One sentence per blocked finding."),r.push("- [NEEDS-DECISION] **Needs user decision** — list the options inline."),r.push(""),r.push(`**The single answer you cannot use: "skipped" or "didn't get to it."** Stop and tell the user if you ran out of context — do not silently omit items.`),r.push(""),r.push("**Required final response format** — completion table:"),r.push(""),r.push("```"),r.push("| # | Rule ID | Page(s) | Status | Notes |"),r.push("|---|---------|---------|--------|-------|"),r.push("| 1 | <rule> | <url> | DONE / BLOCKED / NEEDS-DECISION | <file:line OR reason> |"),r.push(`| ${d} | ... | ... | ... | ... |`),r.push("```"),r.push(""),r.push("**Every row must be present.** One pass through this prompt should produce a fix or a clear next action for every finding — not a partial sweep that forces the user to re-run."),r.push(""),r.push("---"),r.push(""),r.push(_h),r.push("---"),r.push(""),r.push("## Site-wide summary"),r.push(""),r.push(`- **Start URL:** ${t.startUrl}`),r.push(`- **Pages audited:** ${t.pagesAudited}${t.pagesFailed>0?` (${t.pagesFailed} failed)`:""}`),r.push(`- **Site grade:** ${Gr(t.siteGrade,t.spotCheckGate)}`),r.push(`- **Total findings site-wide:** ${d} (${t.totalUniqueViolations} axe, ${i} pixel-contrast-sampler)`),r.push(`- **Severity breakdown:** ${t.totals.critical} critical · ${t.totals.serious+i} serious · ${t.totals.moderate} moderate · ${t.totals.minor} minor`),r.push(""),r.push(`> ${_r}`),r.push(""),r.push(`- **Audit rigor (settings this grade was earned under):** ${ei(t.auditSettings)}`);{const u=ti(t.auditSettings);u?r.push(` - [!] **${u}**`):r.push(" - [OK] **Full 2.1 AA audit** — settings cleared the conformance bar.")}r.push("");const l=t.topViolations.filter(u=>!/-needs-site-crawl$/.test(u.ruleId));if(l.length>0){r.push("**Most-frequent rules across the site** (fixing one of these in a shared component / layout resolves N pages at once):"),r.push("");for(const u of l.slice(0,8)){const h=u.urlsAffected>=Math.ceil(t.pagesAudited/2)?" — **likely a single shared-component fix**":"";r.push(`- \`${u.ruleId}\` — ${u.impact} — found on ${u.urlsAffected} page${u.urlsAffected===1?"":"s"} (${u.totalOccurrences} total instance${u.totalOccurrences===1?"":"s"})${h}`)}r.push("")}const p=t.consistencyFindings??[];if(p.length>0){r.push("## Cross-page consistency findings"),r.push(""),r.push("These are divergences detected by comparing structure across pages (WCAG 3.2.3, 3.2.4, 3.2.6, 3.3.7). Each finding references multiple pages — typically the fix is in a SHARED component (header / nav / footer / form)."),r.push("");for(const u of p){const g=u.aiVerdict?u.aiVerdict.verdict==="uncertain"?" _(AI: uncertain — verify manually)_":` _(AI: confirmed real, confidence ${(u.aiVerdict.confidence*100).toFixed(0)}%)_`:"";r.push(`### ${u.wcagCriterion} ${u.impact} — ${u.description}${g}`),r.push("");for(const h of u.details.split(`
|
|
2062
2062
|
`))r.push(h);(m=u.aiVerdict)!=null&&m.reasoning&&(r.push(""),r.push(`AI reasoning: ${u.aiVerdict.reasoning}`)),r.push("")}}else r.push("## Cross-page consistency findings"),r.push(""),r.push("No cross-page divergences detected by the heuristic comparators. Still verify manually that nav order, accessible names of shared components, and help-mechanism position are consistent across pages."),r.push("");return r.push("---"),r.push(""),r.push(`## Per-page findings (${e.length} page${e.length===1?"":"s"})`),r.push(""),r.push("Each page section below is shaped like a single-page audit report — violations to fix + relevant recipes. Fix one page at a time. The cross-page findings above usually point at a SHARED component; fix that once and re-run the audit to see N pages resolve together."),r.push(""),e.forEach((u,g)=>{const h=g+1;if(r.push(`### Page ${h} of ${e.length}: ${u.url}`),r.push(""),u.error){r.push(`> [!] Audit failed for this page: \`${u.error}\``),r.push(""),r.push("---"),r.push("");return}const f=u.results.flatMap(A=>A.violations).filter(A=>!sp.test(A.ruleId)),w=n.get(Hn(u.url))??[];if(f.length===0&&w.length===0){const A=a.get(u.componentId)??[],S=A.filter(F=>F.verdict==="fail");if(S.length>0?r.push(`No axe/automated violations on this page, but **${S.length} AI-walkthrough finding${S.length===1?"":"s"}** below require a fix. Also apply the manual checklist.`):r.push("No automated violations detected on this page. Still apply the manual checklist below."),r.push(""),A.length>0){r.push("**AI walkthrough verdicts on this page:**");for(const F of A){const T=F.verdict==="pass"?"[PASS]":F.verdict==="fail"?"[FAIL]":"[?]";r.push(`- ${T} WCAG ${F.criterionId} — ${F.verdict.toUpperCase()}`)}r.push(""),r.push(...Yo(S))}r.push("---"),r.push("");return}const b=new Map,k=new Map;for(const A of f){const S=Ne(A.ruleId,A.target.selector);b.has(S)||b.set(S,A);const F=k.get(S)??[];F.push(A),k.set(S,F)}const v=Array.from(b.entries()).map(([A,S])=>({v:S,instances:k.get(A)??[S]})).sort((A,S)=>{const F={critical:0,serious:1,moderate:2,minor:3};return(F[A.v.impact]??99)-(F[S.v.impact]??99)}),s={critical:0,serious:0,moderate:0,minor:0};for(const{v:A}of v)s[A.impact]++;s.serious+=w.length;const x=v.length+w.length;r.push(`**${x} finding${x===1?"":"s"}** · severity: ${s.critical} critical · ${s.serious} serious · ${s.moderate} moderate · ${s.minor} minor`),r.push(""),v.forEach(({v:A,instances:S},F)=>{const T=lt[A.ruleId],I=Ho(`${A.ruleId} ${A.wcagCriterion}`)==="human-verify"?"[Human-verify]":"[AI-fixable]";r.push(`#### ${h}.${F+1} \`${A.ruleId}\` — ${A.impact} — WCAG ${A.wcagCriterion} ${A.wcagLevel} — ${I}`),r.push(""),r.push(`**Description:** ${A.description}`),r.push(""),r.push(Cr(A)),r.push(""),r.push(`**Selector:** \`${A.target.selector}\``);const{stateCount:N,states:C}=ip(S);N>1?(r.push(""),r.push(`_Failed in ${N} matrix state${N===1?"":"s"}:_ ${C.join(", ")}`)):S.length>1&&(r.push(""),r.push(`_${S.length} element instances on this page (same state)._`)),r.push(""),A.target.failureSummary&&(r.push("**Diagnostic:**"),r.push("```"),r.push(A.target.failureSummary.trim()),r.push("```"),r.push("")),A.target.outerHTML&&(r.push("**Element HTML:**"),r.push("```html"),r.push(A.target.outerHTML.slice(0,500)),r.push("```"),r.push(""));const _=Pi(A);if(_){r.push("**Suggested fix for this element** (apply to the source, not the rendered DOM):"),r.push("```diff");for(const M of _.current.split(`
|
|
2063
2063
|
`))r.push(`- ${M}`);for(const M of _.suggested.split(`
|
|
2064
2064
|
`))r.push(`+ ${M}`);r.push("```"),_.note&&r.push(`> ${_.note}`),r.push("")}T!=null&&T.summary&&(r.push("**Recipe:**"),r.push(T.summary),T.snippet&&(r.push(""),r.push("```"+(T.snippetLang??"")),r.push(T.snippet),r.push("```")),r.push("")),A.helpUrl&&(r.push(`**More info:** ${A.helpUrl}`),r.push(""))}),w.forEach((A,S)=>{const F=v.length+S,T=Ho(`${A.ruleId} ${A.wcagCriterion??"1.4.3"}`)==="human-verify"?"[Human-verify]":"[AI-fixable]";r.push(`#### ${h}.${F+1} \`${A.ruleId}\` — serious — WCAG ${A.wcagCriterion??"1.4.3"} AA — ${T}`),r.push(""),r.push("**Description:** Empirical pixel-contrast measurement below the AA threshold (pixel-contrast-sampler)."),r.push(""),r.push(`**Selector:** \`${A.selector}\``),r.push(""),r.push("**Diagnostic:**"),r.push("```"),r.push(A.reasoning.trim()),r.push("```"),r.push(""),r.push("**Recipe:**"),r.push('Pixel-level contrast measurement — the sampler diffed hide-text vs visible viewport screenshots and recovered the rendered foreground/background colors at the worst-contrast pixel. Verify the live computed colors at the cited selector (devtools → Computed → color / background-color). Fix by raising the foreground contrast against the actual rendered background (account for transparency, gradients, and overlay layers). If the sampler caught a transient state (loading skeleton, hover) and the steady-state contrast is fine, surface that as a "Blocked" entry with a note.'),r.push("")});const $=a.get(u.componentId)??[];if($.length>0){r.push("**AI walkthrough verdicts on this page:**");for(const A of $){const S=A.verdict==="pass"?"[PASS]":A.verdict==="fail"?"[FAIL]":"[?]";r.push(`- ${S} WCAG ${A.criterionId} — ${A.verdict.toUpperCase()}: ${A.reasoning.slice(0,240)}${A.reasoning.length>240?"…":""}`)}r.push(""),r.push(...Yo($.filter(A=>A.verdict==="fail")))}r.push("---"),r.push("")}),r.push(Ph(o)),r.push(""),r.push("## Combined verification checklist"),r.push(""),r.push("After all per-page fixes:"),r.push(""),r.push("1. **Re-run the site crawl** at the same depth used here."),r.push("2. Verify the **cross-page consistency findings** above all resolve (or document why a flagged divergence is intentional)."),r.push("3. Verify the **top-frequency rules** across pages resolve in bulk (a single shared-component fix should drop the count on multiple pages)."),r.push("4. **Manual WCAG checks** (audio/video transcripts, character key shortcuts, timing controls, etc.) — the per-page sections above only contain automatable findings. The non-automatable WCAG criteria still apply per page."),r.push("5. **Diff review** — every fix should be reviewable in under 10 minutes. One atomic commit per fix."),r.push(""),r.push(`_Generated by ${ke} v${Ee()} — site crawl mode (${e.length} pages)._`),r.join(`
|
|
2065
|
-
`)}const rf=Object.freeze(Object.defineProperty({__proto__:null,normUrlKey:Hn,registerReportExporterHandlers:rp,renderAiWalkthroughsForPrompt:Xi,toAiPrompt:Bi,toAiPromptForSiteCrawl:Zi,toAiPromptOwner:Yi,toDefenseBundle:Ft,toDeveloperLetter:zi,toEvidenceBundle:qi,toHtmlPrint:jn,toJUnit:Gi,toJson:_i,toLawfarePromptForSiteCrawl:Qi,toOwnerReport:Ki,toSarif:Ui,toVpat:Hi,toolVersion:Ee},Symbol.toStringTag,{value:"Module"})),Kt=Z("sw-auto-export");function dp(e,t,a){const n=ys(e,a);let o="audit";try{o=new URL(t).hostname.replace(/^www\./,"").replace(/[^a-z0-9.-]/gi,"-")}catch{}return`wcagcheckr/${o}/${n}`}async function up(e,t,a){try{if(e==="html-print")return jn(t,a);if(e==="defense-bundle"){const n=await Tt().catch(()=>[]);return Ft(t,a,"defense",void 0,n)}if(e==="deposition-packet")return(await Mi({results:t,delta:a,manualRuns:void 0,aiSummary:null})).html}catch(n){return Kt.warn(`buildContent failed for format=${e}`,n),null}return null}async function hp(e,t){var a,n,o;if(e.length!==0)try{const r=await gs();if(!r.enabled||await je())return;const i=await le().catch(()=>"free");if(!ce(i,"autoExportAuditReports"))return;const c=((a=e[0])==null?void 0:a.pageUrl)??((n=e[0])==null?void 0:n.scope)??"",d=((o=e[0])==null?void 0:o.componentId)??null;for(const l of r.formats)try{const p=await up(l,e,t);if(!p||p.length<200)continue;const m=dp(l,c,d),u=l==="ai-prompt"?"text/markdown":"text/html",{body:g}=ms(p,u),h=`data:${u};charset=utf-8,${encodeURIComponent(g)}`,f=await chrome.downloads.download({url:h,filename:m,saveAs:!1,conflictAction:"uniquify"});Kt.info(`scheduled auto-export started (id=${f}, file=${m})`),c&&await bs({url:c,exportedAt:new Date().toISOString(),format:l,filename:m})}catch(p){Kt.warn(`format ${l} failed in scheduled auto-export`,p)}}catch(r){Kt.warn("autoExportFromScheduledRun failed (audit itself still succeeded)",r)}}const Ge=Z("scheduled-audit-runner"),ha="scheduled-audit:",Jo={hourly:60,"every-4-hours":240,daily:1440,weekly:10080};function Bn(e){return`${ha}${e}`}function sf(e){return e.startsWith(ha)?e.slice(ha.length):null}async function pp(e){const t=Bn(e.id);await chrome.alarms.create(t,{periodInMinutes:Jo[e.cadence],delayInMinutes:Jo[e.cadence]})}async function cf(e){await chrome.alarms.clear(Bn(e))}async function lf(){try{const{listScheduledAudits:e}=await bn(async()=>{const{listScheduledAudits:o}=await Promise.resolve().then(()=>Ws);return{listScheduledAudits:o}},void 0),t=await e(),a=await chrome.alarms.getAll(),n=new Set(t.filter(o=>o.enabled).map(o=>Bn(o.id)));for(const o of a)o.name.startsWith(ha)&&!n.has(o.name)&&await chrome.alarms.clear(o.name);for(const o of t)o.enabled&&await pp(o);Ge.info(`reconciled ${t.length} scheduled-audit alarms`)}catch(e){Ge.warn("failed to reconcile scheduled alarms",e)}}async function df(e){var r;const t=Date.now(),a=await rr(e);if(!a){Ge.warn(`scheduled audit ${e} not found — alarm fired for deleted schedule`);return}if(!a.enabled){Ge.debug(`scheduled audit ${e} is disabled — skipping`);return}if(await je()){await Ct(a,{result:"failed",error:Ze,durationMs:Date.now()-t,totalViolations:0,newVsBaselineViolations:0});return}const n=await le();if(!ce(n,"scheduledAudits")){await Ct(a,{result:"failed",error:"Scheduled audits are not available on your current plan.",durationMs:Date.now()-t,totalViolations:0,newVsBaselineViolations:0});return}await xn({...a,lastResult:"running",lastRunAt:new Date().toISOString()});let o;try{if(o=(await chrome.tabs.create({url:a.url,active:!1})).id,!o)throw new Error("chrome.tabs.create returned no id");await fp(o,3e4);const d=await pt("html",{isCanceled:()=>!1},0,void 0,{asSubroutine:!0,tabIdOverride:o});if(!d.ok){await Ct(a,{result:"failed",error:d.error??"audit failed",durationMs:Date.now()-t,totalViolations:0,newVsBaselineViolations:0});return}const l=d.results.reduce((g,h)=>g+h.violations.length,0),p=(r=d.results[0])==null?void 0:r.delta,m=(p==null?void 0:p.newCount)??0,u=new Date().toISOString();if(await Ct(a,{result:"ok",durationMs:Date.now()-t,totalViolations:l,newVsBaselineViolations:m}),hp(d.results,p),m>0)try{const g=await Yu({scheduleId:a.id,auditedUrl:a.url,ranAt:u,results:d.results});Ge.debug(`alert outcome for ${a.id}`,g)}catch(g){Ge.warn(`alert fire threw for ${a.id}`,g)}}catch(i){const c=i instanceof Error?i.message:String(i);Ge.warn(`scheduled audit ${e} failed:`,c),await Ct(a,{result:"failed",error:c,durationMs:Date.now()-t,totalViolations:0,newVsBaselineViolations:0})}finally{if(o!==void 0)try{await chrome.tabs.remove(o)}catch{}}}async function Ct(e,t){const a=new Date().toISOString();await ir({scheduleId:e.id,ranAt:a,durationMs:t.durationMs,totalViolations:t.totalViolations,newVsBaselineViolations:t.newVsBaselineViolations,error:t.error}),await xn({...e,lastRunAt:a,lastResult:t.result,lastError:t.error,lastTotalViolations:t.totalViolations,lastNewViolationCount:t.newVsBaselineViolations}),await sr(e.id);try{await ah()}catch(n){Ge.warn("status badge refresh failed",n)}}function fp(e,t){return new Promise(a=>{const n=setTimeout(()=>{chrome.tabs.onUpdated.removeListener(o),a()},t),o=(r,i)=>{r===e&&i.status==="complete"&&(clearTimeout(n),chrome.tabs.onUpdated.removeListener(o),setTimeout(a,500))};chrome.tabs.onUpdated.addListener(o)})}export{tt as
|
|
2065
|
+
`)}const rf=Object.freeze(Object.defineProperty({__proto__:null,normUrlKey:Hn,registerReportExporterHandlers:rp,renderAiWalkthroughsForPrompt:Xi,toAiPrompt:Bi,toAiPromptForSiteCrawl:Zi,toAiPromptOwner:Yi,toDefenseBundle:Ft,toDeveloperLetter:zi,toEvidenceBundle:qi,toHtmlPrint:jn,toJUnit:Gi,toJson:_i,toLawfarePromptForSiteCrawl:Qi,toOwnerReport:Ki,toSarif:Ui,toVpat:Hi,toolVersion:Ee},Symbol.toStringTag,{value:"Module"})),Kt=Z("sw-auto-export");function dp(e,t,a){const n=ys(e,a);let o="audit";try{o=new URL(t).hostname.replace(/^www\./,"").replace(/[^a-z0-9.-]/gi,"-")}catch{}return`wcagcheckr/${o}/${n}`}async function up(e,t,a){try{if(e==="html-print")return jn(t,a);if(e==="defense-bundle"){const n=await Tt().catch(()=>[]);return Ft(t,a,"defense",void 0,n)}if(e==="deposition-packet")return(await Mi({results:t,delta:a,manualRuns:void 0,aiSummary:null})).html}catch(n){return Kt.warn(`buildContent failed for format=${e}`,n),null}return null}async function hp(e,t){var a,n,o;if(e.length!==0)try{const r=await gs();if(!r.enabled||await je())return;const i=await le().catch(()=>"free");if(!ce(i,"autoExportAuditReports"))return;const c=((a=e[0])==null?void 0:a.pageUrl)??((n=e[0])==null?void 0:n.scope)??"",d=((o=e[0])==null?void 0:o.componentId)??null;for(const l of r.formats)try{const p=await up(l,e,t);if(!p||p.length<200)continue;const m=dp(l,c,d),u=l==="ai-prompt"?"text/markdown":"text/html",{body:g}=ms(p,u),h=`data:${u};charset=utf-8,${encodeURIComponent(g)}`,f=await chrome.downloads.download({url:h,filename:m,saveAs:!1,conflictAction:"uniquify"});Kt.info(`scheduled auto-export started (id=${f}, file=${m})`),c&&await bs({url:c,exportedAt:new Date().toISOString(),format:l,filename:m})}catch(p){Kt.warn(`format ${l} failed in scheduled auto-export`,p)}}catch(r){Kt.warn("autoExportFromScheduledRun failed (audit itself still succeeded)",r)}}const Ge=Z("scheduled-audit-runner"),ha="scheduled-audit:",Jo={hourly:60,"every-4-hours":240,daily:1440,weekly:10080};function Bn(e){return`${ha}${e}`}function sf(e){return e.startsWith(ha)?e.slice(ha.length):null}async function pp(e){const t=Bn(e.id);await chrome.alarms.create(t,{periodInMinutes:Jo[e.cadence],delayInMinutes:Jo[e.cadence]})}async function cf(e){await chrome.alarms.clear(Bn(e))}async function lf(){try{const{listScheduledAudits:e}=await bn(async()=>{const{listScheduledAudits:o}=await Promise.resolve().then(()=>Ws);return{listScheduledAudits:o}},void 0),t=await e(),a=await chrome.alarms.getAll(),n=new Set(t.filter(o=>o.enabled).map(o=>Bn(o.id)));for(const o of a)o.name.startsWith(ha)&&!n.has(o.name)&&await chrome.alarms.clear(o.name);for(const o of t)o.enabled&&await pp(o);Ge.info(`reconciled ${t.length} scheduled-audit alarms`)}catch(e){Ge.warn("failed to reconcile scheduled alarms",e)}}async function df(e){var r;const t=Date.now(),a=await rr(e);if(!a){Ge.warn(`scheduled audit ${e} not found — alarm fired for deleted schedule`);return}if(!a.enabled){Ge.debug(`scheduled audit ${e} is disabled — skipping`);return}if(await je()){await Ct(a,{result:"failed",error:Ze,durationMs:Date.now()-t,totalViolations:0,newVsBaselineViolations:0});return}const n=await le();if(!ce(n,"scheduledAudits")){await Ct(a,{result:"failed",error:"Scheduled audits are not available on your current plan.",durationMs:Date.now()-t,totalViolations:0,newVsBaselineViolations:0});return}await xn({...a,lastResult:"running",lastRunAt:new Date().toISOString()});let o;try{if(o=(await chrome.tabs.create({url:a.url,active:!1})).id,!o)throw new Error("chrome.tabs.create returned no id");await fp(o,3e4);const d=await pt("html",{isCanceled:()=>!1},0,void 0,{asSubroutine:!0,tabIdOverride:o});if(!d.ok){await Ct(a,{result:"failed",error:d.error??"audit failed",durationMs:Date.now()-t,totalViolations:0,newVsBaselineViolations:0});return}const l=d.results.reduce((g,h)=>g+h.violations.length,0),p=(r=d.results[0])==null?void 0:r.delta,m=(p==null?void 0:p.newCount)??0,u=new Date().toISOString();if(await Ct(a,{result:"ok",durationMs:Date.now()-t,totalViolations:l,newVsBaselineViolations:m}),hp(d.results,p),m>0)try{const g=await Yu({scheduleId:a.id,auditedUrl:a.url,ranAt:u,results:d.results});Ge.debug(`alert outcome for ${a.id}`,g)}catch(g){Ge.warn(`alert fire threw for ${a.id}`,g)}}catch(i){const c=i instanceof Error?i.message:String(i);Ge.warn(`scheduled audit ${e} failed:`,c),await Ct(a,{result:"failed",error:c,durationMs:Date.now()-t,totalViolations:0,newVsBaselineViolations:0})}finally{if(o!==void 0)try{await chrome.tabs.remove(o)}catch{}}}async function Ct(e,t){const a=new Date().toISOString();await ir({scheduleId:e.id,ranAt:a,durationMs:t.durationMs,totalViolations:t.totalViolations,newVsBaselineViolations:t.newVsBaselineViolations,error:t.error}),await xn({...e,lastRunAt:a,lastResult:t.result,lastError:t.error,lastTotalViolations:t.totalViolations,lastNewViolationCount:t.newVsBaselineViolations}),await sr(e.id);try{await ah()}catch(n){Ge.warn("status badge refresh failed",n)}}function fp(e,t){return new Promise(a=>{const n=setTimeout(()=>{chrome.tabs.onUpdated.removeListener(o),a()},t),o=(r,i)=>{r===e&&i.status==="complete"&&(clearTimeout(n),chrome.tabs.onUpdated.removeListener(o),setTimeout(a,500))};chrome.tabs.onUpdated.addListener(o)})}export{tt as $,$p as A,xc as B,ho as C,Lp as D,js as E,Dp as F,Np as G,Pp as H,Dr as I,et as J,qt as K,Up as L,Wp as M,Tr as N,Me as O,Rc as P,Vr as Q,yr as R,gr as S,wr as T,_r as U,Sa as V,Vp as W,Hp as X,jp as Y,_p as Z,Fp as _,zp as a,Ll as a$,Ir as a0,Gp as a1,Fr as a2,Tt as a3,al as a4,Ta as a5,dt as a6,xa as a7,qp as a8,ro as a9,il as aA,Qp as aB,Yp as aC,Xp as aD,Jp as aE,uc as aF,lf as aG,sf as aH,df as aI,Zp as aJ,rp as aK,of as aL,Bp as aM,pt as aN,Du as aO,Mu as aP,Kp as aQ,Wu as aR,Uu as aS,Gu as aT,ef as aU,Ip as aV,nf as aW,Op as aX,Ac as aY,ei as aZ,ti as a_,Mn as aa,ea as ab,_e as ac,pc as ad,Rp as ae,lc as af,zu as ag,Ku as ah,or as ai,af as aj,xn as ak,pp as al,ah as am,Jo as an,cf as ao,Ps as ap,Us as aq,_s as ar,de as as,Le as at,Nd as au,Rd as av,Od as aw,$d as ax,xd as ay,Ad as az,va as b,Gn as b0,hn as b1,zt as b2,vp as b3,Cp as b4,cc as b5,Tp as b6,tf as b7,rf as b8,Nl as c,ku as d,We as e,Vs as f,Mp as g,Ur as h,ba as i,In as j,cr as k,Jt as l,xp as m,kp as n,Sp as o,Ap as p,dr as q,fr as r,Ep as s,Ne as t,vr as u,Xt as v,xr as w,Qs as x,wc as y,ac as z};
|