@wcag-checkr/ci 1.0.0-rc.29 → 1.0.0-rc.31
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.
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{c as F,o as P,e as D,f as sn,g as an,d as W,b as Z,i as on,h as rn}from"./crash-reporter-Dc5lvxvY.js";import{m as me,d as cn,T as ln,a as st,c as Be,i as dn}from"./diff-D4sCAdXf.js";import{o as un,i as at,l as ce,h as te,A as ye,F as pn,k as Et,O as $t,j as Tt,R as hn,n as Ct,p as Se,q as ke,r as Ee,t as fn,g as gn,u as mn,f as le,e as It,a as qe,v as yn,w as bn}from"./forensic-log-B1UCXZ23.js";import{s as ge,T as ot,b as Pe,i as wn}from"./state-PELIq3oj.js";import{m as Ke,D as vn,s as An,h as xn,b as Sn,g as kn,d as En,e as $n}from"./ai-usage-log-Dj9Ub_DT.js";const Tn=F("chrome-api"),Cn="1.3";class In{constructor(t){this.target=t}async attach(){await chrome.debugger.attach(this.target,Cn)}async detach(){try{await chrome.debugger.detach(this.target)}catch(t){Tn.debug("detach soft-failure",t)}}async send(t,n){return chrome.debugger.sendCommand(this.target,t,n)}}function Rn(e){const t=e instanceof Error?e.message:String(e);return/Another debugger is already attached|Cannot attach to/i.test(t)}function On(e){const t=e instanceof Error?e.message:String(e);return Rn(e)?me("DEBUGGER_BUSY","DevTools is open on this tab. Close it to run the audit.",!0,{raw:t}):me("STATE_DRIVE_FAILED",t,!1)}function _n(e){const t=(n,a)=>e(n,a);return chrome.debugger.onDetach.addListener(t),()=>chrome.debugger.onDetach.removeListener(t)}const V=F("state-driver"),Mn=60*1e3,Y=new Map,be=new Map,oe=new Map,se=new Map,Un={default:[],hover:["hover"],focus:["focus"],"focus-visible":["focus","focus-visible"],active:["active"],disabled:[]};async function Rt(e){let t=Y.get(e);if(!t){t=new In({tabId:e});try{await t.attach(),await t.send("Runtime.enable"),await t.send("Page.enable"),await t.send("DOM.enable"),await t.send("DOM.getDocument",{depth:-1,pierce:!0}),await t.send("CSS.enable")}catch(n){try{await t.detach()}catch{}throw n}Y.set(e,t),oe.set(e,new Map),Dn(e),V.info("attached",{tabId:e})}return Pn(e),t}function Dn(e){const t=(n,a,o)=>{var l,r;if(n.tabId!==e)return;const i=oe.get(e);if(i)if(a==="Runtime.executionContextCreated"){const d=o,c=d==null?void 0:d.context;c&&((l=c.auxData)!=null&&l.isDefault)&&((r=c.auxData)!=null&&r.frameId)&&i.set(c.auxData.frameId,c.id)}else if(a==="Runtime.executionContextDestroyed"){const d=o,c=d==null?void 0:d.executionContextId;if(c!==void 0)for(const[u,h]of i.entries())h===c&&i.delete(u)}else a==="Runtime.executionContextsCleared"&&i.clear()};chrome.debugger.onEvent.addListener(t)}async function Nn(e){const t=Y.get(e);if(Y.delete(e),oe.delete(e),se.delete(e),t)try{await t.detach()}catch{}}function Pn(e){const t=be.get(e);t&&clearTimeout(t),be.set(e,setTimeout(()=>{Ye(e).catch(n=>V.warn("idle detach failed",n))},Mn))}async function Ye(e){const t=Y.get(e);t&&(await t.detach(),Y.delete(e)),oe.delete(e),se.delete(e);const n=be.get(e);n&&(clearTimeout(n),be.delete(e))}function Ot(e){const t=[{id:e.frame.id,url:e.frame.url}];for(const n of e.childFrames??[])t.push(...Ot(n));return t}async function Ln(e,t,n){var i;if(!n||n===0)return;const a=oe.get(t);if(!a)return;let o;try{o=(i=(await chrome.scripting.executeScript({target:{tabId:t,frameIds:[n]},func:()=>location.href}))[0])==null?void 0:i.result}catch(l){V.debug("frame URL lookup failed",l)}try{const l=await e.send("Page.getFrameTree"),r=Ot(l.frameTree);if(o){const u=r.find(h=>h.url===o);if(u){const h=a.get(u.id);if(h!==void 0)return h}}const d=l.frameTree.frame.id,c=r.find(u=>u.id!==d);return c?a.get(c.id):void 0}catch(l){V.warn("Page.getFrameTree failed",l);return}}async function Fn(e,t,n){var l;const a={expression:`document.querySelector(${JSON.stringify(t)})`,objectGroup:"wcag-auditor",returnByValue:!1};n!==void 0&&(a.contextId=n);const i=(l=(await e.send("Runtime.evaluate",a)).result)==null?void 0:l.objectId;if(!i)return null;try{return(await e.send("DOM.requestNode",{objectId:i})).nodeId??null}finally{e.send("Runtime.releaseObject",{objectId:i}).catch(()=>{})}}async function _t(e){const t=se.get(e);if(!t)return;const n=Y.get(e);if(n){try{await n.send("CSS.forcePseudoState",{nodeId:t.nodeId,forcedPseudoClasses:[]})}catch(a){V.debug("forcePseudoState clear failed (node may be gone)",a)}se.delete(e)}}async function Le(e,t,n,a){await chrome.scripting.executeScript({target:Je(e,t),func:(o,i)=>{const l=document.querySelector(o);l&&(i?l.setAttribute("disabled",""):l.removeAttribute("disabled"))},args:[n,a]})}async function Mt(e,t){await chrome.scripting.executeScript({target:Je(e,t),func:()=>{const n=document.activeElement;n&&typeof n.blur=="function"&&n.blur()}})}async function Ut(e,t){await chrome.scripting.executeScript({target:{tabId:e,frameIds:[0]},func:n=>{document.documentElement.setAttribute("dir",n)},args:[t]})}async function Gn(e,t,n,a){await chrome.scripting.executeScript({target:Je(e,t),func:(o,i)=>{const l=document.querySelector(o);if(l)for(const[r,d]of Object.entries(i))l.setAttribute(r,d)},args:[n,a]})}function Je(e,t){return t!==void 0&&t!==0?{tabId:e,frameIds:[t]}:{tabId:e}}async function Wn(e,t,n,a){const o=await Rt(e);if(await _t(e),n){t.pseudoState==="disabled"?await Le(e,a,n,!0):(await Le(e,a,n,!1),await Mt(e,a));const l=Un[t.pseudoState];if(l.length>0){const r=await Ln(o,e,a),d=await Fn(o,n,r);if(d!==null)try{await o.send("CSS.forcePseudoState",{nodeId:d,forcedPseudoClasses:l}),se.set(e,{sessionTabId:e,nodeId:d})}catch(c){V.warn(`CSS.forcePseudoState failed for :${t.pseudoState}`,c)}else V.debug(`could not resolve nodeId for ${n} (frameId=${a})`)}}const i=[{name:"prefers-reduced-motion",value:"reduce"}];t.theme==="dark"?(i.push({name:"prefers-color-scheme",value:"dark"}),i.push({name:"forced-colors",value:"none"})):t.theme==="light"?(i.push({name:"prefers-color-scheme",value:"light"}),i.push({name:"forced-colors",value:"none"})):t.theme==="forced-colors-active"?i.push({name:"forced-colors",value:"active"}):t.theme==="forced-colors-none"&&i.push({name:"forced-colors",value:"none"}),await o.send("Emulation.setEmulatedMedia",{features:i}),await new Promise(l=>setTimeout(l,50)),await Ut(e,t.direction),await o.send("Emulation.setDeviceMetricsOverride",{width:t.breakpoint.width,height:t.breakpoint.height,deviceScaleFactor:t.breakpoint.deviceScaleFactor,mobile:t.breakpoint.mobile}),t.ariaVariation&&n&&await Gn(e,a,n,t.ariaVariation.attributes)}async function jn(e,t,n){const a=Y.get(e);if(a){if(await _t(e),t)try{await Le(e,n,t,!1),await Mt(e,n),await Ut(e,"ltr")}catch(o){V.warn("element-level reset partial failure",o)}try{await a.send("Emulation.setEmulatedMedia",{features:[]}),await a.send("Emulation.clearDeviceMetricsOverride")}catch(o){V.warn("reset partial failure",o)}await Ye(e)}}async function Fe(e,t,n,a){try{return await Wn(e,t,n,a),D({type:"STATE_CHANGED_EVENT",tabId:e,currentState:t}),{type:"STATE_DRIVE_RESPONSE",success:!0,appliedState:t}}catch(o){return V.error("state drive failed",o),await Nn(e),{type:"STATE_DRIVE_RESPONSE",success:!1,error:On(o)}}}async function Dt(e,t,n){await jn(e,t,n)}async function Vn(e,t={}){try{const n=await Rt(e),a={format:"jpeg",quality:t.quality??75};t.fullPage&&(a.captureBeyondViewport=!0);const o=await n.send("Page.captureScreenshot",a);return o!=null&&o.data?`data:image/jpeg;base64,${o.data}`:null}catch(n){return V.debug("Page.captureScreenshot failed",n),null}}function zn(){var n;const e=[];if(e.push(P("STATE_DRIVE_REQUEST",a=>Fe(a.tabId,a.state,a.scope,a.frameId))),e.push(P("STATE_RESET_REQUEST",a=>Dt(a.tabId))),(n=chrome.tabs)!=null&&n.onRemoved){const a=o=>{Ye(o).catch(()=>{})};chrome.tabs.onRemoved.addListener(a),e.push(()=>chrome.tabs.onRemoved.removeListener(a))}const t=_n((a,o)=>{a.tabId!==void 0&&(Y.delete(a.tabId),oe.delete(a.tabId),se.delete(a.tabId),V.warn("unexpected detach",{tabId:a.tabId,reason:o}))});return e.push(t),V.info("handlers registered"),()=>e.forEach(a=>a())}const Hn="wcag-component-auditor",Bn=1;let Re=null;function $e(){return Re||(Re=un(Hn,Bn,{upgrade(e,t){if(t<1){const n=e.createObjectStore("baselines",{keyPath:"componentId"});n.createIndex("byUrl","snapshotMeta.url"),n.createIndex("byDate","lastUpdated");const a=e.createObjectStore("audit-history",{keyPath:"auditId",autoIncrement:!0});a.createIndex("byComponentId","componentId"),a.createIndex("byDate","runDate")}}})),Re}async function Xe(e){return(await $e()).get("baselines",e)}async function Nt(e){await(await $e()).put("baselines",e)}async function qn(e){await(await $e()).delete("baselines",e)}async function Kn(){return(await $e()).getAll("baselines")}const ne=F("cloud-sync"),it="cloudSyncEnabled",Oe="cloudSyncEndpoint",_e="licenseToken",Ge="cloudSyncVersionCache";async function Qe(){try{const e=await chrome.storage.local.get([it,Oe,_e]),t=e[it]===!0,n=typeof e[Oe]=="string"?e[Oe]:"",a=typeof e[_e]=="string"?e[_e]:"";return!t||!n||!a?null:{endpoint:n.replace(/\/$/,""),token:a,enabled:t}}catch{return null}}async function Pt(){try{const t=(await chrome.storage.local.get(Ge))[Ge];return t&&typeof t=="object"?t:{}}catch{return{}}}async function we(e){try{await chrome.storage.local.set({[Ge]:e})}catch{}}async function Yn(e,t,n){const a=await Qe();if(!a)return!1;const o=await Pt(),i=o[e];try{const l=`${a.endpoint}/v1/products/wcagcheckr/baselines/${encodeURIComponent(e)}`,r=await fetch(l,{method:"PUT",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a.token}`},body:JSON.stringify({snapshot:t,axeVersion:n,expectedVersion:i})});if(r.status===409)return ne.warn(`baseline ${e} version conflict on push`,{expectedVersion:i}),delete o[e],await we(o),!1;if(!r.ok)return ne.warn("baseline push failed",{status:r.status}),!1;const d=await r.json();return typeof d.version=="number"&&(o[e]=d.version,await we(o)),!0}catch(l){return ne.debug("push network failure (offline?)",l),!1}}async function Jn(e){const t=await Qe();if(!t)return!1;try{const n=`${t.endpoint}/v1/products/wcagcheckr/baselines/${encodeURIComponent(e)}`,a=await fetch(n,{method:"DELETE",headers:{Authorization:`Bearer ${t.token}`}});if(a.ok){const o=await Pt();delete o[e],await we(o)}return a.ok}catch(n){return ne.debug("delete network failure",n),!1}}async function Xn(){const e=await Qe();if(!e)return[];try{const t=await fetch(`${e.endpoint}/v1/products/wcagcheckr/baselines`,{headers:{Authorization:`Bearer ${e.token}`}});if(!t.ok)return ne.warn("pull failed",{status:t.status}),[];const a=(await t.json()).items??[],o={};for(const i of a)o[i.componentId]=i.version;return await we(o),a}catch(t){return ne.debug("pull network failure",t),[]}}const Lt=F("baseline-store"),rt="componentIdAliases";async function ae(e,t){try{return await t()}catch(n){const a=n instanceof Error?n.message:String(n);throw Lt.error(`baseline IDB ${e} failed: ${a}`),new ln({code:"BASELINE_DB_ERROR",message:`Baseline storage is unavailable (${e}: ${a}).`,recoverable:!1,details:a})}}async function Te(e){return((await chrome.storage.local.get(rt))[rt]??{})[e]??e}async function Qn(e){const t=await Te(e),n=await ae("get",()=>Xe(t));return n?{violations:n.violations,snapshotMeta:n.snapshotMeta}:null}async function Zn(e,t,n,a){const o=await Te(e),i=await ae("get-for-set",()=>Xe(o)),l=(i==null?void 0:i.seenOnUrls)??[],r=l.includes(n.url)?l:[...l,n.url];await ae("set",()=>Nt({componentId:o,violations:t,snapshotMeta:n,lastUpdated:new Date().toISOString(),axeVersion:n.axeVersion,announcements:a==null?void 0:a.announcements,focusEvents:a==null?void 0:a.focusEvents,seenOnUrls:r})),Yn(o,{violations:t,snapshotMeta:n},n.axeVersion).catch(()=>{})}function es(e,t){if(!e||!t||st(e)===st(t))return;const n=i=>{var l,r,d,c,u;return(((l=i.pseudoStates)==null?void 0:l.length)??0)*Math.max(1,((r=i.ariaVariations)==null?void 0:r.length)??0)*(((d=i.themes)==null?void 0:d.length)??0)*(((c=i.directions)==null?void 0:c.length)??0)*(((u=i.breakpoints)==null?void 0:u.length)??0)},a=n(e),o=n(t);return`Baseline was captured under a different state-matrix configuration (${a} states) than this run (${o} states). Per-state-instance deltas inflate when the matrix changes — the unique-violation deltas above are still reliable.`}async function Ft(e,t,n,a){var d;const o=await Te(e),i=await ae("compare",()=>Xe(o));if(!i)return{new:t,persistent:[],fixed:[],newCount:t.length,persistentCount:0,fixedCount:0,baselineSnapshotMeta:null,comparedAt:new Date().toISOString(),newAnnouncements:n==null?void 0:n.announcements,newFocusEvents:n==null?void 0:n.focusEvents};const l=cn(i.violations,t,i.snapshotMeta,{baselineAnnouncements:i.announcements,currentAnnouncements:n==null?void 0:n.announcements,baselineFocusEvents:i.focusEvents,currentFocusEvents:n==null?void 0:n.focusEvents}),r=es((d=i.snapshotMeta)==null?void 0:d.matrixConfig,a);return r&&(l.matrixMismatchWarning=r),l}async function ts(e){const t=await Te(e);await ae("delete",()=>qn(t)),Jn(t).catch(()=>{})}async function ns(){const e=await Xn();for(const t of e)await Nt({componentId:t.componentId,violations:t.snapshot.violations,snapshotMeta:t.snapshot.snapshotMeta,lastUpdated:t.updatedAt,axeVersion:t.axeVersion});return e.length>0&&D({type:"SCORECARD_UPDATED_EVENT"}),e.length}async function ss(e){let t=await ae("list",()=>Kn());return e!=null&&e.url&&(t=t.filter(n=>n.snapshotMeta.url===e.url)),t.map(n=>{const a=(n.focusEvents??[]).filter(c=>c.isFocusReset).length,o=(n.announcements??[]).length,i=n.violations.filter(c=>c.ruleId==="target-size").length,l=n.violations.some(c=>c.ruleId==="color-contrast"&&c.currentState.pseudoState==="hover"),r=n.violations.filter(c=>c.impact==="critical").length,d=n.violations.filter(c=>c.impact==="serious").length;return{componentId:n.componentId,violationCount:n.violations.length,lastUpdated:n.lastUpdated,seenOnUrlsCount:(n.seenOnUrls??[]).length,metrics:{criticalCount:r,seriousCount:d,focusResetCount:a,announcementCount:o,targetSizeFailCount:i,hoverContrastFail:l}}})}function as(){const e=[];return e.push(P("BASELINE_GET",async t=>({type:"BASELINE_GET_RESPONSE",baseline:await Qn(t.componentId)}))),e.push(P("BASELINE_SET",async t=>{await Zn(t.componentId,t.violations,t.snapshotMeta,{announcements:t.announcements,focusEvents:t.focusEvents}),D({type:"SCORECARD_UPDATED_EVENT"})})),e.push(P("BASELINE_COMPARE",async t=>({type:"BASELINE_COMPARE_RESPONSE",delta:await Ft(t.componentId,t.currentViolations,{announcements:t.announcements,focusEvents:t.focusEvents},t.currentMatrix)}))),e.push(P("BASELINE_DELETE",async t=>{await ts(t.componentId),D({type:"SCORECARD_UPDATED_EVENT"})})),e.push(P("BASELINE_LIST",async t=>({type:"BASELINE_LIST_RESPONSE",items:await ss(t.filter)}))),Lt.info("handlers registered"),()=>e.forEach(t=>t())}const We="tierConfig:cache",os=60*60*1e3,is=24*60*60*1e3,rs=7*24*60*60*1e3;async function cs(){return(await chrome.storage.local.get(We))[We]??null}async function ls(e){await chrome.storage.local.set({[We]:e})}function ds(e,t=Date.now()){const n=t-e.fetchedAt;return n<os?"fresh":n<is?"stale":n<rs?"grace":"expired"}function us(e,t){const n={...e},a=E=>{if(typeof E=="number")return E===-1?1/0:E},o=E=>typeof E=="boolean"?E:void 0,i=E=>{if(E==="all"||E==="configurable"||E==="default-only")return E},l=a(t.maxComponents);l!==void 0&&(n.maxComponents=l);const r=a(t.maxBaselines);r!==void 0&&(n.maxBaselines=r);const d=i(t.stateMatrix);d!==void 0&&(n.stateMatrix=d);const c=o(t.storybookAutoIterate);c!==void 0&&(n.storybookAutoIterate=c);const u=o(t.exportJson);u!==void 0&&(n.exportJson=u);const h=o(t.exportSarif);h!==void 0&&(n.exportSarif=h);const s=o(t.exportJunit);s!==void 0&&(n.exportJunit=s);const x=o(t.cloudSync);x!==void 0&&(n.cloudSync=x);const S=o(t.forensicAnchoring);return S!==void 0&&(n.forensicAnchoring=S),n}function ps(e,t){var a;if(e==="trial")return((a=t.trial)==null?void 0:a.features)??null;const n=t.plans.find(o=>o.code===e||o.code.startsWith(e+"-"));return(n==null?void 0:n.features)??null}const je=F("tier-config-client"),hs="wcagcheckr",fs=`https://api.wcagcheckr.com/v1/products/${hs}/tier-config`,gs=1e4,ct="tier-config-refresh",ms=60,ys=["trial","free","solo","team"];function Gt(e){const t={};for(const n of ys){const a=ps(n,e);t[n]=a?us(ot[n],a):{...ot[n]}}return t}async function bs(){const e=await cs();if(!e){ge(null);return}if(ds(e)==="expired"){je.warn("cached tier-config is expired (>7d); using hardcoded defaults"),ge(null);return}ge(Gt(e.config))}async function Wt(){try{const e=await fetch(fs,{signal:AbortSignal.timeout(gs),headers:{accept:"application/json"}});if(!e.ok)throw new Error(`http ${e.status}`);const t=await e.json();await ls({fetchedAt:Date.now(),config:t}),ge(Gt(t)),je.info("tier-config refreshed from server")}catch(e){je.warn("tier-config refresh failed; keeping prior cache",e)}}function ws(){chrome.alarms.create(ct,{periodInMinutes:ms}),chrome.alarms.onAlarm.addListener(e=>{e.name===ct&&Wt()})}const vs=F("settings-store"),lt=1,dt="__schemaVersion",Ve={stateMatrix:Pe,componentIdAliases:{},ignorePatterns:[]};async function ve(e,t){return(await chrome.storage.local.get(e))[e]??t}async function As(){const e=await chrome.storage.local.get();if(e[dt]===lt)return;const t={[dt]:lt};for(const[n,a]of Object.entries(Ve))n in e||(t[n]=a);await chrome.storage.local.set(t),vs.info("defaults ensured")}const xs=["__","inflight:","license:","support:"];function Ss(e){return!xs.some(t=>e.startsWith(t))}function ks(){const e=[];return e.push(P("SETTINGS_GET",async t=>({type:"SETTINGS_RESPONSE",data:(await chrome.storage.local.get(t.key))[t.key]??Ve[t.key]}))),e.push(P("SETTINGS_SET",async t=>{await chrome.storage.local.set({[t.key]:t.value})})),e.push(P("SETTINGS_GET_ALL",async()=>{const t=await chrome.storage.local.get(),n=Object.fromEntries(Object.entries(t).filter(([a])=>Ss(a)));return{type:"SETTINGS_RESPONSE",data:{...Ve,...n}}})),()=>e.forEach(t=>t())}const Es={input:3/1e6,output:15/1e6},$s={input:1/1e6,output:5/1e6},Ts="claude-sonnet-4-6",Cs="https://api.anthropic.com/v1/messages",Is="2023-06-01";function Rs(e){const t=e.model||Ts,n=t.includes("haiku")?$s:Es;return{providerId:"anthropic",async judgeAltText({imageUrl:a,alt:o,surroundingContext:i}){const l=await ut(a);if(!l)return pt("Could not fetch image to verify alt text",0,t);const r=Ds(o,i),d=await z({apiKey:e.apiKey,model:t,maxTokens:300,messages:[{role:"user",content:[{type:"image",source:{type:"base64",media_type:l.mediaType,data:l.data}},{type:"text",text:r}]}]});return K(d,n)},async judgeHeading({headingText:a,sectionContent:o}){const i=await z({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:Ns(a,o)}]}]});return K(i,n)},async judgeSensoryLanguage({instructionText:a}){const o=await z({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:Ps(a)}]}]});return K(o,n)},async judgeAriaSemantics({elementHtml:a,computedRole:o,surroundingHtml:i}){const l=await z({apiKey:e.apiKey,model:t,maxTokens:350,messages:[{role:"user",content:[{type:"text",text:Ls(a,o,i)}]}]});return K(l,n)},async judgeImageOfText({imageUrl:a,accessibleName:o}){const i=await ut(a);if(!i)return pt("Could not fetch image to inspect for embedded text",0,t);const l=await z({apiKey:e.apiKey,model:t,maxTokens:300,messages:[{role:"user",content:[{type:"image",source:{type:"base64",media_type:i.mediaType,data:i.data}},{type:"text",text:Fs(o)}]}]});return K(l,n)},async judgeColorOnlyMeaning({elementHtml:a,contextDescription:o}){const i=await z({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:Gs(a,o)}]}]});return K(i,n)},async judgeGenericLinkText({linkText:a,surroundingText:o}){const i=await z({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:Ws(a,o)}]}]});return K(i,n)},async judgeWallOfText({blockText:a,structuralChildrenCount:o}){const i=await z({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:js(a,o)}]}]});return K(i,n)},async judgeLanguageMismatch({declaredLang:a,bodyTextSample:o}){const i=await z({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:Vs(a,o)}]}]});return K(i,n)},async suggestColorFix(a){const o=await z({apiKey:e.apiKey,model:t,maxTokens:600,messages:[{role:"user",content:[{type:"text",text:zs(a)}]}]});return Bs(o,n)},async generateExecutiveSummary(a){const o=await z({apiKey:e.apiKey,model:t,maxTokens:800,messages:[{role:"user",content:[{type:"text",text:Hs(a)}]}]});return qs(o,n)}}}const ze=3e4,Os=1e4,_s="https://api.wcagcheckr.com/v1/products/wcagcheckr/ai/proxy";async function Ms(e){var o,i,l,r;let t=null;try{const d=await((l=(i=(o=chrome.storage)==null?void 0:o.local)==null?void 0:i.get)==null?void 0:l.call(i,"licenseToken"));t=(d==null?void 0:d.licenseToken)??null}catch{t=null}if(!t)return null;const n=new AbortController,a=setTimeout(()=>n.abort(),ze);try{const d=await fetch(_s,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({token:t,checkName:e.checkName??"unspecified",model:e.model,maxTokens:e.maxTokens,messages:e.messages}),signal:n.signal});if(d.status===503||d.status===403||d.status===401)return null;if(!d.ok){const u=((r=(await d.json().catch(()=>({}))).error)==null?void 0:r.message)??`${d.status} ${d.statusText}`;throw new Error(`Anthropic proxy: ${u}`)}return await d.json()}catch(d){if(d instanceof Error&&d.name==="AbortError"||d instanceof TypeError)return null;throw d}finally{clearTimeout(a)}}async function z(e){var o;const t=await Ms(e);if(t)return t;const n=new AbortController,a=setTimeout(()=>n.abort(),ze);try{const i=await fetch(Cs,{method:"POST",headers:{"x-api-key":e.apiKey,"anthropic-version":Is,"content-type":"application/json","anthropic-dangerous-direct-browser-access":"true"},body:JSON.stringify({model:e.model,max_tokens:e.maxTokens,messages:e.messages}),signal:n.signal});if(!i.ok){const r=((o=(await i.json().catch(()=>({}))).error)==null?void 0:o.message)??`${i.status} ${i.statusText}`;throw new Error(`Anthropic API: ${r}`)}return await i.json()}catch(i){throw i instanceof Error&&i.name==="AbortError"?new Error(`Anthropic API: request timed out after ${ze/1e3}s`):i}finally{clearTimeout(a)}}async function ut(e){var a,o;const t=new AbortController,n=setTimeout(()=>t.abort(),Os);try{const i=await fetch(e,{signal:t.signal});if(!i.ok)return null;const l=((o=(a=i.headers.get("content-type"))==null?void 0:a.split(";")[0])==null?void 0:o.trim())??"image/png";if(!l.startsWith("image/"))return null;const r=await i.arrayBuffer();let d="";const c=new Uint8Array(r),u=32768;for(let h=0;h<c.length;h+=u)d+=String.fromCharCode(...c.slice(h,h+u));return{data:btoa(d),mediaType:l}}catch{return null}finally{clearTimeout(n)}}function K(e,t){var i;const n=((i=e.content.find(l=>l.type==="text"))==null?void 0:i.text)??"",a=Us(n),o=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;return{verdict:a.verdict,reasoning:a.reasoning,confidence:a.confidence,costUsd:o,model:e.model}}function Us(e){const n=e.replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/);if(!n)return{verdict:"uncertain",reasoning:"Model returned no parseable JSON",confidence:0};try{const a=JSON.parse(n[0]),o=a.verdict==="pass"||a.verdict==="fail"?a.verdict:"uncertain",i=(a.reasoning??"").trim().slice(0,600),l=typeof a.confidence=="number"?a.confidence:.5,r=Math.max(0,Math.min(1,l));return{verdict:o,reasoning:i,confidence:r}}catch{return{verdict:"uncertain",reasoning:"Model returned invalid JSON",confidence:0}}}function pt(e,t,n){return{verdict:"uncertain",reasoning:e,confidence:0,costUsd:t,model:n}}const J=`
|
|
1
|
+
import{c as F,o as P,e as D,f as sn,g as an,d as W,b as Z,i as on,h as rn}from"./crash-reporter-Dc5lvxvY.js";import{m as me,d as cn,T as ln,a as nt,c as He,i as dn}from"./diff-D4sCAdXf.js";import{o as un,i as st,l as ce,h as te,A as ye,F as pn,k as Et,O as $t,j as Tt,R as hn,n as Ct,p as Se,q as ke,r as Ee,t as fn,g as gn,u as mn,f as le,e as It,a as Be,v as yn,w as bn}from"./forensic-log-B1UCXZ23.js";import{s as ge,T as at,b as Pe,i as wn}from"./state-PELIq3oj.js";import{m as qe,D as vn,s as An,h as xn,b as Sn,g as kn,d as En,e as $n}from"./ai-usage-log-Dj9Ub_DT.js";const Tn=F("chrome-api"),Cn="1.3";class In{constructor(t){this.target=t}async attach(){await chrome.debugger.attach(this.target,Cn)}async detach(){try{await chrome.debugger.detach(this.target)}catch(t){Tn.debug("detach soft-failure",t)}}async send(t,n){return chrome.debugger.sendCommand(this.target,t,n)}}function Rn(e){const t=e instanceof Error?e.message:String(e);return/Another debugger is already attached|Cannot attach to/i.test(t)}function On(e){const t=e instanceof Error?e.message:String(e);return Rn(e)?me("DEBUGGER_BUSY","DevTools is open on this tab. Close it to run the audit.",!0,{raw:t}):me("STATE_DRIVE_FAILED",t,!1)}function _n(e){const t=(n,a)=>e(n,a);return chrome.debugger.onDetach.addListener(t),()=>chrome.debugger.onDetach.removeListener(t)}const V=F("state-driver"),Mn=60*1e3,Y=new Map,be=new Map,oe=new Map,se=new Map,Un={default:[],hover:["hover"],focus:["focus"],"focus-visible":["focus","focus-visible"],active:["active"],disabled:[]};async function Rt(e){let t=Y.get(e);if(!t){t=new In({tabId:e});try{await t.attach(),await t.send("Runtime.enable"),await t.send("Page.enable"),await t.send("DOM.enable"),await t.send("DOM.getDocument",{depth:-1,pierce:!0}),await t.send("CSS.enable")}catch(n){try{await t.detach()}catch{}throw n}Y.set(e,t),oe.set(e,new Map),Dn(e),V.info("attached",{tabId:e})}return Pn(e),t}function Dn(e){const t=(n,a,o)=>{var l,r;if(n.tabId!==e)return;const i=oe.get(e);if(i)if(a==="Runtime.executionContextCreated"){const u=o,c=u==null?void 0:u.context;c&&((l=c.auxData)!=null&&l.isDefault)&&((r=c.auxData)!=null&&r.frameId)&&i.set(c.auxData.frameId,c.id)}else if(a==="Runtime.executionContextDestroyed"){const u=o,c=u==null?void 0:u.executionContextId;if(c!==void 0)for(const[d,h]of i.entries())h===c&&i.delete(d)}else a==="Runtime.executionContextsCleared"&&i.clear()};chrome.debugger.onEvent.addListener(t)}async function Nn(e){const t=Y.get(e);if(Y.delete(e),oe.delete(e),se.delete(e),t)try{await t.detach()}catch{}}function Pn(e){const t=be.get(e);t&&clearTimeout(t),be.set(e,setTimeout(()=>{Ke(e).catch(n=>V.warn("idle detach failed",n))},Mn))}async function Ke(e){const t=Y.get(e);t&&(await t.detach(),Y.delete(e)),oe.delete(e),se.delete(e);const n=be.get(e);n&&(clearTimeout(n),be.delete(e))}function Ot(e){const t=[{id:e.frame.id,url:e.frame.url}];for(const n of e.childFrames??[])t.push(...Ot(n));return t}async function Ln(e,t,n){var i;if(!n||n===0)return;const a=oe.get(t);if(!a)return;let o;try{o=(i=(await chrome.scripting.executeScript({target:{tabId:t,frameIds:[n]},func:()=>location.href}))[0])==null?void 0:i.result}catch(l){V.debug("frame URL lookup failed",l)}try{const l=await e.send("Page.getFrameTree"),r=Ot(l.frameTree);if(o){const d=r.find(h=>h.url===o);if(d){const h=a.get(d.id);if(h!==void 0)return h}}const u=l.frameTree.frame.id,c=r.find(d=>d.id!==u);return c?a.get(c.id):void 0}catch(l){V.warn("Page.getFrameTree failed",l);return}}async function Fn(e,t,n){var l;const a={expression:`document.querySelector(${JSON.stringify(t)})`,objectGroup:"wcag-auditor",returnByValue:!1};n!==void 0&&(a.contextId=n);const i=(l=(await e.send("Runtime.evaluate",a)).result)==null?void 0:l.objectId;if(!i)return null;try{return(await e.send("DOM.requestNode",{objectId:i})).nodeId??null}finally{e.send("Runtime.releaseObject",{objectId:i}).catch(()=>{})}}async function _t(e){const t=se.get(e);if(!t)return;const n=Y.get(e);if(n){try{await n.send("CSS.forcePseudoState",{nodeId:t.nodeId,forcedPseudoClasses:[]})}catch(a){V.debug("forcePseudoState clear failed (node may be gone)",a)}se.delete(e)}}async function Le(e,t,n,a){await chrome.scripting.executeScript({target:Ye(e,t),func:(o,i)=>{const l=document.querySelector(o);l&&(i?l.setAttribute("disabled",""):l.removeAttribute("disabled"))},args:[n,a]})}async function Mt(e,t){await chrome.scripting.executeScript({target:Ye(e,t),func:()=>{const n=document.activeElement;n&&typeof n.blur=="function"&&n.blur()}})}async function Ut(e,t){await chrome.scripting.executeScript({target:{tabId:e,frameIds:[0]},func:n=>{document.documentElement.setAttribute("dir",n)},args:[t]})}async function Gn(e,t,n,a){await chrome.scripting.executeScript({target:Ye(e,t),func:(o,i)=>{const l=document.querySelector(o);if(l)for(const[r,u]of Object.entries(i))l.setAttribute(r,u)},args:[n,a]})}function Ye(e,t){return t!==void 0&&t!==0?{tabId:e,frameIds:[t]}:{tabId:e}}async function Wn(e,t,n,a){const o=await Rt(e);if(await _t(e),n){t.pseudoState==="disabled"?await Le(e,a,n,!0):(await Le(e,a,n,!1),await Mt(e,a));const l=Un[t.pseudoState];if(l.length>0){const r=await Ln(o,e,a),u=await Fn(o,n,r);if(u!==null)try{await o.send("CSS.forcePseudoState",{nodeId:u,forcedPseudoClasses:l}),se.set(e,{sessionTabId:e,nodeId:u})}catch(c){V.warn(`CSS.forcePseudoState failed for :${t.pseudoState}`,c)}else V.debug(`could not resolve nodeId for ${n} (frameId=${a})`)}}const i=[{name:"prefers-reduced-motion",value:"reduce"}];t.theme==="dark"?(i.push({name:"prefers-color-scheme",value:"dark"}),i.push({name:"forced-colors",value:"none"})):t.theme==="light"?(i.push({name:"prefers-color-scheme",value:"light"}),i.push({name:"forced-colors",value:"none"})):t.theme==="forced-colors-active"?i.push({name:"forced-colors",value:"active"}):t.theme==="forced-colors-none"&&i.push({name:"forced-colors",value:"none"}),await o.send("Emulation.setEmulatedMedia",{features:i}),await new Promise(l=>setTimeout(l,50)),await Ut(e,t.direction),await o.send("Emulation.setDeviceMetricsOverride",{width:t.breakpoint.width,height:t.breakpoint.height,deviceScaleFactor:t.breakpoint.deviceScaleFactor,mobile:t.breakpoint.mobile}),t.ariaVariation&&n&&await Gn(e,a,n,t.ariaVariation.attributes)}async function jn(e,t,n){const a=Y.get(e);if(a){if(await _t(e),t)try{await Le(e,n,t,!1),await Mt(e,n),await Ut(e,"ltr")}catch(o){V.warn("element-level reset partial failure",o)}try{await a.send("Emulation.setEmulatedMedia",{features:[]}),await a.send("Emulation.clearDeviceMetricsOverride")}catch(o){V.warn("reset partial failure",o)}await Ke(e)}}async function Fe(e,t,n,a){try{return await Wn(e,t,n,a),D({type:"STATE_CHANGED_EVENT",tabId:e,currentState:t}),{type:"STATE_DRIVE_RESPONSE",success:!0,appliedState:t}}catch(o){return V.error("state drive failed",o),await Nn(e),{type:"STATE_DRIVE_RESPONSE",success:!1,error:On(o)}}}async function Dt(e,t,n){await jn(e,t,n)}async function Vn(e,t={}){try{const n=await Rt(e),a={format:"jpeg",quality:t.quality??75};t.fullPage&&(a.captureBeyondViewport=!0);const o=await n.send("Page.captureScreenshot",a);return o!=null&&o.data?`data:image/jpeg;base64,${o.data}`:null}catch(n){return V.debug("Page.captureScreenshot failed",n),null}}function zn(){var n;const e=[];if(e.push(P("STATE_DRIVE_REQUEST",a=>Fe(a.tabId,a.state,a.scope,a.frameId))),e.push(P("STATE_RESET_REQUEST",a=>Dt(a.tabId))),(n=chrome.tabs)!=null&&n.onRemoved){const a=o=>{Ke(o).catch(()=>{})};chrome.tabs.onRemoved.addListener(a),e.push(()=>chrome.tabs.onRemoved.removeListener(a))}const t=_n((a,o)=>{a.tabId!==void 0&&(Y.delete(a.tabId),oe.delete(a.tabId),se.delete(a.tabId),V.warn("unexpected detach",{tabId:a.tabId,reason:o}))});return e.push(t),V.info("handlers registered"),()=>e.forEach(a=>a())}const Hn="wcag-component-auditor",Bn=1;let Re=null;function $e(){return Re||(Re=un(Hn,Bn,{upgrade(e,t){if(t<1){const n=e.createObjectStore("baselines",{keyPath:"componentId"});n.createIndex("byUrl","snapshotMeta.url"),n.createIndex("byDate","lastUpdated");const a=e.createObjectStore("audit-history",{keyPath:"auditId",autoIncrement:!0});a.createIndex("byComponentId","componentId"),a.createIndex("byDate","runDate")}}})),Re}async function Je(e){return(await $e()).get("baselines",e)}async function Nt(e){await(await $e()).put("baselines",e)}async function qn(e){await(await $e()).delete("baselines",e)}async function Kn(){return(await $e()).getAll("baselines")}const ne=F("cloud-sync"),ot="cloudSyncEnabled",Oe="cloudSyncEndpoint",_e="licenseToken",Ge="cloudSyncVersionCache";async function Xe(){try{const e=await chrome.storage.local.get([ot,Oe,_e]),t=e[ot]===!0,n=typeof e[Oe]=="string"?e[Oe]:"",a=typeof e[_e]=="string"?e[_e]:"";return!t||!n||!a?null:{endpoint:n.replace(/\/$/,""),token:a,enabled:t}}catch{return null}}async function Pt(){try{const t=(await chrome.storage.local.get(Ge))[Ge];return t&&typeof t=="object"?t:{}}catch{return{}}}async function we(e){try{await chrome.storage.local.set({[Ge]:e})}catch{}}async function Yn(e,t,n){const a=await Xe();if(!a)return!1;const o=await Pt(),i=o[e];try{const l=`${a.endpoint}/v1/products/wcagcheckr/baselines/${encodeURIComponent(e)}`,r=await fetch(l,{method:"PUT",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a.token}`},body:JSON.stringify({snapshot:t,axeVersion:n,expectedVersion:i})});if(r.status===409)return ne.warn(`baseline ${e} version conflict on push`,{expectedVersion:i}),delete o[e],await we(o),!1;if(!r.ok)return ne.warn("baseline push failed",{status:r.status}),!1;const u=await r.json();return typeof u.version=="number"&&(o[e]=u.version,await we(o)),!0}catch(l){return ne.debug("push network failure (offline?)",l),!1}}async function Jn(e){const t=await Xe();if(!t)return!1;try{const n=`${t.endpoint}/v1/products/wcagcheckr/baselines/${encodeURIComponent(e)}`,a=await fetch(n,{method:"DELETE",headers:{Authorization:`Bearer ${t.token}`}});if(a.ok){const o=await Pt();delete o[e],await we(o)}return a.ok}catch(n){return ne.debug("delete network failure",n),!1}}async function Xn(){const e=await Xe();if(!e)return[];try{const t=await fetch(`${e.endpoint}/v1/products/wcagcheckr/baselines`,{headers:{Authorization:`Bearer ${e.token}`}});if(!t.ok)return ne.warn("pull failed",{status:t.status}),[];const a=(await t.json()).items??[],o={};for(const i of a)o[i.componentId]=i.version;return await we(o),a}catch(t){return ne.debug("pull network failure",t),[]}}const Lt=F("baseline-store"),it="componentIdAliases";async function ae(e,t){try{return await t()}catch(n){const a=n instanceof Error?n.message:String(n);throw Lt.error(`baseline IDB ${e} failed: ${a}`),new ln({code:"BASELINE_DB_ERROR",message:`Baseline storage is unavailable (${e}: ${a}).`,recoverable:!1,details:a})}}async function Te(e){return((await chrome.storage.local.get(it))[it]??{})[e]??e}async function Qn(e){const t=await Te(e),n=await ae("get",()=>Je(t));return n?{violations:n.violations,snapshotMeta:n.snapshotMeta}:null}async function Zn(e,t,n,a){const o=await Te(e),i=await ae("get-for-set",()=>Je(o)),l=(i==null?void 0:i.seenOnUrls)??[],r=l.includes(n.url)?l:[...l,n.url];await ae("set",()=>Nt({componentId:o,violations:t,snapshotMeta:n,lastUpdated:new Date().toISOString(),axeVersion:n.axeVersion,announcements:a==null?void 0:a.announcements,focusEvents:a==null?void 0:a.focusEvents,seenOnUrls:r})),Yn(o,{violations:t,snapshotMeta:n},n.axeVersion).catch(()=>{})}function es(e,t){if(!e||!t||nt(e)===nt(t))return;const n=i=>{var l,r,u,c,d;return(((l=i.pseudoStates)==null?void 0:l.length)??0)*Math.max(1,((r=i.ariaVariations)==null?void 0:r.length)??0)*(((u=i.themes)==null?void 0:u.length)??0)*(((c=i.directions)==null?void 0:c.length)??0)*(((d=i.breakpoints)==null?void 0:d.length)??0)},a=n(e),o=n(t);return`Baseline was captured under a different state-matrix configuration (${a} states) than this run (${o} states). Per-state-instance deltas inflate when the matrix changes — the unique-violation deltas above are still reliable.`}async function Ft(e,t,n,a){var u;const o=await Te(e),i=await ae("compare",()=>Je(o));if(!i)return{new:t,persistent:[],fixed:[],newCount:t.length,persistentCount:0,fixedCount:0,baselineSnapshotMeta:null,comparedAt:new Date().toISOString(),newAnnouncements:n==null?void 0:n.announcements,newFocusEvents:n==null?void 0:n.focusEvents};const l=cn(i.violations,t,i.snapshotMeta,{baselineAnnouncements:i.announcements,currentAnnouncements:n==null?void 0:n.announcements,baselineFocusEvents:i.focusEvents,currentFocusEvents:n==null?void 0:n.focusEvents}),r=es((u=i.snapshotMeta)==null?void 0:u.matrixConfig,a);return r&&(l.matrixMismatchWarning=r),l}async function ts(e){const t=await Te(e);await ae("delete",()=>qn(t)),Jn(t).catch(()=>{})}async function ns(){const e=await Xn();for(const t of e)await Nt({componentId:t.componentId,violations:t.snapshot.violations,snapshotMeta:t.snapshot.snapshotMeta,lastUpdated:t.updatedAt,axeVersion:t.axeVersion});return e.length>0&&D({type:"SCORECARD_UPDATED_EVENT"}),e.length}async function ss(e){let t=await ae("list",()=>Kn());return e!=null&&e.url&&(t=t.filter(n=>n.snapshotMeta.url===e.url)),t.map(n=>{const a=(n.focusEvents??[]).filter(c=>c.isFocusReset).length,o=(n.announcements??[]).length,i=n.violations.filter(c=>c.ruleId==="target-size").length,l=n.violations.some(c=>c.ruleId==="color-contrast"&&c.currentState.pseudoState==="hover"),r=n.violations.filter(c=>c.impact==="critical").length,u=n.violations.filter(c=>c.impact==="serious").length;return{componentId:n.componentId,violationCount:n.violations.length,lastUpdated:n.lastUpdated,seenOnUrlsCount:(n.seenOnUrls??[]).length,metrics:{criticalCount:r,seriousCount:u,focusResetCount:a,announcementCount:o,targetSizeFailCount:i,hoverContrastFail:l}}})}function as(){const e=[];return e.push(P("BASELINE_GET",async t=>({type:"BASELINE_GET_RESPONSE",baseline:await Qn(t.componentId)}))),e.push(P("BASELINE_SET",async t=>{await Zn(t.componentId,t.violations,t.snapshotMeta,{announcements:t.announcements,focusEvents:t.focusEvents}),D({type:"SCORECARD_UPDATED_EVENT"})})),e.push(P("BASELINE_COMPARE",async t=>({type:"BASELINE_COMPARE_RESPONSE",delta:await Ft(t.componentId,t.currentViolations,{announcements:t.announcements,focusEvents:t.focusEvents},t.currentMatrix)}))),e.push(P("BASELINE_DELETE",async t=>{await ts(t.componentId),D({type:"SCORECARD_UPDATED_EVENT"})})),e.push(P("BASELINE_LIST",async t=>({type:"BASELINE_LIST_RESPONSE",items:await ss(t.filter)}))),Lt.info("handlers registered"),()=>e.forEach(t=>t())}const We="tierConfig:cache",os=60*60*1e3,is=24*60*60*1e3,rs=7*24*60*60*1e3;async function cs(){return(await chrome.storage.local.get(We))[We]??null}async function ls(e){await chrome.storage.local.set({[We]:e})}function ds(e,t=Date.now()){const n=t-e.fetchedAt;return n<os?"fresh":n<is?"stale":n<rs?"grace":"expired"}function us(e,t){const n={...e},a=E=>{if(typeof E=="number")return E===-1?1/0:E},o=E=>typeof E=="boolean"?E:void 0,i=E=>{if(E==="all"||E==="configurable"||E==="default-only")return E},l=a(t.maxComponents);l!==void 0&&(n.maxComponents=l);const r=a(t.maxBaselines);r!==void 0&&(n.maxBaselines=r);const u=i(t.stateMatrix);u!==void 0&&(n.stateMatrix=u);const c=o(t.storybookAutoIterate);c!==void 0&&(n.storybookAutoIterate=c);const d=o(t.exportJson);d!==void 0&&(n.exportJson=d);const h=o(t.exportSarif);h!==void 0&&(n.exportSarif=h);const s=o(t.exportJunit);s!==void 0&&(n.exportJunit=s);const x=o(t.cloudSync);x!==void 0&&(n.cloudSync=x);const S=o(t.forensicAnchoring);return S!==void 0&&(n.forensicAnchoring=S),n}function ps(e,t){var a;if(e==="trial")return((a=t.trial)==null?void 0:a.features)??null;const n=t.plans.find(o=>o.code===e||o.code.startsWith(e+"-"));return(n==null?void 0:n.features)??null}const je=F("tier-config-client"),hs="wcagcheckr",fs=`https://api.wcagcheckr.com/v1/products/${hs}/tier-config`,gs=1e4,rt="tier-config-refresh",ms=60,ys=["trial","free","solo","team"];function Gt(e){const t={};for(const n of ys){const a=ps(n,e);t[n]=a?us(at[n],a):{...at[n]}}return t}async function bs(){const e=await cs();if(!e){ge(null);return}if(ds(e)==="expired"){je.warn("cached tier-config is expired (>7d); using hardcoded defaults"),ge(null);return}ge(Gt(e.config))}async function Wt(){try{const e=await fetch(fs,{signal:AbortSignal.timeout(gs),headers:{accept:"application/json"}});if(!e.ok)throw new Error(`http ${e.status}`);const t=await e.json();await ls({fetchedAt:Date.now(),config:t}),ge(Gt(t)),je.info("tier-config refreshed from server")}catch(e){je.warn("tier-config refresh failed; keeping prior cache",e)}}function ws(){chrome.alarms.create(rt,{periodInMinutes:ms}),chrome.alarms.onAlarm.addListener(e=>{e.name===rt&&Wt()})}const vs=F("settings-store"),ct=1,lt="__schemaVersion",Ve={stateMatrix:Pe,componentIdAliases:{},ignorePatterns:[]};async function ve(e,t){return(await chrome.storage.local.get(e))[e]??t}async function As(){const e=await chrome.storage.local.get();if(e[lt]===ct)return;const t={[lt]:ct};for(const[n,a]of Object.entries(Ve))n in e||(t[n]=a);await chrome.storage.local.set(t),vs.info("defaults ensured")}const xs=["__","inflight:","license:","support:"];function Ss(e){return!xs.some(t=>e.startsWith(t))}function ks(){const e=[];return e.push(P("SETTINGS_GET",async t=>({type:"SETTINGS_RESPONSE",data:(await chrome.storage.local.get(t.key))[t.key]??Ve[t.key]}))),e.push(P("SETTINGS_SET",async t=>{await chrome.storage.local.set({[t.key]:t.value})})),e.push(P("SETTINGS_GET_ALL",async()=>{const t=await chrome.storage.local.get(),n=Object.fromEntries(Object.entries(t).filter(([a])=>Ss(a)));return{type:"SETTINGS_RESPONSE",data:{...Ve,...n}}})),()=>e.forEach(t=>t())}const Es={input:3/1e6,output:15/1e6},$s={input:1/1e6,output:5/1e6},Ts="claude-sonnet-4-6",Cs="https://api.anthropic.com/v1/messages",Is="2023-06-01";function Rs(e){const t=e.model||Ts,n=t.includes("haiku")?$s:Es;return{providerId:"anthropic",async judgeAltText({imageUrl:a,alt:o,surroundingContext:i}){const l=await ut(a);if(!l)return pt("Could not fetch image to verify alt text",0,t);const r=Ms(o,i),u=await z({apiKey:e.apiKey,model:t,maxTokens:300,messages:[{role:"user",content:[{type:"image",source:{type:"base64",media_type:l.mediaType,data:l.data}},{type:"text",text:r}]}]});return K(u,n)},async judgeHeading({headingText:a,sectionContent:o}){const i=await z({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:Us(a,o)}]}]});return K(i,n)},async judgeSensoryLanguage({instructionText:a}){const o=await z({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:Ds(a)}]}]});return K(o,n)},async judgeAriaSemantics({elementHtml:a,computedRole:o,surroundingHtml:i}){const l=await z({apiKey:e.apiKey,model:t,maxTokens:350,messages:[{role:"user",content:[{type:"text",text:Ns(a,o,i)}]}]});return K(l,n)},async judgeImageOfText({imageUrl:a,accessibleName:o}){const i=await ut(a);if(!i)return pt("Could not fetch image to inspect for embedded text",0,t);const l=await z({apiKey:e.apiKey,model:t,maxTokens:300,messages:[{role:"user",content:[{type:"image",source:{type:"base64",media_type:i.mediaType,data:i.data}},{type:"text",text:Ps(o)}]}]});return K(l,n)},async judgeColorOnlyMeaning({elementHtml:a,contextDescription:o}){const i=await z({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:Ls(a,o)}]}]});return K(i,n)},async judgeGenericLinkText({linkText:a,surroundingText:o}){const i=await z({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:Fs(a,o)}]}]});return K(i,n)},async judgeWallOfText({blockText:a,structuralChildrenCount:o}){const i=await z({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:Gs(a,o)}]}]});return K(i,n)},async judgeLanguageMismatch({declaredLang:a,bodyTextSample:o}){const i=await z({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:Ws(a,o)}]}]});return K(i,n)},async suggestColorFix(a){const o=await z({apiKey:e.apiKey,model:t,maxTokens:600,messages:[{role:"user",content:[{type:"text",text:js(a)}]}]});return zs(o,n)},async generateExecutiveSummary(a){const o=await z({apiKey:e.apiKey,model:t,maxTokens:800,messages:[{role:"user",content:[{type:"text",text:Vs(a)}]}]});return Hs(o,n)}}}const dt=3e4,Os=1e4;async function z(e){var a;const t=new AbortController,n=setTimeout(()=>t.abort(),dt);try{const o=await fetch(Cs,{method:"POST",headers:{"x-api-key":e.apiKey,"anthropic-version":Is,"content-type":"application/json","anthropic-dangerous-direct-browser-access":"true"},body:JSON.stringify({model:e.model,max_tokens:e.maxTokens,messages:e.messages}),signal:t.signal});if(!o.ok){const l=((a=(await o.json().catch(()=>({}))).error)==null?void 0:a.message)??`${o.status} ${o.statusText}`;throw new Error(`Anthropic API: ${l}`)}return await o.json()}catch(o){throw o instanceof Error&&o.name==="AbortError"?new Error(`Anthropic API: request timed out after ${dt/1e3}s`):o}finally{clearTimeout(n)}}async function ut(e){var a,o;const t=new AbortController,n=setTimeout(()=>t.abort(),Os);try{const i=await fetch(e,{signal:t.signal});if(!i.ok)return null;const l=((o=(a=i.headers.get("content-type"))==null?void 0:a.split(";")[0])==null?void 0:o.trim())??"image/png";if(!l.startsWith("image/"))return null;const r=await i.arrayBuffer();let u="";const c=new Uint8Array(r),d=32768;for(let h=0;h<c.length;h+=d)u+=String.fromCharCode(...c.slice(h,h+d));return{data:btoa(u),mediaType:l}}catch{return null}finally{clearTimeout(n)}}function K(e,t){var i;const n=((i=e.content.find(l=>l.type==="text"))==null?void 0:i.text)??"",a=_s(n),o=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;return{verdict:a.verdict,reasoning:a.reasoning,confidence:a.confidence,costUsd:o,model:e.model}}function _s(e){const n=e.replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/);if(!n)return{verdict:"uncertain",reasoning:"Model returned no parseable JSON",confidence:0};try{const a=JSON.parse(n[0]),o=a.verdict==="pass"||a.verdict==="fail"?a.verdict:"uncertain",i=(a.reasoning??"").trim().slice(0,600),l=typeof a.confidence=="number"?a.confidence:.5,r=Math.max(0,Math.min(1,l));return{verdict:o,reasoning:i,confidence:r}}catch{return{verdict:"uncertain",reasoning:"Model returned invalid JSON",confidence:0}}}function pt(e,t,n){return{verdict:"uncertain",reasoning:e,confidence:0,costUsd:t,model:n}}const J=`
|
|
2
2
|
Respond ONLY with a single JSON object — no preamble, no markdown:
|
|
3
3
|
{ "verdict": "pass" | "fail" | "uncertain", "reasoning": "1-3 sentences", "confidence": 0.0-1.0 }
|
|
4
|
-
`.trim();function
|
|
4
|
+
`.trim();function Ms(e,t){return`You are an accessibility auditor verifying alt text correctness for screen-reader users (WCAG 1.1.1).
|
|
5
5
|
|
|
6
6
|
Image is attached. Current alt text: ${e?`"${e}"`:"(empty / missing)"}
|
|
7
7
|
${t?`
|
|
@@ -12,7 +12,7 @@ Judge whether the alt text accurately represents the image's content or function
|
|
|
12
12
|
- "fail" = alt is wrong, misleading, generic ("image", "photo"), filename-style ("img-1234.jpg"), or omitted on a meaningful image
|
|
13
13
|
- "uncertain" = you can't tell from the image alone whether the alt is appropriate
|
|
14
14
|
|
|
15
|
-
${J}`}function
|
|
15
|
+
${J}`}function Us(e,t){return`You are an accessibility auditor verifying that headings describe their sections (WCAG 2.4.6).
|
|
16
16
|
|
|
17
17
|
Heading text: "${e}"
|
|
18
18
|
Section content beneath it (first 800 chars): "${t.slice(0,800)}"
|
|
@@ -22,7 +22,7 @@ Judge whether the heading actually describes what the section contains:
|
|
|
22
22
|
- "fail" = heading is non-descriptive ("Section 1", "Welcome", "More info"), unrelated to the content, or generic placeholder
|
|
23
23
|
- "uncertain" = section content is too short or ambiguous to judge
|
|
24
24
|
|
|
25
|
-
${J}`}function
|
|
25
|
+
${J}`}function Ds(e){return`You are an accessibility auditor checking instructions for sensory-characteristic dependencies (WCAG 1.3.3).
|
|
26
26
|
|
|
27
27
|
Instruction text: "${e.slice(0,800)}"
|
|
28
28
|
|
|
@@ -31,7 +31,7 @@ WCAG 1.3.3 says instructions must NOT rely solely on shape, size, color, visual
|
|
|
31
31
|
- "pass" = instruction uses programmatic identifiers (button label, link text) — sensory cues are fine when accompanied by a name
|
|
32
32
|
- "uncertain" = ambiguous whether a sensory descriptor is the only cue
|
|
33
33
|
|
|
34
|
-
${J}`}function
|
|
34
|
+
${J}`}function Ns(e,t,n){return`You are an accessibility auditor verifying ARIA role appropriateness (WCAG 4.1.2).
|
|
35
35
|
|
|
36
36
|
Element (truncated): ${e.slice(0,600)}
|
|
37
37
|
Computed role: ${t}
|
|
@@ -42,7 +42,7 @@ Judge whether the role is semantically appropriate for this element's apparent p
|
|
|
42
42
|
- "fail" = role is misused — e.g., role="button" on a div with no event handlers, role="dialog" on a non-modal overlay, role="navigation" on a non-nav block
|
|
43
43
|
- "uncertain" = can't tell the element's true behavior from this static snapshot
|
|
44
44
|
|
|
45
|
-
${J}`}function
|
|
45
|
+
${J}`}function Ps(e){return`You are an accessibility auditor checking for WCAG 1.4.5 (Images of Text) compliance.
|
|
46
46
|
|
|
47
47
|
WCAG 1.4.5 says: if text can be presented using actual text instead of a baked-in image, it should be. Exceptions: logos, decorative use, or text that's essential to a specific visual presentation (e.g., a screenshot of code).
|
|
48
48
|
|
|
@@ -54,7 +54,7 @@ Judge whether the image bakes substantial text into the raster:
|
|
|
54
54
|
- "pass" = the image is purely visual (photo, illustration, icon) with no substantial baked-in text, OR the baked-in text is a logo / brand mark / unavoidable visual artifact (code screenshot, equation, etc.).
|
|
55
55
|
- "uncertain" = the image is too small / low-resolution to tell, or the text is partial/ambiguous.
|
|
56
56
|
|
|
57
|
-
${J}`}function
|
|
57
|
+
${J}`}function Ls(e,t){return`You are an accessibility auditor checking for WCAG 1.4.1 (Use of Color) compliance.
|
|
58
58
|
|
|
59
59
|
WCAG 1.4.1 says: color must not be the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element. There must always be a parallel non-color signal — a text label, an icon shape, a position cue, etc.
|
|
60
60
|
|
|
@@ -68,7 +68,7 @@ Judge whether the element conveys meaning ONLY through color:
|
|
|
68
68
|
- "pass" = the element has a visible text label, an icon child, an aria-label, OR the color is decorative — any non-color signal that a color-blind / grayscale user could detect.
|
|
69
69
|
- "uncertain" = markup is ambiguous (e.g. the contextDescription says "has icon child" but the icon may itself be color-only).
|
|
70
70
|
|
|
71
|
-
${J}`}function
|
|
71
|
+
${J}`}function Fs(e,t){return`You are an accessibility auditor checking for WCAG 2.4.4 (Link Purpose in Context).
|
|
72
72
|
|
|
73
73
|
WCAG 2.4.4 says: the purpose of each link must be determinable from the link text alone, OR from the link text together with its programmatically-determined context (the surrounding sentence / paragraph / list item).
|
|
74
74
|
|
|
@@ -80,7 +80,7 @@ Judge whether a screen-reader user activating "links list" navigation could unde
|
|
|
80
80
|
- "pass" = link text alone is descriptive, OR the surrounding context unambiguously clarifies the destination (e.g., "Pricing details are available — learn more →" makes "learn more" passable).
|
|
81
81
|
- "uncertain" = link text is borderline-generic and context is ambiguous.
|
|
82
82
|
|
|
83
|
-
${J}`}function
|
|
83
|
+
${J}`}function Gs(e,t){return`You are an accessibility auditor checking for WCAG 3.1.5 (Reading Level) and broader cognitive-accessibility issues.
|
|
84
84
|
|
|
85
85
|
A "wall of text" is body content that exceeds reasonable read-without-fatigue size with no structural breaks (subheadings, lists, paragraphs, blockquotes). Cognitively-disabled users + speed-skim readers can't scan it.
|
|
86
86
|
|
|
@@ -92,7 +92,7 @@ Judge whether this block presents a wall-of-text problem:
|
|
|
92
92
|
- "pass" = block is short enough OR has adequate structural breaks (paragraphs, sub-headings, lists) for scannable reading.
|
|
93
93
|
- "uncertain" = block is borderline (~200-300 words with minimal structure).
|
|
94
94
|
|
|
95
|
-
${J}`}function
|
|
95
|
+
${J}`}function Ws(e,t){return`You are an accessibility auditor checking for WCAG 3.1.1 (Language of Page) and 3.1.2 (Language of Parts).
|
|
96
96
|
|
|
97
97
|
The page declares its default human language via the HTML \`lang\` attribute. Screen readers use that to pick the correct pronunciation engine. If the declared language doesn't match the actual content, screen-reader output is unintelligible.
|
|
98
98
|
|
|
@@ -104,7 +104,7 @@ Judge whether the declared language matches the content:
|
|
|
104
104
|
- "pass" = declared language matches the content's primary language.
|
|
105
105
|
- "uncertain" = content is too short, too multilingual, or in a language closely related to the declared one.
|
|
106
106
|
|
|
107
|
-
${J}`}function
|
|
107
|
+
${J}`}function js(e){const t=e.targetLevel==="AAA"?e.fontSize>=18.66||e.fontSize>=14&&e.fontWeight>=700?"4.5":"7.0":e.fontSize>=18.66||e.fontSize>=14&&e.fontWeight>=700?"3.0":"4.5";return`You are a color accessibility expert. Suggest replacement colors that fix a contrast failure while preserving design intent.
|
|
108
108
|
|
|
109
109
|
Current pair (FAILING):
|
|
110
110
|
- Foreground: ${e.foreground}
|
|
@@ -131,7 +131,7 @@ Respond ONLY with a JSON object — no preamble, no markdown:
|
|
|
131
131
|
{ "foreground": "#hexHEX", "background": "#hexHEX", "contrast": 4.6, "rationale": "1 sentence" }
|
|
132
132
|
],
|
|
133
133
|
"reasoning": "1-3 sentences explaining your overall approach or why no suggestions are possible"
|
|
134
|
-
}`}function
|
|
134
|
+
}`}function Vs(e){return`You are an accessibility auditor writing the executive summary for an automated audit report.
|
|
135
135
|
|
|
136
136
|
Audience: ${e.framing==="defense"?"an internal stakeholder team (engineering + legal + risk-management)":e.framing==="evidence"?"an attorney or accessibility advocate considering a notice-and-cure step":"a non-technical site owner who needs to understand their accessibility status"}.
|
|
137
137
|
Framing: ${e.framing} report.
|
|
@@ -162,15 +162,15 @@ Respond ONLY with a JSON object — no preamble, no markdown:
|
|
|
162
162
|
"lead": "2-3 sentence headline framing",
|
|
163
163
|
"body": "2-3 paragraphs of risk explanation + audit-date framing + ongoing-diligence context if applicable. ~150-300 words total.",
|
|
164
164
|
"closer": "1-2 sentence closing line"
|
|
165
|
-
}`}function
|
|
165
|
+
}`}function zs(e,t){var l;const o=(((l=e.content.find(r=>r.type==="text"))==null?void 0:l.text)??"").replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/),i=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;if(!o)return{verdict:"error",candidates:[],reasoning:"Model returned no parseable JSON",costUsd:i,model:e.model};try{const r=JSON.parse(o[0]),u=(r.candidates??[]).slice(0,3).map(c=>({foreground:typeof c.foreground=="string"?c.foreground:"",background:typeof c.background=="string"?c.background:"",contrast:typeof c.contrast=="number"?c.contrast:0,rationale:typeof c.rationale=="string"?c.rationale:""})).filter(c=>c.foreground&&c.background);return{verdict:r.verdict==="suggested"&&u.length>0?"suggested":"no-suggestion",candidates:u,reasoning:typeof r.reasoning=="string"?r.reasoning:"",costUsd:i,model:e.model}}catch{return{verdict:"error",candidates:[],reasoning:"Could not parse color suggestions",costUsd:i,model:e.model}}}function Hs(e,t){var l;const n=((l=e.content.find(r=>r.type==="text"))==null?void 0:l.text)??"",o=n.replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/),i=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;if(!o)return{lead:"",body:n||"Summary generation failed.",closer:"",costUsd:i,model:e.model};try{const r=JSON.parse(o[0]);return{lead:typeof r.lead=="string"?r.lead:"",body:typeof r.body=="string"?r.body:"",closer:typeof r.closer=="string"?r.closer:"",costUsd:i,model:e.model}}catch{return{lead:"",body:n,closer:"",costUsd:i,model:e.model}}}function B(e){if(!e.enabled)return{ok:!1,reason:"AI augmentation is disabled in settings"};if(!e.apiKey)return{ok:!1,reason:"No API key configured"};switch(e.provider){case"anthropic":return{ok:!0,client:Rs({apiKey:e.apiKey,model:e.model})};case"openai":return{ok:!1,reason:"OpenAI provider not yet implemented"};case"gemini":return{ok:!1,reason:"Gemini provider not yet implemented"};default:return{ok:!1,reason:`Unknown provider: ${e.provider}`}}}function q(e){const t={capUsd:Math.max(0,e),spentUsd:0,exceeded:!1};return{state:t,canCharge(){return!t.exceeded&&t.spentUsd<t.capUsd},recordCharge(n){t.spentUsd+=Math.max(0,n),t.spentUsd>=t.capUsd&&(t.exceeded=!0)}}}const Me=F("ai-compliance-summarizer");async function jt(e,t){if(!t.enabled)return null;const n=B(t);if(!n.ok)return Me.debug(`AI client unavailable: ${n.reason}`),null;const a=q(t.costCapUsd);if(!a.canCharge())return null;try{const o=await n.client.generateExecutiveSummary(e);return a.recordCharge(o.costUsd),!o.lead&&!o.body?(Me.debug("AI summary returned empty"),null):o}catch(o){return Me.warn("compliance-summary generation failed",o),null}}async function Bs(){const e=await chrome.storage.local.get("aiConfig");return qe(e.aiConfig)}function qs(){return P("AI_COMPLIANCE_SUMMARY_REQUEST",async e=>{const t=await Bs();if(!t.enabled)return{type:"AI_COMPLIANCE_SUMMARY_RESPONSE",summary:null,unavailableReason:"AI augmentation is disabled in Settings → AI augmentation."};if(!t.apiKey)return{type:"AI_COMPLIANCE_SUMMARY_RESPONSE",summary:null,unavailableReason:"No AI API key configured."};const n=await jt(e.input,t);return{type:"AI_COMPLIANCE_SUMMARY_RESPONSE",summary:n,unavailableReason:n?void 0:"AI summary generation returned no result."}})}const H="wcag-component-auditor";function ee(){try{return chrome.runtime.getManifest().version}catch{return"0.0.0"}}function X(e){const t=e[0];return t?t.pageUrl?t.pageUrl:t.scope&&/^https?:\/\//.test(t.scope)?t.scope:t.scope?`(selector: ${t.scope})`:"unknown":"unknown"}function Ks(e,t){return{schemaVersion:1,generatedAt:new Date().toISOString(),tool:{name:H,version:ee()},results:e,delta:t}}function ht(e){return e==="critical"||e==="serious"?"error":e==="moderate"?"warning":"note"}function Ys(e,t){const n=t?t.new:e.flatMap(r=>r.violations),a=new Set,o=[];for(const r of n)a.has(r.ruleId)||(a.add(r.ruleId),o.push({id:r.ruleId,shortDescription:{text:r.description||r.ruleId},helpUri:r.helpUrl||void 0,defaultConfiguration:{level:ht(r.impact)},properties:{tags:[r.wcagCriterion,`wcag-${r.wcagLevel}`]}}));const i=X(e),l=n.map(r=>({ruleId:r.ruleId,level:ht(r.impact),message:{text:`${r.description}`+(r.target.failureSummary?`
|
|
166
166
|
|
|
167
|
-
${r.target.failureSummary}`:"")},locations:[{physicalLocation:{artifactLocation:{uri:i&&/^https?:\/\//.test(i)?`${i}#${r.target.selector}`:r.target.selector?`selector://${r.target.selector}`:"unknown"},region:{snippet:{text:r.target.outerHTML}}},logicalLocations:[{name:r.componentId,kind:"module"}]}],properties:{componentId:r.componentId,wcagCriterion:r.wcagCriterion,wcagLevel:r.wcagLevel,pseudoState:r.currentState.pseudoState,theme:r.currentState.theme,direction:r.currentState.direction,breakpoint:r.currentState.breakpoint.id,detectedAt:r.detectedAt}}));return{$schema:"https://json.schemastore.org/sarif-2.1.0.json",version:"2.1.0",runs:[{tool:{driver:{name:H,version:ee(),informationUri:"https://wcagcheckr.com",rules:o}},results:l}]}}function Vt(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function Q(e){return Vt(e).replace(/\n/g," ").replace(/\r/g," ")}function
|
|
167
|
+
${r.target.failureSummary}`:"")},locations:[{physicalLocation:{artifactLocation:{uri:i&&/^https?:\/\//.test(i)?`${i}#${r.target.selector}`:r.target.selector?`selector://${r.target.selector}`:"unknown"},region:{snippet:{text:r.target.outerHTML}}},logicalLocations:[{name:r.componentId,kind:"module"}]}],properties:{componentId:r.componentId,wcagCriterion:r.wcagCriterion,wcagLevel:r.wcagLevel,pseudoState:r.currentState.pseudoState,theme:r.currentState.theme,direction:r.currentState.direction,breakpoint:r.currentState.breakpoint.id,detectedAt:r.detectedAt}}));return{$schema:"https://json.schemastore.org/sarif-2.1.0.json",version:"2.1.0",runs:[{tool:{driver:{name:H,version:ee(),informationUri:"https://wcagcheckr.com",rules:o}},results:l}]}}function Vt(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function Q(e){return Vt(e).replace(/\n/g," ").replace(/\r/g," ")}function Js(e,t){const n=!!t,a=c=>n?c.violations.filter(d=>t.new.some(h=>h.matchKey===d.matchKey)):c.violations,o=e.reduce((c,d)=>c+Math.max(1,d.violations.length),0),i=e.reduce((c,d)=>c+a(d).length,0),l=e.reduce((c,d)=>c+d.durationMs,0)/1e3,r=[];r.push('<?xml version="1.0" encoding="UTF-8"?>');const u=`${H} — ${X(e)}`;r.push(`<testsuites name="${Q(u)}" tests="${o}" failures="${i}" time="${l.toFixed(3)}">`);for(const c of e){const d=`${c.state.pseudoState}_${c.state.theme}_${c.state.direction}_${c.state.breakpoint.id}`,h=`${c.componentId}::${d}`,s=a(c),x=Math.max(1,c.violations.length);if(r.push(` <testsuite name="${Q(h)}" tests="${x}" failures="${s.length}" time="${(c.durationMs/1e3).toFixed(3)}">`),c.violations.length===0)r.push(` <testcase classname="${Q(h)}" name="no-violations" time="${(c.durationMs/1e3).toFixed(3)}" />`);else for(const S of c.violations){const E=s.some(T=>T.matchKey===S.matchKey),v=`classname="${Q(h)}" name="${Q(S.ruleId)}" time="0"`;if(!E){r.push(` <testcase ${v} />`);continue}const I=`${S.impact} - ${S.description}`,w=`Selector: ${S.target.selector}
|
|
168
168
|
WCAG: ${S.wcagCriterion} (${S.wcagLevel})
|
|
169
|
-
State: ${
|
|
169
|
+
State: ${d}
|
|
170
170
|
`+(S.helpUrl?`Help: ${S.helpUrl}
|
|
171
171
|
`:"")+(S.target.failureSummary?`
|
|
172
172
|
${S.target.failureSummary}`:"");r.push(` <testcase ${v}>`),r.push(` <failure message="${Q(I)}" type="${Q(S.ruleId)}">${Vt(w)}</failure>`),r.push(" </testcase>")}r.push(" </testsuite>")}return r.push("</testsuites>"),r.join(`
|
|
173
|
-
`)}function f(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function
|
|
173
|
+
`)}function f(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function Xs(e,t){var I,w,T,k,M;const n=((I=e[0])==null?void 0:I.componentId)??"unknown",a=X(e),o=e.reduce((g,R)=>g+R.durationMs,0),i=e.flatMap(g=>g.violations),l=new Map;for(const g of i){const R=`${g.ruleId}::${g.target.selector}`,b=`:${g.currentState.pseudoState} · ${g.currentState.theme} · ${g.currentState.direction}`,C=l.get(R);if(C){C._states.includes(b)||C._states.push(b);continue}l.set(R,{...g,_states:[b]})}const r=Array.from(l.values()),u={critical:[],serious:[],moderate:[],minor:[]};for(const g of r)(u[g.impact]??u.moderate).push(g);const c=new Set(((w=t==null?void 0:t.new)==null?void 0:w.map(g=>`${g.ruleId}::${g.target.selector}`))??[]),d=new Set(((T=t==null?void 0:t.persistent)==null?void 0:T.map(g=>`${g.ruleId}::${g.target.selector}`))??[]),h=new Set(((k=t==null?void 0:t.fixed)==null?void 0:k.map(g=>`${g.ruleId}::${g.target.selector}`))??[]);let s=0,x=0;for(const g of r){const R=`${g.ruleId}::${g.target.selector}`;d.has(R)?x++:c.has(R)&&s++}let S=0;for(const g of h)!d.has(g)&&!c.has(g)&&S++;const E=g=>{const R=c.has(`${g.ruleId}::${g.target.selector}`);return`
|
|
174
174
|
<article class="violation impact-${f(g.impact)}${R?" is-new":""}">
|
|
175
175
|
<header>
|
|
176
176
|
<span class="rule">${f(g.ruleId)}</span>
|
|
@@ -277,22 +277,22 @@ ${S.target.failureSummary}`:"");r.push(` <testcase ${v}>`),r.push(` <fai
|
|
|
277
277
|
Tip: use your browser's print dialog (Ctrl+P / Cmd+P) → "Save as PDF" to produce a PDF file.
|
|
278
278
|
</p>
|
|
279
279
|
</header>
|
|
280
|
-
${v("critical",
|
|
281
|
-
${v("serious",
|
|
282
|
-
${v("moderate",
|
|
283
|
-
${v("minor",
|
|
280
|
+
${v("critical",u.critical)}
|
|
281
|
+
${v("serious",u.serious)}
|
|
282
|
+
${v("moderate",u.moderate)}
|
|
283
|
+
${v("minor",u.minor)}
|
|
284
284
|
<footer>
|
|
285
285
|
Generated by ${H} v${ee()}. Full audit — all detected violations included, deduped across states. Items flagged "NEW vs baseline" were not present in the most-recent accepted baseline.
|
|
286
286
|
</footer>
|
|
287
287
|
</body>
|
|
288
|
-
</html>`}const zt=[{ref:"1.1.1",title:"Non-text Content",level:"A",rules:["image-alt","input-image-alt","area-alt","object-alt","svg-img-alt"]},{ref:"1.2.1",title:"Audio-only and Video-only (Prerecorded)",level:"A",rules:[]},{ref:"1.2.2",title:"Captions (Prerecorded)",level:"A",rules:["video-caption"]},{ref:"1.3.1",title:"Info and Relationships",level:"A",rules:["heading-order","list","listitem","definition-list","dlitem","th-has-data-cells","td-headers-attr","aria-required-children","aria-required-parent","label"]},{ref:"1.3.2",title:"Meaningful Sequence",level:"A",rules:[]},{ref:"1.3.3",title:"Sensory Characteristics",level:"A",rules:[]},{ref:"1.3.4",title:"Orientation",level:"AA",rules:["css-orientation-lock"]},{ref:"1.3.5",title:"Identify Input Purpose",level:"AA",rules:["autocomplete-valid"]},{ref:"1.4.1",title:"Use of Color",level:"A",rules:["link-in-text-block"]},{ref:"1.4.2",title:"Audio Control",level:"A",rules:["no-autoplay-audio"]},{ref:"1.4.3",title:"Contrast (Minimum)",level:"AA",rules:["color-contrast"]},{ref:"1.4.4",title:"Resize Text",level:"AA",rules:["meta-viewport"]},{ref:"1.4.10",title:"Reflow",level:"AA",rules:[]},{ref:"1.4.11",title:"Non-text Contrast",level:"AA",rules:[]},{ref:"1.4.12",title:"Text Spacing",level:"AA",rules:[]},{ref:"1.4.13",title:"Content on Hover or Focus",level:"AA",rules:[]},{ref:"2.1.1",title:"Keyboard",level:"A",rules:["nested-interactive","frame-focusable-content"]},{ref:"2.1.2",title:"No Keyboard Trap",level:"A",rules:[]},{ref:"2.1.4",title:"Character Key Shortcuts",level:"A",rules:[]},{ref:"2.2.1",title:"Timing Adjustable",level:"A",rules:["meta-refresh"]},{ref:"2.2.2",title:"Pause, Stop, Hide",level:"A",rules:["blink","marquee"]},{ref:"2.4.1",title:"Bypass Blocks",level:"A",rules:["bypass"]},{ref:"2.4.2",title:"Page Titled",level:"A",rules:["document-title"]},{ref:"2.4.3",title:"Focus Order",level:"A",rules:["tabindex"]},{ref:"2.4.4",title:"Link Purpose (In Context)",level:"A",rules:["link-name"]},{ref:"2.4.5",title:"Multiple Ways",level:"AA",rules:[]},{ref:"2.4.6",title:"Headings and Labels",level:"AA",rules:["empty-heading","empty-table-header"]},{ref:"2.4.7",title:"Focus Visible",level:"AA",rules:[]},{ref:"2.5.1",title:"Pointer Gestures",level:"A",rules:[]},{ref:"2.5.2",title:"Pointer Cancellation",level:"A",rules:[]},{ref:"2.5.3",title:"Label in Name",level:"A",rules:["label-content-name-mismatch"]},{ref:"2.5.4",title:"Motion Actuation",level:"A",rules:[]},{ref:"2.5.8",title:"Target Size (Minimum)",level:"AA",rules:["target-size"]},{ref:"3.1.1",title:"Language of Page",level:"A",rules:["html-has-lang","html-lang-valid"]},{ref:"3.1.2",title:"Language of Parts",level:"AA",rules:["valid-lang"]},{ref:"3.2.1",title:"On Focus",level:"A",rules:[]},{ref:"3.2.2",title:"On Input",level:"A",rules:[]},{ref:"3.2.3",title:"Consistent Navigation",level:"AA",rules:[]},{ref:"3.2.4",title:"Consistent Identification",level:"AA",rules:[]},{ref:"3.3.1",title:"Error Identification",level:"A",rules:[]},{ref:"3.3.2",title:"Labels or Instructions",level:"A",rules:["label","form-field-multiple-labels","select-name"]},{ref:"3.3.3",title:"Error Suggestion",level:"AA",rules:[]},{ref:"3.3.4",title:"Error Prevention (Legal, Financial, Data)",level:"AA",rules:[]},{ref:"4.1.1",title:"Parsing",level:"A",rules:["duplicate-id","duplicate-id-aria","duplicate-id-active"]},{ref:"4.1.2",title:"Name, Role, Value",level:"A",rules:["button-name","aria-required-attr","aria-allowed-attr","aria-roles","aria-valid-attr","aria-valid-attr-value","aria-input-field-name","aria-toggle-field-name","select-name","input-button-name"]},{ref:"4.1.3",title:"Status Messages",level:"AA",rules:[]}];function Ht(e,t){if(e.rules.length===0)return{conformance:"Not Applicable",hits:[]};const n=e.rules.filter(a=>t.has(a));return{conformance:n.length>0?"Partially Supports":"Supports",hits:n}}function
|
|
288
|
+
</html>`}const zt=[{ref:"1.1.1",title:"Non-text Content",level:"A",rules:["image-alt","input-image-alt","area-alt","object-alt","svg-img-alt"]},{ref:"1.2.1",title:"Audio-only and Video-only (Prerecorded)",level:"A",rules:[]},{ref:"1.2.2",title:"Captions (Prerecorded)",level:"A",rules:["video-caption"]},{ref:"1.3.1",title:"Info and Relationships",level:"A",rules:["heading-order","list","listitem","definition-list","dlitem","th-has-data-cells","td-headers-attr","aria-required-children","aria-required-parent","label"]},{ref:"1.3.2",title:"Meaningful Sequence",level:"A",rules:[]},{ref:"1.3.3",title:"Sensory Characteristics",level:"A",rules:[]},{ref:"1.3.4",title:"Orientation",level:"AA",rules:["css-orientation-lock"]},{ref:"1.3.5",title:"Identify Input Purpose",level:"AA",rules:["autocomplete-valid"]},{ref:"1.4.1",title:"Use of Color",level:"A",rules:["link-in-text-block"]},{ref:"1.4.2",title:"Audio Control",level:"A",rules:["no-autoplay-audio"]},{ref:"1.4.3",title:"Contrast (Minimum)",level:"AA",rules:["color-contrast"]},{ref:"1.4.4",title:"Resize Text",level:"AA",rules:["meta-viewport"]},{ref:"1.4.10",title:"Reflow",level:"AA",rules:[]},{ref:"1.4.11",title:"Non-text Contrast",level:"AA",rules:[]},{ref:"1.4.12",title:"Text Spacing",level:"AA",rules:[]},{ref:"1.4.13",title:"Content on Hover or Focus",level:"AA",rules:[]},{ref:"2.1.1",title:"Keyboard",level:"A",rules:["nested-interactive","frame-focusable-content"]},{ref:"2.1.2",title:"No Keyboard Trap",level:"A",rules:[]},{ref:"2.1.4",title:"Character Key Shortcuts",level:"A",rules:[]},{ref:"2.2.1",title:"Timing Adjustable",level:"A",rules:["meta-refresh"]},{ref:"2.2.2",title:"Pause, Stop, Hide",level:"A",rules:["blink","marquee"]},{ref:"2.4.1",title:"Bypass Blocks",level:"A",rules:["bypass"]},{ref:"2.4.2",title:"Page Titled",level:"A",rules:["document-title"]},{ref:"2.4.3",title:"Focus Order",level:"A",rules:["tabindex"]},{ref:"2.4.4",title:"Link Purpose (In Context)",level:"A",rules:["link-name"]},{ref:"2.4.5",title:"Multiple Ways",level:"AA",rules:[]},{ref:"2.4.6",title:"Headings and Labels",level:"AA",rules:["empty-heading","empty-table-header"]},{ref:"2.4.7",title:"Focus Visible",level:"AA",rules:[]},{ref:"2.5.1",title:"Pointer Gestures",level:"A",rules:[]},{ref:"2.5.2",title:"Pointer Cancellation",level:"A",rules:[]},{ref:"2.5.3",title:"Label in Name",level:"A",rules:["label-content-name-mismatch"]},{ref:"2.5.4",title:"Motion Actuation",level:"A",rules:[]},{ref:"2.5.8",title:"Target Size (Minimum)",level:"AA",rules:["target-size"]},{ref:"3.1.1",title:"Language of Page",level:"A",rules:["html-has-lang","html-lang-valid"]},{ref:"3.1.2",title:"Language of Parts",level:"AA",rules:["valid-lang"]},{ref:"3.2.1",title:"On Focus",level:"A",rules:[]},{ref:"3.2.2",title:"On Input",level:"A",rules:[]},{ref:"3.2.3",title:"Consistent Navigation",level:"AA",rules:[]},{ref:"3.2.4",title:"Consistent Identification",level:"AA",rules:[]},{ref:"3.3.1",title:"Error Identification",level:"A",rules:[]},{ref:"3.3.2",title:"Labels or Instructions",level:"A",rules:["label","form-field-multiple-labels","select-name"]},{ref:"3.3.3",title:"Error Suggestion",level:"AA",rules:[]},{ref:"3.3.4",title:"Error Prevention (Legal, Financial, Data)",level:"AA",rules:[]},{ref:"4.1.1",title:"Parsing",level:"A",rules:["duplicate-id","duplicate-id-aria","duplicate-id-active"]},{ref:"4.1.2",title:"Name, Role, Value",level:"A",rules:["button-name","aria-required-attr","aria-allowed-attr","aria-roles","aria-valid-attr","aria-valid-attr-value","aria-input-field-name","aria-toggle-field-name","select-name","input-button-name"]},{ref:"4.1.3",title:"Status Messages",level:"AA",rules:[]}];function Ht(e,t){if(e.rules.length===0)return{conformance:"Not Applicable",hits:[]};const n=e.rules.filter(a=>t.has(a));return{conformance:n.length>0?"Partially Supports":"Supports",hits:n}}function Qs(e,t){var u,c;const n=((u=e[0])==null?void 0:u.componentId)??"unknown",a=X(e),o=e.flatMap(d=>d.violations),i=new Set(o.map(d=>d.ruleId)),l=d=>{const{conformance:h,hits:s}=Ht(d,i);return`
|
|
289
289
|
<tr class="${h==="Supports"?"supports":h==="Partially Supports"?"partial":"na"}">
|
|
290
|
-
<td class="ref">${f(
|
|
291
|
-
<td class="title">${f(
|
|
290
|
+
<td class="ref">${f(d.ref)}</td>
|
|
291
|
+
<td class="title">${f(d.title)} <span class="level">(${d.level})</span></td>
|
|
292
292
|
<td class="conf">${h}</td>
|
|
293
293
|
<td class="notes">${s.length>0?`Auto-detected violations of: ${s.map(S=>`<code>${f(S)}</code>`).join(", ")}. Manual review recommended.`:h==="Not Applicable"?"No automated rule maps to this criterion. Manual evaluation required.":"No automated violations detected. Manual confirmation recommended for full claim."}</td>
|
|
294
|
-
</tr>`},r=
|
|
295
|
-
<h2>WCAG 2.1 Level ${
|
|
294
|
+
</tr>`},r=d=>{const h=zt.filter(s=>s.level===d).map(l).join("");return`
|
|
295
|
+
<h2>WCAG 2.1 Level ${d}</h2>
|
|
296
296
|
<table>
|
|
297
297
|
<thead>
|
|
298
298
|
<tr><th>Criterion</th><th>Title</th><th>Conformance</th><th>Notes</th></tr>
|
|
@@ -341,10 +341,10 @@ ${S.target.failureSummary}`:"");r.push(` <testcase ${v}>`),r.push(` <fai
|
|
|
341
341
|
Generated by ${H} v${ee()}. Conformance assessed against all violations detected during the audit (not delta-filtered).
|
|
342
342
|
</footer>
|
|
343
343
|
</body>
|
|
344
|
-
</html>`}function ta(e){const t=[],n=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 a=/\bsrc\s*=\s*"([^"]+)"|\bsrc\s*=\s*'([^']+)'/i.exec(n);a&&t.push({label:"Image source",value:`\`${a[1]??a[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(n);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 a=/\baria-label\s*=\s*"([^"]+)"/i.exec(n);a&&t.push({label:"Existing aria-label",value:a[1]});const o=/<svg[^>]*>[\s\S]*?<title>([^<]+)<\/title>/i.exec(n);o&&t.push({label:"SVG <title> inside",value:o[1]});const i=/<(?:i|svg|span)[^>]*class\s*=\s*"([^"]*(?:icon|fa-|material-icon)[^"]*)"/i.exec(n);i&&t.push({label:"Icon class hint",value:`\`${i[1]}\` — name suggests intent`})}if(e.ruleId==="link-name"||e.ruleId==="empty-link"){const a=/\bhref\s*=\s*"([^"]+)"|\bhref\s*=\s*'([^']+)'/i.exec(n);a&&t.push({label:"Link href",value:`\`${a[1]??a[2]}\` — destination may suggest the right link text`});const o=/\baria-label\s*=\s*"([^"]+)"/i.exec(n);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 a=/\bname\s*=\s*"([^"]+)"|\bname\s*=\s*'([^']+)'/i.exec(n);a&&t.push({label:"Field name attribute",value:`\`${a[1]??a[2]}\` — often hints at the intended label`});const o=/\bplaceholder\s*=\s*"([^"]+)"|\bplaceholder\s*=\s*'([^']+)'/i.exec(n);o&&t.push({label:"Placeholder text (visible)",value:o[1]??o[2]});const i=/\btype\s*=\s*"([^"]+)"|\btype\s*=\s*'([^']+)'/i.exec(n);i&&t.push({label:"Input type",value:`\`${i[1]??i[2]}\``});const l=/\bautocomplete\s*=\s*"([^"]+)"|\bautocomplete\s*=\s*'([^']+)'/i.exec(n);l&&t.push({label:"Autocomplete",value:l[1]??l[2]})}return t}function na(e,t,n,a,o){var g,R,b,C,m,O;const i=new Set(o??[]),l=((g=e[0])==null?void 0:g.componentId)??"unknown",r=X(e),d=!!(t&&t.baselineSnapshotMeta),c=e.flatMap(p=>p.violations),u=new Map;for(const p of c){const y=p.ruleId.startsWith("ai-")?`${p.ruleId}::${p.target.selector}::${(p.target.outerHTML??"").slice(0,200)}`:ce(p.ruleId,p.target.selector),A=`${p.currentState.pseudoState} · ${p.currentState.theme} · ${p.currentState.direction} · ${p.currentState.breakpoint.id}`,_=u.get(y);if(_){_._states.includes(A)||_._states.push(A),_._instanceSelectors.includes(p.target.selector)||_._instanceSelectors.push(p.target.selector);continue}u.set(y,{...p,_states:[A],_instanceSelectors:[p.target.selector]})}const h=Array.from(u.values()).sort((p,y)=>{const A={critical:0,serious:1,moderate:2,minor:3};return(A[p.impact]??99)-(A[y.impact]??99)}),s=[];s.push("# Accessibility Fix Request"),s.push(""),s.push(`You are a senior accessibility engineer pairing with the user on their codebase. The component below has ${h.length} unique WCAG violation${h.length===1?"":"s"} detected by an automated audit running axe-core ${((R=e[0])==null?void 0:R.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("");const x=te(c);s.push("## Context"),s.push(""),s.push(`- **Component / scope:** \`${l}\``),s.push(`- **Audited URL:** ${r}`),s.push("- **WCAG target:** WCAG 2.1 AA + WCAG 2.2 AA + best-practice rules"),s.push("- **Source filter:** Full audit — every detected violation"),s.push(`- **Compliance posture:** Grade **${x.letter}** · **${x.risk.toUpperCase()} risk** (${x.totals.critical} critical · ${x.totals.serious} serious · ${x.totals.moderate} moderate · ${x.totals.minor} minor). ${x.risk==="critical"||x.risk==="high"?"Treat as triage priority — these violations represent meaningful lawsuit exposure under ADA Title III, EAA, and EN 301 549.":x.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."}`),d&&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("## ⛔ 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("## 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("---"),s.push("");const S=(((b=e[0])==null?void 0:b.readingOrderIssues)??[]).filter(p=>!i.has(`reading-order::${p.selector}`)),E=(C=e[0])==null?void 0:C.positionAnalysisCapturedAt,v=E?`${E.breakpoint.width}×${E.breakpoint.height} (${E.breakpoint.label}), ${E.direction.toUpperCase()}, ${E.theme} theme, ${E.pseudoState} pseudo-state`:"(reference state unavailable — findings reflect last matrix state)";if(S.length>0){s.push("## Reading-order concerns (DOM ≠ visual order)"),s.push(""),s.push(`_Positions captured at: **${v}**. 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. ${S.length} element${S.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 p of S){const y=p.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");s.push(`| ${p.domIndex} | ${p.visualIndex} | \`${p.selector}\` | ${y} |`)}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 I=(((m=e[0])==null?void 0:m.tabOrderIssues)??[]).filter(p=>!i.has(`tab-order::${p.selector}`));if(I.length>0){const p=I.filter(A=>A.flag==="visual"||A.flag==="both").length,y=I.filter(A=>A.flag==="tabindex"||A.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: **${v}**. 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. ${I.length} focusable element${I.length===1?" has":"s have"} a notable divergence. Of these: ${p} ${p===1?"has":"have"} a visual-vs-tab mismatch (focus jumps around the page); ${y} ${y===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 A of I){const _=A.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");s.push(`| ${A.tabPosition} | ${A.visualPosition} | ${A.domPosition} | ${A.flag} | \`${A.selector}\` | ${_} |`)}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 w=e.flatMap(p=>p.announcements??[]),k=e.flatMap(p=>p.focusEvents??[]).filter(p=>p.isFocusReset);if(w.length>0||k.length>0){if(s.push("## Runtime behavioral observations"),s.push(""),w.length>0){const p=w.filter(A=>A.politeness==="assertive").length;s.push(`**Live-region announcements** (${w.length} total, ${p} 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 y=w.slice(0,8);for(const A of y){const _=A.text.replace(/\|/g,"\\|").replace(/\n/g," ").slice(0,120);s.push(`- \`[${A.politeness}${A.role?`/${A.role}`:""}]\` ${_}`)}w.length>8&&s.push(`- (and ${w.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(k.length>0){s.push(`**Focus resets to body** (${k.length} occurrence${k.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 p of k.slice(0,5))s.push(`- \`${p.fromSelector??"(none)"}\` → \`${p.toSelector}\``);k.length>5&&s.push(`- (and ${k.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 M=((O=e[0])==null?void 0:O.undefinedCustomProperties)??[];if(M.length>0){s.push("## Undefined CSS custom properties (cascade root cause)"),s.push(""),s.push(`${M.length} CSS custom propert${M.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 p of M){const y=p.sampleSites.map(A=>`\`${A.selector} { ${A.property} }\``).join("; ");s.push(`| \`${p.name}\` | ${p.referenceCount} | ${y} |`)}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(n&&n.length>0){s.push("## Manual checks already completed (Guided Tests)"),s.push(""),s.push(`The consultant has run ${n.length} Intelligent Guided Test workflow${n.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 p=ye;s.push("| Workflow | Status | Notes |"),s.push("|---|---|---|");for(const N of n){const U=p.find(Ie=>Ie.id===N.workflowId);if(!U)continue;let L=0,G=0,ue=0;for(const Ie of U.steps){const pe=N.steps[Ie.id];pe&&(pe.status==="pass"?L++:pe.status==="fail"?G++:pe.status==="skip"&&ue++)}const nt=L+G+ue,tn=G>0?`❌ ${G} failed`:ue===nt?"➖ skipped":`✓ ${L}/${nt} passed`,nn=`${L}p · ${G}f · ${ue}s of ${U.steps.length} steps`;s.push(`| ${U.name} | ${tn} | ${nn} |`)}s.push("");const y=[];for(const N of n){const U=p.find(L=>L.id===N.workflowId);if(U)for(const L of U.steps){const G=N.steps[L.id];(G==null?void 0:G.status)==="fail"&&y.push({workflow:U.name,stepTitle:L.question,notes:G.notes})}}if(y.length>0){s.push("**Failed manual checks** (these are real WCAG failures the consultant verified by hand):"),s.push("");for(const N of y)s.push(`- **${N.workflow} → ${N.stepTitle}**${N.notes?`: ${N.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 A=new Set(n.map(N=>N.workflowId)),_=p.filter(N=>!A.has(N.id));_.length>0&&(s.push(`**Not yet manually verified** (${_.length} workflow${_.length===1?"":"s"}): `+_.map(N=>N.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(a&&a.pagesAudited>1){const p=(()=>{try{return new URL(r).origin}catch{return null}})(),y=(()=>{try{return new URL(a.startUrl).origin}catch{return null}})();if(p&&y&&p===y){if(s.push("## Site-wide context (from a recent site crawl)"),s.push(""),s.push(`A recent crawl of ${a.pagesAudited} page${a.pagesAudited===1?"":"s"} on this origin produced grade **${a.siteGrade}** site-wide with **${a.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(""),a.topViolations.length>0){s.push("**Most-frequent violations across the site** (rules to prioritize because they affect multiple pages):"),s.push("");for(const A of a.topViolations.slice(0,6))s.push(`- \`${A.ruleId}\` — ${A.impact} — found on ${A.urlsAffected} page${A.urlsAffected===1?"":"s"} (${A.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("")}s.push("**Manual cross-page checks the AI should walk through:**"),s.push(""),s.push("- **3.2.3 Consistent Navigation** — verify primary nav appears in the same DOM order on every page audited above. Inconsistent ordering between pages is a Level AA failure."),s.push('- **3.2.4 Consistent Identification** — verify components with identical functionality (logo "home" link, search button, menu toggle) have identical accessible names across pages.'),s.push("- **3.2.6 Consistent Help (WCAG 2.2 A)** — verify any help mechanism (chat widget, contact link, FAQ link) appears in the same relative position on every page that has one."),s.push(""),s.push("---"),s.push("")}}return s.push("## Violations to fix"),s.push(""),h.forEach((p,y)=>{const A=pn[p.ruleId],_=p._instanceSelectors.length;if(s.push(`### ${y+1}. \`${p.ruleId}\` — ${p.impact} — WCAG ${p.wcagCriterion} ${p.wcagLevel}${_>1?` (${_} instances)`:""}`),s.push(""),s.push(`**Description:** ${p.description}`),s.push(""),_>1){s.push(`**Affects ${_} spots — fix the rule once, applies to all:**`);for(const L of p._instanceSelectors.slice(0,8))s.push(`- \`${L}\``);p._instanceSelectors.length>8&&s.push(`- (and ${p._instanceSelectors.length-8} more)`)}else s.push(`**Selector:** \`${p.target.selector}\``);s.push(""),s.push(`**Found in state(s):** ${p._states.join(" / ")}`),s.push("");const N=ta(p);if(N.length>0){for(const L of N)s.push(`**${L.label}:** ${L.value}`);s.push("")}p.target.failureSummary&&(s.push(`**Diagnostic:** ${p.target.failureSummary.replace(/\n+/g," ")}`),s.push("")),p.ai&&(s.push(`**🤖 AI-verified finding** (model: ${p.ai.model}, confidence: ${(p.ai.confidence*100).toFixed(0)}%)`),s.push(`> ${p.ai.reasoning}`),s.push(""));const U=p.target.cascadeChain;if(U&&(U.color||U.backgroundColor)){if(s.push("**CSS cascade chain (authored → rendered):**"),U.color){const L=U.color.vars.length>0?` (vars: ${U.color.vars.map(G=>`\`${G}\``).join(", ")})`:"";s.push(`- \`color\`: \`${U.color.authored}\` → \`${U.color.rendered}\`${L}${U.color.ruleSelector?` — set in rule \`${U.color.ruleSelector}\``:""}`)}if(U.backgroundColor){const L=U.backgroundColor.vars.length>0?` (vars: ${U.backgroundColor.vars.map(G=>`\`${G}\``).join(", ")})`:"";s.push(`- \`background-color\`: \`${U.backgroundColor.authored}\` → \`${U.backgroundColor.rendered}\`${L}${U.backgroundColor.ruleSelector?` — set in rule \`${U.backgroundColor.ruleSelector}\``:""}`)}s.push("")}s.push("**Element HTML:**"),s.push("```html"),s.push(p.target.outerHTML),s.push("```"),s.push(""),A?(s.push(`**Recipe:** ${A.summary}`),A.snippet&&(s.push(""),s.push("```"+(A.snippetLang??"")),s.push(A.snippet),s.push("```"))):s.push(`**Recipe:** No curated recipe for this rule — apply the standard WCAG ${p.wcagCriterion} guidance.`),p.helpUrl&&(s.push(""),s.push(`**More info:** ${p.helpUrl}`)),s.push(""),s.push("---"),s.push("")}),s.push("## Manual verification checklist — non-automatable WCAG 2.1 / 2.2 AA criteria"),s.push(""),s.push("Automated audits catch ~30–50% of WCAG criteria. The remaining ~50% require human judgment, multi-page comparison, or runtime interaction we cannot script. **After applying the fixes above, walk the user through each item below before declaring the audit complete.** Mark each one passed, failed (and add to the fix queue), or N/A (with reasoning)."),s.push(""),s.push('Items the auditor has already flagged in earlier sections (reading-order, tab-order, behavioral observations, IGT manual checks) are listed below as "ALREADY FLAGGED" — confirm those fixes are in place.'),s.push(""),s.push("### Perceivable"),s.push(""),s.push("- [ ] **1.2.1 Audio-only / Video-only Prerecorded (A)** — every audio-only file has a text transcript; every silent-video file has a text alternative or audio description. Ask the user: does the page have media? If yes, list each `<audio>` / `<video>` and verify the alternative."),s.push('- [ ] **1.2.2 Captions Prerecorded (A)** — every prerecorded video with audio has synchronized captions. If `<video>` elements exist without `<track kind="captions">`, ask the user about caption files.'),s.push("- [ ] **1.2.3 Audio Description / Media Alternative Prerecorded (A)** — prerecorded video has either audio description or a full text alternative."),s.push("- [ ] **1.2.4 Captions Live (AA)** — live audio content (livestream, real-time meeting) has captions. Usually N/A for static sites."),s.push("- [ ] **1.2.5 Audio Description Prerecorded (AA)** — prerecorded video has audio description for visual-only information."),s.push("- [ ] **1.4.2 Audio Control (A)** — if any audio plays automatically for >3 seconds, there is a mechanism to pause / stop / control volume. Test by loading the page; if anything autoplays audibly, fail."),s.push("- [ ] **1.4.4 Resize Text (AA)** — zoom the browser to 200%. All text remains readable; nothing is cut off. No horizontal scrolling within content blocks."),s.push("- [ ] **1.4.5 Images of Text (AA)** — confirm text is rendered as actual text, not as images. Exceptions: logos, brand wordmarks. Walk the user through their hero / heading sections."),s.push("- [ ] **1.4.10 Reflow (AA)** — resize browser to 320px wide × 256px tall. Page is usable; no horizontal scrollbar appears (except for data tables / maps / code)."),s.push("- [ ] **1.4.12 Text Spacing (AA)** — apply user-stylesheet overrides: line-height ≥1.5×; paragraph spacing ≥2×; letter-spacing ≥0.12×; word-spacing ≥0.16×. Layout doesn't break, no clipped/overlapping text."),s.push("- [ ] **1.4.13 Content on Hover or Focus (AA)** — for any tooltip / popover / hover-revealed content: verify it is *dismissible* (Esc closes), *hoverable* (mouse can move into it without it disappearing), and *persistent* (doesn't auto-disappear unless user dismisses)."),s.push(""),s.push("### Operable"),s.push(""),s.push("- [ ] **2.1.2 No Keyboard Trap (A)** — Tab through the entire page from start to finish; verify focus never gets stuck in a region you can't leave with Tab/Shift+Tab/Esc. Common offenders: video players, custom widgets, modals without proper Esc handling."),s.push('- [ ] **2.1.4 Character Key Shortcuts (A)** — if the site implements single-key shortcuts (pressing "j" advances feed, etc.), they must be either turn-off-able OR only active when focus is on a specific control. Ask the user about implemented shortcuts.'),s.push('- [ ] **2.2.1 Timing Adjustable (A)** — if any timeout exists (auto-logout, session expiry, "complete this in X seconds"), user can turn it off, adjust it, or extend it (≥10× the default). Exception: real-time events. Ask the user.'),s.push("- [ ] **2.2.2 Pause, Stop, Hide (A)** — for moving / blinking / scrolling / auto-updating content lasting >5s: there is a mechanism to pause/stop/hide it. Carousels, tickers, animations, autoplaying video — all need controls."),s.push("- [ ] **2.3.1 Three Flashes or Below Threshold (A)** — no content flashes more than 3× per second. Test with anything that pulses, blinks, or has rapid color changes (loading animations, alert flashing). Use https://trace.umd.edu/peat/ for borderline cases."),I.length>0?s.push("- [x] **2.4.3 Focus Order (A)** — ALREADY FLAGGED above (`Tab-order concerns` section). Confirm fixes are in place."):s.push("- [ ] **2.4.3 Focus Order (A)** — auto-detector flagged 0 issues, but spot-check by tabbing through the page: focus visits elements in a logical order (matches visual / semantic flow)."),s.push("- [ ] **2.4.5 Multiple Ways (AA)** — at least two ways to find any page within a set: site search + nav menu, OR menu + sitemap, OR menu + table of contents, etc. Single-page apps may exempt; ask user."),s.push("- [ ] **2.5.1 Pointer Gestures (A)** — any multi-point gesture (pinch-zoom, two-finger swipe) or path-based gesture (signature, drag-along-curve) has a single-pointer alternative. Common offender: maps, signatures, drawing canvases."),s.push("- [ ] **2.5.2 Pointer Cancellation (A)** — actions trigger on the up-event (mouseup / pointerup), NOT the down-event. Pressing then dragging-away cancels. Test by mouse-down on a button → drag off → release: action should not fire."),s.push("- [ ] **2.5.4 Motion Actuation (A)** — features triggered by device motion (shake-to-undo, tilt-to-scroll) can be disabled AND have UI-button alternatives. Usually N/A for desktop sites."),s.push("- [ ] **2.5.7 Dragging Movements (AA, WCAG 2.2)** — any drag-only operation (kanban board, slider) has a non-drag alternative (buttons, click-to-place). Test sliders, drag-and-drop, sortable lists."),s.push(""),s.push("### Understandable"),s.push(""),s.push("- [ ] **3.2.1 On Focus (A)** — focusing any element does NOT trigger a context change (URL change, viewport scroll, modal open, form submit). Tab through every focusable element; verify none cause unexpected behavior."),s.push("- [ ] **3.2.2 On Input (A)** — changing the value of any form control does NOT trigger context change unless the user was warned. Common offender: `<select onChange={navigate}>` — must include adjacent submit button or warn the user."),a&&a.pagesAudited>1?(s.push("- [x] **3.2.3 Consistent Navigation (AA)** — site-crawl context provided above. Walk the user through verifying nav appears in the same order across pages."),s.push("- [x] **3.2.4 Consistent Identification (AA)** — verify components with the same function have the same accessible name across pages (logo home link, search button, menu toggle).")):(s.push("- [ ] **3.2.3 Consistent Navigation (AA)** — REQUIRES MULTI-PAGE COMPARISON. Ask the user for 2-3 representative pages; verify primary nav, footer, and search appear in the same DOM order on each."),s.push('- [ ] **3.2.4 Consistent Identification (AA)** — REQUIRES MULTI-PAGE COMPARISON. Components with identical functionality must have identical accessible names. Compare logo "home" link, search button, menu toggle across pages.')),s.push("- [ ] **3.2.6 Consistent Help (A, WCAG 2.2)** — REQUIRES MULTI-PAGE COMPARISON. If a help mechanism exists (chat, contact link, FAQ), it appears in the same relative order on every page where it appears."),s.push("- [ ] **3.3.4 Error Prevention — Legal, Financial, Data (AA)** — for any form that submits legal commitments (contracts, agreement), financial transactions (payments), or modifies/deletes user data: there is at least ONE of (a) reversibility, (b) error-checking with confirmation, (c) review-and-confirm step before final submit."),s.push("- [ ] **3.3.7 Redundant Entry (A, WCAG 2.2)** — forms in a multi-step process do NOT ask for the same information twice unless essential (re-entering password for confirmation is allowed). Auto-fill or pre-fill where possible."),s.push("- [ ] **3.3.8 Accessible Authentication — Minimum (AA, WCAG 2.2)** — login does not require a cognitive function test (puzzle, riddle, character-recognition) unless an alternative is provided (object recognition + autofill support are allowed; CAPTCHAs that require pattern recognition typically fail)."),s.push(""),s.push("### Robust"),s.push(""),w.length>0||k.length>0?s.push("- [x] **4.1.3 Status Messages (AA)** — runtime observations provided above (`Runtime behavioral observations` section). Confirm announcements use appropriate `aria-live` politeness and focus management is correct."):s.push('- [ ] **4.1.3 Status Messages (AA)** — status updates that don\'t cause a focus change (form-validation errors, "saved" confirmations, search-result counts) must use `role="status"` (polite) or `role="alert"` (assertive). Ask the user to demo their form-error and async-update flows; verify SR announces them.'),s.push(""),s.push("### Manual checks STILL TO DO"),s.push(""),s.push(`Walk the user through each unchecked box above (or batch them by criterion type — "let's test all the keyboard ones together"). For each: either mark passed (and move on), failed (add to the fix queue and resolve), or N/A (briefly justify why this site doesn't require this criterion).`),s.push(""),s.push('**Do not declare the audit complete until every box above is checked.** A site that passes only the automated portion is "axe-clean," not WCAG-compliant.'),s.push(""),s.push("---"),s.push(""),s.push("## When you're done — verification"),s.push(""),s.push("Before reporting completion, do all of the following:"),s.push(""),s.push(`1. **Confirm all ${h.length} violation${h.length===1?"":"s"} above are addressed.** Walk back through the list with the user, ticking each one off.`),s.push("2. **Re-run the auditor** (or whatever method we agreed in question 6 above). Verify zero NEW violations were introduced by the fixes."),s.push(`3. **Walk through the Manual verification checklist above** — every item should be marked passed, failed-and-fixed, or N/A. This is the half of WCAG that automation can't reach; skipping it means "axe-clean," not "compliant."`),s.push("4. **Visual diff** — for design-affecting fixes (color, spacing, layout), confirm the change with the user against the original. Storybook stories and visual regression suites are good checkpoints if the codebase has them."),s.push("5. **Commit log review** — surface the list of commits you produced. Each should be atomic and revertable."),s.push("6. **Report what you couldn't do** explicitly. If any violation required information we don't have (e.g., correct alt text), surface a TODO list for the user rather than guessing."),s.push(""),s.push("## Success criteria"),s.push(""),s.push("- All "+h.length+" violation"+(h.length===1?"":"s")+" above are resolved or explicitly deferred with reason."),s.push('- **Every reading-order and tab-order finding** is either fixed OR documented as a verified false positive (per § "Do not dismiss findings without empirical verification" above). Pattern-matching dismissals do not count.'),s.push('- **Every item in the manual verification checklist** above is marked passed, failed-and-fixed, or N/A-with-reason. (This is what separates "axe-clean" from "WCAG-compliant" — do not skip.)'),s.push("- No new violations introduced."),s.push("- State-specific behavior preserved (`:hover`, `:focus`, dark-mode, RTL, breakpoints)."),s.push("- One atomic commit per fix; clean revertable history."),s.push("- No unrelated code reformatted, no dependencies bumped."),s.push("- Diff is reviewable in under 10 minutes per fix."),s.push(""),s.push(`_Generated by ${H} v${ee()}._`),s.join(`
|
|
345
|
-
`)}const
|
|
346
|
-
`);let
|
|
347
|
-
`)}function Bt(e,t){const n=e.flatMap(s=>s.violations),a=te(n),o=X(e),i=new Date,l=new Map;for(const s of n){const x=ce(s.ruleId,s.target.selector);l.has(x)||l.set(x,s)}const r=Array.from(l.values()).sort((s,x)=>{const S={critical:0,serious:1,moderate:2,minor:3};return(S[s.impact]??99)-(S[x.impact]??99)}),
|
|
344
|
+
</html>`}function Zs(e){const t=[],n=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 a=/\bsrc\s*=\s*"([^"]+)"|\bsrc\s*=\s*'([^']+)'/i.exec(n);a&&t.push({label:"Image source",value:`\`${a[1]??a[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(n);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 a=/\baria-label\s*=\s*"([^"]+)"/i.exec(n);a&&t.push({label:"Existing aria-label",value:a[1]});const o=/<svg[^>]*>[\s\S]*?<title>([^<]+)<\/title>/i.exec(n);o&&t.push({label:"SVG <title> inside",value:o[1]});const i=/<(?:i|svg|span)[^>]*class\s*=\s*"([^"]*(?:icon|fa-|material-icon)[^"]*)"/i.exec(n);i&&t.push({label:"Icon class hint",value:`\`${i[1]}\` — name suggests intent`})}if(e.ruleId==="link-name"||e.ruleId==="empty-link"){const a=/\bhref\s*=\s*"([^"]+)"|\bhref\s*=\s*'([^']+)'/i.exec(n);a&&t.push({label:"Link href",value:`\`${a[1]??a[2]}\` — destination may suggest the right link text`});const o=/\baria-label\s*=\s*"([^"]+)"/i.exec(n);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 a=/\bname\s*=\s*"([^"]+)"|\bname\s*=\s*'([^']+)'/i.exec(n);a&&t.push({label:"Field name attribute",value:`\`${a[1]??a[2]}\` — often hints at the intended label`});const o=/\bplaceholder\s*=\s*"([^"]+)"|\bplaceholder\s*=\s*'([^']+)'/i.exec(n);o&&t.push({label:"Placeholder text (visible)",value:o[1]??o[2]});const i=/\btype\s*=\s*"([^"]+)"|\btype\s*=\s*'([^']+)'/i.exec(n);i&&t.push({label:"Input type",value:`\`${i[1]??i[2]}\``});const l=/\bautocomplete\s*=\s*"([^"]+)"|\bautocomplete\s*=\s*'([^']+)'/i.exec(n);l&&t.push({label:"Autocomplete",value:l[1]??l[2]})}return t}function ea(e,t,n,a,o){var g,R,b,C,m,O;const i=new Set(o??[]),l=((g=e[0])==null?void 0:g.componentId)??"unknown",r=X(e),u=!!(t&&t.baselineSnapshotMeta),c=e.flatMap(p=>p.violations),d=new Map;for(const p of c){const y=p.ruleId.startsWith("ai-")?`${p.ruleId}::${p.target.selector}::${(p.target.outerHTML??"").slice(0,200)}`:ce(p.ruleId,p.target.selector),A=`${p.currentState.pseudoState} · ${p.currentState.theme} · ${p.currentState.direction} · ${p.currentState.breakpoint.id}`,_=d.get(y);if(_){_._states.includes(A)||_._states.push(A),_._instanceSelectors.includes(p.target.selector)||_._instanceSelectors.push(p.target.selector);continue}d.set(y,{...p,_states:[A],_instanceSelectors:[p.target.selector]})}const h=Array.from(d.values()).sort((p,y)=>{const A={critical:0,serious:1,moderate:2,minor:3};return(A[p.impact]??99)-(A[y.impact]??99)}),s=[];s.push("# Accessibility Fix Request"),s.push(""),s.push(`You are a senior accessibility engineer pairing with the user on their codebase. The component below has ${h.length} unique WCAG violation${h.length===1?"":"s"} detected by an automated audit running axe-core ${((R=e[0])==null?void 0:R.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("");const x=te(c);s.push("## Context"),s.push(""),s.push(`- **Component / scope:** \`${l}\``),s.push(`- **Audited URL:** ${r}`),s.push("- **WCAG target:** WCAG 2.1 AA + WCAG 2.2 AA + best-practice rules"),s.push("- **Source filter:** Full audit — every detected violation"),s.push(`- **Compliance posture:** Grade **${x.letter}** · **${x.risk.toUpperCase()} risk** (${x.totals.critical} critical · ${x.totals.serious} serious · ${x.totals.moderate} moderate · ${x.totals.minor} minor). ${x.risk==="critical"||x.risk==="high"?"Treat as triage priority — these violations represent meaningful lawsuit exposure under ADA Title III, EAA, and EN 301 549.":x.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."}`),u&&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("## ⛔ 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("## 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("---"),s.push("");const S=(((b=e[0])==null?void 0:b.readingOrderIssues)??[]).filter(p=>!i.has(`reading-order::${p.selector}`)),E=(C=e[0])==null?void 0:C.positionAnalysisCapturedAt,v=E?`${E.breakpoint.width}×${E.breakpoint.height} (${E.breakpoint.label}), ${E.direction.toUpperCase()}, ${E.theme} theme, ${E.pseudoState} pseudo-state`:"(reference state unavailable — findings reflect last matrix state)";if(S.length>0){s.push("## Reading-order concerns (DOM ≠ visual order)"),s.push(""),s.push(`_Positions captured at: **${v}**. 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. ${S.length} element${S.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 p of S){const y=p.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");s.push(`| ${p.domIndex} | ${p.visualIndex} | \`${p.selector}\` | ${y} |`)}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 I=(((m=e[0])==null?void 0:m.tabOrderIssues)??[]).filter(p=>!i.has(`tab-order::${p.selector}`));if(I.length>0){const p=I.filter(A=>A.flag==="visual"||A.flag==="both").length,y=I.filter(A=>A.flag==="tabindex"||A.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: **${v}**. 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. ${I.length} focusable element${I.length===1?" has":"s have"} a notable divergence. Of these: ${p} ${p===1?"has":"have"} a visual-vs-tab mismatch (focus jumps around the page); ${y} ${y===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 A of I){const _=A.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");s.push(`| ${A.tabPosition} | ${A.visualPosition} | ${A.domPosition} | ${A.flag} | \`${A.selector}\` | ${_} |`)}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 w=e.flatMap(p=>p.announcements??[]),k=e.flatMap(p=>p.focusEvents??[]).filter(p=>p.isFocusReset);if(w.length>0||k.length>0){if(s.push("## Runtime behavioral observations"),s.push(""),w.length>0){const p=w.filter(A=>A.politeness==="assertive").length;s.push(`**Live-region announcements** (${w.length} total, ${p} 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 y=w.slice(0,8);for(const A of y){const _=A.text.replace(/\|/g,"\\|").replace(/\n/g," ").slice(0,120);s.push(`- \`[${A.politeness}${A.role?`/${A.role}`:""}]\` ${_}`)}w.length>8&&s.push(`- (and ${w.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(k.length>0){s.push(`**Focus resets to body** (${k.length} occurrence${k.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 p of k.slice(0,5))s.push(`- \`${p.fromSelector??"(none)"}\` → \`${p.toSelector}\``);k.length>5&&s.push(`- (and ${k.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 M=((O=e[0])==null?void 0:O.undefinedCustomProperties)??[];if(M.length>0){s.push("## Undefined CSS custom properties (cascade root cause)"),s.push(""),s.push(`${M.length} CSS custom propert${M.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 p of M){const y=p.sampleSites.map(A=>`\`${A.selector} { ${A.property} }\``).join("; ");s.push(`| \`${p.name}\` | ${p.referenceCount} | ${y} |`)}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(n&&n.length>0){s.push("## Manual checks already completed (Guided Tests)"),s.push(""),s.push(`The consultant has run ${n.length} Intelligent Guided Test workflow${n.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 p=ye;s.push("| Workflow | Status | Notes |"),s.push("|---|---|---|");for(const N of n){const U=p.find(Ie=>Ie.id===N.workflowId);if(!U)continue;let L=0,G=0,ue=0;for(const Ie of U.steps){const pe=N.steps[Ie.id];pe&&(pe.status==="pass"?L++:pe.status==="fail"?G++:pe.status==="skip"&&ue++)}const tt=L+G+ue,tn=G>0?`❌ ${G} failed`:ue===tt?"➖ skipped":`✓ ${L}/${tt} passed`,nn=`${L}p · ${G}f · ${ue}s of ${U.steps.length} steps`;s.push(`| ${U.name} | ${tn} | ${nn} |`)}s.push("");const y=[];for(const N of n){const U=p.find(L=>L.id===N.workflowId);if(U)for(const L of U.steps){const G=N.steps[L.id];(G==null?void 0:G.status)==="fail"&&y.push({workflow:U.name,stepTitle:L.question,notes:G.notes})}}if(y.length>0){s.push("**Failed manual checks** (these are real WCAG failures the consultant verified by hand):"),s.push("");for(const N of y)s.push(`- **${N.workflow} → ${N.stepTitle}**${N.notes?`: ${N.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 A=new Set(n.map(N=>N.workflowId)),_=p.filter(N=>!A.has(N.id));_.length>0&&(s.push(`**Not yet manually verified** (${_.length} workflow${_.length===1?"":"s"}): `+_.map(N=>N.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(a&&a.pagesAudited>1){const p=(()=>{try{return new URL(r).origin}catch{return null}})(),y=(()=>{try{return new URL(a.startUrl).origin}catch{return null}})();if(p&&y&&p===y){if(s.push("## Site-wide context (from a recent site crawl)"),s.push(""),s.push(`A recent crawl of ${a.pagesAudited} page${a.pagesAudited===1?"":"s"} on this origin produced grade **${a.siteGrade}** site-wide with **${a.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(""),a.topViolations.length>0){s.push("**Most-frequent violations across the site** (rules to prioritize because they affect multiple pages):"),s.push("");for(const A of a.topViolations.slice(0,6))s.push(`- \`${A.ruleId}\` — ${A.impact} — found on ${A.urlsAffected} page${A.urlsAffected===1?"":"s"} (${A.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("")}s.push("**Manual cross-page checks the AI should walk through:**"),s.push(""),s.push("- **3.2.3 Consistent Navigation** — verify primary nav appears in the same DOM order on every page audited above. Inconsistent ordering between pages is a Level AA failure."),s.push('- **3.2.4 Consistent Identification** — verify components with identical functionality (logo "home" link, search button, menu toggle) have identical accessible names across pages.'),s.push("- **3.2.6 Consistent Help (WCAG 2.2 A)** — verify any help mechanism (chat widget, contact link, FAQ link) appears in the same relative position on every page that has one."),s.push(""),s.push("---"),s.push("")}}return s.push("## Violations to fix"),s.push(""),h.forEach((p,y)=>{const A=pn[p.ruleId],_=p._instanceSelectors.length;if(s.push(`### ${y+1}. \`${p.ruleId}\` — ${p.impact} — WCAG ${p.wcagCriterion} ${p.wcagLevel}${_>1?` (${_} instances)`:""}`),s.push(""),s.push(`**Description:** ${p.description}`),s.push(""),_>1){s.push(`**Affects ${_} spots — fix the rule once, applies to all:**`);for(const L of p._instanceSelectors.slice(0,8))s.push(`- \`${L}\``);p._instanceSelectors.length>8&&s.push(`- (and ${p._instanceSelectors.length-8} more)`)}else s.push(`**Selector:** \`${p.target.selector}\``);s.push(""),s.push(`**Found in state(s):** ${p._states.join(" / ")}`),s.push("");const N=Zs(p);if(N.length>0){for(const L of N)s.push(`**${L.label}:** ${L.value}`);s.push("")}p.target.failureSummary&&(s.push(`**Diagnostic:** ${p.target.failureSummary.replace(/\n+/g," ")}`),s.push("")),p.ai&&(s.push(`**🤖 AI-verified finding** (model: ${p.ai.model}, confidence: ${(p.ai.confidence*100).toFixed(0)}%)`),s.push(`> ${p.ai.reasoning}`),s.push(""));const U=p.target.cascadeChain;if(U&&(U.color||U.backgroundColor)){if(s.push("**CSS cascade chain (authored → rendered):**"),U.color){const L=U.color.vars.length>0?` (vars: ${U.color.vars.map(G=>`\`${G}\``).join(", ")})`:"";s.push(`- \`color\`: \`${U.color.authored}\` → \`${U.color.rendered}\`${L}${U.color.ruleSelector?` — set in rule \`${U.color.ruleSelector}\``:""}`)}if(U.backgroundColor){const L=U.backgroundColor.vars.length>0?` (vars: ${U.backgroundColor.vars.map(G=>`\`${G}\``).join(", ")})`:"";s.push(`- \`background-color\`: \`${U.backgroundColor.authored}\` → \`${U.backgroundColor.rendered}\`${L}${U.backgroundColor.ruleSelector?` — set in rule \`${U.backgroundColor.ruleSelector}\``:""}`)}s.push("")}s.push("**Element HTML:**"),s.push("```html"),s.push(p.target.outerHTML),s.push("```"),s.push(""),A?(s.push(`**Recipe:** ${A.summary}`),A.snippet&&(s.push(""),s.push("```"+(A.snippetLang??"")),s.push(A.snippet),s.push("```"))):s.push(`**Recipe:** No curated recipe for this rule — apply the standard WCAG ${p.wcagCriterion} guidance.`),p.helpUrl&&(s.push(""),s.push(`**More info:** ${p.helpUrl}`)),s.push(""),s.push("---"),s.push("")}),s.push("## Manual verification checklist — non-automatable WCAG 2.1 / 2.2 AA criteria"),s.push(""),s.push("Automated audits catch ~30–50% of WCAG criteria. The remaining ~50% require human judgment, multi-page comparison, or runtime interaction we cannot script. **After applying the fixes above, walk the user through each item below before declaring the audit complete.** Mark each one passed, failed (and add to the fix queue), or N/A (with reasoning)."),s.push(""),s.push('Items the auditor has already flagged in earlier sections (reading-order, tab-order, behavioral observations, IGT manual checks) are listed below as "ALREADY FLAGGED" — confirm those fixes are in place.'),s.push(""),s.push("### Perceivable"),s.push(""),s.push("- [ ] **1.2.1 Audio-only / Video-only Prerecorded (A)** — every audio-only file has a text transcript; every silent-video file has a text alternative or audio description. Ask the user: does the page have media? If yes, list each `<audio>` / `<video>` and verify the alternative."),s.push('- [ ] **1.2.2 Captions Prerecorded (A)** — every prerecorded video with audio has synchronized captions. If `<video>` elements exist without `<track kind="captions">`, ask the user about caption files.'),s.push("- [ ] **1.2.3 Audio Description / Media Alternative Prerecorded (A)** — prerecorded video has either audio description or a full text alternative."),s.push("- [ ] **1.2.4 Captions Live (AA)** — live audio content (livestream, real-time meeting) has captions. Usually N/A for static sites."),s.push("- [ ] **1.2.5 Audio Description Prerecorded (AA)** — prerecorded video has audio description for visual-only information."),s.push("- [ ] **1.4.2 Audio Control (A)** — if any audio plays automatically for >3 seconds, there is a mechanism to pause / stop / control volume. Test by loading the page; if anything autoplays audibly, fail."),s.push("- [ ] **1.4.4 Resize Text (AA)** — zoom the browser to 200%. All text remains readable; nothing is cut off. No horizontal scrolling within content blocks."),s.push("- [ ] **1.4.5 Images of Text (AA)** — confirm text is rendered as actual text, not as images. Exceptions: logos, brand wordmarks. Walk the user through their hero / heading sections."),s.push("- [ ] **1.4.10 Reflow (AA)** — resize browser to 320px wide × 256px tall. Page is usable; no horizontal scrollbar appears (except for data tables / maps / code)."),s.push("- [ ] **1.4.12 Text Spacing (AA)** — apply user-stylesheet overrides: line-height ≥1.5×; paragraph spacing ≥2×; letter-spacing ≥0.12×; word-spacing ≥0.16×. Layout doesn't break, no clipped/overlapping text."),s.push("- [ ] **1.4.13 Content on Hover or Focus (AA)** — for any tooltip / popover / hover-revealed content: verify it is *dismissible* (Esc closes), *hoverable* (mouse can move into it without it disappearing), and *persistent* (doesn't auto-disappear unless user dismisses)."),s.push(""),s.push("### Operable"),s.push(""),s.push("- [ ] **2.1.2 No Keyboard Trap (A)** — Tab through the entire page from start to finish; verify focus never gets stuck in a region you can't leave with Tab/Shift+Tab/Esc. Common offenders: video players, custom widgets, modals without proper Esc handling."),s.push('- [ ] **2.1.4 Character Key Shortcuts (A)** — if the site implements single-key shortcuts (pressing "j" advances feed, etc.), they must be either turn-off-able OR only active when focus is on a specific control. Ask the user about implemented shortcuts.'),s.push('- [ ] **2.2.1 Timing Adjustable (A)** — if any timeout exists (auto-logout, session expiry, "complete this in X seconds"), user can turn it off, adjust it, or extend it (≥10× the default). Exception: real-time events. Ask the user.'),s.push("- [ ] **2.2.2 Pause, Stop, Hide (A)** — for moving / blinking / scrolling / auto-updating content lasting >5s: there is a mechanism to pause/stop/hide it. Carousels, tickers, animations, autoplaying video — all need controls."),s.push("- [ ] **2.3.1 Three Flashes or Below Threshold (A)** — no content flashes more than 3× per second. Test with anything that pulses, blinks, or has rapid color changes (loading animations, alert flashing). Use https://trace.umd.edu/peat/ for borderline cases."),I.length>0?s.push("- [x] **2.4.3 Focus Order (A)** — ALREADY FLAGGED above (`Tab-order concerns` section). Confirm fixes are in place."):s.push("- [ ] **2.4.3 Focus Order (A)** — auto-detector flagged 0 issues, but spot-check by tabbing through the page: focus visits elements in a logical order (matches visual / semantic flow)."),s.push("- [ ] **2.4.5 Multiple Ways (AA)** — at least two ways to find any page within a set: site search + nav menu, OR menu + sitemap, OR menu + table of contents, etc. Single-page apps may exempt; ask user."),s.push("- [ ] **2.5.1 Pointer Gestures (A)** — any multi-point gesture (pinch-zoom, two-finger swipe) or path-based gesture (signature, drag-along-curve) has a single-pointer alternative. Common offender: maps, signatures, drawing canvases."),s.push("- [ ] **2.5.2 Pointer Cancellation (A)** — actions trigger on the up-event (mouseup / pointerup), NOT the down-event. Pressing then dragging-away cancels. Test by mouse-down on a button → drag off → release: action should not fire."),s.push("- [ ] **2.5.4 Motion Actuation (A)** — features triggered by device motion (shake-to-undo, tilt-to-scroll) can be disabled AND have UI-button alternatives. Usually N/A for desktop sites."),s.push("- [ ] **2.5.7 Dragging Movements (AA, WCAG 2.2)** — any drag-only operation (kanban board, slider) has a non-drag alternative (buttons, click-to-place). Test sliders, drag-and-drop, sortable lists."),s.push(""),s.push("### Understandable"),s.push(""),s.push("- [ ] **3.2.1 On Focus (A)** — focusing any element does NOT trigger a context change (URL change, viewport scroll, modal open, form submit). Tab through every focusable element; verify none cause unexpected behavior."),s.push("- [ ] **3.2.2 On Input (A)** — changing the value of any form control does NOT trigger context change unless the user was warned. Common offender: `<select onChange={navigate}>` — must include adjacent submit button or warn the user."),a&&a.pagesAudited>1?(s.push("- [x] **3.2.3 Consistent Navigation (AA)** — site-crawl context provided above. Walk the user through verifying nav appears in the same order across pages."),s.push("- [x] **3.2.4 Consistent Identification (AA)** — verify components with the same function have the same accessible name across pages (logo home link, search button, menu toggle).")):(s.push("- [ ] **3.2.3 Consistent Navigation (AA)** — REQUIRES MULTI-PAGE COMPARISON. Ask the user for 2-3 representative pages; verify primary nav, footer, and search appear in the same DOM order on each."),s.push('- [ ] **3.2.4 Consistent Identification (AA)** — REQUIRES MULTI-PAGE COMPARISON. Components with identical functionality must have identical accessible names. Compare logo "home" link, search button, menu toggle across pages.')),s.push("- [ ] **3.2.6 Consistent Help (A, WCAG 2.2)** — REQUIRES MULTI-PAGE COMPARISON. If a help mechanism exists (chat, contact link, FAQ), it appears in the same relative order on every page where it appears."),s.push("- [ ] **3.3.4 Error Prevention — Legal, Financial, Data (AA)** — for any form that submits legal commitments (contracts, agreement), financial transactions (payments), or modifies/deletes user data: there is at least ONE of (a) reversibility, (b) error-checking with confirmation, (c) review-and-confirm step before final submit."),s.push("- [ ] **3.3.7 Redundant Entry (A, WCAG 2.2)** — forms in a multi-step process do NOT ask for the same information twice unless essential (re-entering password for confirmation is allowed). Auto-fill or pre-fill where possible."),s.push("- [ ] **3.3.8 Accessible Authentication — Minimum (AA, WCAG 2.2)** — login does not require a cognitive function test (puzzle, riddle, character-recognition) unless an alternative is provided (object recognition + autofill support are allowed; CAPTCHAs that require pattern recognition typically fail)."),s.push(""),s.push("### Robust"),s.push(""),w.length>0||k.length>0?s.push("- [x] **4.1.3 Status Messages (AA)** — runtime observations provided above (`Runtime behavioral observations` section). Confirm announcements use appropriate `aria-live` politeness and focus management is correct."):s.push('- [ ] **4.1.3 Status Messages (AA)** — status updates that don\'t cause a focus change (form-validation errors, "saved" confirmations, search-result counts) must use `role="status"` (polite) or `role="alert"` (assertive). Ask the user to demo their form-error and async-update flows; verify SR announces them.'),s.push(""),s.push("### Manual checks STILL TO DO"),s.push(""),s.push(`Walk the user through each unchecked box above (or batch them by criterion type — "let's test all the keyboard ones together"). For each: either mark passed (and move on), failed (add to the fix queue and resolve), or N/A (briefly justify why this site doesn't require this criterion).`),s.push(""),s.push('**Do not declare the audit complete until every box above is checked.** A site that passes only the automated portion is "axe-clean," not WCAG-compliant.'),s.push(""),s.push("---"),s.push(""),s.push("## When you're done — verification"),s.push(""),s.push("Before reporting completion, do all of the following:"),s.push(""),s.push(`1. **Confirm all ${h.length} violation${h.length===1?"":"s"} above are addressed.** Walk back through the list with the user, ticking each one off.`),s.push("2. **Re-run the auditor** (or whatever method we agreed in question 6 above). Verify zero NEW violations were introduced by the fixes."),s.push(`3. **Walk through the Manual verification checklist above** — every item should be marked passed, failed-and-fixed, or N/A. This is the half of WCAG that automation can't reach; skipping it means "axe-clean," not "compliant."`),s.push("4. **Visual diff** — for design-affecting fixes (color, spacing, layout), confirm the change with the user against the original. Storybook stories and visual regression suites are good checkpoints if the codebase has them."),s.push("5. **Commit log review** — surface the list of commits you produced. Each should be atomic and revertable."),s.push("6. **Report what you couldn't do** explicitly. If any violation required information we don't have (e.g., correct alt text), surface a TODO list for the user rather than guessing."),s.push(""),s.push("## Success criteria"),s.push(""),s.push("- All "+h.length+" violation"+(h.length===1?"":"s")+" above are resolved or explicitly deferred with reason."),s.push('- **Every reading-order and tab-order finding** is either fixed OR documented as a verified false positive (per § "Do not dismiss findings without empirical verification" above). Pattern-matching dismissals do not count.'),s.push('- **Every item in the manual verification checklist** above is marked passed, failed-and-fixed, or N/A-with-reason. (This is what separates "axe-clean" from "WCAG-compliant" — do not skip.)'),s.push("- No new violations introduced."),s.push("- State-specific behavior preserved (`:hover`, `:focus`, dark-mode, RTL, breakpoints)."),s.push("- One atomic commit per fix; clean revertable history."),s.push("- No unrelated code reformatted, no dependencies bumped."),s.push("- Diff is reviewable in under 10 minutes per fix."),s.push(""),s.push(`_Generated by ${H} v${ee()}._`),s.join(`
|
|
345
|
+
`)}const ta=5;function na(e){const t=new Map;let n=0;for(const a of e){if(!a.screenshotDataUrl||a.violations.length===0)continue;const o=`${a.state.pseudoState} · ${a.state.theme} · ${a.state.direction} · ${a.state.breakpoint.id}`,i=o;t.has(i)||t.set(i,{stateLabel:o,screenshotDataUrl:a.screenshotDataUrl,violations:[]});const l=t.get(i);for(const r of a.violations)n++,l.violations.push({...r,_idx:n})}return Array.from(t.values()).sort((a,o)=>o.violations.length-a.violations.length).slice(0,ta)}const ft=1280,gt=800,sa={low:"Low risk",moderate:"Moderate risk",high:"High risk",critical:"Critical risk"},aa={low:"#d1fae5",moderate:"#fef3c7",high:"#fed7aa",critical:"#fecaca"},oa={low:"#065f46",moderate:"#92400e",high:"#9a3412",critical:"#991b1b"},ia={A:"#10b981",B:"#84cc16",C:"#eab308",D:"#f97316",F:"#dc2626"},ra={defense:{title:"Accessibility Defense Bundle",documentLabel:"engineering, design, and risk-management",disclaimerHeading:"This document is not a compliance certification.",disclaimerBody:"Automated accessibility audits, including this one, detect a subset (typically 30–50%) of WCAG success criteria. Conformance with the full WCAG specification requires manual evaluation by qualified accessibility professionals, including (but not limited to): keyboard-only operability testing, screen-reader compatibility testing across NVDA / JAWS / VoiceOver, manual review of dynamic content and live regions, and content-quality review for alt text, link text, and labels."},evidence:{title:"Accessibility Compliance Evidence",documentLabel:"documentation and review",disclaimerHeading:"This document is automated audit evidence — not a determination of liability or conformance.",disclaimerBody:"This report documents detected accessibility defects in the audited resource at the date and time of capture. The audit was conducted using axe-core, an industry-standard automated accessibility-rule engine maintained by Deque Systems and used by Microsoft (Accessibility Insights), Google (Lighthouse), IBM, and others. The findings represent objective rule-engine output, not subjective evaluation. Automated audits surface a subset (typically 30–50%) of WCAG success criteria; the absence of a violation in this report is not evidence of conformance, and the presence of a violation is not, by itself, a determination of legal liability."}};function ca(e,t,n,a,o){return qt(e,t,"evidence",n,a,o)}function la(e){return Bt(e,"letter")}function da(e){return Bt(e,"report")}function ua(e){var c;const t=e.flatMap(d=>d.violations),n=te(t),a=X(e),o=new Map;for(const d of t){const h=ce(d.ruleId,d.target.selector);o.has(h)||o.set(h,d)}const i=Array.from(o.values()).sort((d,h)=>{const s={critical:0,serious:1,moderate:2,minor:3};return(s[d.impact]??99)-(s[h.impact]??99)}),l=new Map;for(const d of i){const h=l.get(d.ruleId)??[];h.push(d),l.set(d.ruleId,h)}const r=[];if(r.push("# Help me fix accessibility issues on my website"),r.push(""),r.push("You are an accessibility advisor helping me — a non-technical site owner — understand and fix accessibility issues on my website. I ran an automated audit and got the issues below. I may not have technical knowledge, so please explain things in plain English."),r.push(""),r.push("## What I need from you"),r.push(""),r.push("1. **First, ask me what I use to manage my website.** Common platforms: Squarespace, Wix, Shopify, WordPress (with theme name), Webflow, Framer, custom-built. Your advice will be different for each."),r.push("2. **Then, for each issue below, tell me clearly:**"),r.push(" - Whether **I can fix it myself** in my site editor (changing colors, adding alt text, editing button labels, etc.) — and if so, walk me through where to look in my platform"),r.push(" - Or whether **my developer needs to do this** (code-level fix) — and what to ask them to do"),r.push("3. **Prioritize ruthlessly.** I want to start with the issues that:"),r.push(" - Are most likely to come up in an ADA-related lawsuit (critical-impact items)"),r.push(" - Are easiest for me to fix without involving a developer"),r.push("4. **Don't bury me in jargon.** If you have to use a technical term, explain it in plain English first."),r.push("5. **If something is genuinely beyond a non-technical fix, say so.** Tell me what to ask my developer or website-builder support team."),r.push(""),r.push("## Audit summary"),r.push(""),r.push(`- **Page audited:** ${a}`),r.push(`- **Lawsuit-target risk:** ${$t[n.risk]} — ${Tt[n.risk]}`),r.push(`- **Issues found:** ${n.totals.unique} unique problems (${n.totals.critical} critical · ${n.totals.serious} serious · ${n.totals.moderate} moderate · ${n.totals.minor} minor)`),r.push(""),r.push("## The issues, in plain language"),r.push(""),l.size===0)return r.push("No automated issues were detected. Note: automated checks only catch about 30–50% of accessibility problems. Manual testing covers the rest."),r.join(`
|
|
346
|
+
`);let u=1;for(const[d,h]of l.entries()){const s=(c=h.find(E=>E.target.opacityContext))==null?void 0:c.target.opacityContext,x=Et(d,s),S=h[0].impact;r.push(`### ${u}. [${S.toUpperCase()}] ${x.whatsWrong}`),r.push(""),r.push(`**Why this matters.** ${x.whyItMatters}`),r.push(""),r.push(`**Plain-language fix guidance.** ${x.howToFix}`),r.push(""),r.push(`**Where on the page (${h.length} ${h.length===1?"spot":"spots"}):**`);for(const E of h.slice(0,8))r.push(`- \`${E.target.selector}\``);h.length>8&&r.push(`- (and ${h.length-8} more spots)`),r.push(""),r.push(`*Technical reference for your developer:* axe-core rule \`${d}\` — full docs at https://dequeuniversity.com/rules/axe/4.11/${d}`),r.push(""),u++}return r.push("---"),r.push(""),r.push("## How to use this output"),r.push(""),r.push("Walk through this list with me one issue at a time. For each:"),r.push("1. Help me understand the issue with a real example of who it affects."),r.push("2. Tell me clearly if I can fix it myself, and if so, walk me through it for my platform."),r.push("3. If I need a developer, give me a short paragraph I can paste into an email or message to them."),r.push("4. Help me prioritize — I have limited time and want to fix the riskiest things first."),r.push(""),r.push("Start by asking me which platform I use."),r.join(`
|
|
347
|
+
`)}function Bt(e,t){const n=e.flatMap(s=>s.violations),a=te(n),o=X(e),i=new Date,l=new Map;for(const s of n){const x=ce(s.ruleId,s.target.selector);l.has(x)||l.set(x,s)}const r=Array.from(l.values()).sort((s,x)=>{const S={critical:0,serious:1,moderate:2,minor:3};return(S[s.impact]??99)-(S[x.impact]??99)}),u=new Map;for(const s of r){const x=u.get(s.ruleId)??[];x.push(s),u.set(s.ruleId,x)}const c=Array.from(u.entries()).map(([s,x])=>{var T;const S=(T=x.find(k=>k.target.opacityContext))==null?void 0:T.target.opacityContext,E=Et(s,S),v=x[0].impact,I=x.slice(0,8).map(k=>`<code>${f(k.target.selector)}</code>`).join(", "),w=x.length>8?` <em>(and ${x.length-8} more)</em>`:"";return`
|
|
348
348
|
<section class="rule">
|
|
349
349
|
<div class="rule-header">
|
|
350
350
|
<span class="impact impact-${f(v)}">${f(v)}</span>
|
|
@@ -360,7 +360,7 @@ ${S.target.failureSummary}`:"");r.push(` <testcase ${v}>`),r.push(` <fai
|
|
|
360
360
|
<p>axe-core rule ID: <code>${f(s)}</code>. Full rule documentation:
|
|
361
361
|
<a href="https://dequeuniversity.com/rules/axe/4.11/${f(s)}">dequeuniversity.com/rules/axe/4.11/${f(s)}</a>.</p>
|
|
362
362
|
</details>
|
|
363
|
-
</section>`}).join(""),
|
|
363
|
+
</section>`}).join(""),d=t==="letter"?a.totals.critical>0?"Hi! I ran an automated accessibility check on our site and found some things I think we should fix. A few are flagged as <strong>critical</strong> — those tend to be the ones that come up in ADA-related lawsuits, so they're worth prioritizing.":a.totals.serious>0?"Hi! I ran an automated accessibility check on our site. We have some serious issues to fix — nothing immediately critical, but real problems that affect real users.":"Hi! I ran an automated accessibility check on our site. The issues found are mostly minor or moderate — worth tidying up but not urgent.":a.totals.critical>0?"This report summarizes the accessibility findings for the page below. <strong>Critical-impact issues</strong> were detected — these are the categories that most commonly drive ADA-related lawsuits and should be prioritized.":a.totals.serious>0?"This report summarizes the accessibility findings for the page below. Serious issues were detected — real problems for real users, though none rise to the critical level.":"This report summarizes the accessibility findings for the page below. Findings are mostly minor or moderate — worth addressing during normal site maintenance.",h=t==="letter"?"Accessibility check":"Accessibility report";return`<!doctype html>
|
|
364
364
|
<html lang="en">
|
|
365
365
|
<head>
|
|
366
366
|
<meta charset="utf-8" />
|
|
@@ -393,9 +393,9 @@ ${S.target.failureSummary}`:"");r.push(` <testcase ${v}>`),r.push(` <fai
|
|
|
393
393
|
</head>
|
|
394
394
|
<body>
|
|
395
395
|
<h1>${f(h)} for ${f(o)}</h1>
|
|
396
|
-
<p class="meta">${t==="letter"?"Ran":"Audit run"} ${f(i.toLocaleString())} · ${r.length} issue${r.length===1?"":"s"} grouped into ${
|
|
396
|
+
<p class="meta">${t==="letter"?"Ran":"Audit run"} ${f(i.toLocaleString())} · ${r.length} issue${r.length===1?"":"s"} grouped into ${u.size} ${u.size===1?"category":"categories"}</p>
|
|
397
397
|
|
|
398
|
-
<p>${
|
|
398
|
+
<p>${d}</p>
|
|
399
399
|
|
|
400
400
|
<div class="summary">
|
|
401
401
|
<p style="margin: 0;"><strong>${f($t[a.risk])}.</strong> ${f(Tt[a.risk])}</p>
|
|
@@ -435,7 +435,7 @@ ${t==="letter"?`
|
|
|
435
435
|
Automated audits typically catch around 30–50% of accessibility issues; some categories require manual review.</p>
|
|
436
436
|
</footer>
|
|
437
437
|
</body>
|
|
438
|
-
</html>`}function
|
|
438
|
+
</html>`}function pa(e){if(!e||e.length===0)return`
|
|
439
439
|
<h2 id="manual">5. Manual assessment results</h2>
|
|
440
440
|
<p style="font-size: 10pt; color: #475569;">
|
|
441
441
|
No manual workflows were completed for this audit. Automated audits cover ~30–50% of WCAG;
|
|
@@ -445,7 +445,7 @@ ${t==="letter"?`
|
|
|
445
445
|
behaviors, error prevention, consistency).
|
|
446
446
|
When manual workflows are completed, their results are integrated into both the executive
|
|
447
447
|
summary's compliance grade and this section's per-criterion findings.
|
|
448
|
-
</p>`;const t=new Map(e.map(r=>[r.workflowId,r])),n=ye.map(r=>
|
|
448
|
+
</p>`;const t=new Map(e.map(r=>[r.workflowId,r])),n=ye.map(r=>ha(r,t.get(r.id))).filter(r=>r!==null).join("");let a=0,o=0,i=0,l=0;for(const r of ye){l+=r.steps.length;const u=t.get(r.id);if(u)for(const c of r.steps){const d=u.steps[c.id];d&&(i++,d.status==="fail"&&(c.severity==="required"?a++:o++))}}return`
|
|
449
449
|
<h2 id="manual">5. Manual assessment results</h2>
|
|
450
450
|
<p style="font-size: 10pt; color: #475569; margin-bottom: 8pt;">
|
|
451
451
|
Manual workflows performed by the auditor. ${i} of ${l} steps answered
|
|
@@ -453,11 +453,11 @@ ${t==="letter"?`
|
|
|
453
453
|
${a>0?`<strong style="color: #b91c1c;">${a} required step${a===1?"":"s"} failed</strong> — each is a confirmed WCAG breach contributing to the compliance grade above.`:"No required steps failed."}
|
|
454
454
|
${o>0?` ${o} advisory step${o===1?"":"s"} failed (best-practice findings).`:""}
|
|
455
455
|
</p>
|
|
456
|
-
${n}`}function
|
|
456
|
+
${n}`}function ha(e,t){if(!t)return null;const n=[];for(const u of e.steps){const c=t.steps[u.id];c&&n.push({step:u,status:c.status,notes:c.notes})}if(n.length===0)return null;const a=n.filter(u=>u.status==="pass").length,o=n.filter(u=>u.status==="fail").length,i=n.filter(u=>u.status==="skip").length,r=n.filter(u=>u.status==="fail").map(u=>`
|
|
457
457
|
<tr>
|
|
458
|
-
<td><span class="impact impact-${
|
|
459
|
-
<td>${
|
|
460
|
-
<td><strong>${f(
|
|
458
|
+
<td><span class="impact impact-${u.step.severity==="required"?"serious":"moderate"}">${f(u.step.severity)}</span></td>
|
|
459
|
+
<td>${u.step.wcag?`<code>${f(u.step.wcag)}</code>`:"—"}</td>
|
|
460
|
+
<td><strong>${f(u.step.question)}</strong>${u.notes?`<br/><span style="font-size: 9pt; color: #475569;">Auditor notes: ${f(u.notes)}</span>`:""}</td>
|
|
461
461
|
</tr>
|
|
462
462
|
`).join("");return`
|
|
463
463
|
<h3>${f(e.name)}</h3>
|
|
@@ -473,7 +473,7 @@ ${o>0?`
|
|
|
473
473
|
<thead><tr><th style="width: 80pt;">Severity</th><th style="width: 70pt;">WCAG</th><th>Failed check + auditor notes</th></tr></thead>
|
|
474
474
|
<tbody>${r}</tbody>
|
|
475
475
|
</table>
|
|
476
|
-
`:""}`}function qt(e,t,n="defense",a,o,i){var M,g,R;const l=
|
|
476
|
+
`:""}`}function qt(e,t,n="defense",a,o,i){var M,g,R;const l=ra[n],r=((M=e[0])==null?void 0:M.componentId)??"unknown",u=X(e),c=e.reduce((b,C)=>b+C.durationMs,0),d=e.flatMap(b=>b.violations),h=a&&a.length>0?{runs:a,workflows:ye}:void 0,s=te(d,h),x=new Date,S=new Map;for(const b of d){const C=ce(b.ruleId,b.target.selector),m=`${b.currentState.pseudoState} · ${b.currentState.theme} · ${b.currentState.direction}`,O=S.get(C);if(O){O._states.includes(m)||O._states.push(m);continue}S.set(C,{...b,_states:[m]})}const E=Array.from(S.values()).sort((b,C)=>{const m={critical:0,serious:1,moderate:2,minor:3};return(m[b.impact]??99)-(m[C.impact]??99)}),v=na(e),I=new Map;for(const b of v)for(const C of b.violations){const m=ce(C.ruleId,C.target.selector);I.has(m)||I.set(m,C._idx)}const w=E.map((b,C)=>`
|
|
477
477
|
<tr>
|
|
478
478
|
<td>${C+1}</td>
|
|
479
479
|
<td><span class="impact impact-${f(b.impact)}">${f(b.impact)}</span></td>
|
|
@@ -482,7 +482,7 @@ ${o>0?`
|
|
|
482
482
|
<td>${f(b.description)}</td>
|
|
483
483
|
<td><code class="sel">${f(b.target.selector)}</code></td>
|
|
484
484
|
<td>${f(b._states.join(", "))}</td>
|
|
485
|
-
</tr>`).join(""),T=new Set(
|
|
485
|
+
</tr>`).join(""),T=new Set(d.map(b=>b.ruleId)),k=zt.map(b=>{const{conformance:C,hits:m}=Ht(b,T);return`
|
|
486
486
|
<tr class="${C==="Supports"?"supports":C==="Partially Supports"?"partial":"na"}">
|
|
487
487
|
<td><code>${f(b.ref)}</code></td>
|
|
488
488
|
<td>${f(b.title)} <span class="lvl">(${b.level})</span></td>
|
|
@@ -545,7 +545,7 @@ ${o>0?`
|
|
|
545
545
|
<h1>${f(l.title)}</h1>
|
|
546
546
|
<p class="doc-meta">
|
|
547
547
|
<strong>Subject:</strong> <code>${f(r)}</code> ·
|
|
548
|
-
<strong>URL:</strong> ${f(
|
|
548
|
+
<strong>URL:</strong> ${f(u)}<br/>
|
|
549
549
|
<strong>Audit date:</strong> ${f(x.toLocaleString())} ·
|
|
550
550
|
<strong>Audit engine:</strong> axe-core ${f(((g=e[0])==null?void 0:g.axeVersion)??"n/a")} ·
|
|
551
551
|
<strong>Tool:</strong> ${f(H)} v${f(ee())}
|
|
@@ -567,10 +567,10 @@ ${o>0?`
|
|
|
567
567
|
|
|
568
568
|
<h2 id="exec">1. Executive summary</h2>
|
|
569
569
|
<div class="exec">
|
|
570
|
-
<div class="exec-grade" style="background: ${
|
|
570
|
+
<div class="exec-grade" style="background: ${ia[s.letter]};">${s.letter}</div>
|
|
571
571
|
<div class="exec-body">
|
|
572
|
-
<span class="risk-tier" style="background: ${
|
|
573
|
-
${f(
|
|
572
|
+
<span class="risk-tier" style="background: ${aa[s.risk]}; color: ${oa[s.risk]};">
|
|
573
|
+
${f(sa[s.risk])}
|
|
574
574
|
</span>
|
|
575
575
|
<p style="margin: 0 0 6pt;"><strong>${s.totals.unique} unique violations detected</strong> across ${e.length} state combinations
|
|
576
576
|
(${s.totals.critical} critical · ${s.totals.serious} serious · ${s.totals.moderate} moderate · ${s.totals.minor} minor).</p>
|
|
@@ -637,7 +637,7 @@ ${v.length===0?'<p style="font-size: 10pt; color: #64748b;">No per-state screens
|
|
|
637
637
|
</div>
|
|
638
638
|
</div>`}).join("")}
|
|
639
639
|
|
|
640
|
-
${
|
|
640
|
+
${pa(a)}
|
|
641
641
|
|
|
642
642
|
<h2 id="conformance">6. WCAG 2.1 conformance assessment</h2>
|
|
643
643
|
<p style="font-size: 10pt;">Per-criterion conformance based on automated detection. Conformance values follow the VPAT 2.5-INT vocabulary.${t&&t.baselineSnapshotMeta?" Comparison against an accepted baseline is available; see Section 6.":""}</p>
|
|
@@ -712,4 +712,4 @@ ${p}
|
|
|
712
712
|
</footer>
|
|
713
713
|
|
|
714
714
|
</body>
|
|
715
|
-
</html>`}async function ma(){const t=(await chrome.storage.local.get("aiConfig")).aiConfig;return{...vn,...t??{}}}async function mt(e,t,n){const a=await ma();if(!a.enabled||!a.apiKey||e.length===0)return null;const o=e.flatMap(d=>d.violations),i=te(o),l=i.categories.filter(d=>d.status==="fail").sort((d,c)=>c.violationCount-d.violationCount).slice(0,3).map(d=>d.label),r=e[0];return jt({framing:t,riskTier:i.risk,grade:i.letter,totals:{critical:i.totals.critical,serious:i.totals.serious,moderate:i.totals.moderate,minor:i.totals.minor,unique:i.totals.unique},riskDrivers:l,pageUrl:(r==null?void 0:r.pageUrl)??(r==null?void 0:r.scope)??"",auditDate:(r==null?void 0:r.startedAt)??new Date().toISOString(),priorAuditCount:n},a)}function ya(){return P("EXPORT_REQUEST",async e=>{if(e.format==="sarif")return{type:"EXPORT_RESPONSE",format:"sarif",content:JSON.stringify(Xs(e.results,e.delta),null,2)};if(e.format==="junit")return{type:"EXPORT_RESPONSE",format:"junit",content:Qs(e.results,e.delta)};if(e.format==="html-print")return{type:"EXPORT_RESPONSE",format:"html-print",content:Zs(e.results,e.delta)};if(e.format==="vpat")return{type:"EXPORT_RESPONSE",format:"vpat",content:ea(e.results,e.delta)};if(e.format==="ai-prompt")return{type:"EXPORT_RESPONSE",format:"ai-prompt",content:na(e.results,e.delta,e.manualRuns,e.siteCrawlReport,e.dismissedKeys)};if(e.format==="defense-bundle"){const t=await at(),n=await mt(e.results,"defense",t.length);return{type:"EXPORT_RESPONSE",format:"defense-bundle",content:qt(e.results,e.delta,"defense",e.manualRuns,t,n)}}if(e.format==="evidence-bundle"){const t=await at(),n=await mt(e.results,"evidence",t.length);return{type:"EXPORT_RESPONSE",format:"evidence-bundle",content:da(e.results,e.delta,e.manualRuns,t,n)}}return e.format==="developer-letter"?{type:"EXPORT_RESPONSE",format:"developer-letter",content:ua(e.results)}:e.format==="owner-report"?{type:"EXPORT_RESPONSE",format:"owner-report",content:pa(e.results)}:e.format==="ai-prompt-owner"?{type:"EXPORT_RESPONSE",format:"ai-prompt-owner",content:ha(e.results)}:{type:"EXPORT_RESPONSE",format:"json",content:JSON.stringify(Js(e.results,e.delta),null,2)}})}const ba=F("support-messenger"),wa="https://api.wcagcheckr.com/v1/products/wcagcheckr/support",Ue="support:rate-window",va=5,Aa=10*60*1e3,xa=2,Sa="wcagcheckr",yt="storybook:lastDetected";async function ka(e){if(!e)return;const t=await chrome.runtime.getPlatformInfo(),a=(await chrome.storage.local.get(yt))[yt];return{extensionVersion:chrome.runtime.getManifest().version,platform:t,storybookDetected:a!=null&&a.detected?a.version??"detected":"none",licenseTier:await Ct(),logTail:sn(an())}}async function Ea(e=Date.now()){const n=((await chrome.storage.local.get(Ue))[Ue]??[]).filter(a=>e-a<Aa);return n.length>=va?!1:(n.push(e),await chrome.storage.local.set({[Ue]:n}),!0)}async function Kt(e,t=0){try{const n=await fetch(wa,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!n.ok)throw new Error(`http ${n.status}`);const a=await n.json();if(!a.success)throw new Error(a.error??"server returned success=false");return{ticketRef:a.ticketRef??""}}catch(n){if(t<xa){const a=(t+1)*1e3;return await new Promise(o=>setTimeout(o,a)),Kt(e,t+1)}throw n}}function $a(){return P("SUPPORT_MESSAGE_REQUEST",async e=>{if(!await Ea())return{type:"SUPPORT_MESSAGE_RESPONSE",success:!1,error:me("NETWORK","Rate limit reached. Try again in 10 minutes.",!0)};try{const t=await ka(e.includeContext);return{type:"SUPPORT_MESSAGE_RESPONSE",success:!0,ticketRef:(await Kt({productSlug:Sa,subject:e.subject,body:e.body,replyEmail:e.replyEmail,context:t,extensionVersion:chrome.runtime.getManifest().version,timestamp:new Date().toISOString()})).ticketRef}}catch(t){ba.error("send failed",t);const n=t instanceof Error?t.message:String(t);return{type:"SUPPORT_MESSAGE_RESPONSE",success:!1,error:me("NETWORK",n,!0)}}})}const Yt=F("ai-color-suggester");async function Ta(e,t,n=[]){if(!t.enabled||e.length===0)return{results:[],totalCostUsd:0,capExceeded:!1};const a=B(t);if(!a.ok)return{results:[],totalCostUsd:0,capExceeded:!1};const o=a.client,i=q(t.costCapUsd),l=[];for(const r of e){if(!i.canCharge())break;try{const d=await o.suggestColorFix({foreground:r.foreground,background:r.background,fontSize:r.fontSize,fontWeight:r.fontWeight,targetLevel:r.targetLevel,paletteHints:n});i.recordCharge(d.costUsd),d.verdict==="suggested"&&d.candidates.length>0&&l.push({matchKey:r.matchKey,suggestions:d.candidates,reasoning:d.reasoning,costUsd:d.costUsd})}catch(d){Yt.warn(`color-fix suggestion failed for ${r.matchKey}`,d)}}return{results:l,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded}}async function Ca(){const e=await chrome.storage.local.get("aiConfig");return Ke(e.aiConfig)}function Ia(){return P("AI_COLOR_SUGGEST_REQUEST",async e=>{const t=await Ca();if(!t.enabled)return{type:"AI_COLOR_SUGGEST_RESPONSE",results:[],totalCostUsd:0,capExceeded:!1,unavailableReason:"AI augmentation is disabled in Settings → AI augmentation."};if(!t.apiKey)return{type:"AI_COLOR_SUGGEST_RESPONSE",results:[],totalCostUsd:0,capExceeded:!1,unavailableReason:"No AI API key configured. Add one in Settings → AI augmentation."};try{const{results:n,totalCostUsd:a,capExceeded:o}=await Ta(e.inputs,t,e.paletteHints);return{type:"AI_COLOR_SUGGEST_RESPONSE",results:n,totalCostUsd:a,capExceeded:o}}catch(n){return Yt.warn("color-suggest handler failed",n),{type:"AI_COLOR_SUGGEST_RESPONSE",results:[],totalCostUsd:0,capExceeded:!1,unavailableReason:n instanceof Error?n.message:String(n)}}})}const Ae={A:0,B:1,C:2,D:3,F:4},bt={low:0,moderate:1,high:2,critical:3};function Ra(e,t){return Ae[e]>Ae[t]?e:t}function Oa(e,t){return bt[e]>bt[t]?e:t}function _a(e){const t=[];for(const n of e)for(const a of n.results)for(const o of a.violations)t.push({url:n.url,v:o});return t}function Ma(e,t,n,a,o){const i=t.filter(w=>!w.error).length,l=t.filter(w=>!!w.error).length,r=t.map(w=>{if(w.error)return{url:w.url,grade:"F",risk:"critical",uniqueViolations:0,totals:{critical:0,serious:0,moderate:0,minor:0},durationMs:w.durationMs,error:w.error};const T=w.results.flatMap(M=>M.violations),k=te(T);return{url:w.url,grade:k.letter,risk:k.risk,uniqueViolations:k.totals.unique,totals:{critical:k.totals.critical,serious:k.totals.serious,moderate:k.totals.moderate,minor:k.totals.minor},durationMs:w.durationMs}});r.sort((w,T)=>{const k=Ae[T.grade]-Ae[w.grade];return k!==0?k:T.uniqueViolations-w.uniqueViolations});let d="A",c="low";for(const w of r)d=Ra(d,w.grade),c=Oa(c,w.risk);const u=_a(t),h=w=>`${w.ruleId}::${w.target.selector}`,s=new Map;for(const{v:w}of u){const T=h(w);s.has(T)||s.set(T,w)}const x=s.size,S={critical:0,serious:0,moderate:0,minor:0};for(const w of s.values())S[w.impact]++;const E=new Map;for(const{url:w,v:T}of u){let k=E.get(T.ruleId);k||(k={ruleId:T.ruleId,description:T.description,impact:T.impact,urls:new Set,totalOccurrences:0},E.set(T.ruleId,k)),k.urls.add(w),k.totalOccurrences++}const v=Array.from(E.values()).map(w=>({ruleId:w.ruleId,description:w.description,impact:w.impact,urlsAffected:w.urls.size,totalOccurrences:w.totalOccurrences,sampleUrls:Array.from(w.urls).slice(0,5)})).sort((w,T)=>T.urlsAffected!==w.urlsAffected?T.urlsAffected-w.urlsAffected:T.totalOccurrences-w.totalOccurrences),I={A:0,B:0,C:0,D:0,F:0};for(const w of r)I[w.grade]++;return{startedAt:n,finishedAt:a,startUrl:e,pagesAudited:i,pagesFailed:l,totalDurationMs:o,siteGrade:d,siteRisk:c,totalUniqueViolations:x,totals:S,topViolations:v,pages:r,pagesByGrade:I}}function Ua(e){const t=new Map(e.breakpointPresets.map(a=>[a.id,a])),n=[];for(const a of e.breakpoints){const o=t.get(a);if(o)for(const i of e.directions)for(const l of e.themes){const r=e.ariaVariations.length===0?[null]:e.ariaVariations;for(const d of r)for(const c of e.pseudoStates)n.push({pseudoState:c,ariaVariation:d,theme:l,direction:i,breakpoint:o})}}return n}const Da=F("ai-alt-text");async function Na(e,t){if(!t.enabled||!t.enabledChecks.altText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const n=B(t);if(!n.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${n.reason}`]};const a=n.client,o=q(t.costCapUsd),i=[],l=[],r=Math.max(1,t.maxCandidatesPerCheck??10);for(const d of e.slice(0,r)){if(!o.canCharge())break;try{const c=await a.judgeAltText({imageUrl:d.imageUrl,alt:d.alt,surroundingContext:d.surroundingContext});o.recordCharge(c.costUsd),(c.verdict==="fail"||c.verdict==="uncertain"&&c.confidence<.6)&&i.push(await Pa(d,c))}catch(c){const u=c instanceof Error?c.message:String(c);l.push(`${d.selector}: ${u}`),Da.warn("alt-text judgment failed",c)}}return{findings:i,totalCostUsd:o.state.spentUsd,capExceeded:o.state.exceeded,errors:l}}async function Pa(e,t){const n=t.verdict==="uncertain"?"ai-alt-uncertain":"ai-alt-misleading",a={selector:e.selector,outerHTML:e.outerHTML.slice(0,500),failureSummary:`AI assessment: ${t.reasoning}`,tagName:"IMG",role:null,accessibleName:e.alt,textSnippet:null,attrId:null,attrTestid:null},o=await Be({ruleId:n,componentId:e.componentId,currentState:e.currentState,target:a});return{ruleId:n,wcagCriterion:"wcag111",wcagLevel:"A",impact:t.verdict==="uncertain"?"minor":"serious",description:t.verdict==="uncertain"?"AI couldn't verify whether this alt text is appropriate — flag for human review.":"AI judged the alt text to be misleading, generic, or otherwise inappropriate.",helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/non-text-content.html",target:a,componentId:e.componentId,currentState:e.currentState,axeVersion:e.axeVersion,detectedAt:new Date().toISOString(),matchKey:o,ai:{reasoning:t.reasoning,confidence:t.confidence,model:t.model,costUsd:t.costUsd}}}const ie=F("ai-text");async function La(e,t,n){if(!n.enabled||!n.enabledChecks.headings)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=B(n);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,i=q(n.costCapUsd),l=[],r=[],d=Math.max(1,n.maxCandidatesPerCheck??10);for(const c of e.slice(0,d)){if(!i.canCharge())break;try{const u=await o.judgeHeading({headingText:c.text,sectionContent:c.sectionContent});i.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.6&&l.push(await re("ai-heading-not-descriptive","wcag246","AA","moderate","AI judged this heading does not describe its section content.",c.selector,c.outerHTML,c.text,u,t))}catch(u){const h=u instanceof Error?u.message:String(u);r.push(`${c.selector}: ${h}`),ie.warn("heading judgment failed",u)}}return{findings:l,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:r}}async function Fa(e,t,n){if(!n.enabled||!n.enabledChecks.sensory)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=B(n);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,i=q(n.costCapUsd),l=[],r=[],d=Math.max(1,n.maxCandidatesPerCheck??10);for(const c of e.slice(0,d)){if(!i.canCharge())break;try{const u=await o.judgeSensoryLanguage({instructionText:c.text});i.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.6&&l.push(await re("ai-sensory-instruction","wcag133","A","serious","AI flagged this instruction as relying on sensory characteristics (shape, color, location) without a programmatic identifier.",c.selector,c.outerHTML,c.text.slice(0,100),u,t))}catch(u){const h=u instanceof Error?u.message:String(u);r.push(`${c.selector}: ${h}`),ie.warn("sensory judgment failed",u)}}return{findings:l,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:r}}async function Ga(e,t,n){if(!n.enabled||!n.enabledChecks.aria)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=B(n);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,i=q(n.costCapUsd),l=[],r=[],d=Math.max(1,n.maxCandidatesPerCheck??10);for(const c of e.slice(0,d)){if(!i.canCharge())break;try{const u=await o.judgeAriaSemantics({elementHtml:c.outerHTML,computedRole:c.role,surroundingHtml:c.surroundingHtml});i.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.65&&l.push(await re("ai-aria-misuse","wcag412","A","serious",`AI judged role="${c.role}" is being used inappropriately on this element.`,c.selector,c.outerHTML,c.role,u,t))}catch(u){const h=u instanceof Error?u.message:String(u);r.push(`${c.selector}: ${h}`),ie.warn("aria judgment failed",u)}}return{findings:l,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:r}}async function Wa(e,t,n){if(!n.enabled||!n.enabledChecks.genericLinkText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=B(n);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,i=q(n.costCapUsd),l=[],r=[],d=Math.max(1,n.maxCandidatesPerCheck??10);for(const c of e.slice(0,d)){if(!i.canCharge())break;try{const u=await o.judgeGenericLinkText({linkText:c.linkText,surroundingText:c.surroundingText});i.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.6&&l.push(await re("ai-generic-link-text","wcag244","A","serious","AI judged this link's purpose is not clear from its text or surrounding context.",c.selector,c.outerHTML,c.linkText,u,t))}catch(u){const h=u instanceof Error?u.message:String(u);r.push(`${c.selector}: ${h}`),ie.warn("generic-link judgment failed",u)}}return{findings:l,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:r}}async function ja(e,t,n){if(!n.enabled||!n.enabledChecks.wallOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=B(n);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,i=q(n.costCapUsd),l=[],r=[],d=Math.max(1,n.maxCandidatesPerCheck??10);for(const c of e.slice(0,d)){if(!i.canCharge())break;try{const u=await o.judgeWallOfText({blockText:c.blockText,structuralChildrenCount:c.structuralChildrenCount});i.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.6&&l.push(await re("ai-wall-of-text","wcag315","AAA","moderate","AI flagged this block as a wall of text with insufficient structural breaks (subheadings, lists, paragraphs).",c.selector,c.outerHTML,c.blockText.slice(0,80),u,t))}catch(u){const h=u instanceof Error?u.message:String(u);r.push(`${c.selector}: ${h}`),ie.warn("wall-of-text judgment failed",u)}}return{findings:l,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:r}}async function Va(e,t,n){if(!n.enabled||!n.enabledChecks.languageMismatch)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=B(n);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,i=q(n.costCapUsd),l=[],r=[],d=Math.max(1,n.maxCandidatesPerCheck??10);for(const c of e.slice(0,d)){if(!i.canCharge())break;try{const u=await o.judgeLanguageMismatch({declaredLang:c.declaredLang,bodyTextSample:c.bodyTextSample});i.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.7&&l.push(await re("ai-language-mismatch","wcag311","A","serious",`AI judged the page's content does not match the declared lang="${c.declaredLang||"(none)"}".`,c.selector,c.outerHTML,c.bodyTextSample.slice(0,80),u,t))}catch(u){const h=u instanceof Error?u.message:String(u);r.push(`${c.selector}: ${h}`),ie.warn("language-mismatch judgment failed",u)}}return{findings:l,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:r}}async function re(e,t,n,a,o,i,l,r,d,c){const u={selector:i,outerHTML:l.slice(0,500),failureSummary:`AI assessment: ${d.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:r.slice(0,100)||null,attrId:null,attrTestid:null},h=await Be({ruleId:e,componentId:c.componentId,currentState:c.currentState,target:u});return{ruleId:e,wcagCriterion:t,wcagLevel:n,impact:a,description:o,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:u,componentId:c.componentId,currentState:c.currentState,axeVersion:c.axeVersion,detectedAt:new Date().toISOString(),matchKey:h,ai:{reasoning:d.reasoning,confidence:d.confidence,model:d.model,costUsd:d.costUsd}}}const Jt=F("ai-vision");async function za(e,t,n){if(!n.enabled||!n.enabledChecks.imageOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=B(n);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,i=q(n.costCapUsd),l=Math.max(1,n.imageOfTextMaxImages??5),r=[...e].sort((u,h)=>h.pixelArea-u.pixelArea).slice(0,l),d=[],c=[];for(const u of r){if(!i.canCharge())break;try{const h=await o.judgeImageOfText({imageUrl:u.imageUrl,accessibleName:u.accessibleName});i.recordCharge(h.costUsd),h.verdict==="fail"&&h.confidence>=.65&&d.push(await Xt("ai-image-of-text","wcag145","AA","moderate","AI judged this image bakes substantial text into the raster — should be presented as actual HTML text instead.",u.selector,u.outerHTML,u.accessibleName??"",h,t))}catch(h){const s=h instanceof Error?h.message:String(h);c.push(`${u.selector}: ${s}`),Jt.warn("image-of-text judgment failed",h)}}return{findings:d,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:c}}async function Ha(e,t,n){if(!n.enabled||!n.enabledChecks.colorOnlyMeaning)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=B(n);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[a.reason]};const o=a.client,i=q(n.costCapUsd),l=[],r=[],d=Math.max(1,n.maxCandidatesPerCheck??10);for(const c of e.slice(0,d)){if(!i.canCharge())break;try{const u=await o.judgeColorOnlyMeaning({elementHtml:c.elementHtml,contextDescription:c.contextDescription});i.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.65&&l.push(await Xt("ai-color-only-meaning","wcag141","A","serious","AI judged this region conveys information only through color (no text label, icon glyph, or shape difference).",c.selector,c.outerHTML,c.contextDescription,u,t))}catch(u){const h=u instanceof Error?u.message:String(u);r.push(h),Jt.warn("color-only judgment failed",u)}}return{findings:l,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:r}}async function Xt(e,t,n,a,o,i,l,r,d,c){const u={selector:i,outerHTML:l.slice(0,500),failureSummary:`AI assessment: ${d.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:r.slice(0,100)||null,attrId:null,attrTestid:null},h=await Be({ruleId:e,componentId:c.componentId,currentState:c.currentState,target:u});return{ruleId:e,wcagCriterion:t,wcagLevel:n,impact:a,description:o,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:u,componentId:c.componentId,currentState:c.currentState,axeVersion:c.axeVersion,detectedAt:new Date().toISOString(),matchKey:h,ai:{reasoning:d.reasoning,confidence:d.confidence,model:d.model,costUsd:d.costUsd}}}const He=F("ensure-content-script"),Ba=1500,qa=300;async function Qt(e,t){if(await wt(e,t))return{ok:!0};const n=chrome.runtime.getManifest(),a=[];for(const o of n.content_scripts??[])Array.isArray(o.js)&&a.push(...o.js);if(a.length===0)return He.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:a})}catch(o){return Ka(o)}return await Ya(qa),await wt(e,t)?(He.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 wt(e,t){try{const n=t!==void 0?{frameId:t}:void 0,a=await Promise.race([chrome.tabs.sendMessage(e,{type:"PING_REQUEST"},n),new Promise((o,i)=>setTimeout(()=>i(new Error("ping timeout")),Ba))]);return(a==null?void 0:a.type)==="PING_RESPONSE"}catch{return!1}}function Ka(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}:(He.warn("content-script injection failed",e),{ok:!1,reason:"inject-failed",detail:t})}function Ya(e){return new Promise(t=>setTimeout(t,e))}function Zt(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 De=F("forensic-anchor-client"),Ja="https://api.wcagcheckr.com",Xa="wcagcheckr",Qa=15e3;function Za(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 eo(e,t){const n={auditHash:e.hash,pageUrl:e.pageUrl,capturedAt:e.capturedAt,...t?{licenseId:t}:{}},a=`${Ja}/v1/products/${Xa}/forensic/anchor`;try{const o=await fetch(a,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n),signal:AbortSignal.timeout(Qa)});if(!o.ok)return De.warn(`anchor HTTP ${o.status}; entry stays local-only`),null;const i=await o.json();return Za(i)?i:(De.warn("anchor returned malformed receipt; entry stays local-only",i),null)}catch(o){return De.warn("anchor request failed; entry stays local-only",o),null}}const vt="4.11.4";async function to(e){try{return(await chrome.tabs.get(e)).url??""}catch{return""}}const $=F("flows");async function Ce(){const e=await qe();if(!e)throw new Error("no audit target tab found");return e}async function no(){var n,a,o,i,l;const e=await ve("stateMatrix",Pe);return((n=e.pseudoStates)==null?void 0:n.length)>0&&((a=e.themes)==null?void 0:a.length)>0&&((o=e.directions)==null?void 0:o.length)>0&&((i=e.breakpoints)==null?void 0:i.length)>0&&((l=e.breakpointPresets)==null?void 0:l.length)>0?e:($.warn("stored stateMatrix is invalid; using defaults"),Pe)}async function so(){const e=await ve("runsPerState",1);return typeof e!="number"||e<1||e>5?1:Math.floor(e)}async function ao(e,t,n){const a=t.themes.includes("light"),o=t.themes.includes("dark");if(!a||!o)return t;let i=null;try{i=await W(e,{type:"THEME_AWARENESS_REQUEST"},n)}catch{return t}return!i||i.hasUnreadableSheets?t:i.hasDarkModeCss&&!i.hasLightModeCss?($.info("theme-axis prune: site is dark-only, skipping light pass"),{...t,themes:t.themes.filter(l=>l!=="light")}):i.hasLightModeCss&&!i.hasDarkModeCss?($.info("theme-axis prune: site is light-only, skipping dark pass"),{...t,themes:t.themes.filter(l=>l!=="dark")}):t}function oo(e){if(e.length<=1)return e[0]??[];const t=new Map;return e.forEach((n,a)=>{for(const o of n){const i=`${o.ruleId}::${o.target.selector}`,l=t.get(i);l?l.runIndices.add(a):t.set(i,{v:o,runIndices:new Set([a])})}}),Array.from(t.values()).map(({v:n,runIndices:a})=>({...n,flakyAcrossRuns:a.size<e.length}))}async function de(e,t,n,a){var M;const o=await Ce(),i=await Qt(o,n);if(!i.ok){D({type:"AUDIT_FAILED_EVENT",error:{code:i.reason==="restricted-url"?"RESTRICTED_URL":"CONTENT_SCRIPT_UNREACHABLE",message:Zt(i),recoverable:i.reason!=="restricted-url"}});return}const l=a??await no(),r=await ao(o,l,n),d=Ua(r),c=await so(),u=await An(`${await to(o)}|${e}|${n??0}`),h=await xn(r),s=await ve("axeDisabledRules",[]),x=await Sn(s);let S=null;try{S=(await W(o,{type:"SCOPE_FINGERPRINT_REQUEST",selector:e},n)).fingerprint}catch(g){$.debug("scope fingerprint failed; skipping cache fast-path",g)}if(S){const g=await kn(u);if(g&&g.fingerprint===S&&g.axeVersion===vt&&g.matrixConfigHash===h&&g.disabledRulesHash===x){$.info(`audit-cache HIT for ${g.componentId} — skipping matrix iteration`),D({type:"AUDIT_COMPLETE_EVENT",componentId:g.componentId,results:g.results,delta:g.delta}),D({type:"SCORECARD_UPDATED_EVENT"});return}}await Se({sessionId:ke(),mode:"single-element",scope:e,startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"}),await Z(o,{type:"LIVE_REGIONS_ARM_REQUEST"},n).catch(()=>{}),await Z(o,{type:"FOCUS_TRACE_ARM_REQUEST"},n).catch(()=>{}),await W(o,{type:"WARMUP_REQUEST"},n).catch(()=>{});const E=[],v=[];let I="",w,T=!1,k=null;try{for(let g=0;g<d.length;g++){if(t.isCanceled()){$.info("single audit canceled by user");break}const R=d[g];try{const b=await Fe(o,R,e,n);if(!b.success){w=(M=b.error)==null?void 0:M.message,$.warn(`state ${g+1}/${d.length} drive failed`,w);continue}R.breakpoint.id!==k&&(await W(o,{type:"WARMUP_REQUEST"},n).catch(()=>{}),k=R.breakpoint.id);const C=[];let m=null;for(let O=0;O<c;O++){const p=await W(o,{type:"AUDIT_REQUEST",selector:e,currentState:R},n);p.success&&p.result?(C.push(p.result.violations),m=p.result):p.error&&(w=p.error.message,$.warn(`state ${g+1}/${d.length} run ${O+1} failed`,w))}if(m&&C.length>0){T=!0;const O=oo(C),p=await Vn(o,{quality:75})??void 0;let y,A;try{y=(await W(o,{type:"LIVE_REGIONS_DRAIN_REQUEST"},n)).announcements}catch{}try{A=(await W(o,{type:"FOCUS_TRACE_DRAIN_REQUEST"},n)).focusEvents}catch{}v.push({...m,violations:O,frameId:n,announcements:y,focusEvents:A,screenshotDataUrl:p}),E.push(...O),I=m.componentId}}catch(b){w=b instanceof Error?b.message:String(b),$.warn(`state ${g+1}/${d.length} threw`,b)}D({type:"AUDIT_PROGRESS_EVENT",current:g+1,total:d.length,currentState:R,componentId:I||void 0}),await Ee({})}if(T&&I){const g={pseudoState:"default",ariaVariation:null,theme:"light",direction:"ltr",breakpoint:{id:"desktop",label:"Desktop",width:1280,height:800,deviceScaleFactor:1,mobile:!1}};try{await Fe(o,g,e,n),await new Promise(m=>setTimeout(m,200))}catch(m){$.debug("reference-state drive failed; analyzers will use last matrix state",m)}try{const m=await W(o,{type:"READING_ORDER_REQUEST",selector:e},n);m.issues.length>0&&v[0]&&(v[0].readingOrderIssues=m.issues,v[0].positionAnalysisCapturedAt=g)}catch(m){$.debug("reading-order analysis skipped",m)}try{const m=await W(o,{type:"TAB_ORDER_ANALYZE_REQUEST"},n);m.issues.length>0&&v[0]&&(v[0].tabOrderIssues=m.issues,v[0].positionAnalysisCapturedAt=g)}catch(m){$.debug("tab-order analysis skipped",m)}try{const m=await W(o,{type:"TYPOGRAPHY_ANALYZE_REQUEST",selector:e},n);m.issues.length>0&&v[0]&&(v[0].typographyIssues=m.issues)}catch(m){$.debug("typography analysis skipped",m)}try{const m=await W(o,{type:"CUSTOM_PROPERTY_REQUEST"},n);m.undefined.length>0&&v[0]&&(v[0].undefinedCustomProperties=m.undefined)}catch(m){$.debug("custom-property analysis skipped",m)}try{const m=await ve("aiConfig",{}),O=Ke(m);O.enabled&&O.apiKey&&await ko(o,e,n,O,v,I,t)}catch(m){$.warn("ai augmentation skipped",m)}const R=v.flatMap(m=>m.announcements??[]),b=v.flatMap(m=>m.focusEvents??[]),C=await Ft(I,E,{announcements:R,focusEvents:b},l);try{const m=te(E),O=v.reduce((y,A)=>y+A.durationMs,0),p=await fn(v,m,O);if(p){const y=await Ct();if(wn(y,"forensicAnchoring"))try{const A=await gn(),_=await eo(p,A??void 0);_&&await mn(p.componentId,p.capturedAt,_)}catch(A){$.warn("forensic anchoring failed (entry stays local-only)",A)}}}catch(m){$.warn("forensic log recording failed",m)}if(S)try{await En({lookupKey:u,fingerprint:S,axeVersion:vt,matrixConfigHash:h,disabledRulesHash:x,results:v,delta:C,componentId:I,cachedAt:Date.now()})}catch(m){$.warn("audit-cache write failed",m)}D({type:"AUDIT_COMPLETE_EVENT",componentId:I,results:v,delta:C})}else D({type:"AUDIT_FAILED_EVENT",error:{code:"AXE_FAILED",message:w?`All audits failed. Last error: ${w}`:"No state produced results. Selector may not exist in the audit target frame.",recoverable:!0}});await le()}catch(g){$.error("single audit failed",g),D({type:"AUDIT_FAILED_EVENT",componentId:I||void 0,error:dn(g)?g.payload:{code:"UNKNOWN",message:String(g),recoverable:!1}})}finally{await Z(o,{type:"LIVE_REGIONS_DISARM_REQUEST"},n).catch(()=>{}),await Z(o,{type:"FOCUS_TRACE_DISARM_REQUEST"},n).catch(()=>{}),await Dt(o,e,n)}}async function io(e){var t;try{const a=(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{var i;const o=window;return{detected:typeof o.__STORYBOOK_PREVIEW__=="object",version:(i=o.__STORYBOOK_PREVIEW__)==null?void 0:i.version}}})).find(o=>{var i;return(i=o.result)==null?void 0:i.detected});if(a)return{detected:!0,version:(t=a.result)==null?void 0:t.version,frameId:a.frameId}}catch(n){$.warn("storybook detection scripting failed",n)}return{detected:!1}}async function ro(e){const t=await Ce(),n=await mo(t);if(await chrome.storage.local.set({"storybook:lastDetected":{detected:n.kind==="storybook"&&n.detected,version:n.version,detectedAt:new Date().toISOString()},"playground:lastDetected":{kind:n.kind,detected:n.detected,version:n.version,detectedAt:new Date().toISOString()}}),!n.detected){D({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(n.kind==="bit"){D({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 a=n.frameId??0,o=n.kind==="storybook"?await lo(t,a):await ho(t,a);if(!o.length){$.warn(`${n.kind} detected but no stories returned`),D({type:"AUDIT_FAILED_EVENT",error:{code:"STORYBOOK_NOT_DETECTED",message:`${n.kind==="ladle"?"Ladle":"Storybook"} detected but no stories were returned. Try reloading the page.`,recoverable:!0}});return}await Se({sessionId:ke(),mode:"storybook-all",totalStories:o.length,completedStories:[],startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});for(const i of o){if(e.isCanceled()){$.info(`${n.kind} iteration canceled by user`);break}try{if(n.kind==="storybook"){await uo(t,a,i.id);const r=await Z(t,{type:"STORYBOOK_GET_STORY_TARGET_REQUEST",tabId:t},a);await de(r.selector,e,a)}else await fo(t,a,i.id),await de("#ladle-root",e,a)}catch(r){$.warn(`story ${i.id} failed; continuing with next`,r)}const l=await It();if((l==null?void 0:l.state)==="interrupted"){$.info("storybook iteration interrupted");break}await Ee({completedStories:[...(l==null?void 0:l.completedStories)??[],i.id]})}D({type:"SCORECARD_UPDATED_EVENT"}),await le()}async function co(e){const t=await Ce();let n=[];try{n=(await chrome.scripting.executeScript({target:{tabId:t,allFrames:!0},func:()=>({url:window.location.href})})).map(o=>{var i;return{frameId:o.frameId,url:((i=o.result)==null?void 0:i.url)??""}}).filter(o=>/^https?:/i.test(o.url))}catch(a){$.warn("multi-frame discovery failed",a)}if(n.length===0){D({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 Se({sessionId:ke(),mode:"all-frames",totalFrames:n.length,completedFrames:[],startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});for(const a of n){if(e.isCanceled()){$.info("all-frames iteration canceled");break}try{await de("html",e,a.frameId)}catch(i){$.warn(`frame ${a.frameId} (${a.url.slice(0,80)}) audit failed; continuing`,i)}const o=await It();if((o==null?void 0:o.state)==="interrupted"){$.info("all-frames iteration interrupted");break}await Ee({completedFrames:[...(o==null?void 0:o.completedFrames)??[],a.frameId]})}D({type:"SCORECARD_UPDATED_EVENT"}),await le()}async function lo(e,t){var n;try{return((n=(await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",func:async()=>{var d,c;const o=window.__STORYBOOK_PREVIEW__;if(!o)return[];const i=o.storyStoreValue??o.storyStore;if(!i)return[];typeof i.cacheAllCSFFiles=="function"&&await i.cacheAllCSFFiles();const l=((d=i.extract)==null?void 0:d.call(i))??((c=i.cachedCSFFiles)==null?void 0:c.call(i));if(!l)return[];const r=[];if(l instanceof Map)for(const[u,h]of l){const s=h;r.push({id:String(u),name:s.name??String(u),kind:s.title??s.kind??""})}else if(typeof l=="object"&&l!==null)for(const[u,h]of Object.entries(l)){const s=h;r.push({id:u,name:s.name??u,kind:s.title??s.kind??""})}return r}}))[0])==null?void 0:n.result)??[]}catch(a){return $.warn("listStoriesViaMainWorld failed",a),[]}}async function uo(e,t,n){try{await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",args:[n,3e3],func:async(a,o)=>{const i=new URL(window.location.href);i.searchParams.set("id",a),i.searchParams.set("viewMode","story"),window.location.href!==i.href&&(window.history.pushState({},"",i.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(l=>{const r=setTimeout(()=>l(),o),d=()=>{clearTimeout(r),l()};window.addEventListener("storyrendered",d,{once:!0})})}})}catch(a){$.warn(`navigateToStoryViaMainWorld(${n}) failed`,a)}}async function po(e){var t;try{const a=(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{const o=document.getElementById("ladle-root")!==null,i=document.title==="Ladle",l=!!document.querySelector('link[href*="ladle.css"]');return{detected:o&&(i||l),version:void 0}}})).find(o=>{var i;return(i=o.result)==null?void 0:i.detected});if(a)return{detected:!0,version:(t=a.result)==null?void 0:t.version,frameId:a.frameId}}catch(n){$.warn("ladle detection scripting failed",n)}return{detected:!1}}async function ho(e,t){var n;try{return((n=(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 l=(await o.json()).stories??{};return Object.entries(l).map(([r,d])=>({id:r,name:d.name??r,kind:(d.levels??[]).join(" / ")}))}catch{return[]}}}))[0])==null?void 0:n.result)??[]}catch(a){return $.warn("listLadleStoriesViaMainWorld failed",a),[]}}async function fo(e,t,n){try{await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",args:[n,3e3],func:async(a,o)=>{const i=new URL(window.location.href);i.searchParams.set("story",a),window.location.href!==i.href&&(window.history.pushState({},"",i.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(l=>setTimeout(l,Math.min(o,500)))}})}catch(a){$.warn(`navigateToLadleStoryViaMainWorld(${n}) failed`,a)}}async function go(e){try{return(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{const n=window.location.href;return/bit\.dev|bit-dev|bitsrc|\/~aspect|\/composition/i.test(n)}})).some(n=>n.result===!0)}catch{return!1}}async function mo(e){const t=await io(e);if(t.detected)return{kind:"storybook",...t};const n=await po(e);return n.detected?{kind:"ladle",...n}:await go(e)?{kind:"bit",detected:!0}:{kind:null,detected:!1}}const yo=200,bo=6e4;function At(e,t){try{return new URL(e).origin===new URL(t).origin}catch{return!1}}function wo(e,t){try{const n=new URL(e,t);return n.hash="",n.toString()}catch{return null}}async function vo(e,t,n){await chrome.tabs.update(e,{url:t}),await new Promise((a,o)=>{const i=setTimeout(()=>{chrome.tabs.onUpdated.removeListener(l),o(new Error(`navigation timeout (${n}ms): ${t}`))},n);function l(r,d){r===e&&d.status==="complete"&&(clearTimeout(i),chrome.tabs.onUpdated.removeListener(l),a())}chrome.tabs.onUpdated.addListener(l)}),await new Promise(a=>setTimeout(a,400))}async function Ao(e){var t;try{return((t=(await chrome.scripting.executeScript({target:{tabId:e},func:()=>Array.from(document.querySelectorAll("a[href]")).map(a=>a.getAttribute("href")).filter(a=>typeof a=="string"&&!a.startsWith("javascript:"))}))[0])==null?void 0:t.result)??[]}catch(n){return $.debug("link discovery failed",n),[]}}async function xo(e,t,n){var s;const a=await Ce(),o=Math.min(t.maxPages??25,yo),i=t.includeRegex?xt(t.includeRegex):null,l=t.excludeRegex?xt(t.excludeRegex):null,r=new Date().toISOString(),d=Date.now(),c=[e],u=new Set,h=[];await Se({sessionId:ke(),mode:"storybook-all",scope:e,startedAt:r,lastProgressAt:new Date().toISOString(),state:"running"});try{for(;c.length>0&&u.size<o;){if(n.isCanceled()){$.info("site crawl canceled by user");break}const v=c.shift();if(!v||u.has(v)||!At(v,e)||i&&!i.test(v)||l&&l.test(v))continue;u.add(v);const I=u.size,w=Date.now();D({type:"SITE_CRAWL_PROGRESS_EVENT",current:I,total:Math.min(o,u.size+c.length),url:v,status:"auditing"});try{await vo(a,v,bo);const T=await Qt(a,0);if(!T.ok)throw new Error(Zt(T));await W(a,{type:"WARMUP_REQUEST"},0).catch(()=>{});const k=await W(a,{type:"AUDIT_REQUEST",selector:"html",currentState:{pseudoState:"default",ariaVariation:null,theme:"light",direction:"ltr",breakpoint:{id:"desktop",label:"Desktop",width:1280,height:800,deviceScaleFactor:1,mobile:!1}}},0),M=Date.now()-w;if(k.success&&k.result)h.push({url:v,results:[k.result],delta:null,componentId:k.result.componentId,durationMs:M}),D({type:"SITE_CRAWL_PROGRESS_EVENT",current:I,total:Math.min(o,u.size+c.length),url:v,status:"completed",violations:k.result.violations.length});else{const R=((s=k.error)==null?void 0:s.message)??"audit returned no result";h.push({url:v,results:[],delta:null,componentId:v,durationMs:M,error:R}),D({type:"SITE_CRAWL_PROGRESS_EVENT",current:I,total:Math.min(o,u.size+c.length),url:v,status:"failed",error:R})}const g=await Ao(a);for(const R of g){const b=wo(R,v);b&&At(b,e)&&!u.has(b)&&!c.includes(b)&&c.push(b)}}catch(T){const k=Date.now()-w,M=T instanceof Error?T.message:String(T);h.push({url:v,results:[],delta:null,componentId:v,durationMs:k,error:M}),D({type:"SITE_CRAWL_PROGRESS_EVENT",current:I,total:Math.min(o,u.size+c.length),url:v,status:"failed",error:M})}await Ee({})}const x=new Date().toISOString(),S=Date.now()-d,E=Ma(e,h,r,x,S);D({type:"SITE_CRAWL_COMPLETE_EVENT",report:E}),await le()}catch(x){$.error("site crawl failed",x),D({type:"SITE_CRAWL_FAILED_EVENT",error:{code:"UNKNOWN",message:String(x),recoverable:!1}}),await le()}}function xt(e){try{return new RegExp(e)}catch{return null}}const Ne=3*6e4,St=6e4,So={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…"};async function ko(e,t,n,a,o,i,l){let r;try{r=await W(e,{type:"AI_CANDIDATES_REQUEST",selector:t},n)}catch(p){$.debug("ai candidates fetch failed",p);return}const d=o[0];if(!d)return;const c={componentId:i,currentState:d.state,axeVersion:d.axeVersion},u={at:new Date().toISOString(),componentId:i,pageUrl:d.pageUrl??t,enabledChecks:a.enabledChecks,candidateCounts:{images:r.images.length,headings:r.headings.length,instructions:r.instructions.length,ariaElements:r.ariaElements.length,links:r.links.length,longTextBlocks:r.longTextBlocks.length,colorOnlyRegions:r.colorOnlyRegions.length,languageInfo:r.languageInfo?"present":"null"}};$.info("ai augmentation gate snapshot",JSON.stringify(u));try{await chrome.storage.local.set({aiDiagnosticLatest:u})}catch(p){$.warn("failed to persist ai diagnostic to storage.local",p)}try{const p=JSON.stringify(u,null,2),y=`data:application/json;charset=utf-8,${encodeURIComponent(p)}`,A=u.at.replace(/[:.]/g,"-"),_=await chrome.downloads.download({url:y,filename:`wcagcheckr-ai-diag-${A}.json`,saveAs:!1,conflictAction:"uniquify"});$.info(`ai diagnostic download started (id=${_})`)}catch(p){$.warn("failed to write ai diagnostic to Downloads (storage.local copy is still available under aiDiagnosticLatest)",p)}const h=[];if(a.enabledChecks.altText&&h.push({key:"altText",run:()=>{const p=r.images.map(y=>({imageUrl:y.imageUrl,alt:y.alt,selector:y.selector,outerHTML:y.outerHTML,surroundingContext:y.surroundingContext,...c}));return Na(p,a)}}),a.enabledChecks.headings&&h.push({key:"headings",run:()=>La(r.headings,c,a)}),a.enabledChecks.sensory&&h.push({key:"sensory",run:()=>Fa(r.instructions,c,a)}),a.enabledChecks.aria&&h.push({key:"aria",run:()=>Ga(r.ariaElements,c,a)}),a.enabledChecks.imageOfText&&h.push({key:"imageOfText",run:()=>{const p=r.images.map(y=>({imageUrl:y.imageUrl,accessibleName:y.alt||void 0,selector:y.selector,outerHTML:y.outerHTML,pixelArea:y.pixelArea}));return za(p,c,a)}}),a.enabledChecks.colorOnlyMeaning&&h.push({key:"colorOnlyMeaning",run:()=>Ha(r.colorOnlyRegions,c,a)}),a.enabledChecks.genericLinkText&&h.push({key:"genericLinkText",run:()=>Wa(r.links,c,a)}),a.enabledChecks.wallOfText&&h.push({key:"wallOfText",run:()=>ja(r.longTextBlocks,c,a)}),a.enabledChecks.languageMismatch){const p=r.languageInfo;h.push({key:"languageMismatch",run:()=>Va(p?[p]:[],c,a)})}if(h.length===0)return;let s=0,x=0,S=!1;const E={};let v=0,I=0,w=0;const T=[];let k=!1,M=!1;function g(p,y){if(E[p]=(E[p]??0)+y.totalCostUsd,s+=y.totalCostUsd,x+=y.findings.length,S=S||y.capExceeded,v++,y.errors.length>0){w++;for(const A of y.errors)T.includes(A)||T.push(A)}else I++;$.info(`ai ${p}: ${y.findings.length} findings, $${y.totalCostUsd.toFixed(4)}, errs=${y.errors.length}`),y.capExceeded&&$.warn(`ai cost cap exceeded after $${y.totalCostUsd.toFixed(4)} during ${p}`)}const R=(async()=>{for(let p=0;p<h.length;p++){if(l!=null&&l.isCanceled()){M=!0;break}const y=h[p];D({type:"AI_AUGMENTATION_PROGRESS_EVENT",componentId:i,currentCheck:y.key,currentCheckLabel:So[y.key],current:p+1,total:h.length});const A=await Promise.race([y.run(),new Promise(_=>setTimeout(()=>_({findings:[],totalCostUsd:0,capExceeded:!1,errors:[`${y.key} timed out after ${St/1e3}s — single check hung; moving on`]}),St))]);A.findings.length>0&&(d.violations=[...d.violations,...A.findings]),g(y.key,A)}})(),b=new Promise(p=>{setTimeout(()=>p("timeout"),Ne)});await Promise.race([R.then(()=>"done"),b])==="timeout"&&(k=!0,$.warn(`ai augmentation hit global ${Ne/1e3}s timeout after ${v}/${h.length} checks`));let m;if(w>0||k||M){k?m=`AI augmentation timed out after ${Ne/1e3}s`:M?m="AI augmentation canceled":m=T[0]??"AI analyzer error";const p=I===0?"total":"partial";D({type:"AI_AUGMENTATION_FAILED_EVENT",componentId:i,severity:p,reason:m,checksAttempted:v,checksSucceeded:I,checksErrored:w,errorDetails:k||M?[m,...T.slice(0,4)]:T.slice(0,5)}),$.warn(`ai augmentation ${p} failure: ${w}/${v} checks errored — ${m}`)}(s>0||x>0||m)&&await $n({at:new Date().toISOString(),pageUrl:d.pageUrl??t,componentId:i,totalCostUsd:s,byCheck:E,findingsCount:x,capExceeded:S,failureReason:m})}const j=F("service-worker");on("service-worker");As();var fe,kt;(kt=(fe=chrome.sidePanel)==null?void 0:fe.setPanelBehavior)==null||kt.call(fe,{openPanelOnActionClick:!0}).catch(e=>j.warn("setPanelBehavior failed",e));zn();as();yn();ns().catch(e=>j.warn("initial cloud pull failed",e));(async()=>(await bs(),Wt()))();ws();ks();ya();$a();Ia();Ys();self.addEventListener("unhandledrejection",e=>{const t=e.reason;rn(t instanceof Error?t:new Error(String(t)))});let xe=!1;chrome.runtime.onConnect.addListener(e=>{if(e.name==="audit-keepalive"){j.debug("keepalive connected"),e.onDisconnect.addListener(async()=>{j.debug("keepalive disconnected"),await bn()});return}if(e.name==="sidepanel-tracker"){xe=!0,j.debug("chrome.sidePanel opened"),e.onDisconnect.addListener(async()=>{xe=!1,j.debug("chrome.sidePanel closed — restoring in-page overlay"),await et()});return}});let Ze=!1;const he={isCanceled:()=>Ze};async function en(){const e=await qe();if(e)try{await Z(e,{type:"SIDEBAR_HIDE_REQUEST"})}catch(t){j.warn("sidebar hide failed",t)}}async function et(){const e=await qe();if(e)try{await Z(e,{type:"SIDEBAR_SHOW_REQUEST"})}catch(t){j.warn("sidebar show failed",t)}}P("START_AUDIT",async e=>{Ze=!1,await en();let t;if(e.mode==="single-element"){if(!e.scope)throw new Error("START_AUDIT single-element requires scope");t=de(e.scope,he,e.frameId,e.matrixOverride)}else e.mode==="full-page"?t=de("html",he,0,e.matrixOverride):e.mode==="all-frames"?t=co(he):t=ro(he);t.catch(n=>{j.error("audit failed",n);const a=n instanceof Error?n.message:String(n),o=/no audit target tab/i.test(a);D({type:"AUDIT_FAILED_EVENT",error:{code:o?"RESTRICTED_URL":"UNKNOWN",message:o?"No audit target tab found. Open a regular http(s) page (not chrome:// or about:blank) and try again.":`Audit failed: ${a}`,recoverable:!0}})}).finally(()=>{xe||et()})});P("CANCEL_AUDIT",async()=>{Ze=!0,j.info("cancel requested")});let tt=!1;const Eo={isCanceled:()=>tt};P("START_SITE_CRAWL",async e=>{tt=!1,await en(),xo(e.startUrl,{maxPages:e.maxPages,includeRegex:e.includeRegex,excludeRegex:e.excludeRegex},Eo).catch(t=>j.error("site crawl failed",t)).finally(()=>{xe||et()})});P("CANCEL_SITE_CRAWL",async()=>{tt=!0,j.info("site crawl cancel requested")});P("OPEN_SETTINGS",()=>{chrome.runtime.openOptionsPage()});j.info("service worker ready");
|
|
715
|
+
</html>`}async function fa(){const t=(await chrome.storage.local.get("aiConfig")).aiConfig;return{...vn,...t??{}}}async function mt(e,t,n){const a=await fa();if(!a.enabled||!a.apiKey||e.length===0)return null;const o=e.flatMap(u=>u.violations),i=te(o),l=i.categories.filter(u=>u.status==="fail").sort((u,c)=>c.violationCount-u.violationCount).slice(0,3).map(u=>u.label),r=e[0];return jt({framing:t,riskTier:i.risk,grade:i.letter,totals:{critical:i.totals.critical,serious:i.totals.serious,moderate:i.totals.moderate,minor:i.totals.minor,unique:i.totals.unique},riskDrivers:l,pageUrl:(r==null?void 0:r.pageUrl)??(r==null?void 0:r.scope)??"",auditDate:(r==null?void 0:r.startedAt)??new Date().toISOString(),priorAuditCount:n},a)}function ga(){return P("EXPORT_REQUEST",async e=>{if(e.format==="sarif")return{type:"EXPORT_RESPONSE",format:"sarif",content:JSON.stringify(Ys(e.results,e.delta),null,2)};if(e.format==="junit")return{type:"EXPORT_RESPONSE",format:"junit",content:Js(e.results,e.delta)};if(e.format==="html-print")return{type:"EXPORT_RESPONSE",format:"html-print",content:Xs(e.results,e.delta)};if(e.format==="vpat")return{type:"EXPORT_RESPONSE",format:"vpat",content:Qs(e.results,e.delta)};if(e.format==="ai-prompt")return{type:"EXPORT_RESPONSE",format:"ai-prompt",content:ea(e.results,e.delta,e.manualRuns,e.siteCrawlReport,e.dismissedKeys)};if(e.format==="defense-bundle"){const t=await st(),n=await mt(e.results,"defense",t.length);return{type:"EXPORT_RESPONSE",format:"defense-bundle",content:qt(e.results,e.delta,"defense",e.manualRuns,t,n)}}if(e.format==="evidence-bundle"){const t=await st(),n=await mt(e.results,"evidence",t.length);return{type:"EXPORT_RESPONSE",format:"evidence-bundle",content:ca(e.results,e.delta,e.manualRuns,t,n)}}return e.format==="developer-letter"?{type:"EXPORT_RESPONSE",format:"developer-letter",content:la(e.results)}:e.format==="owner-report"?{type:"EXPORT_RESPONSE",format:"owner-report",content:da(e.results)}:e.format==="ai-prompt-owner"?{type:"EXPORT_RESPONSE",format:"ai-prompt-owner",content:ua(e.results)}:{type:"EXPORT_RESPONSE",format:"json",content:JSON.stringify(Ks(e.results,e.delta),null,2)}})}const ma=F("support-messenger"),ya="https://api.wcagcheckr.com/v1/products/wcagcheckr/support",Ue="support:rate-window",ba=5,wa=10*60*1e3,va=2,Aa="wcagcheckr",yt="storybook:lastDetected";async function xa(e){if(!e)return;const t=await chrome.runtime.getPlatformInfo(),a=(await chrome.storage.local.get(yt))[yt];return{extensionVersion:chrome.runtime.getManifest().version,platform:t,storybookDetected:a!=null&&a.detected?a.version??"detected":"none",licenseTier:await Ct(),logTail:sn(an())}}async function Sa(e=Date.now()){const n=((await chrome.storage.local.get(Ue))[Ue]??[]).filter(a=>e-a<wa);return n.length>=ba?!1:(n.push(e),await chrome.storage.local.set({[Ue]:n}),!0)}async function Kt(e,t=0){try{const n=await fetch(ya,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!n.ok)throw new Error(`http ${n.status}`);const a=await n.json();if(!a.success)throw new Error(a.error??"server returned success=false");return{ticketRef:a.ticketRef??""}}catch(n){if(t<va){const a=(t+1)*1e3;return await new Promise(o=>setTimeout(o,a)),Kt(e,t+1)}throw n}}function ka(){return P("SUPPORT_MESSAGE_REQUEST",async e=>{if(!await Sa())return{type:"SUPPORT_MESSAGE_RESPONSE",success:!1,error:me("NETWORK","Rate limit reached. Try again in 10 minutes.",!0)};try{const t=await xa(e.includeContext);return{type:"SUPPORT_MESSAGE_RESPONSE",success:!0,ticketRef:(await Kt({productSlug:Aa,subject:e.subject,body:e.body,replyEmail:e.replyEmail,context:t,extensionVersion:chrome.runtime.getManifest().version,timestamp:new Date().toISOString()})).ticketRef}}catch(t){ma.error("send failed",t);const n=t instanceof Error?t.message:String(t);return{type:"SUPPORT_MESSAGE_RESPONSE",success:!1,error:me("NETWORK",n,!0)}}})}const Yt=F("ai-color-suggester");async function Ea(e,t,n=[]){if(!t.enabled||e.length===0)return{results:[],totalCostUsd:0,capExceeded:!1};const a=B(t);if(!a.ok)return{results:[],totalCostUsd:0,capExceeded:!1};const o=a.client,i=q(t.costCapUsd),l=[];for(const r of e){if(!i.canCharge())break;try{const u=await o.suggestColorFix({foreground:r.foreground,background:r.background,fontSize:r.fontSize,fontWeight:r.fontWeight,targetLevel:r.targetLevel,paletteHints:n});i.recordCharge(u.costUsd),u.verdict==="suggested"&&u.candidates.length>0&&l.push({matchKey:r.matchKey,suggestions:u.candidates,reasoning:u.reasoning,costUsd:u.costUsd})}catch(u){Yt.warn(`color-fix suggestion failed for ${r.matchKey}`,u)}}return{results:l,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded}}async function $a(){const e=await chrome.storage.local.get("aiConfig");return qe(e.aiConfig)}function Ta(){return P("AI_COLOR_SUGGEST_REQUEST",async e=>{const t=await $a();if(!t.enabled)return{type:"AI_COLOR_SUGGEST_RESPONSE",results:[],totalCostUsd:0,capExceeded:!1,unavailableReason:"AI augmentation is disabled in Settings → AI augmentation."};if(!t.apiKey)return{type:"AI_COLOR_SUGGEST_RESPONSE",results:[],totalCostUsd:0,capExceeded:!1,unavailableReason:"No AI API key configured. Add one in Settings → AI augmentation."};try{const{results:n,totalCostUsd:a,capExceeded:o}=await Ea(e.inputs,t,e.paletteHints);return{type:"AI_COLOR_SUGGEST_RESPONSE",results:n,totalCostUsd:a,capExceeded:o}}catch(n){return Yt.warn("color-suggest handler failed",n),{type:"AI_COLOR_SUGGEST_RESPONSE",results:[],totalCostUsd:0,capExceeded:!1,unavailableReason:n instanceof Error?n.message:String(n)}}})}const Ae={A:0,B:1,C:2,D:3,F:4},bt={low:0,moderate:1,high:2,critical:3};function Ca(e,t){return Ae[e]>Ae[t]?e:t}function Ia(e,t){return bt[e]>bt[t]?e:t}function Ra(e){const t=[];for(const n of e)for(const a of n.results)for(const o of a.violations)t.push({url:n.url,v:o});return t}function Oa(e,t,n,a,o){const i=t.filter(w=>!w.error).length,l=t.filter(w=>!!w.error).length,r=t.map(w=>{if(w.error)return{url:w.url,grade:"F",risk:"critical",uniqueViolations:0,totals:{critical:0,serious:0,moderate:0,minor:0},durationMs:w.durationMs,error:w.error};const T=w.results.flatMap(M=>M.violations),k=te(T);return{url:w.url,grade:k.letter,risk:k.risk,uniqueViolations:k.totals.unique,totals:{critical:k.totals.critical,serious:k.totals.serious,moderate:k.totals.moderate,minor:k.totals.minor},durationMs:w.durationMs}});r.sort((w,T)=>{const k=Ae[T.grade]-Ae[w.grade];return k!==0?k:T.uniqueViolations-w.uniqueViolations});let u="A",c="low";for(const w of r)u=Ca(u,w.grade),c=Ia(c,w.risk);const d=Ra(t),h=w=>`${w.ruleId}::${w.target.selector}`,s=new Map;for(const{v:w}of d){const T=h(w);s.has(T)||s.set(T,w)}const x=s.size,S={critical:0,serious:0,moderate:0,minor:0};for(const w of s.values())S[w.impact]++;const E=new Map;for(const{url:w,v:T}of d){let k=E.get(T.ruleId);k||(k={ruleId:T.ruleId,description:T.description,impact:T.impact,urls:new Set,totalOccurrences:0},E.set(T.ruleId,k)),k.urls.add(w),k.totalOccurrences++}const v=Array.from(E.values()).map(w=>({ruleId:w.ruleId,description:w.description,impact:w.impact,urlsAffected:w.urls.size,totalOccurrences:w.totalOccurrences,sampleUrls:Array.from(w.urls).slice(0,5)})).sort((w,T)=>T.urlsAffected!==w.urlsAffected?T.urlsAffected-w.urlsAffected:T.totalOccurrences-w.totalOccurrences),I={A:0,B:0,C:0,D:0,F:0};for(const w of r)I[w.grade]++;return{startedAt:n,finishedAt:a,startUrl:e,pagesAudited:i,pagesFailed:l,totalDurationMs:o,siteGrade:u,siteRisk:c,totalUniqueViolations:x,totals:S,topViolations:v,pages:r,pagesByGrade:I}}function _a(e){const t=new Map(e.breakpointPresets.map(a=>[a.id,a])),n=[];for(const a of e.breakpoints){const o=t.get(a);if(o)for(const i of e.directions)for(const l of e.themes){const r=e.ariaVariations.length===0?[null]:e.ariaVariations;for(const u of r)for(const c of e.pseudoStates)n.push({pseudoState:c,ariaVariation:u,theme:l,direction:i,breakpoint:o})}}return n}const Ma=F("ai-alt-text");async function Ua(e,t){if(!t.enabled||!t.enabledChecks.altText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const n=B(t);if(!n.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${n.reason}`]};const a=n.client,o=q(t.costCapUsd),i=[],l=[],r=Math.max(1,t.maxCandidatesPerCheck??10);for(const u of e.slice(0,r)){if(!o.canCharge())break;try{const c=await a.judgeAltText({imageUrl:u.imageUrl,alt:u.alt,surroundingContext:u.surroundingContext});o.recordCharge(c.costUsd),(c.verdict==="fail"||c.verdict==="uncertain"&&c.confidence<.6)&&i.push(await Da(u,c))}catch(c){const d=c instanceof Error?c.message:String(c);l.push(`${u.selector}: ${d}`),Ma.warn("alt-text judgment failed",c)}}return{findings:i,totalCostUsd:o.state.spentUsd,capExceeded:o.state.exceeded,errors:l}}async function Da(e,t){const n=t.verdict==="uncertain"?"ai-alt-uncertain":"ai-alt-misleading",a={selector:e.selector,outerHTML:e.outerHTML.slice(0,500),failureSummary:`AI assessment: ${t.reasoning}`,tagName:"IMG",role:null,accessibleName:e.alt,textSnippet:null,attrId:null,attrTestid:null},o=await He({ruleId:n,componentId:e.componentId,currentState:e.currentState,target:a});return{ruleId:n,wcagCriterion:"wcag111",wcagLevel:"A",impact:t.verdict==="uncertain"?"minor":"serious",description:t.verdict==="uncertain"?"AI couldn't verify whether this alt text is appropriate — flag for human review.":"AI judged the alt text to be misleading, generic, or otherwise inappropriate.",helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/non-text-content.html",target:a,componentId:e.componentId,currentState:e.currentState,axeVersion:e.axeVersion,detectedAt:new Date().toISOString(),matchKey:o,ai:{reasoning:t.reasoning,confidence:t.confidence,model:t.model,costUsd:t.costUsd}}}const ie=F("ai-text");async function Na(e,t,n){if(!n.enabled||!n.enabledChecks.headings)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=B(n);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,i=q(n.costCapUsd),l=[],r=[],u=Math.max(1,n.maxCandidatesPerCheck??10);for(const c of e.slice(0,u)){if(!i.canCharge())break;try{const d=await o.judgeHeading({headingText:c.text,sectionContent:c.sectionContent});i.recordCharge(d.costUsd),d.verdict==="fail"&&d.confidence>=.6&&l.push(await re("ai-heading-not-descriptive","wcag246","AA","moderate","AI judged this heading does not describe its section content.",c.selector,c.outerHTML,c.text,d,t))}catch(d){const h=d instanceof Error?d.message:String(d);r.push(`${c.selector}: ${h}`),ie.warn("heading judgment failed",d)}}return{findings:l,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:r}}async function Pa(e,t,n){if(!n.enabled||!n.enabledChecks.sensory)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=B(n);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,i=q(n.costCapUsd),l=[],r=[],u=Math.max(1,n.maxCandidatesPerCheck??10);for(const c of e.slice(0,u)){if(!i.canCharge())break;try{const d=await o.judgeSensoryLanguage({instructionText:c.text});i.recordCharge(d.costUsd),d.verdict==="fail"&&d.confidence>=.6&&l.push(await re("ai-sensory-instruction","wcag133","A","serious","AI flagged this instruction as relying on sensory characteristics (shape, color, location) without a programmatic identifier.",c.selector,c.outerHTML,c.text.slice(0,100),d,t))}catch(d){const h=d instanceof Error?d.message:String(d);r.push(`${c.selector}: ${h}`),ie.warn("sensory judgment failed",d)}}return{findings:l,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:r}}async function La(e,t,n){if(!n.enabled||!n.enabledChecks.aria)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=B(n);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,i=q(n.costCapUsd),l=[],r=[],u=Math.max(1,n.maxCandidatesPerCheck??10);for(const c of e.slice(0,u)){if(!i.canCharge())break;try{const d=await o.judgeAriaSemantics({elementHtml:c.outerHTML,computedRole:c.role,surroundingHtml:c.surroundingHtml});i.recordCharge(d.costUsd),d.verdict==="fail"&&d.confidence>=.65&&l.push(await re("ai-aria-misuse","wcag412","A","serious",`AI judged role="${c.role}" is being used inappropriately on this element.`,c.selector,c.outerHTML,c.role,d,t))}catch(d){const h=d instanceof Error?d.message:String(d);r.push(`${c.selector}: ${h}`),ie.warn("aria judgment failed",d)}}return{findings:l,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:r}}async function Fa(e,t,n){if(!n.enabled||!n.enabledChecks.genericLinkText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=B(n);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,i=q(n.costCapUsd),l=[],r=[],u=Math.max(1,n.maxCandidatesPerCheck??10);for(const c of e.slice(0,u)){if(!i.canCharge())break;try{const d=await o.judgeGenericLinkText({linkText:c.linkText,surroundingText:c.surroundingText});i.recordCharge(d.costUsd),d.verdict==="fail"&&d.confidence>=.6&&l.push(await re("ai-generic-link-text","wcag244","A","serious","AI judged this link's purpose is not clear from its text or surrounding context.",c.selector,c.outerHTML,c.linkText,d,t))}catch(d){const h=d instanceof Error?d.message:String(d);r.push(`${c.selector}: ${h}`),ie.warn("generic-link judgment failed",d)}}return{findings:l,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:r}}async function Ga(e,t,n){if(!n.enabled||!n.enabledChecks.wallOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=B(n);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,i=q(n.costCapUsd),l=[],r=[],u=Math.max(1,n.maxCandidatesPerCheck??10);for(const c of e.slice(0,u)){if(!i.canCharge())break;try{const d=await o.judgeWallOfText({blockText:c.blockText,structuralChildrenCount:c.structuralChildrenCount});i.recordCharge(d.costUsd),d.verdict==="fail"&&d.confidence>=.6&&l.push(await re("ai-wall-of-text","wcag315","AAA","moderate","AI flagged this block as a wall of text with insufficient structural breaks (subheadings, lists, paragraphs).",c.selector,c.outerHTML,c.blockText.slice(0,80),d,t))}catch(d){const h=d instanceof Error?d.message:String(d);r.push(`${c.selector}: ${h}`),ie.warn("wall-of-text judgment failed",d)}}return{findings:l,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:r}}async function Wa(e,t,n){if(!n.enabled||!n.enabledChecks.languageMismatch)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=B(n);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,i=q(n.costCapUsd),l=[],r=[],u=Math.max(1,n.maxCandidatesPerCheck??10);for(const c of e.slice(0,u)){if(!i.canCharge())break;try{const d=await o.judgeLanguageMismatch({declaredLang:c.declaredLang,bodyTextSample:c.bodyTextSample});i.recordCharge(d.costUsd),d.verdict==="fail"&&d.confidence>=.7&&l.push(await re("ai-language-mismatch","wcag311","A","serious",`AI judged the page's content does not match the declared lang="${c.declaredLang||"(none)"}".`,c.selector,c.outerHTML,c.bodyTextSample.slice(0,80),d,t))}catch(d){const h=d instanceof Error?d.message:String(d);r.push(`${c.selector}: ${h}`),ie.warn("language-mismatch judgment failed",d)}}return{findings:l,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:r}}async function re(e,t,n,a,o,i,l,r,u,c){const d={selector:i,outerHTML:l.slice(0,500),failureSummary:`AI assessment: ${u.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:r.slice(0,100)||null,attrId:null,attrTestid:null},h=await He({ruleId:e,componentId:c.componentId,currentState:c.currentState,target:d});return{ruleId:e,wcagCriterion:t,wcagLevel:n,impact:a,description:o,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:d,componentId:c.componentId,currentState:c.currentState,axeVersion:c.axeVersion,detectedAt:new Date().toISOString(),matchKey:h,ai:{reasoning:u.reasoning,confidence:u.confidence,model:u.model,costUsd:u.costUsd}}}const Jt=F("ai-vision");async function ja(e,t,n){if(!n.enabled||!n.enabledChecks.imageOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=B(n);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,i=q(n.costCapUsd),l=Math.max(1,n.imageOfTextMaxImages??5),r=[...e].sort((d,h)=>h.pixelArea-d.pixelArea).slice(0,l),u=[],c=[];for(const d of r){if(!i.canCharge())break;try{const h=await o.judgeImageOfText({imageUrl:d.imageUrl,accessibleName:d.accessibleName});i.recordCharge(h.costUsd),h.verdict==="fail"&&h.confidence>=.65&&u.push(await Xt("ai-image-of-text","wcag145","AA","moderate","AI judged this image bakes substantial text into the raster — should be presented as actual HTML text instead.",d.selector,d.outerHTML,d.accessibleName??"",h,t))}catch(h){const s=h instanceof Error?h.message:String(h);c.push(`${d.selector}: ${s}`),Jt.warn("image-of-text judgment failed",h)}}return{findings:u,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:c}}async function Va(e,t,n){if(!n.enabled||!n.enabledChecks.colorOnlyMeaning)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=B(n);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[a.reason]};const o=a.client,i=q(n.costCapUsd),l=[],r=[],u=Math.max(1,n.maxCandidatesPerCheck??10);for(const c of e.slice(0,u)){if(!i.canCharge())break;try{const d=await o.judgeColorOnlyMeaning({elementHtml:c.elementHtml,contextDescription:c.contextDescription});i.recordCharge(d.costUsd),d.verdict==="fail"&&d.confidence>=.65&&l.push(await Xt("ai-color-only-meaning","wcag141","A","serious","AI judged this region conveys information only through color (no text label, icon glyph, or shape difference).",c.selector,c.outerHTML,c.contextDescription,d,t))}catch(d){const h=d instanceof Error?d.message:String(d);r.push(h),Jt.warn("color-only judgment failed",d)}}return{findings:l,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:r}}async function Xt(e,t,n,a,o,i,l,r,u,c){const d={selector:i,outerHTML:l.slice(0,500),failureSummary:`AI assessment: ${u.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:r.slice(0,100)||null,attrId:null,attrTestid:null},h=await He({ruleId:e,componentId:c.componentId,currentState:c.currentState,target:d});return{ruleId:e,wcagCriterion:t,wcagLevel:n,impact:a,description:o,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:d,componentId:c.componentId,currentState:c.currentState,axeVersion:c.axeVersion,detectedAt:new Date().toISOString(),matchKey:h,ai:{reasoning:u.reasoning,confidence:u.confidence,model:u.model,costUsd:u.costUsd}}}const ze=F("ensure-content-script"),za=1500,Ha=300;async function Qt(e,t){if(await wt(e,t))return{ok:!0};const n=chrome.runtime.getManifest(),a=[];for(const o of n.content_scripts??[])Array.isArray(o.js)&&a.push(...o.js);if(a.length===0)return ze.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:a})}catch(o){return Ba(o)}return await qa(Ha),await wt(e,t)?(ze.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 wt(e,t){try{const n=t!==void 0?{frameId:t}:void 0,a=await Promise.race([chrome.tabs.sendMessage(e,{type:"PING_REQUEST"},n),new Promise((o,i)=>setTimeout(()=>i(new Error("ping timeout")),za))]);return(a==null?void 0:a.type)==="PING_RESPONSE"}catch{return!1}}function Ba(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}:(ze.warn("content-script injection failed",e),{ok:!1,reason:"inject-failed",detail:t})}function qa(e){return new Promise(t=>setTimeout(t,e))}function Zt(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 De=F("forensic-anchor-client"),Ka="https://api.wcagcheckr.com",Ya="wcagcheckr",Ja=15e3;function Xa(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 Qa(e,t){const n={auditHash:e.hash,pageUrl:e.pageUrl,capturedAt:e.capturedAt,...t?{licenseId:t}:{}},a=`${Ka}/v1/products/${Ya}/forensic/anchor`;try{const o=await fetch(a,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n),signal:AbortSignal.timeout(Ja)});if(!o.ok)return De.warn(`anchor HTTP ${o.status}; entry stays local-only`),null;const i=await o.json();return Xa(i)?i:(De.warn("anchor returned malformed receipt; entry stays local-only",i),null)}catch(o){return De.warn("anchor request failed; entry stays local-only",o),null}}const vt="4.11.4";async function Za(e){try{return(await chrome.tabs.get(e)).url??""}catch{return""}}const $=F("flows");async function Ce(){const e=await Be();if(!e)throw new Error("no audit target tab found");return e}async function eo(){var n,a,o,i,l;const e=await ve("stateMatrix",Pe);return((n=e.pseudoStates)==null?void 0:n.length)>0&&((a=e.themes)==null?void 0:a.length)>0&&((o=e.directions)==null?void 0:o.length)>0&&((i=e.breakpoints)==null?void 0:i.length)>0&&((l=e.breakpointPresets)==null?void 0:l.length)>0?e:($.warn("stored stateMatrix is invalid; using defaults"),Pe)}async function to(){const e=await ve("runsPerState",1);return typeof e!="number"||e<1||e>5?1:Math.floor(e)}async function no(e,t,n){const a=t.themes.includes("light"),o=t.themes.includes("dark");if(!a||!o)return t;let i=null;try{i=await W(e,{type:"THEME_AWARENESS_REQUEST"},n)}catch{return t}return!i||i.hasUnreadableSheets?t:i.hasDarkModeCss&&!i.hasLightModeCss?($.info("theme-axis prune: site is dark-only, skipping light pass"),{...t,themes:t.themes.filter(l=>l!=="light")}):i.hasLightModeCss&&!i.hasDarkModeCss?($.info("theme-axis prune: site is light-only, skipping dark pass"),{...t,themes:t.themes.filter(l=>l!=="dark")}):t}function so(e){if(e.length<=1)return e[0]??[];const t=new Map;return e.forEach((n,a)=>{for(const o of n){const i=`${o.ruleId}::${o.target.selector}`,l=t.get(i);l?l.runIndices.add(a):t.set(i,{v:o,runIndices:new Set([a])})}}),Array.from(t.values()).map(({v:n,runIndices:a})=>({...n,flakyAcrossRuns:a.size<e.length}))}async function de(e,t,n,a){var M;const o=await Ce(),i=await Qt(o,n);if(!i.ok){D({type:"AUDIT_FAILED_EVENT",error:{code:i.reason==="restricted-url"?"RESTRICTED_URL":"CONTENT_SCRIPT_UNREACHABLE",message:Zt(i),recoverable:i.reason!=="restricted-url"}});return}const l=a??await eo(),r=await no(o,l,n),u=_a(r),c=await to(),d=await An(`${await Za(o)}|${e}|${n??0}`),h=await xn(r),s=await ve("axeDisabledRules",[]),x=await Sn(s);let S=null;try{S=(await W(o,{type:"SCOPE_FINGERPRINT_REQUEST",selector:e},n)).fingerprint}catch(g){$.debug("scope fingerprint failed; skipping cache fast-path",g)}if(S){const g=await kn(d);if(g&&g.fingerprint===S&&g.axeVersion===vt&&g.matrixConfigHash===h&&g.disabledRulesHash===x){$.info(`audit-cache HIT for ${g.componentId} — skipping matrix iteration`),D({type:"AUDIT_COMPLETE_EVENT",componentId:g.componentId,results:g.results,delta:g.delta}),D({type:"SCORECARD_UPDATED_EVENT"});return}}await Se({sessionId:ke(),mode:"single-element",scope:e,startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"}),await Z(o,{type:"LIVE_REGIONS_ARM_REQUEST"},n).catch(()=>{}),await Z(o,{type:"FOCUS_TRACE_ARM_REQUEST"},n).catch(()=>{}),await W(o,{type:"WARMUP_REQUEST"},n).catch(()=>{});const E=[],v=[];let I="",w,T=!1,k=null;try{for(let g=0;g<u.length;g++){if(t.isCanceled()){$.info("single audit canceled by user");break}const R=u[g];try{const b=await Fe(o,R,e,n);if(!b.success){w=(M=b.error)==null?void 0:M.message,$.warn(`state ${g+1}/${u.length} drive failed`,w);continue}R.breakpoint.id!==k&&(await W(o,{type:"WARMUP_REQUEST"},n).catch(()=>{}),k=R.breakpoint.id);const C=[];let m=null;for(let O=0;O<c;O++){const p=await W(o,{type:"AUDIT_REQUEST",selector:e,currentState:R},n);p.success&&p.result?(C.push(p.result.violations),m=p.result):p.error&&(w=p.error.message,$.warn(`state ${g+1}/${u.length} run ${O+1} failed`,w))}if(m&&C.length>0){T=!0;const O=so(C),p=await Vn(o,{quality:75})??void 0;let y,A;try{y=(await W(o,{type:"LIVE_REGIONS_DRAIN_REQUEST"},n)).announcements}catch{}try{A=(await W(o,{type:"FOCUS_TRACE_DRAIN_REQUEST"},n)).focusEvents}catch{}v.push({...m,violations:O,frameId:n,announcements:y,focusEvents:A,screenshotDataUrl:p}),E.push(...O),I=m.componentId}}catch(b){w=b instanceof Error?b.message:String(b),$.warn(`state ${g+1}/${u.length} threw`,b)}D({type:"AUDIT_PROGRESS_EVENT",current:g+1,total:u.length,currentState:R,componentId:I||void 0}),await Ee({})}if(T&&I){const g={pseudoState:"default",ariaVariation:null,theme:"light",direction:"ltr",breakpoint:{id:"desktop",label:"Desktop",width:1280,height:800,deviceScaleFactor:1,mobile:!1}};try{await Fe(o,g,e,n),await new Promise(m=>setTimeout(m,200))}catch(m){$.debug("reference-state drive failed; analyzers will use last matrix state",m)}try{const m=await W(o,{type:"READING_ORDER_REQUEST",selector:e},n);m.issues.length>0&&v[0]&&(v[0].readingOrderIssues=m.issues,v[0].positionAnalysisCapturedAt=g)}catch(m){$.debug("reading-order analysis skipped",m)}try{const m=await W(o,{type:"TAB_ORDER_ANALYZE_REQUEST"},n);m.issues.length>0&&v[0]&&(v[0].tabOrderIssues=m.issues,v[0].positionAnalysisCapturedAt=g)}catch(m){$.debug("tab-order analysis skipped",m)}try{const m=await W(o,{type:"TYPOGRAPHY_ANALYZE_REQUEST",selector:e},n);m.issues.length>0&&v[0]&&(v[0].typographyIssues=m.issues)}catch(m){$.debug("typography analysis skipped",m)}try{const m=await W(o,{type:"CUSTOM_PROPERTY_REQUEST"},n);m.undefined.length>0&&v[0]&&(v[0].undefinedCustomProperties=m.undefined)}catch(m){$.debug("custom-property analysis skipped",m)}try{const m=await ve("aiConfig",{}),O=qe(m);O.enabled&&O.apiKey&&await xo(o,e,n,O,v,I,t)}catch(m){$.warn("ai augmentation skipped",m)}const R=v.flatMap(m=>m.announcements??[]),b=v.flatMap(m=>m.focusEvents??[]),C=await Ft(I,E,{announcements:R,focusEvents:b},l);try{const m=te(E),O=v.reduce((y,A)=>y+A.durationMs,0),p=await fn(v,m,O);if(p){const y=await Ct();if(wn(y,"forensicAnchoring"))try{const A=await gn(),_=await Qa(p,A??void 0);_&&await mn(p.componentId,p.capturedAt,_)}catch(A){$.warn("forensic anchoring failed (entry stays local-only)",A)}}}catch(m){$.warn("forensic log recording failed",m)}if(S)try{await En({lookupKey:d,fingerprint:S,axeVersion:vt,matrixConfigHash:h,disabledRulesHash:x,results:v,delta:C,componentId:I,cachedAt:Date.now()})}catch(m){$.warn("audit-cache write failed",m)}D({type:"AUDIT_COMPLETE_EVENT",componentId:I,results:v,delta:C})}else D({type:"AUDIT_FAILED_EVENT",error:{code:"AXE_FAILED",message:w?`All audits failed. Last error: ${w}`:"No state produced results. Selector may not exist in the audit target frame.",recoverable:!0}});await le()}catch(g){$.error("single audit failed",g),D({type:"AUDIT_FAILED_EVENT",componentId:I||void 0,error:dn(g)?g.payload:{code:"UNKNOWN",message:String(g),recoverable:!1}})}finally{await Z(o,{type:"LIVE_REGIONS_DISARM_REQUEST"},n).catch(()=>{}),await Z(o,{type:"FOCUS_TRACE_DISARM_REQUEST"},n).catch(()=>{}),await Dt(o,e,n)}}async function ao(e){var t;try{const a=(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{var i;const o=window;return{detected:typeof o.__STORYBOOK_PREVIEW__=="object",version:(i=o.__STORYBOOK_PREVIEW__)==null?void 0:i.version}}})).find(o=>{var i;return(i=o.result)==null?void 0:i.detected});if(a)return{detected:!0,version:(t=a.result)==null?void 0:t.version,frameId:a.frameId}}catch(n){$.warn("storybook detection scripting failed",n)}return{detected:!1}}async function oo(e){const t=await Ce(),n=await fo(t);if(await chrome.storage.local.set({"storybook:lastDetected":{detected:n.kind==="storybook"&&n.detected,version:n.version,detectedAt:new Date().toISOString()},"playground:lastDetected":{kind:n.kind,detected:n.detected,version:n.version,detectedAt:new Date().toISOString()}}),!n.detected){D({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(n.kind==="bit"){D({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 a=n.frameId??0,o=n.kind==="storybook"?await ro(t,a):await uo(t,a);if(!o.length){$.warn(`${n.kind} detected but no stories returned`),D({type:"AUDIT_FAILED_EVENT",error:{code:"STORYBOOK_NOT_DETECTED",message:`${n.kind==="ladle"?"Ladle":"Storybook"} detected but no stories were returned. Try reloading the page.`,recoverable:!0}});return}await Se({sessionId:ke(),mode:"storybook-all",totalStories:o.length,completedStories:[],startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});for(const i of o){if(e.isCanceled()){$.info(`${n.kind} iteration canceled by user`);break}try{if(n.kind==="storybook"){await co(t,a,i.id);const r=await Z(t,{type:"STORYBOOK_GET_STORY_TARGET_REQUEST",tabId:t},a);await de(r.selector,e,a)}else await po(t,a,i.id),await de("#ladle-root",e,a)}catch(r){$.warn(`story ${i.id} failed; continuing with next`,r)}const l=await It();if((l==null?void 0:l.state)==="interrupted"){$.info("storybook iteration interrupted");break}await Ee({completedStories:[...(l==null?void 0:l.completedStories)??[],i.id]})}D({type:"SCORECARD_UPDATED_EVENT"}),await le()}async function io(e){const t=await Ce();let n=[];try{n=(await chrome.scripting.executeScript({target:{tabId:t,allFrames:!0},func:()=>({url:window.location.href})})).map(o=>{var i;return{frameId:o.frameId,url:((i=o.result)==null?void 0:i.url)??""}}).filter(o=>/^https?:/i.test(o.url))}catch(a){$.warn("multi-frame discovery failed",a)}if(n.length===0){D({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 Se({sessionId:ke(),mode:"all-frames",totalFrames:n.length,completedFrames:[],startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});for(const a of n){if(e.isCanceled()){$.info("all-frames iteration canceled");break}try{await de("html",e,a.frameId)}catch(i){$.warn(`frame ${a.frameId} (${a.url.slice(0,80)}) audit failed; continuing`,i)}const o=await It();if((o==null?void 0:o.state)==="interrupted"){$.info("all-frames iteration interrupted");break}await Ee({completedFrames:[...(o==null?void 0:o.completedFrames)??[],a.frameId]})}D({type:"SCORECARD_UPDATED_EVENT"}),await le()}async function ro(e,t){var n;try{return((n=(await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",func:async()=>{var u,c;const o=window.__STORYBOOK_PREVIEW__;if(!o)return[];const i=o.storyStoreValue??o.storyStore;if(!i)return[];typeof i.cacheAllCSFFiles=="function"&&await i.cacheAllCSFFiles();const l=((u=i.extract)==null?void 0:u.call(i))??((c=i.cachedCSFFiles)==null?void 0:c.call(i));if(!l)return[];const r=[];if(l instanceof Map)for(const[d,h]of l){const s=h;r.push({id:String(d),name:s.name??String(d),kind:s.title??s.kind??""})}else if(typeof l=="object"&&l!==null)for(const[d,h]of Object.entries(l)){const s=h;r.push({id:d,name:s.name??d,kind:s.title??s.kind??""})}return r}}))[0])==null?void 0:n.result)??[]}catch(a){return $.warn("listStoriesViaMainWorld failed",a),[]}}async function co(e,t,n){try{await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",args:[n,3e3],func:async(a,o)=>{const i=new URL(window.location.href);i.searchParams.set("id",a),i.searchParams.set("viewMode","story"),window.location.href!==i.href&&(window.history.pushState({},"",i.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(l=>{const r=setTimeout(()=>l(),o),u=()=>{clearTimeout(r),l()};window.addEventListener("storyrendered",u,{once:!0})})}})}catch(a){$.warn(`navigateToStoryViaMainWorld(${n}) failed`,a)}}async function lo(e){var t;try{const a=(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{const o=document.getElementById("ladle-root")!==null,i=document.title==="Ladle",l=!!document.querySelector('link[href*="ladle.css"]');return{detected:o&&(i||l),version:void 0}}})).find(o=>{var i;return(i=o.result)==null?void 0:i.detected});if(a)return{detected:!0,version:(t=a.result)==null?void 0:t.version,frameId:a.frameId}}catch(n){$.warn("ladle detection scripting failed",n)}return{detected:!1}}async function uo(e,t){var n;try{return((n=(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 l=(await o.json()).stories??{};return Object.entries(l).map(([r,u])=>({id:r,name:u.name??r,kind:(u.levels??[]).join(" / ")}))}catch{return[]}}}))[0])==null?void 0:n.result)??[]}catch(a){return $.warn("listLadleStoriesViaMainWorld failed",a),[]}}async function po(e,t,n){try{await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",args:[n,3e3],func:async(a,o)=>{const i=new URL(window.location.href);i.searchParams.set("story",a),window.location.href!==i.href&&(window.history.pushState({},"",i.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(l=>setTimeout(l,Math.min(o,500)))}})}catch(a){$.warn(`navigateToLadleStoryViaMainWorld(${n}) failed`,a)}}async function ho(e){try{return(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{const n=window.location.href;return/bit\.dev|bit-dev|bitsrc|\/~aspect|\/composition/i.test(n)}})).some(n=>n.result===!0)}catch{return!1}}async function fo(e){const t=await ao(e);if(t.detected)return{kind:"storybook",...t};const n=await lo(e);return n.detected?{kind:"ladle",...n}:await ho(e)?{kind:"bit",detected:!0}:{kind:null,detected:!1}}const go=200,mo=6e4;function At(e,t){try{return new URL(e).origin===new URL(t).origin}catch{return!1}}function yo(e,t){try{const n=new URL(e,t);return n.hash="",n.toString()}catch{return null}}async function bo(e,t,n){await chrome.tabs.update(e,{url:t}),await new Promise((a,o)=>{const i=setTimeout(()=>{chrome.tabs.onUpdated.removeListener(l),o(new Error(`navigation timeout (${n}ms): ${t}`))},n);function l(r,u){r===e&&u.status==="complete"&&(clearTimeout(i),chrome.tabs.onUpdated.removeListener(l),a())}chrome.tabs.onUpdated.addListener(l)}),await new Promise(a=>setTimeout(a,400))}async function wo(e){var t;try{return((t=(await chrome.scripting.executeScript({target:{tabId:e},func:()=>Array.from(document.querySelectorAll("a[href]")).map(a=>a.getAttribute("href")).filter(a=>typeof a=="string"&&!a.startsWith("javascript:"))}))[0])==null?void 0:t.result)??[]}catch(n){return $.debug("link discovery failed",n),[]}}async function vo(e,t,n){var s;const a=await Ce(),o=Math.min(t.maxPages??25,go),i=t.includeRegex?xt(t.includeRegex):null,l=t.excludeRegex?xt(t.excludeRegex):null,r=new Date().toISOString(),u=Date.now(),c=[e],d=new Set,h=[];await Se({sessionId:ke(),mode:"storybook-all",scope:e,startedAt:r,lastProgressAt:new Date().toISOString(),state:"running"});try{for(;c.length>0&&d.size<o;){if(n.isCanceled()){$.info("site crawl canceled by user");break}const v=c.shift();if(!v||d.has(v)||!At(v,e)||i&&!i.test(v)||l&&l.test(v))continue;d.add(v);const I=d.size,w=Date.now();D({type:"SITE_CRAWL_PROGRESS_EVENT",current:I,total:Math.min(o,d.size+c.length),url:v,status:"auditing"});try{await bo(a,v,mo);const T=await Qt(a,0);if(!T.ok)throw new Error(Zt(T));await W(a,{type:"WARMUP_REQUEST"},0).catch(()=>{});const k=await W(a,{type:"AUDIT_REQUEST",selector:"html",currentState:{pseudoState:"default",ariaVariation:null,theme:"light",direction:"ltr",breakpoint:{id:"desktop",label:"Desktop",width:1280,height:800,deviceScaleFactor:1,mobile:!1}}},0),M=Date.now()-w;if(k.success&&k.result)h.push({url:v,results:[k.result],delta:null,componentId:k.result.componentId,durationMs:M}),D({type:"SITE_CRAWL_PROGRESS_EVENT",current:I,total:Math.min(o,d.size+c.length),url:v,status:"completed",violations:k.result.violations.length});else{const R=((s=k.error)==null?void 0:s.message)??"audit returned no result";h.push({url:v,results:[],delta:null,componentId:v,durationMs:M,error:R}),D({type:"SITE_CRAWL_PROGRESS_EVENT",current:I,total:Math.min(o,d.size+c.length),url:v,status:"failed",error:R})}const g=await wo(a);for(const R of g){const b=yo(R,v);b&&At(b,e)&&!d.has(b)&&!c.includes(b)&&c.push(b)}}catch(T){const k=Date.now()-w,M=T instanceof Error?T.message:String(T);h.push({url:v,results:[],delta:null,componentId:v,durationMs:k,error:M}),D({type:"SITE_CRAWL_PROGRESS_EVENT",current:I,total:Math.min(o,d.size+c.length),url:v,status:"failed",error:M})}await Ee({})}const x=new Date().toISOString(),S=Date.now()-u,E=Oa(e,h,r,x,S);D({type:"SITE_CRAWL_COMPLETE_EVENT",report:E}),await le()}catch(x){$.error("site crawl failed",x),D({type:"SITE_CRAWL_FAILED_EVENT",error:{code:"UNKNOWN",message:String(x),recoverable:!1}}),await le()}}function xt(e){try{return new RegExp(e)}catch{return null}}const Ne=3*6e4,St=6e4,Ao={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…"};async function xo(e,t,n,a,o,i,l){let r;try{r=await W(e,{type:"AI_CANDIDATES_REQUEST",selector:t},n)}catch(p){$.debug("ai candidates fetch failed",p);return}const u=o[0];if(!u)return;const c={componentId:i,currentState:u.state,axeVersion:u.axeVersion},d={at:new Date().toISOString(),componentId:i,pageUrl:u.pageUrl??t,enabledChecks:a.enabledChecks,candidateCounts:{images:r.images.length,headings:r.headings.length,instructions:r.instructions.length,ariaElements:r.ariaElements.length,links:r.links.length,longTextBlocks:r.longTextBlocks.length,colorOnlyRegions:r.colorOnlyRegions.length,languageInfo:r.languageInfo?"present":"null"}};$.info("ai augmentation gate snapshot",JSON.stringify(d));try{await chrome.storage.local.set({aiDiagnosticLatest:d})}catch(p){$.warn("failed to persist ai diagnostic to storage.local",p)}try{const p=JSON.stringify(d,null,2),y=`data:application/json;charset=utf-8,${encodeURIComponent(p)}`,A=d.at.replace(/[:.]/g,"-"),_=await chrome.downloads.download({url:y,filename:`wcagcheckr-ai-diag-${A}.json`,saveAs:!1,conflictAction:"uniquify"});$.info(`ai diagnostic download started (id=${_})`)}catch(p){$.warn("failed to write ai diagnostic to Downloads (storage.local copy is still available under aiDiagnosticLatest)",p)}const h=[];if(a.enabledChecks.altText&&h.push({key:"altText",run:()=>{const p=r.images.map(y=>({imageUrl:y.imageUrl,alt:y.alt,selector:y.selector,outerHTML:y.outerHTML,surroundingContext:y.surroundingContext,...c}));return Ua(p,a)}}),a.enabledChecks.headings&&h.push({key:"headings",run:()=>Na(r.headings,c,a)}),a.enabledChecks.sensory&&h.push({key:"sensory",run:()=>Pa(r.instructions,c,a)}),a.enabledChecks.aria&&h.push({key:"aria",run:()=>La(r.ariaElements,c,a)}),a.enabledChecks.imageOfText&&h.push({key:"imageOfText",run:()=>{const p=r.images.map(y=>({imageUrl:y.imageUrl,accessibleName:y.alt||void 0,selector:y.selector,outerHTML:y.outerHTML,pixelArea:y.pixelArea}));return ja(p,c,a)}}),a.enabledChecks.colorOnlyMeaning&&h.push({key:"colorOnlyMeaning",run:()=>Va(r.colorOnlyRegions,c,a)}),a.enabledChecks.genericLinkText&&h.push({key:"genericLinkText",run:()=>Fa(r.links,c,a)}),a.enabledChecks.wallOfText&&h.push({key:"wallOfText",run:()=>Ga(r.longTextBlocks,c,a)}),a.enabledChecks.languageMismatch){const p=r.languageInfo;h.push({key:"languageMismatch",run:()=>Wa(p?[p]:[],c,a)})}if(h.length===0)return;let s=0,x=0,S=!1;const E={};let v=0,I=0,w=0;const T=[];let k=!1,M=!1;function g(p,y){if(E[p]=(E[p]??0)+y.totalCostUsd,s+=y.totalCostUsd,x+=y.findings.length,S=S||y.capExceeded,v++,y.errors.length>0){w++;for(const A of y.errors)T.includes(A)||T.push(A)}else I++;$.info(`ai ${p}: ${y.findings.length} findings, $${y.totalCostUsd.toFixed(4)}, errs=${y.errors.length}`),y.capExceeded&&$.warn(`ai cost cap exceeded after $${y.totalCostUsd.toFixed(4)} during ${p}`)}const R=(async()=>{for(let p=0;p<h.length;p++){if(l!=null&&l.isCanceled()){M=!0;break}const y=h[p];D({type:"AI_AUGMENTATION_PROGRESS_EVENT",componentId:i,currentCheck:y.key,currentCheckLabel:Ao[y.key],current:p+1,total:h.length});const A=await Promise.race([y.run(),new Promise(_=>setTimeout(()=>_({findings:[],totalCostUsd:0,capExceeded:!1,errors:[`${y.key} timed out after ${St/1e3}s — single check hung; moving on`]}),St))]);A.findings.length>0&&(u.violations=[...u.violations,...A.findings]),g(y.key,A)}})(),b=new Promise(p=>{setTimeout(()=>p("timeout"),Ne)});await Promise.race([R.then(()=>"done"),b])==="timeout"&&(k=!0,$.warn(`ai augmentation hit global ${Ne/1e3}s timeout after ${v}/${h.length} checks`));let m;if(w>0||k||M){k?m=`AI augmentation timed out after ${Ne/1e3}s`:M?m="AI augmentation canceled":m=T[0]??"AI analyzer error";const p=I===0?"total":"partial";D({type:"AI_AUGMENTATION_FAILED_EVENT",componentId:i,severity:p,reason:m,checksAttempted:v,checksSucceeded:I,checksErrored:w,errorDetails:k||M?[m,...T.slice(0,4)]:T.slice(0,5)}),$.warn(`ai augmentation ${p} failure: ${w}/${v} checks errored — ${m}`)}(s>0||x>0||m)&&await $n({at:new Date().toISOString(),pageUrl:u.pageUrl??t,componentId:i,totalCostUsd:s,byCheck:E,findingsCount:x,capExceeded:S,failureReason:m})}const j=F("service-worker");on("service-worker");As();var fe,kt;(kt=(fe=chrome.sidePanel)==null?void 0:fe.setPanelBehavior)==null||kt.call(fe,{openPanelOnActionClick:!0}).catch(e=>j.warn("setPanelBehavior failed",e));zn();as();yn();ns().catch(e=>j.warn("initial cloud pull failed",e));(async()=>(await bs(),Wt()))();ws();ks();ga();ka();Ta();qs();self.addEventListener("unhandledrejection",e=>{const t=e.reason;rn(t instanceof Error?t:new Error(String(t)))});let xe=!1;chrome.runtime.onConnect.addListener(e=>{if(e.name==="audit-keepalive"){j.debug("keepalive connected"),e.onDisconnect.addListener(async()=>{j.debug("keepalive disconnected"),await bn()});return}if(e.name==="sidepanel-tracker"){xe=!0,j.debug("chrome.sidePanel opened"),e.onDisconnect.addListener(async()=>{xe=!1,j.debug("chrome.sidePanel closed — restoring in-page overlay"),await Ze()});return}});let Qe=!1;const he={isCanceled:()=>Qe};async function en(){const e=await Be();if(e)try{await Z(e,{type:"SIDEBAR_HIDE_REQUEST"})}catch(t){j.warn("sidebar hide failed",t)}}async function Ze(){const e=await Be();if(e)try{await Z(e,{type:"SIDEBAR_SHOW_REQUEST"})}catch(t){j.warn("sidebar show failed",t)}}P("START_AUDIT",async e=>{Qe=!1,await en();let t;if(e.mode==="single-element"){if(!e.scope)throw new Error("START_AUDIT single-element requires scope");t=de(e.scope,he,e.frameId,e.matrixOverride)}else e.mode==="full-page"?t=de("html",he,0,e.matrixOverride):e.mode==="all-frames"?t=io(he):t=oo(he);t.catch(n=>{j.error("audit failed",n);const a=n instanceof Error?n.message:String(n),o=/no audit target tab/i.test(a);D({type:"AUDIT_FAILED_EVENT",error:{code:o?"RESTRICTED_URL":"UNKNOWN",message:o?"No audit target tab found. Open a regular http(s) page (not chrome:// or about:blank) and try again.":`Audit failed: ${a}`,recoverable:!0}})}).finally(()=>{xe||Ze()})});P("CANCEL_AUDIT",async()=>{Qe=!0,j.info("cancel requested")});let et=!1;const So={isCanceled:()=>et};P("START_SITE_CRAWL",async e=>{et=!1,await en(),vo(e.startUrl,{maxPages:e.maxPages,includeRegex:e.includeRegex,excludeRegex:e.excludeRegex},So).catch(t=>j.error("site crawl failed",t)).finally(()=>{xe||Ze()})});P("CANCEL_SITE_CRAWL",async()=>{et=!0,j.info("site crawl cancel requested")});P("OPEN_SETTINGS",()=>{chrome.runtime.openOptionsPage()});j.info("service worker ready");
|
package/dist/manifest.json
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "wcagcheckr",
|
|
4
4
|
"description": "Audit components across hover, focus, dark mode, forced colors, RTL — every state your users actually encounter. Per-component baselines surface only NEW violations.",
|
|
5
|
-
"version": "1.0.0.
|
|
6
|
-
"version_name": "1.0.0-rc.
|
|
5
|
+
"version": "1.0.0.31",
|
|
6
|
+
"version_name": "1.0.0-rc.31",
|
|
7
7
|
"author": "Locustware",
|
|
8
8
|
"homepage_url": "https://wcagcheckr.com",
|
|
9
9
|
"icons": {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import './assets/service-worker.ts-
|
|
1
|
+
import './assets/service-worker.ts-CO86CV_p.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wcag-checkr/ci",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.31",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Headless wcagcheckr accessibility audit runner for CI/CD pipelines. Drives the wcagcheckr Chrome extension via Playwright, runs full-page audits across the state matrix (108 combinations: hover, focus, dark mode, RTL, breakpoints), outputs JSON / SARIF / JUnit, exits with severity-aware codes.",
|
|
6
6
|
"license": "UNLICENSED",
|