@wcag-checkr/ci 1.0.0-rc.49 → 1.0.0-rc.50
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.
|
@@ -0,0 +1,717 @@
|
|
|
1
|
+
import{c as W,o as F,e as D,f as fn,g as gn,d as j,b as ee,i as mn,h as yn}from"./crash-reporter-Dc5lvxvY.js";import{m as Se,d as bn,T as wn,a as ct,c as Qe,i as vn}from"./diff-DIBMr3fQ.js";import{r as An,t as xn,k as lt,l as fe,j as ne,A as ge,F as Sn,m as _t,O as Mt,v as Ut,R as kn,w as Pt,x as Ie,y as Re,z as Oe,B as En,g as $n,C as Tn,i as me,h as Dt,a as Ze,D as Cn,E as In}from"./forensic-log-BD4uVNC4.js";import{s as xe,T as dt,b as ze,i as Rn}from"./state-PELIq3oj.js";import{m as et,D as On,s as _n,h as Mn,b as Un,g as Pn,d as Dn,e as Nn}from"./ai-usage-log-Dj9Ub_DT.js";const Ln=W("chrome-api"),Fn="1.3";class Gn{constructor(t){this.target=t}async attach(){await chrome.debugger.attach(this.target,Fn)}async detach(){try{await chrome.debugger.detach(this.target)}catch(t){Ln.debug("detach soft-failure",t)}}async send(t,n){return chrome.debugger.sendCommand(this.target,t,n)}}function Wn(e){const t=e instanceof Error?e.message:String(e);return/Another debugger is already attached|Cannot attach to/i.test(t)}function jn(e){const t=e instanceof Error?e.message:String(e);return Wn(e)?Se("DEBUGGER_BUSY","DevTools is open on this tab. Close it to run the audit.",!0,{raw:t}):Se("STATE_DRIVE_FAILED",t,!1)}function Vn(e){const t=(n,s)=>e(n,s);return chrome.debugger.onDetach.addListener(t),()=>chrome.debugger.onDetach.removeListener(t)}const z=W("state-driver"),zn=60*1e3,J=new Map,ke=new Map,ie=new Map,oe=new Map,Hn={default:[],hover:["hover"],focus:["focus"],"focus-visible":["focus","focus-visible"],active:["active"],disabled:[]};async function Nt(e){let t=J.get(e);if(!t){t=new Gn({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}J.set(e,t),ie.set(e,new Map),Bn(e),z.info("attached",{tabId:e})}return Kn(e),t}function Bn(e){const t=(n,s,o)=>{var c,r;if(n.tabId!==e)return;const i=ie.get(e);if(i)if(s==="Runtime.executionContextCreated"){const d=o,l=d==null?void 0:d.context;l&&((c=l.auxData)!=null&&c.isDefault)&&((r=l.auxData)!=null&&r.frameId)&&i.set(l.auxData.frameId,l.id)}else if(s==="Runtime.executionContextDestroyed"){const d=o,l=d==null?void 0:d.executionContextId;if(l!==void 0)for(const[p,f]of i.entries())f===l&&i.delete(p)}else s==="Runtime.executionContextsCleared"&&i.clear()};chrome.debugger.onEvent.addListener(t)}async function qn(e){const t=J.get(e);if(J.delete(e),ie.delete(e),oe.delete(e),t)try{await t.detach()}catch{}}function Kn(e){const t=ke.get(e);t&&clearTimeout(t),ke.set(e,setTimeout(()=>{tt(e).catch(n=>z.warn("idle detach failed",n))},zn))}async function tt(e){const t=J.get(e);t&&(await t.detach(),J.delete(e)),ie.delete(e),oe.delete(e);const n=ke.get(e);n&&(clearTimeout(n),ke.delete(e))}function Lt(e){const t=[{id:e.frame.id,url:e.frame.url}];for(const n of e.childFrames??[])t.push(...Lt(n));return t}async function Yn(e,t,n){var i;if(!n||n===0)return;const s=ie.get(t);if(!s)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(c){z.debug("frame URL lookup failed",c)}try{const c=await e.send("Page.getFrameTree"),r=Lt(c.frameTree);if(o){const p=r.find(f=>f.url===o);if(p){const f=s.get(p.id);if(f!==void 0)return f}}const d=c.frameTree.frame.id,l=r.find(p=>p.id!==d);return l?s.get(l.id):void 0}catch(c){z.warn("Page.getFrameTree failed",c);return}}async function Jn(e,t,n){var c;const s={expression:`document.querySelector(${JSON.stringify(t)})`,objectGroup:"wcag-auditor",returnByValue:!1};n!==void 0&&(s.contextId=n);const i=(c=(await e.send("Runtime.evaluate",s)).result)==null?void 0:c.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 Ft(e){const t=oe.get(e);if(!t)return;const n=J.get(e);if(n){try{await n.send("CSS.forcePseudoState",{nodeId:t.nodeId,forcedPseudoClasses:[]})}catch(s){z.debug("forcePseudoState clear failed (node may be gone)",s)}oe.delete(e)}}async function He(e,t,n,s){await chrome.scripting.executeScript({target:nt(e,t),func:(o,i)=>{const c=document.querySelector(o);c&&(i?c.setAttribute("disabled",""):c.removeAttribute("disabled"))},args:[n,s]})}async function Gt(e,t){await chrome.scripting.executeScript({target:nt(e,t),func:()=>{const n=document.activeElement;n&&typeof n.blur=="function"&&n.blur()}})}async function Wt(e,t){await chrome.scripting.executeScript({target:{tabId:e,frameIds:[0]},func:n=>{document.documentElement.setAttribute("dir",n)},args:[t]})}async function Xn(e,t,n,s){await chrome.scripting.executeScript({target:nt(e,t),func:(o,i)=>{const c=document.querySelector(o);if(c)for(const[r,d]of Object.entries(i))c.setAttribute(r,d)},args:[n,s]})}function nt(e,t){return t!==void 0&&t!==0?{tabId:e,frameIds:[t]}:{tabId:e}}async function Qn(e,t,n,s){const o=await Nt(e);if(await Ft(e),n){t.pseudoState==="disabled"?await He(e,s,n,!0):(await He(e,s,n,!1),await Gt(e,s));const c=Hn[t.pseudoState];if(c.length>0){const r=await Yn(o,e,s),d=await Jn(o,n,r);if(d!==null)try{await o.send("CSS.forcePseudoState",{nodeId:d,forcedPseudoClasses:c}),oe.set(e,{sessionTabId:e,nodeId:d})}catch(l){z.warn(`CSS.forcePseudoState failed for :${t.pseudoState}`,l)}else z.debug(`could not resolve nodeId for ${n} (frameId=${s})`)}}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(c=>setTimeout(c,50)),await Wt(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 Xn(e,s,n,t.ariaVariation.attributes)}async function Zn(e,t,n){const s=J.get(e);if(s){if(await Ft(e),t)try{await He(e,n,t,!1),await Gt(e,n),await Wt(e,"ltr")}catch(o){z.warn("element-level reset partial failure",o)}try{await s.send("Emulation.setEmulatedMedia",{features:[]}),await s.send("Emulation.clearDeviceMetricsOverride")}catch(o){z.warn("reset partial failure",o)}await tt(e)}}async function Be(e,t,n,s){try{return await Qn(e,t,n,s),D({type:"STATE_CHANGED_EVENT",tabId:e,currentState:t}),{type:"STATE_DRIVE_RESPONSE",success:!0,appliedState:t}}catch(o){return z.error("state drive failed",o),await qn(e),{type:"STATE_DRIVE_RESPONSE",success:!1,error:jn(o)}}}async function jt(e,t,n){await Zn(e,t,n)}async function ea(e,t={}){try{const n=await Nt(e),s={format:"jpeg",quality:t.quality??75};t.fullPage&&(s.captureBeyondViewport=!0);const o=await n.send("Page.captureScreenshot",s);return o!=null&&o.data?`data:image/jpeg;base64,${o.data}`:null}catch(n){return z.debug("Page.captureScreenshot failed",n),null}}function ta(){var n;const e=[];if(e.push(F("STATE_DRIVE_REQUEST",s=>Be(s.tabId,s.state,s.scope,s.frameId))),e.push(F("STATE_RESET_REQUEST",s=>jt(s.tabId))),(n=chrome.tabs)!=null&&n.onRemoved){const s=o=>{tt(o).catch(()=>{})};chrome.tabs.onRemoved.addListener(s),e.push(()=>chrome.tabs.onRemoved.removeListener(s))}const t=Vn((s,o)=>{s.tabId!==void 0&&(J.delete(s.tabId),ie.delete(s.tabId),oe.delete(s.tabId),z.warn("unexpected detach",{tabId:s.tabId,reason:o}))});return e.push(t),z.info("handlers registered"),()=>e.forEach(s=>s())}const na="wcag-component-auditor",aa=1;let Ne=null;function _e(){return Ne||(Ne=An(na,aa,{upgrade(e,t){if(t<1){const n=e.createObjectStore("baselines",{keyPath:"componentId"});n.createIndex("byUrl","snapshotMeta.url"),n.createIndex("byDate","lastUpdated");const s=e.createObjectStore("audit-history",{keyPath:"auditId",autoIncrement:!0});s.createIndex("byComponentId","componentId"),s.createIndex("byDate","runDate")}}})),Ne}async function at(e){return(await _e()).get("baselines",e)}async function Vt(e){await(await _e()).put("baselines",e)}async function sa(e){await(await _e()).delete("baselines",e)}async function oa(){return(await _e()).getAll("baselines")}const se=W("cloud-sync"),ut="cloudSyncEnabled",Le="cloudSyncEndpoint",Fe="licenseToken",qe="cloudSyncVersionCache";async function st(){try{const e=await chrome.storage.local.get([ut,Le,Fe]),t=e[ut]===!0,n=typeof e[Le]=="string"?e[Le]:"",s=typeof e[Fe]=="string"?e[Fe]:"";return!t||!n||!s?null:{endpoint:n.replace(/\/$/,""),token:s,enabled:t}}catch{return null}}async function zt(){try{const t=(await chrome.storage.local.get(qe))[qe];return t&&typeof t=="object"?t:{}}catch{return{}}}async function Ee(e){try{await chrome.storage.local.set({[qe]:e})}catch{}}async function ra(e,t,n){const s=await st();if(!s)return!1;const o=await zt(),i=o[e];try{const c=`${s.endpoint}/v1/products/wcagcheckr/baselines/${encodeURIComponent(e)}`,r=await fetch(c,{method:"PUT",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s.token}`},body:JSON.stringify({snapshot:t,axeVersion:n,expectedVersion:i})});if(r.status===409)return se.warn(`baseline ${e} version conflict on push`,{expectedVersion:i}),delete o[e],await Ee(o),!1;if(!r.ok)return se.warn("baseline push failed",{status:r.status}),!1;const d=await r.json();return typeof d.version=="number"&&(o[e]=d.version,await Ee(o)),!0}catch(c){return se.debug("push network failure (offline?)",c),!1}}async function ia(e){const t=await st();if(!t)return!1;try{const n=`${t.endpoint}/v1/products/wcagcheckr/baselines/${encodeURIComponent(e)}`,s=await fetch(n,{method:"DELETE",headers:{Authorization:`Bearer ${t.token}`}});if(s.ok){const o=await zt();delete o[e],await Ee(o)}return s.ok}catch(n){return se.debug("delete network failure",n),!1}}async function ca(){const e=await st();if(!e)return[];try{const t=await fetch(`${e.endpoint}/v1/products/wcagcheckr/baselines`,{headers:{Authorization:`Bearer ${e.token}`}});if(!t.ok)return se.warn("pull failed",{status:t.status}),[];const s=(await t.json()).items??[],o={};for(const i of s)o[i.componentId]=i.version;return await Ee(o),s}catch(t){return se.debug("pull network failure",t),[]}}const Ht=W("baseline-store"),pt="componentIdAliases";async function re(e,t){try{return await t()}catch(n){const s=n instanceof Error?n.message:String(n);throw Ht.error(`baseline IDB ${e} failed: ${s}`),new wn({code:"BASELINE_DB_ERROR",message:`Baseline storage is unavailable (${e}: ${s}).`,recoverable:!1,details:s})}}async function Me(e){return((await chrome.storage.local.get(pt))[pt]??{})[e]??e}async function la(e){const t=await Me(e),n=await re("get",()=>at(t));return n?{violations:n.violations,snapshotMeta:n.snapshotMeta}:null}async function da(e,t,n,s){const o=await Me(e),i=await re("get-for-set",()=>at(o)),c=(i==null?void 0:i.seenOnUrls)??[],r=c.includes(n.url)?c:[...c,n.url];await re("set",()=>Vt({componentId:o,violations:t,snapshotMeta:n,lastUpdated:new Date().toISOString(),axeVersion:n.axeVersion,announcements:s==null?void 0:s.announcements,focusEvents:s==null?void 0:s.focusEvents,seenOnUrls:r})),ra(o,{violations:t,snapshotMeta:n},n.axeVersion).catch(()=>{})}function ua(e,t){if(!e||!t||ct(e)===ct(t))return;const n=i=>{var c,r,d,l,p;return(((c=i.pseudoStates)==null?void 0:c.length)??0)*Math.max(1,((r=i.ariaVariations)==null?void 0:r.length)??0)*(((d=i.themes)==null?void 0:d.length)??0)*(((l=i.directions)==null?void 0:l.length)??0)*(((p=i.breakpoints)==null?void 0:p.length)??0)},s=n(e),o=n(t);return`Baseline was captured under a different state-matrix configuration (${s} 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 Bt(e,t,n,s){var l;const o=await Me(e),i=await xn(o),c=await re("compare",()=>at(o));if(!c){const p=t.filter(a=>i.has(a.matchKey)),f=t.filter(a=>!i.has(a.matchKey));return{new:f,persistent:[],fixed:[],newCount:f.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,acknowledged:p,acknowledgedCount:p.length}}const r=bn(c.violations,t,c.snapshotMeta,{baselineAnnouncements:c.announcements,currentAnnouncements:n==null?void 0:n.announcements,baselineFocusEvents:c.focusEvents,currentFocusEvents:n==null?void 0:n.focusEvents},i),d=ua((l=c.snapshotMeta)==null?void 0:l.matrixConfig,s);return d&&(r.matrixMismatchWarning=d),r}async function pa(e){const t=await Me(e);await re("delete",()=>sa(t)),ia(t).catch(()=>{})}async function ha(){const e=await ca();for(const t of e)await Vt({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 fa(e){let t=await re("list",()=>oa());return e!=null&&e.url&&(t=t.filter(n=>n.snapshotMeta.url===e.url)),t.map(n=>{const s=(n.focusEvents??[]).filter(l=>l.isFocusReset).length,o=(n.announcements??[]).length,i=n.violations.filter(l=>l.ruleId==="target-size").length,c=n.violations.some(l=>l.ruleId==="color-contrast"&&l.currentState.pseudoState==="hover"),r=n.violations.filter(l=>l.impact==="critical").length,d=n.violations.filter(l=>l.impact==="serious").length;return{componentId:n.componentId,violationCount:n.violations.length,lastUpdated:n.lastUpdated,seenOnUrlsCount:(n.seenOnUrls??[]).length,metrics:{criticalCount:r,seriousCount:d,focusResetCount:s,announcementCount:o,targetSizeFailCount:i,hoverContrastFail:c}}})}function ga(){const e=[];return e.push(F("BASELINE_GET",async t=>({type:"BASELINE_GET_RESPONSE",baseline:await la(t.componentId)}))),e.push(F("BASELINE_SET",async t=>{await da(t.componentId,t.violations,t.snapshotMeta,{announcements:t.announcements,focusEvents:t.focusEvents}),D({type:"SCORECARD_UPDATED_EVENT"})})),e.push(F("BASELINE_COMPARE",async t=>({type:"BASELINE_COMPARE_RESPONSE",delta:await Bt(t.componentId,t.currentViolations,{announcements:t.announcements,focusEvents:t.focusEvents},t.currentMatrix)}))),e.push(F("BASELINE_DELETE",async t=>{await pa(t.componentId),D({type:"SCORECARD_UPDATED_EVENT"})})),e.push(F("BASELINE_LIST",async t=>({type:"BASELINE_LIST_RESPONSE",items:await fa(t.filter)}))),Ht.info("handlers registered"),()=>e.forEach(t=>t())}const Ke="tierConfig:cache",ma=60*60*1e3,ya=24*60*60*1e3,ba=7*24*60*60*1e3;async function wa(){return(await chrome.storage.local.get(Ke))[Ke]??null}async function va(e){await chrome.storage.local.set({[Ke]:e})}function Aa(e,t=Date.now()){const n=t-e.fetchedAt;return n<ma?"fresh":n<ya?"stale":n<ba?"grace":"expired"}function xa(e,t){const n={...e},s=u=>{if(typeof u=="number")return u===-1?1/0:u},o=u=>typeof u=="boolean"?u:void 0,i=u=>{if(u==="all"||u==="configurable"||u==="default-only")return u},c=s(t.maxComponents);c!==void 0&&(n.maxComponents=c);const r=s(t.maxBaselines);r!==void 0&&(n.maxBaselines=r);const d=i(t.stateMatrix);d!==void 0&&(n.stateMatrix=d);const l=o(t.storybookAutoIterate);l!==void 0&&(n.storybookAutoIterate=l);const p=o(t.exportJson);p!==void 0&&(n.exportJson=p);const f=o(t.exportSarif);f!==void 0&&(n.exportSarif=f);const a=o(t.exportJunit);a!==void 0&&(n.exportJunit=a);const m=o(t.cloudSync);m!==void 0&&(n.cloudSync=m);const h=o(t.forensicAnchoring);return h!==void 0&&(n.forensicAnchoring=h),n}function Sa(e,t){var s;if(e==="trial")return((s=t.trial)==null?void 0:s.features)??null;const n=t.plans.find(o=>o.code===e||o.code.startsWith(e+"-"));return(n==null?void 0:n.features)??null}const Ye=W("tier-config-client"),ka="wcagcheckr",Ea=`https://api.wcagcheckr.com/v1/products/${ka}/tier-config`,$a=1e4,ht="tier-config-refresh",Ta=60,Ca=["trial","free","solo","team"];function qt(e){const t={};for(const n of Ca){const s=Sa(n,e);t[n]=s?xa(dt[n],s):{...dt[n]}}return t}async function Ia(){const e=await wa();if(!e){xe(null);return}if(Aa(e)==="expired"){Ye.warn("cached tier-config is expired (>7d); using hardcoded defaults"),xe(null);return}xe(qt(e.config))}async function Kt(){try{const e=await fetch(Ea,{signal:AbortSignal.timeout($a),headers:{accept:"application/json"}});if(!e.ok)throw new Error(`http ${e.status}`);const t=await e.json();await va({fetchedAt:Date.now(),config:t}),xe(qt(t)),Ye.info("tier-config refreshed from server")}catch(e){Ye.warn("tier-config refresh failed; keeping prior cache",e)}}function Ra(){chrome.alarms.create(ht,{periodInMinutes:Ta}),chrome.alarms.onAlarm.addListener(e=>{e.name===ht&&Kt()})}const Oa=W("settings-store"),ft=1,gt="__schemaVersion",Je={stateMatrix:ze,componentIdAliases:{},ignorePatterns:[]};async function $e(e,t){return(await chrome.storage.local.get(e))[e]??t}async function _a(){const e=await chrome.storage.local.get();if(e[gt]===ft)return;const t={[gt]:ft};for(const[n,s]of Object.entries(Je))n in e||(t[n]=s);await chrome.storage.local.set(t),Oa.info("defaults ensured")}const Ma=["__","inflight:","license:","support:"];function Ua(e){return!Ma.some(t=>e.startsWith(t))}function Pa(){const e=[];return e.push(F("SETTINGS_GET",async t=>({type:"SETTINGS_RESPONSE",data:(await chrome.storage.local.get(t.key))[t.key]??Je[t.key]}))),e.push(F("SETTINGS_SET",async t=>{await chrome.storage.local.set({[t.key]:t.value})})),e.push(F("SETTINGS_GET_ALL",async()=>{const t=await chrome.storage.local.get(),n=Object.fromEntries(Object.entries(t).filter(([s])=>Ua(s)));return{type:"SETTINGS_RESPONSE",data:{...Je,...n}}})),()=>e.forEach(t=>t())}const Da={input:3/1e6,output:15/1e6},Na={input:1/1e6,output:5/1e6},La="claude-sonnet-4-6",Fa="https://api.anthropic.com/v1/messages",Ga="2023-06-01";function Wa(e){const t=e.model||La,n=t.includes("haiku")?Na:Da;return{providerId:"anthropic",async judgeAltText({imageUrl:s,alt:o,surroundingContext:i,signal:c}){const r=await yt(s,c);if(!r)return bt("Could not fetch image to verify alt text",0,t);const d=Ya(o,i),l=await H({apiKey:e.apiKey,model:t,maxTokens:300,signal:c,messages:[{role:"user",content:[{type:"image",source:{type:"base64",media_type:r.mediaType,data:r.data}},{type:"text",text:d}]}]});return Y(l,n)},async judgeHeading({headingText:s,sectionContent:o,signal:i}){const c=await H({apiKey:e.apiKey,model:t,maxTokens:250,signal:i,messages:[{role:"user",content:[{type:"text",text:Ja(s,o)}]}]});return Y(c,n)},async judgeSensoryLanguage({instructionText:s,signal:o}){const i=await H({apiKey:e.apiKey,model:t,maxTokens:250,signal:o,messages:[{role:"user",content:[{type:"text",text:Xa(s)}]}]});return Y(i,n)},async judgeAriaSemantics({elementHtml:s,computedRole:o,surroundingHtml:i,signal:c}){const r=await H({apiKey:e.apiKey,model:t,maxTokens:350,signal:c,messages:[{role:"user",content:[{type:"text",text:Qa(s,o,i)}]}]});return Y(r,n)},async judgeImageOfText({imageUrl:s,accessibleName:o,signal:i}){const c=await yt(s,i);if(!c)return bt("Could not fetch image to inspect for embedded text",0,t);const r=await H({apiKey:e.apiKey,model:t,maxTokens:300,signal:i,messages:[{role:"user",content:[{type:"image",source:{type:"base64",media_type:c.mediaType,data:c.data}},{type:"text",text:Za(o)}]}]});return Y(r,n)},async judgeColorOnlyMeaning({elementHtml:s,contextDescription:o,signal:i}){const c=await H({apiKey:e.apiKey,model:t,maxTokens:250,signal:i,messages:[{role:"user",content:[{type:"text",text:es(s,o)}]}]});return Y(c,n)},async judgeGenericLinkText({linkText:s,surroundingText:o,signal:i}){const c=await H({apiKey:e.apiKey,model:t,maxTokens:250,signal:i,messages:[{role:"user",content:[{type:"text",text:ts(s,o)}]}]});return Y(c,n)},async judgeWallOfText({blockText:s,structuralChildrenCount:o,signal:i}){const c=await H({apiKey:e.apiKey,model:t,maxTokens:250,signal:i,messages:[{role:"user",content:[{type:"text",text:ns(s,o)}]}]});return Y(c,n)},async judgeLanguageMismatch({declaredLang:s,bodyTextSample:o,signal:i}){const c=await H({apiKey:e.apiKey,model:t,maxTokens:250,signal:i,messages:[{role:"user",content:[{type:"text",text:as(s,o)}]}]});return Y(c,n)},async suggestColorFix(s){const o=await H({apiKey:e.apiKey,model:t,maxTokens:600,messages:[{role:"user",content:[{type:"text",text:ss(s)}]}]});return rs(o,n)},async generateExecutiveSummary(s){const o=await H({apiKey:e.apiKey,model:t,maxTokens:800,messages:[{role:"user",content:[{type:"text",text:os(s)}]}]});return is(o,n)}}}class ye extends Error{constructor(){super("Anthropic API: canceled by caller"),this.name="ExternalAbortError"}}const Yt=3e4,mt=3,ja=1e4;function Va(e){return e===429||e>=500}function za(e){return e instanceof Error?e.name==="TimeoutError"||e.name==="AbortError"||e instanceof TypeError:!1}function Ha(e,t){if(!t)return e;if(typeof AbortSignal.any=="function")return AbortSignal.any([e,t]);const n=new AbortController,s=()=>n.abort();return e.aborted?n.abort():e.addEventListener("abort",s,{once:!0}),t.aborted?n.abort():t.addEventListener("abort",s,{once:!0}),n.signal}async function Ba(e){var o,i;const t=AbortSignal.timeout(Yt),n=Ha(t,e.signal);let s;try{s=await fetch(Fa,{method:"POST",headers:{"x-api-key":e.apiKey,"anthropic-version":Ga,"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})}catch(c){throw(o=e.signal)!=null&&o.aborted?new ye:c}if(!s.ok){const r=((i=(await s.json().catch(()=>({}))).error)==null?void 0:i.message)??`${s.status} ${s.statusText}`,d=new Error(`Anthropic API: ${r}`);throw d.status=s.status,d}return await s.json()}async function H(e){var n;let t=null;for(let s=1;s<=mt;s++){if((n=e.signal)!=null&&n.aborted)throw new ye;try{return await Ba(e)}catch(o){if(o instanceof ye)throw o;const i=o.status;if(!(za(o)||typeof i=="number"&&Va(i))||s===mt)throw o instanceof Error&&(o.name==="TimeoutError"||o.name==="AbortError")?new Error(`Anthropic API: timed out after ${Yt/1e3}s on each of ${s} attempt${s===1?"":"s"}`):o;t=o instanceof Error?o:new Error(String(o)),await qa(s*1e3,e.signal)}}throw t??new Error("Anthropic API: all retries failed")}function qa(e,t){return new Promise((n,s)=>{if(t!=null&&t.aborted){s(new ye);return}const o=setTimeout(()=>{t==null||t.removeEventListener("abort",i),n()},e),i=()=>{clearTimeout(o),s(new ye)};t==null||t.addEventListener("abort",i,{once:!0})})}async function yt(e,t){var i,c;const n=new AbortController,s=setTimeout(()=>n.abort(),ja),o=()=>n.abort();t&&(t.aborted?n.abort():t.addEventListener("abort",o,{once:!0}));try{const r=await fetch(e,{signal:n.signal});if(!r.ok)return null;const d=((c=(i=r.headers.get("content-type"))==null?void 0:i.split(";")[0])==null?void 0:c.trim())??"image/png";if(!d.startsWith("image/"))return null;const l=await r.arrayBuffer();let p="";const f=new Uint8Array(l),a=32768;for(let m=0;m<f.length;m+=a)p+=String.fromCharCode(...f.slice(m,m+a));return{data:btoa(p),mediaType:d}}catch{return null}finally{clearTimeout(s),t&&t.removeEventListener("abort",o)}}function Y(e,t){var i;const n=((i=e.content.find(c=>c.type==="text"))==null?void 0:i.text)??"",s=Ka(n),o=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;return{verdict:s.verdict,reasoning:s.reasoning,confidence:s.confidence,costUsd:o,model:e.model}}function Ka(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 s=JSON.parse(n[0]),o=s.verdict==="pass"||s.verdict==="fail"?s.verdict:"uncertain",i=(s.reasoning??"").trim().slice(0,600),c=typeof s.confidence=="number"?s.confidence:.5,r=Math.max(0,Math.min(1,c));return{verdict:o,reasoning:i,confidence:r}}catch{return{verdict:"uncertain",reasoning:"Model returned invalid JSON",confidence:0}}}function bt(e,t,n){return{verdict:"uncertain",reasoning:e,confidence:0,costUsd:t,model:n}}const X=`
|
|
2
|
+
Respond ONLY with a single JSON object — no preamble, no markdown:
|
|
3
|
+
{ "verdict": "pass" | "fail" | "uncertain", "reasoning": "1-3 sentences", "confidence": 0.0-1.0 }
|
|
4
|
+
`.trim();function Ya(e,t){return`You are an accessibility auditor verifying alt text correctness for screen-reader users (WCAG 1.1.1).
|
|
5
|
+
|
|
6
|
+
Image is attached. Current alt text: ${e?`"${e}"`:"(empty / missing)"}
|
|
7
|
+
${t?`
|
|
8
|
+
Nearby page context: ${t.slice(0,500)}`:""}
|
|
9
|
+
|
|
10
|
+
Judge whether the alt text accurately represents the image's content or function:
|
|
11
|
+
- "pass" = alt is meaningful and accurately describes what's in the image OR alt="" is correct because the image is decorative/redundant
|
|
12
|
+
- "fail" = alt is wrong, misleading, generic ("image", "photo"), filename-style ("img-1234.jpg"), or omitted on a meaningful image
|
|
13
|
+
- "uncertain" = you can't tell from the image alone whether the alt is appropriate
|
|
14
|
+
|
|
15
|
+
${X}`}function Ja(e,t){return`You are an accessibility auditor verifying that headings describe their sections (WCAG 2.4.6).
|
|
16
|
+
|
|
17
|
+
Heading text: "${e}"
|
|
18
|
+
Section content beneath it (first 800 chars): "${t.slice(0,800)}"
|
|
19
|
+
|
|
20
|
+
Judge whether the heading actually describes what the section contains:
|
|
21
|
+
- "pass" = heading meaningfully describes the section's topic
|
|
22
|
+
- "fail" = heading is non-descriptive ("Section 1", "Welcome", "More info"), unrelated to the content, or generic placeholder
|
|
23
|
+
- "uncertain" = section content is too short or ambiguous to judge
|
|
24
|
+
|
|
25
|
+
${X}`}function Xa(e){return`You are an accessibility auditor checking instructions for sensory-characteristic dependencies (WCAG 1.3.3).
|
|
26
|
+
|
|
27
|
+
Instruction text: "${e.slice(0,800)}"
|
|
28
|
+
|
|
29
|
+
WCAG 1.3.3 says instructions must NOT rely solely on shape, size, color, visual location, sound, or orientation. Judge:
|
|
30
|
+
- "fail" = instruction relies on a sensory characteristic without a programmatic identifier (e.g., "click the round button", "the green save icon", "the link on the right")
|
|
31
|
+
- "pass" = instruction uses programmatic identifiers (button label, link text) — sensory cues are fine when accompanied by a name
|
|
32
|
+
- "uncertain" = ambiguous whether a sensory descriptor is the only cue
|
|
33
|
+
|
|
34
|
+
${X}`}function Qa(e,t,n){return`You are an accessibility auditor verifying ARIA role appropriateness (WCAG 4.1.2).
|
|
35
|
+
|
|
36
|
+
Element (truncated): ${e.slice(0,600)}
|
|
37
|
+
Computed role: ${t}
|
|
38
|
+
Surrounding HTML context: ${n.slice(0,500)}
|
|
39
|
+
|
|
40
|
+
Judge whether the role is semantically appropriate for this element's apparent purpose:
|
|
41
|
+
- "pass" = role matches the element's actual behavior + content + interactivity
|
|
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
|
+
- "uncertain" = can't tell the element's true behavior from this static snapshot
|
|
44
|
+
|
|
45
|
+
${X}`}function Za(e){return`You are an accessibility auditor checking for WCAG 1.4.5 (Images of Text) compliance.
|
|
46
|
+
|
|
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
|
+
|
|
49
|
+
Image is attached.${e?`
|
|
50
|
+
Accessible name (alt / aria-label) of this image: "${e.slice(0,200)}"`:""}
|
|
51
|
+
|
|
52
|
+
Judge whether the image bakes substantial text into the raster:
|
|
53
|
+
- "fail" = the image contains body text, headings, button labels, product names, headlines, slogans, or paragraphs that could/should be HTML text. Decorative typography (e.g., a logo) does NOT count as fail unless it's clearly extended text.
|
|
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
|
+
- "uncertain" = the image is too small / low-resolution to tell, or the text is partial/ambiguous.
|
|
56
|
+
|
|
57
|
+
${X}`}function es(e,t){return`You are an accessibility auditor checking for WCAG 1.4.1 (Use of Color) compliance.
|
|
58
|
+
|
|
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
|
+
|
|
61
|
+
Element markup (truncated):
|
|
62
|
+
${e.slice(0,800)}
|
|
63
|
+
|
|
64
|
+
Context: ${t.slice(0,400)}
|
|
65
|
+
|
|
66
|
+
Judge whether the element conveys meaning ONLY through color:
|
|
67
|
+
- "fail" = a status / state / action / category is signaled ONLY by color with no text label, no shape difference, no icon glyph, and no aria-label. Common examples: a colored dot or pill conveying status with empty text content + no aria-label; a validation border (aria-invalid="true") with no accompanying icon or text message; "click the red button" instructions in CONTENT.
|
|
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
|
+
- "uncertain" = markup is ambiguous (e.g. the contextDescription says "has icon child" but the icon may itself be color-only).
|
|
70
|
+
|
|
71
|
+
${X}`}function ts(e,t){return`You are an accessibility auditor checking for WCAG 2.4.4 (Link Purpose in Context).
|
|
72
|
+
|
|
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
|
+
|
|
75
|
+
Link visible text: "${e.slice(0,200)}"
|
|
76
|
+
Surrounding paragraph / list item / sentence (programmatically associated): "${t.slice(0,800)}"
|
|
77
|
+
|
|
78
|
+
Judge whether a screen-reader user activating "links list" navigation could understand the link's destination from either the text alone or the link+context:
|
|
79
|
+
- "fail" = generic link text ("click here", "learn more", "read more", "this", "here", "more") where surrounding context ALSO does not clarify the destination.
|
|
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
|
+
- "uncertain" = link text is borderline-generic and context is ambiguous.
|
|
82
|
+
|
|
83
|
+
${X}`}function ns(e,t){return`You are an accessibility auditor checking for WCAG 3.1.5 (Reading Level) and broader cognitive-accessibility issues.
|
|
84
|
+
|
|
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
|
+
|
|
87
|
+
Block text (first 1200 chars): "${e.slice(0,1200)}"
|
|
88
|
+
Structural children inside the block (paragraphs / lists / sub-headings): ${t}
|
|
89
|
+
|
|
90
|
+
Judge whether this block presents a wall-of-text problem:
|
|
91
|
+
- "fail" = block is long-form prose (≥ 300 words) with effectively NO structural breaks (no sub-headings inside it, fewer than ~3 paragraphs, no lists), making it hard to scan.
|
|
92
|
+
- "pass" = block is short enough OR has adequate structural breaks (paragraphs, sub-headings, lists) for scannable reading.
|
|
93
|
+
- "uncertain" = block is borderline (~200-300 words with minimal structure).
|
|
94
|
+
|
|
95
|
+
${X}`}function as(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
|
+
|
|
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
|
+
|
|
99
|
+
Declared language (from <html lang="…">): "${e||"(none / empty)"}"
|
|
100
|
+
Body text sample (first 1200 chars): "${t.slice(0,1200)}"
|
|
101
|
+
|
|
102
|
+
Judge whether the declared language matches the content:
|
|
103
|
+
- "fail" = content is clearly in a DIFFERENT language than declared (e.g., lang="en" but text is Spanish), OR no lang was declared and content is non-trivial.
|
|
104
|
+
- "pass" = declared language matches the content's primary language.
|
|
105
|
+
- "uncertain" = content is too short, too multilingual, or in a language closely related to the declared one.
|
|
106
|
+
|
|
107
|
+
${X}`}function ss(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
|
+
|
|
109
|
+
Current pair (FAILING):
|
|
110
|
+
- Foreground: ${e.foreground}
|
|
111
|
+
- Background: ${e.background}
|
|
112
|
+
- Font size: ${e.fontSize}px, weight: ${e.fontWeight}
|
|
113
|
+
- Required contrast for WCAG ${e.targetLevel}: ${t}:1
|
|
114
|
+
|
|
115
|
+
${e.paletteHints&&e.paletteHints.length>0?`Available palette tokens (prefer these to keep brand cohesion):
|
|
116
|
+
${e.paletteHints.slice(0,30).map(n=>` - ${n}`).join(`
|
|
117
|
+
`)}`:"No palette tokens discovered. Stay close in hue + saturation to the original colors."}
|
|
118
|
+
|
|
119
|
+
${e.brandIntent?`Brand intent: ${e.brandIntent}`:""}
|
|
120
|
+
|
|
121
|
+
Suggest up to 3 candidate replacement pairs. Each candidate should:
|
|
122
|
+
1. Pass the required ${t}:1 contrast (verify your math).
|
|
123
|
+
2. Stay close in hue to the original (don't swap a warm palette for a cool one).
|
|
124
|
+
3. Prefer modifying ONE color (foreground OR background) over both, unless the original is severely off.
|
|
125
|
+
4. If palette tokens are available, prefer in-palette colors.
|
|
126
|
+
|
|
127
|
+
Respond ONLY with a JSON object — no preamble, no markdown:
|
|
128
|
+
{
|
|
129
|
+
"verdict": "suggested" | "no-suggestion",
|
|
130
|
+
"candidates": [
|
|
131
|
+
{ "foreground": "#hexHEX", "background": "#hexHEX", "contrast": 4.6, "rationale": "1 sentence" }
|
|
132
|
+
],
|
|
133
|
+
"reasoning": "1-3 sentences explaining your overall approach or why no suggestions are possible"
|
|
134
|
+
}`}function os(e){return`You are an accessibility auditor writing the executive summary for an automated audit report.
|
|
135
|
+
|
|
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
|
+
Framing: ${e.framing} report.
|
|
138
|
+
|
|
139
|
+
Audit data:
|
|
140
|
+
- Page audited: ${e.pageUrl}
|
|
141
|
+
- Audit date: ${e.auditDate}
|
|
142
|
+
- Compliance grade: ${e.grade}
|
|
143
|
+
- Risk tier: ${e.riskTier}
|
|
144
|
+
- Severity totals: ${e.totals.critical} critical, ${e.totals.serious} serious, ${e.totals.moderate} moderate, ${e.totals.minor} minor (unique: ${e.totals.unique})
|
|
145
|
+
- Top categories driving risk: ${e.riskDrivers.length>0?e.riskDrivers.join(", "):"none flagged"}
|
|
146
|
+
${e.priorAuditCount!==void 0&&e.priorAuditCount>0?`- Prior audit count in record: ${e.priorAuditCount} (ongoing-diligence context)`:""}
|
|
147
|
+
|
|
148
|
+
Tone:
|
|
149
|
+
- Plain-spoken. No legalese, no marketing hype.
|
|
150
|
+
- Direct about risk without alarmism.
|
|
151
|
+
- "Defense" framing: emphasize good-faith remediation effort + that automated audits cover ~30-50% of WCAG; manual checks fill the rest.
|
|
152
|
+
- "Evidence" framing: present findings objectively + suggest notice-and-cure procedures over immediate litigation.
|
|
153
|
+
- "Owner-report" framing: explain what the findings mean for the site owner in everyday terms, no code talk.
|
|
154
|
+
|
|
155
|
+
Constraints:
|
|
156
|
+
- Do NOT suggest specific dollar amounts, settlement figures, or legal predictions.
|
|
157
|
+
- Do NOT claim the audit proves compliance OR non-compliance — it documents detected violations.
|
|
158
|
+
- Do NOT use "ADA-compliant" or "WCAG-compliant" as adjectives — say "documented violations of [specific WCAG SCs]" instead.
|
|
159
|
+
|
|
160
|
+
Respond ONLY with a JSON object — no preamble, no markdown:
|
|
161
|
+
{
|
|
162
|
+
"lead": "2-3 sentence headline framing",
|
|
163
|
+
"body": "2-3 paragraphs of risk explanation + audit-date framing + ongoing-diligence context if applicable. ~150-300 words total.",
|
|
164
|
+
"closer": "1-2 sentence closing line"
|
|
165
|
+
}`}function rs(e,t){var c;const o=(((c=e.content.find(r=>r.type==="text"))==null?void 0:c.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]),d=(r.candidates??[]).slice(0,3).map(l=>({foreground:typeof l.foreground=="string"?l.foreground:"",background:typeof l.background=="string"?l.background:"",contrast:typeof l.contrast=="number"?l.contrast:0,rationale:typeof l.rationale=="string"?l.rationale:""})).filter(l=>l.foreground&&l.background);return{verdict:r.verdict==="suggested"&&d.length>0?"suggested":"no-suggestion",candidates:d,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 is(e,t){var c;const n=((c=e.content.find(r=>r.type==="text"))==null?void 0:c.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 q(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:Wa({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 K(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 Ge=W("ai-compliance-summarizer");async function Jt(e,t){if(!t.enabled)return null;const n=q(t);if(!n.ok)return Ge.debug(`AI client unavailable: ${n.reason}`),null;const s=K(t.costCapUsd);if(!s.canCharge())return null;try{const o=await n.client.generateExecutiveSummary(e);return s.recordCharge(o.costUsd),!o.lead&&!o.body?(Ge.debug("AI summary returned empty"),null):o}catch(o){return Ge.warn("compliance-summary generation failed",o),null}}async function cs(){const e=await chrome.storage.local.get("aiConfig");return et(e.aiConfig)}function ls(){return F("AI_COMPLIANCE_SUMMARY_REQUEST",async e=>{const t=await cs();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 B="wcag-component-auditor";function te(){try{return chrome.runtime.getManifest().version}catch{return"0.0.0"}}function Q(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 ds(e,t){return{schemaVersion:1,generatedAt:new Date().toISOString(),tool:{name:B,version:te()},results:e,delta:t}}function wt(e){return e==="critical"||e==="serious"?"error":e==="moderate"?"warning":"note"}function us(e,t){const n=(t?t.new:e.flatMap(r=>r.violations)).filter(r=>!r.needsReview),s=new Set,o=[];for(const r of n)s.has(r.ruleId)||(s.add(r.ruleId),o.push({id:r.ruleId,shortDescription:{text:r.description||r.ruleId},helpUri:r.helpUrl||void 0,defaultConfiguration:{level:wt(r.impact)},properties:{tags:[r.wcagCriterion,`wcag-${r.wcagLevel}`]}}));const i=Q(e),c=n.map(r=>({ruleId:r.ruleId,level:wt(r.impact),message:{text:`${r.description}`+(r.target.failureSummary?`
|
|
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:B,version:te(),informationUri:"https://wcagcheckr.com",rules:o}},results:c}]}}function Xt(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function Z(e){return Xt(e).replace(/\n/g," ").replace(/\r/g," ")}function ps(e,t){const n=!!t,s=l=>(n?l.violations.filter(p=>t.new.some(f=>f.matchKey===p.matchKey)):l.violations).filter(p=>!p.needsReview),o=e.reduce((l,p)=>l+Math.max(1,p.violations.length),0),i=e.reduce((l,p)=>l+s(p).length,0),c=e.reduce((l,p)=>l+p.durationMs,0)/1e3,r=[];r.push('<?xml version="1.0" encoding="UTF-8"?>');const d=`${B} — ${Q(e)}`;r.push(`<testsuites name="${Z(d)}" tests="${o}" failures="${i}" time="${c.toFixed(3)}">`);for(const l of e){const p=`${l.state.pseudoState}_${l.state.theme}_${l.state.direction}_${l.state.breakpoint.id}`,f=`${l.componentId}::${p}`,a=s(l),m=Math.max(1,l.violations.length);if(r.push(` <testsuite name="${Z(f)}" tests="${m}" failures="${a.length}" time="${(l.durationMs/1e3).toFixed(3)}">`),l.violations.length===0)r.push(` <testcase classname="${Z(f)}" name="no-violations" time="${(l.durationMs/1e3).toFixed(3)}" />`);else for(const h of l.violations){const u=a.some(E=>E.matchKey===h.matchKey),w=`classname="${Z(f)}" name="${Z(h.ruleId)}" time="0"`;if(!u){r.push(` <testcase ${w} />`);continue}const R=`${h.impact} - ${h.description}`,x=`Selector: ${h.target.selector}
|
|
168
|
+
WCAG: ${h.wcagCriterion} (${h.wcagLevel})
|
|
169
|
+
State: ${p}
|
|
170
|
+
`+(h.helpUrl?`Help: ${h.helpUrl}
|
|
171
|
+
`:"")+(h.target.failureSummary?`
|
|
172
|
+
${h.target.failureSummary}`:"");r.push(` <testcase ${w}>`),r.push(` <failure message="${Z(R)}" type="${Z(h.ruleId)}">${Xt(x)}</failure>`),r.push(" </testcase>")}r.push(" </testsuite>")}return r.push("</testsuites>"),r.join(`
|
|
173
|
+
`)}function y(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function hs(e,t){var R,x,E,T,M;const n=((R=e[0])==null?void 0:R.componentId)??"unknown",s=Q(e),o=e.reduce((b,O)=>b+O.durationMs,0),i=e.flatMap(b=>b.violations),c=new Map;for(const b of i){const O=`${b.ruleId}::${b.target.selector}`,U=`:${b.currentState.pseudoState} · ${b.currentState.theme} · ${b.currentState.direction}`,k=c.get(O);if(k){k._states.includes(U)||k._states.push(U);continue}c.set(O,{...b,_states:[U]})}const r=Array.from(c.values()),d={critical:[],serious:[],moderate:[],minor:[]};for(const b of r)(d[b.impact]??d.moderate).push(b);const l=new Set(((x=t==null?void 0:t.new)==null?void 0:x.map(b=>`${b.ruleId}::${b.target.selector}`))??[]),p=new Set(((E=t==null?void 0:t.persistent)==null?void 0:E.map(b=>`${b.ruleId}::${b.target.selector}`))??[]),f=new Set(((T=t==null?void 0:t.fixed)==null?void 0:T.map(b=>`${b.ruleId}::${b.target.selector}`))??[]);let a=0,m=0;for(const b of r){const O=`${b.ruleId}::${b.target.selector}`;p.has(O)?m++:l.has(O)&&a++}let h=0;for(const b of f)!p.has(b)&&!l.has(b)&&h++;const u=b=>{const O=l.has(`${b.ruleId}::${b.target.selector}`);return`
|
|
174
|
+
<article class="violation impact-${y(b.impact)}${O?" is-new":""}">
|
|
175
|
+
<header>
|
|
176
|
+
<span class="rule">${y(b.ruleId)}</span>
|
|
177
|
+
<span class="impact">${y(b.impact)}</span>
|
|
178
|
+
<span class="wcag">${y(b.wcagCriterion)} ${y(b.wcagLevel)}</span>
|
|
179
|
+
${O?'<span class="new-badge">NEW vs baseline</span>':""}
|
|
180
|
+
</header>
|
|
181
|
+
<p class="desc">${y(b.description)}</p>
|
|
182
|
+
<p class="meta"><strong>Selector:</strong> <code>${y(b.target.selector)}</code></p>
|
|
183
|
+
<p class="meta"><strong>Found in state(s):</strong> ${y(b._states.join(", "))}</p>
|
|
184
|
+
<pre class="snippet"><code>${y(b.target.outerHTML)}</code></pre>
|
|
185
|
+
${b.helpUrl?`<p class="help"><a href="${y(b.helpUrl)}">Reference: ${y(b.helpUrl)}</a></p>`:""}
|
|
186
|
+
</article>`},w=(b,O)=>O.length===0?"":`
|
|
187
|
+
<section class="impact-section impact-${y(b)}">
|
|
188
|
+
<h2>${y(b[0].toUpperCase()+b.slice(1))} (${O.length})</h2>
|
|
189
|
+
${O.map(u).join("")}
|
|
190
|
+
</section>`;return`<!doctype html>
|
|
191
|
+
<html lang="en">
|
|
192
|
+
<head>
|
|
193
|
+
<meta charset="utf-8" />
|
|
194
|
+
<title>Accessibility audit — ${y(n)}</title>
|
|
195
|
+
<style>
|
|
196
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
197
|
+
body {
|
|
198
|
+
font: 12pt/1.5 -apple-system, system-ui, "Segoe UI", sans-serif;
|
|
199
|
+
color: #0f172a;
|
|
200
|
+
max-width: 7.5in;
|
|
201
|
+
margin: 0.5in auto;
|
|
202
|
+
padding: 0 0.25in;
|
|
203
|
+
}
|
|
204
|
+
h1 { font-size: 22pt; margin: 0 0 6pt; }
|
|
205
|
+
h2 { font-size: 14pt; margin: 18pt 0 8pt; padding-bottom: 4pt; border-bottom: 1px solid #cbd5e1; }
|
|
206
|
+
header.report-header { margin-bottom: 18pt; }
|
|
207
|
+
.summary { font-size: 11pt; color: #475569; margin-bottom: 12pt; }
|
|
208
|
+
.summary span { margin-right: 12pt; }
|
|
209
|
+
.violation {
|
|
210
|
+
border-left: 4px solid #cbd5e1;
|
|
211
|
+
background: #f8fafc;
|
|
212
|
+
padding: 10pt 12pt;
|
|
213
|
+
margin: 8pt 0;
|
|
214
|
+
page-break-inside: avoid;
|
|
215
|
+
}
|
|
216
|
+
.violation.impact-critical { border-left-color: #dc2626; }
|
|
217
|
+
.violation.impact-serious { border-left-color: #ea580c; }
|
|
218
|
+
.violation.impact-moderate { border-left-color: #ca8a04; }
|
|
219
|
+
.violation.impact-minor { border-left-color: #2563eb; }
|
|
220
|
+
.violation.is-new { background: #fef2f2; }
|
|
221
|
+
.violation .new-badge { font-size: 9pt; padding: 1pt 6pt; border-radius: 3pt; background: #dc2626; color: white; text-transform: uppercase; letter-spacing: 0.04em; }
|
|
222
|
+
.violation header { display: flex; gap: 8pt; align-items: baseline; margin-bottom: 4pt; }
|
|
223
|
+
.violation .rule { font-family: "SF Mono", Menlo, monospace; font-weight: 600; font-size: 11pt; }
|
|
224
|
+
.violation .impact { font-size: 9pt; padding: 1pt 6pt; border-radius: 3pt; background: #e2e8f0; text-transform: uppercase; letter-spacing: 0.04em; }
|
|
225
|
+
.violation .wcag { font-size: 10pt; color: #64748b; }
|
|
226
|
+
.violation .desc { margin: 0 0 4pt; }
|
|
227
|
+
.violation .meta { font-size: 10pt; color: #475569; margin: 2pt 0; }
|
|
228
|
+
.violation code { font-family: "SF Mono", Menlo, monospace; font-size: 10pt; }
|
|
229
|
+
.violation .snippet {
|
|
230
|
+
background: #fff; border: 1px solid #cbd5e1; border-radius: 3pt;
|
|
231
|
+
padding: 6pt 8pt; overflow-x: auto;
|
|
232
|
+
font-family: "SF Mono", Menlo, monospace; font-size: 10pt; white-space: pre-wrap;
|
|
233
|
+
}
|
|
234
|
+
.violation .help { font-size: 10pt; }
|
|
235
|
+
footer { margin-top: 24pt; padding-top: 8pt; border-top: 1px solid #cbd5e1; font-size: 9pt; color: #64748b; }
|
|
236
|
+
@media print {
|
|
237
|
+
body { margin: 0; max-width: none; }
|
|
238
|
+
h2 { page-break-after: avoid; }
|
|
239
|
+
.violation { break-inside: avoid; }
|
|
240
|
+
}
|
|
241
|
+
</style>
|
|
242
|
+
</head>
|
|
243
|
+
<body>
|
|
244
|
+
<header class="report-header">
|
|
245
|
+
<h1>Accessibility audit report</h1>
|
|
246
|
+
<p class="summary" style="font-size: 11pt; word-break: break-all;">
|
|
247
|
+
<strong>URL audited:</strong> <code>${y(s)}</code>
|
|
248
|
+
</p>
|
|
249
|
+
<p class="summary">
|
|
250
|
+
<span><strong>Component:</strong> <code>${y(n)}</code></span>
|
|
251
|
+
<span><strong>Date:</strong> ${y(new Date().toLocaleString())}</span>
|
|
252
|
+
<span><strong>axe-core:</strong> ${y(((M=e[0])==null?void 0:M.axeVersion)??"n/a")}</span>
|
|
253
|
+
</p>
|
|
254
|
+
<p class="summary">
|
|
255
|
+
<span><strong>States audited:</strong> ${e.length}</span>
|
|
256
|
+
<span><strong>Unique violations:</strong> ${r.length}</span>
|
|
257
|
+
<span><strong>Total violation instances:</strong> ${i.length}</span>
|
|
258
|
+
<span><strong>Audit duration:</strong> ${(o/1e3).toFixed(1)}s</span>
|
|
259
|
+
</p>
|
|
260
|
+
${t!=null&&t.matrixMismatchWarning?`
|
|
261
|
+
<p class="summary" style="background: #fef3c7; border: 1px solid #f59e0b; padding: 8pt 10pt; border-radius: 3pt;">
|
|
262
|
+
⚠ ${y(t.matrixMismatchWarning)}
|
|
263
|
+
</p>
|
|
264
|
+
`:""}
|
|
265
|
+
${t&&t.baselineSnapshotMeta?`
|
|
266
|
+
<p class="summary"><strong>Unique-violation deltas (rule + selector pairs):</strong></p>
|
|
267
|
+
<p class="summary">
|
|
268
|
+
<span style="color: #dc2626;"><strong>NEW:</strong> ${a}</span>
|
|
269
|
+
<span><strong>Persistent:</strong> ${m}</span>
|
|
270
|
+
<span style="color: #15803d;"><strong>Fixed since baseline:</strong> ${h}</span>
|
|
271
|
+
</p>
|
|
272
|
+
<p class="summary" style="font-size: 9pt; color: #64748b;">
|
|
273
|
+
Per-state-instance deltas (each state×rule×selector combination): ${t.newCount} new instances · ${t.persistentCount} persistent · ${t.fixedCount} fixed. The unique-violation counts above are usually what you want to track; instance counts inflate when the matrix size changes.
|
|
274
|
+
</p>
|
|
275
|
+
`:t?'<p class="summary"><span style="color: #64748b;">No baseline accepted yet — every violation is unrated.</span></p>':""}
|
|
276
|
+
<p style="font-size: 10pt; color: #64748b;">
|
|
277
|
+
Tip: use your browser's print dialog (Ctrl+P / Cmd+P) → "Save as PDF" to produce a PDF file.
|
|
278
|
+
</p>
|
|
279
|
+
</header>
|
|
280
|
+
${w("critical",d.critical)}
|
|
281
|
+
${w("serious",d.serious)}
|
|
282
|
+
${w("moderate",d.moderate)}
|
|
283
|
+
${w("minor",d.minor)}
|
|
284
|
+
<footer>
|
|
285
|
+
Generated by ${B} v${te()}. Full audit — all detected violations included, deduped across states. Items flagged "NEW vs baseline" were not present in the most-recent accepted baseline.
|
|
286
|
+
</footer>
|
|
287
|
+
</body>
|
|
288
|
+
</html>`}const Qt=[{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 Zt(e,t,n){if(e.rules.length===0)return{conformance:"Not Applicable",hits:[]};const s=e.rules.filter(i=>t.has(i));return s.length>0?{conformance:"Partially Supports",hits:s}:{conformance:e.rules.some(i=>n.has(i))?"Supports":"Not Evaluated",hits:s}}function fs(e,t){var l,p;const n=((l=e[0])==null?void 0:l.componentId)??"unknown",s=Q(e),o=e.flatMap(f=>f.violations),i=new Set(o.map(f=>f.ruleId)),c=new Set(i);for(const f of e){const a=f.axeRulesEvaluated;if(a){for(const m of a.passed)c.add(m.ruleId);for(const m of a.inapplicable)c.add(m.ruleId);for(const m of a.incomplete)c.add(m.ruleId)}}const r=f=>{const{conformance:a,hits:m}=Zt(f,i,c);return`
|
|
289
|
+
<tr class="${a==="Supports"?"supports":a==="Partially Supports"?"partial":a==="Not Evaluated"?"not-evaluated":"na"}">
|
|
290
|
+
<td class="ref">${y(f.ref)}</td>
|
|
291
|
+
<td class="title">${y(f.title)} <span class="level">(${f.level})</span></td>
|
|
292
|
+
<td class="conf">${a}</td>
|
|
293
|
+
<td class="notes">${m.length>0?`Auto-detected violations of: ${m.map(u=>`<code>${y(u)}</code>`).join(", ")}. Manual review recommended.`:a==="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>`},d=f=>{const a=Qt.filter(m=>m.level===f).map(r).join("");return`
|
|
295
|
+
<h2>WCAG 2.1 Level ${f}</h2>
|
|
296
|
+
<table>
|
|
297
|
+
<thead>
|
|
298
|
+
<tr><th>Criterion</th><th>Title</th><th>Conformance</th><th>Notes</th></tr>
|
|
299
|
+
</thead>
|
|
300
|
+
<tbody>${a}</tbody>
|
|
301
|
+
</table>`};return`<!doctype html>
|
|
302
|
+
<html lang="en">
|
|
303
|
+
<head>
|
|
304
|
+
<meta charset="utf-8" />
|
|
305
|
+
<title>VPAT — ${y(n)}</title>
|
|
306
|
+
<style>
|
|
307
|
+
body { font: 11pt/1.5 -apple-system, system-ui, sans-serif; color: #0f172a; max-width: 8in; margin: 0.5in auto; padding: 0 0.25in; }
|
|
308
|
+
h1 { font-size: 18pt; }
|
|
309
|
+
h2 { font-size: 13pt; margin-top: 20pt; padding-bottom: 4pt; border-bottom: 1px solid #cbd5e1; }
|
|
310
|
+
.meta { font-size: 10pt; color: #475569; }
|
|
311
|
+
.preamble { background: #fef3c7; border: 1px solid #f59e0b; padding: 8pt 10pt; border-radius: 3pt; font-size: 10pt; margin: 12pt 0; }
|
|
312
|
+
table { width: 100%; border-collapse: collapse; margin: 6pt 0 12pt; font-size: 10pt; }
|
|
313
|
+
th, td { border: 1px solid #cbd5e1; padding: 5pt 7pt; text-align: left; vertical-align: top; }
|
|
314
|
+
th { background: #f1f5f9; }
|
|
315
|
+
td.ref { font-family: "SF Mono", Menlo, monospace; white-space: nowrap; }
|
|
316
|
+
td.conf { font-weight: 600; }
|
|
317
|
+
tr.supports td.conf { color: #15803d; }
|
|
318
|
+
tr.partial td.conf { color: #b45309; }
|
|
319
|
+
tr.na td.conf { color: #64748b; }
|
|
320
|
+
tr.not-evaluated td.conf { color: #b45309; background: #fef3c7; }
|
|
321
|
+
.level { color: #94a3b8; font-weight: 400; }
|
|
322
|
+
code { font-family: "SF Mono", Menlo, monospace; font-size: 9pt; background: #f1f5f9; padding: 0 3pt; }
|
|
323
|
+
@media print { body { margin: 0; } table, tr { page-break-inside: avoid; } }
|
|
324
|
+
</style>
|
|
325
|
+
</head>
|
|
326
|
+
<body>
|
|
327
|
+
<h1>Voluntary Product Accessibility Template (VPAT 2.5)</h1>
|
|
328
|
+
<p class="meta"><strong>Product:</strong> ${y(n)}</p>
|
|
329
|
+
<p class="meta" style="word-break: break-all;"><strong>URL evaluated:</strong> <code>${y(s)}</code></p>
|
|
330
|
+
<p class="meta"><strong>Date:</strong> ${y(new Date().toLocaleString())}</p>
|
|
331
|
+
<p class="meta"><strong>Evaluation method:</strong> Automated audit via wcagcheckr (axe-core ${y(((p=e[0])==null?void 0:p.axeVersion)??"n/a")}) across ${e.length} configured states.</p>
|
|
332
|
+
<div class="preamble">
|
|
333
|
+
<strong>Auto-generated draft.</strong> This VPAT was produced automatically from automated test
|
|
334
|
+
results. Conformance values reflect what automated testing can detect — they are not a complete
|
|
335
|
+
accessibility claim. Manual review by qualified accessibility evaluators is required before
|
|
336
|
+
using this document for procurement, contracting, or legal purposes. Criteria with no
|
|
337
|
+
automated rule are marked "Not Applicable" until manually evaluated.
|
|
338
|
+
</div>
|
|
339
|
+
${d("A")}
|
|
340
|
+
${d("AA")}
|
|
341
|
+
<footer style="margin-top: 24pt; padding-top: 8pt; border-top: 1px solid #cbd5e1; font-size: 9pt; color: #64748b;">
|
|
342
|
+
Generated by ${B} v${te()}. Conformance assessed against all violations detected during the audit (not delta-filtered).
|
|
343
|
+
</footer>
|
|
344
|
+
</body>
|
|
345
|
+
</html>`}function gs(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 s=/\bsrc\s*=\s*"([^"]+)"|\bsrc\s*=\s*'([^']+)'/i.exec(n);s&&t.push({label:"Image source",value:`\`${s[1]??s[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 s=/\baria-label\s*=\s*"([^"]+)"/i.exec(n);s&&t.push({label:"Existing aria-label",value:s[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 s=/\bhref\s*=\s*"([^"]+)"|\bhref\s*=\s*'([^']+)'/i.exec(n);s&&t.push({label:"Link href",value:`\`${s[1]??s[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 s=/\bname\s*=\s*"([^"]+)"|\bname\s*=\s*'([^']+)'/i.exec(n);s&&t.push({label:"Field name attribute",value:`\`${s[1]??s[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 c=/\bautocomplete\s*=\s*"([^"]+)"|\bautocomplete\s*=\s*'([^']+)'/i.exec(n);c&&t.push({label:"Autocomplete",value:c[1]??c[2]})}return t}function ms(e,t,n,s,o){var O,U,k,v,C,A;const i=new Set(o??[]),c=((O=e[0])==null?void 0:O.componentId)??"unknown",r=Q(e),d=!!(t&&t.baselineSnapshotMeta),l=e.flatMap(g=>g.violations),p=new Map;for(const g of l){const S=g.ruleId.startsWith("ai-")?`${g.ruleId}::${g.target.selector}::${(g.target.outerHTML??"").slice(0,200)}`:fe(g.ruleId,g.target.selector),$=`${g.currentState.pseudoState} · ${g.currentState.theme} · ${g.currentState.direction} · ${g.currentState.breakpoint.id}`,N=p.get(S);if(N){N._states.includes($)||N._states.push($),N._instanceSelectors.includes(g.target.selector)||N._instanceSelectors.push(g.target.selector);continue}p.set(S,{...g,_states:[$],_instanceSelectors:[g.target.selector]})}const f=Array.from(p.values()).sort((g,S)=>{const $={critical:0,serious:1,moderate:2,minor:3};return($[g.impact]??99)-($[S.impact]??99)}),a=[];a.push("# Accessibility Fix Request"),a.push(""),a.push(`You are a senior accessibility engineer pairing with the user on their codebase. The component below has ${f.length} unique WCAG violation${f.length===1?"":"s"} detected by an automated audit running axe-core ${((U=e[0])==null?void 0:U.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.`),a.push("");const m=n&&n.length>0?{runs:n,workflows:ge}:void 0,h=ne(l,m,e);if(a.push("## Context"),a.push(""),a.push(`- **Component / scope:** \`${c}\``),a.push(`- **Audited URL:** ${r}`),a.push("- **WCAG target:** WCAG 2.1 AA + WCAG 2.2 AA + best-practice rules"),a.push("- **Source filter:** Full audit — every detected violation"),a.push(`- **Compliance posture:** Grade **${h.letter}** · **${h.risk.toUpperCase()} risk** (${h.totals.critical} critical · ${h.totals.serious} serious · ${h.totals.moderate} moderate · ${h.totals.minor} minor). ${h.risk==="critical"||h.risk==="high"?"Treat as triage priority — these violations represent meaningful lawsuit exposure under ADA Title III, EAA, and EN 301 549.":h.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."}`),h.coverage){const g=h.coverage.untestedCriteria.length;g>0?a.push(`- **WCAG ${h.coverage.targetVersion} ${h.coverage.targetLevel} coverage:** ${h.coverage.evaluated} of ${h.coverage.totalApplicable} criteria evaluated. **Cannot claim ${h.coverage.targetLevel} conformance** until ${g} untested criteria are evaluated via Guided Tests (Pass / Fail / N/A). Untested: ${h.coverage.untestedCriteria.join(", ")}.`):a.push(`- **WCAG ${h.coverage.targetVersion} ${h.coverage.targetLevel} coverage:** ${h.coverage.evaluated} of ${h.coverage.totalApplicable} criteria evaluated. Full coverage — defensible conformance claim possible once all violations below are fixed.`)}d&&t&&a.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.`),a.push(""),a.push("## Before you start (safety)"),a.push(""),a.push("Do these in order before making any code changes:"),a.push(""),a.push("1. **Verify a clean git working tree** (`git status`). If there are uncommitted changes, ask the user whether to commit, stash, or proceed."),a.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`."),a.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>`."),a.push("4. **Show diffs before applying** when your tooling supports it (Cursor / Copilot Chat agents). Never silently overwrite files."),a.push("5. **Do not bump dependencies** to resolve accessibility issues — fixes should be code changes, not package upgrades."),a.push(""),a.push("## Ask me these questions first"),a.push(""),a.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."),a.push(""),a.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."),a.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?"),a.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.'),a.push("4. **Files or directories that are off-limits?** Generated code, vendored libraries, third-party widgets, lockfiles — confirm what you must NOT touch."),a.push("5. **For color-contrast fixes: prefer adjusting foreground or background?** Brand color preservation is usually a stakeholder call, not a developer call."),a.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?"),a.push(""),a.push("## Decision rules (when ambiguity surfaces during fixes)"),a.push(""),a.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.'),a.push("- **State-specific failures get state-specific fixes.** A `:hover`-only contrast failure goes in the `:hover` rule, not the default selector."),a.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."),a.push("- **When the fix would alter visible design** (color shifts, layout changes, content additions), pause and confirm with the user before applying."),a.push(""),a.push("## ⛔ Do not dismiss findings without empirical verification"),a.push(""),a.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.'),a.push(""),a.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.'),a.push(""),a.push("**To dismiss any finding as a false positive, you must do all of the following:**"),a.push(""),a.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)."),a.push("2. **Inspect the flagged elements directly** — DevTools, real `getBoundingClientRect()` coordinates, real DOM source order."),a.push("3. **Compare your empirical observation to the auditor's claim.** Does the visual layout actually contradict the DOM order, or not?"),a.push("4. **If you conclude false positive, document why** — include the empirical evidence (coordinates, screenshots, DOM excerpt) so the user can audit your reasoning."),a.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").`),a.push(""),a.push("**What does NOT count as verification:**"),a.push(""),a.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)"),a.push('- Pattern-matching the finding shape ("this looks like a perfect arithmetic flip therefore it must be an RTL artifact")'),a.push(`- Reading the auditor's false-positive list and concluding your case "looks like one of those"`),a.push(`- Deciding it's "probably fine" because the structure looks reasonable on a quick read`),a.push(""),a.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."),a.push(""),a.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."),a.push(""),a.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."),a.push(""),a.push("## Content-decision rules (alt text, button labels, link text)"),a.push(""),a.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:"),a.push(""),a.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=""`.'),a.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.'),a.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."),a.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."),a.push("5. **Always show your proposed copy before applying** — let the user approve, edit, or reject."),a.push(""),a.push("## Constraints"),a.push(""),a.push("- Don't fix violations that aren't in this list (no scope creep)."),a.push(`- Don't reformat or reorder unrelated code. No "while I'm here" cleanup.`),a.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.`),a.push("- Don't add new dependencies, configs, or build steps."),a.push("- Add inline comments only where the fix's rationale is non-obvious; otherwise skip comments."),a.push("- Preserve existing functionality and visual design wherever possible."),a.push(""),a.push("## How to use this prompt"),a.push(""),a.push("Paste this into Claude / Cursor / Copilot Chat with your codebase open. The AI should:"),a.push('1. **First**: walk through the "Before you start" + "Ask me these questions" sections with the user.'),a.push("2. **Then**: identify the source files containing each violating element (use the selectors + outerHTML below)."),a.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."),a.push("4. **Finally**: produce one atomic commit per fix, with a clear message."),a.push(""),a.push("---"),a.push("");const u=(((k=e[0])==null?void 0:k.readingOrderIssues)??[]).filter(g=>!i.has(`reading-order::${g.selector}`)),w=(v=e[0])==null?void 0:v.positionAnalysisCapturedAt,R=w?`${w.breakpoint.width}×${w.breakpoint.height} (${w.breakpoint.label}), ${w.direction.toUpperCase()}, ${w.theme} theme, ${w.pseudoState} pseudo-state`:"(reference state unavailable — findings reflect last matrix state)";if(u.length>0){a.push("## Reading-order concerns (DOM ≠ visual order)"),a.push(""),a.push(`_Positions captured at: **${R}**. 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._`),a.push(""),a.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. ${u.length} element${u.length===1?" has":"s have"} a notable gap between their DOM position and their visual position:`),a.push(""),a.push("| DOM index | Visual index | Selector | Text |"),a.push("|---|---|---|---|");for(const g of u){const S=g.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");a.push(`| ${g.domIndex} | ${g.visualIndex} | \`${g.selector}\` | ${S} |`)}a.push(""),a.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."),a.push(""),a.push("**False-positive cases — these are the ONLY legitimate reasons to skip a flagged element:**"),a.push(""),a.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.'),a.push('2. **Skip-links** ("Skip to main content") — visually positioned late but DOM-early on purpose.'),a.push("3. **Caption-first patterns** — captions placed before their image in DOM so SR users get context before the alt text."),a.push("4. **Modal / popover triggers** — the trigger button is DOM-near the modal's portal mount point, which is often `<body>` end."),a.push(""),a.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.'),a.push(""),a.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."),a.push(""),a.push("---"),a.push("")}const x=(((C=e[0])==null?void 0:C.tabOrderIssues)??[]).filter(g=>!i.has(`tab-order::${g.selector}`));if(x.length>0){const g=x.filter($=>$.flag==="visual"||$.flag==="both").length,S=x.filter($=>$.flag==="tabindex"||$.flag==="both").length;a.push("## Tab-order concerns (keyboard focus sequence ≠ visual order) — WCAG 2.4.3 Focus Order (A)"),a.push(""),a.push(`_Positions captured at: **${R}**. 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._`),a.push(""),a.push(`Keyboard users tab through the page in one order; sighted users scan in another. axe-core does not detect this. ${x.length} focusable element${x.length===1?" has":"s have"} a notable divergence. Of these: ${g} ${g===1?"has":"have"} a visual-vs-tab mismatch (focus jumps around the page); ${S} ${S===1?"is":"are"} caused by positive \`tabindex\` reordering DOM source.`),a.push(""),a.push("| Tab pos | Visual pos | DOM pos | Flag | Selector | Text |"),a.push("|---|---|---|---|---|---|");for(const $ of x){const N=$.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");a.push(`| ${$.tabPosition} | ${$.visualPosition} | ${$.domPosition} | ${$.flag} | \`${$.selector}\` | ${N} |`)}a.push(""),a.push("**How to fix:**"),a.push(""),a.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."),a.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.'),a.push("- **`flag: both`** — both conditions present. Fix the DOM order first, then remove the positive `tabindex`."),a.push(""),a.push("**False-positive cases — these are the ONLY legitimate reasons to skip a flagged element:**"),a.push(""),a.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.'),a.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."),a.push("3. **Dropdown menus / popovers** — a button DOM-far from its portal-rendered menu may produce false positives when the menu is open."),a.push(""),a.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.`),a.push(""),a.push("Surface candidates to the user; ask which are intended; only fix the rest."),a.push(""),a.push("---"),a.push("")}const E=e.flatMap(g=>g.announcements??[]),M=e.flatMap(g=>g.focusEvents??[]).filter(g=>g.isFocusReset);if(E.length>0||M.length>0){if(a.push("## Runtime behavioral observations"),a.push(""),E.length>0){const g=E.filter($=>$.politeness==="assertive").length;a.push(`**Live-region announcements** (${E.length} total, ${g} 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:`),a.push("");const S=E.slice(0,8);for(const $ of S){const N=$.text.replace(/\|/g,"\\|").replace(/\n/g," ").slice(0,120);a.push(`- \`[${$.politeness}${$.role?`/${$.role}`:""}]\` ${N}`)}E.length>8&&a.push(`- (and ${E.length-8} more)`),a.push(""),a.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)."),a.push("")}if(M.length>0){a.push(`**Focus resets to body** (${M.length} occurrence${M.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.`),a.push("");for(const g of M.slice(0,5))a.push(`- \`${g.fromSelector??"(none)"}\` → \`${g.toSelector}\``);M.length>5&&a.push(`- (and ${M.length-5} more)`),a.push(""),a.push("Common cause: an element is removed from the DOM (e.g., modal closes) without `.focus()` being called on a sensible destination."),a.push("")}a.push("---"),a.push("")}const b=((A=e[0])==null?void 0:A.undefinedCustomProperties)??[];if(b.length>0){a.push("## Undefined CSS custom properties (cascade root cause)"),a.push(""),a.push(`${b.length} CSS custom propert${b.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.`),a.push(""),a.push("| Custom property | References | First sample sites |"),a.push("|---|---|---|");for(const g of b){const S=g.sampleSites.map($=>`\`${$.selector} { ${$.property} }\``).join("; ");a.push(`| \`${g.name}\` | ${g.referenceCount} | ${S} |`)}a.push(""),a.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."),a.push(""),a.push("---"),a.push("")}if(n&&n.length>0){a.push("## Manual checks already completed (Guided Tests)"),a.push(""),a.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.`),a.push(""),a.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."),a.push("");const g=ge;a.push("| Workflow | Status | Notes |"),a.push("|---|---|---|");for(const P of n){const _=g.find(De=>De.id===P.workflowId);if(!_)continue;let L=0,G=0,pe=0,Pe=0,ae=0;for(const De of _.steps){const he=P.steps[De.id];if(!he){ae++;continue}he.status==="pass"?L++:he.status==="fail"?G++:he.status==="skip"?pe++:he.status==="not-applicable"?Pe++:ae++}const pn=G>0?`❌ ${G} failed`:ae>0?`⚠ ${ae} unanswered`:`✓ all ${_.steps.length} answered`,we=[`${L}p`,`${G}f`];Pe>0&&we.push(`${Pe} N/A`),pe>0&&we.push(`${pe} skip`),ae>0&&we.push(`${ae} unanswered`);const hn=`${we.join(" · ")} of ${_.steps.length} steps`;a.push(`| ${_.name} | ${pn} | ${hn} |`)}a.push("");const S=[];for(const P of n){const _=g.find(L=>L.id===P.workflowId);if(_)for(const L of _.steps){const G=P.steps[L.id];(G==null?void 0:G.status)==="fail"&&S.push({workflow:_.name,stepTitle:L.question,notes:G.notes})}}if(S.length>0){a.push("**Failed manual checks** (these are real WCAG failures the consultant verified by hand):"),a.push("");for(const P of S)a.push(`- **${P.workflow} → ${P.stepTitle}**${P.notes?`: ${P.notes}`:""}`);a.push(""),a.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."),a.push("")}const $=new Set(n.map(P=>P.workflowId)),N=g.filter(P=>!$.has(P.id));N.length>0&&(a.push(`**Not yet manually verified** (${N.length} workflow${N.length===1?"":"s"}): `+N.map(P=>P.name).join(", ")+". These cover WCAG criteria automation cannot detect; ask the consultant whether to run them before declaring the audit complete."),a.push("")),a.push("---"),a.push("")}if(s&&s.pagesAudited>1){const g=(()=>{try{return new URL(r).origin}catch{return null}})(),S=(()=>{try{return new URL(s.startUrl).origin}catch{return null}})();if(g&&S&&g===S){if(a.push("## Site-wide context (from a recent site crawl)"),a.push(""),a.push(`A recent crawl of ${s.pagesAudited} page${s.pagesAudited===1?"":"s"} on this origin produced grade **${s.siteGrade}** site-wide with **${s.totalUniqueViolations}** unique violations. Use this context for WCAG 3.2.3 (Consistent Navigation) and 3.2.4 (Consistent Identification) which require cross-page comparison.`),a.push(""),s.topViolations.length>0){a.push("**Most-frequent violations across the site** (rules to prioritize because they affect multiple pages):"),a.push("");for(const $ of s.topViolations.slice(0,6))a.push(`- \`${$.ruleId}\` — ${$.impact} — found on ${$.urlsAffected} page${$.urlsAffected===1?"":"s"} (${$.totalOccurrences} total instances)`);a.push(""),a.push("Fixing one of these rules in shared components / layouts is leverage — one fix resolves N pages."),a.push("")}a.push("**Manual cross-page checks the AI should walk through:**"),a.push(""),a.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."),a.push('- **3.2.4 Consistent Identification** — verify components with identical functionality (logo "home" link, search button, menu toggle) have identical accessible names across pages.'),a.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."),a.push(""),a.push("---"),a.push("")}}return a.push("## Violations to fix"),a.push(""),f.forEach((g,S)=>{const $=Sn[g.ruleId],N=g._instanceSelectors.length;if(a.push(`### ${S+1}. \`${g.ruleId}\` — ${g.impact} — WCAG ${g.wcagCriterion} ${g.wcagLevel}${N>1?` (${N} instances)`:""}`),a.push(""),a.push(`**Description:** ${g.description}`),a.push(""),N>1){a.push(`**Affects ${N} spots — fix the rule once, applies to all:**`);for(const L of g._instanceSelectors.slice(0,8))a.push(`- \`${L}\``);g._instanceSelectors.length>8&&a.push(`- (and ${g._instanceSelectors.length-8} more)`)}else a.push(`**Selector:** \`${g.target.selector}\``);a.push(""),a.push(`**Found in state(s):** ${g._states.join(" / ")}`),a.push("");const P=gs(g);if(P.length>0){for(const L of P)a.push(`**${L.label}:** ${L.value}`);a.push("")}g.target.failureSummary&&(a.push(`**Diagnostic:** ${g.target.failureSummary.replace(/\n+/g," ")}`),a.push("")),g.ai&&(a.push(`**🤖 AI-verified finding** (model: ${g.ai.model}, confidence: ${(g.ai.confidence*100).toFixed(0)}%)`),a.push(`> ${g.ai.reasoning}`),a.push(""));const _=g.target.cascadeChain;if(_&&(_.color||_.backgroundColor)){if(a.push("**CSS cascade chain (authored → rendered):**"),_.color){const L=_.color.vars.length>0?` (vars: ${_.color.vars.map(G=>`\`${G}\``).join(", ")})`:"";a.push(`- \`color\`: \`${_.color.authored}\` → \`${_.color.rendered}\`${L}${_.color.ruleSelector?` — set in rule \`${_.color.ruleSelector}\``:""}`)}if(_.backgroundColor){const L=_.backgroundColor.vars.length>0?` (vars: ${_.backgroundColor.vars.map(G=>`\`${G}\``).join(", ")})`:"";a.push(`- \`background-color\`: \`${_.backgroundColor.authored}\` → \`${_.backgroundColor.rendered}\`${L}${_.backgroundColor.ruleSelector?` — set in rule \`${_.backgroundColor.ruleSelector}\``:""}`)}a.push("")}a.push("**Element HTML:**"),a.push("```html"),a.push(g.target.outerHTML),a.push("```"),a.push(""),$?(a.push(`**Recipe:** ${$.summary}`),$.snippet&&(a.push(""),a.push("```"+($.snippetLang??"")),a.push($.snippet),a.push("```"))):a.push(`**Recipe:** No curated recipe for this rule — apply the standard WCAG ${g.wcagCriterion} guidance.`),g.helpUrl&&(a.push(""),a.push(`**More info:** ${g.helpUrl}`)),a.push(""),a.push("---"),a.push("")}),a.push("## Manual verification checklist — non-automatable WCAG 2.1 / 2.2 AA criteria"),a.push(""),a.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)."),a.push(""),a.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.'),a.push(""),a.push("### Perceivable"),a.push(""),a.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."),a.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.'),a.push("- [ ] **1.2.3 Audio Description / Media Alternative Prerecorded (A)** — prerecorded video has either audio description or a full text alternative."),a.push("- [ ] **1.2.4 Captions Live (AA)** — live audio content (livestream, real-time meeting) has captions. Usually N/A for static sites."),a.push("- [ ] **1.2.5 Audio Description Prerecorded (AA)** — prerecorded video has audio description for visual-only information."),a.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."),a.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."),a.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."),a.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)."),a.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."),a.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)."),a.push(""),a.push("### Operable"),a.push(""),a.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."),a.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.'),a.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.'),a.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."),a.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."),x.length>0?a.push("- [x] **2.4.3 Focus Order (A)** — ALREADY FLAGGED above (`Tab-order concerns` section). Confirm fixes are in place."):a.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)."),a.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."),a.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."),a.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."),a.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."),a.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."),a.push(""),a.push("### Understandable"),a.push(""),a.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."),a.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."),s&&s.pagesAudited>1?(a.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."),a.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).")):(a.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."),a.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.')),a.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."),a.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."),a.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."),a.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)."),a.push(""),a.push("### Robust"),a.push(""),E.length>0||M.length>0?a.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."):a.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.'),a.push(""),a.push("### Manual checks STILL TO DO"),a.push(""),a.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).`),a.push(""),a.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.'),a.push(""),a.push("---"),a.push(""),a.push("## When you're done — verification"),a.push(""),a.push("Before reporting completion, do all of the following:"),a.push(""),a.push(`1. **Confirm all ${f.length} violation${f.length===1?"":"s"} above are addressed.** Walk back through the list with the user, ticking each one off.`),a.push("2. **Re-run the auditor** (or whatever method we agreed in question 6 above). Verify zero NEW violations were introduced by the fixes."),a.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."`),a.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."),a.push("5. **Commit log review** — surface the list of commits you produced. Each should be atomic and revertable."),a.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."),a.push(""),a.push("## Success criteria"),a.push(""),a.push("- All "+f.length+" violation"+(f.length===1?"":"s")+" above are resolved or explicitly deferred with reason."),a.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.'),a.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.)'),a.push("- No new violations introduced."),a.push("- State-specific behavior preserved (`:hover`, `:focus`, dark-mode, RTL, breakpoints)."),a.push("- One atomic commit per fix; clean revertable history."),a.push("- No unrelated code reformatted, no dependencies bumped."),a.push("- Diff is reviewable in under 10 minutes per fix."),a.push(""),a.push(`_Generated by ${B} v${te()}._`),a.join(`
|
|
346
|
+
`)}const ys=5;function bs(e){const t=new Map;let n=0;for(const s of e){if(!s.screenshotDataUrl||s.violations.length===0)continue;const o=`${s.state.pseudoState} · ${s.state.theme} · ${s.state.direction} · ${s.state.breakpoint.id}`,i=o;t.has(i)||t.set(i,{stateLabel:o,screenshotDataUrl:s.screenshotDataUrl,violations:[]});const c=t.get(i);for(const r of s.violations)n++,c.violations.push({...r,_idx:n})}return Array.from(t.values()).sort((s,o)=>o.violations.length-s.violations.length).slice(0,ys)}const vt=1280,At=800,ws={low:"Low risk",moderate:"Moderate risk",high:"High risk",critical:"Critical risk"},vs={low:"#d1fae5",moderate:"#fef3c7",high:"#fed7aa",critical:"#fecaca"},As={low:"#065f46",moderate:"#92400e",high:"#9a3412",critical:"#991b1b"},xs={A:"#10b981",B:"#84cc16",C:"#eab308",D:"#f97316",F:"#dc2626"},Ss={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 ks(e,t,n,s,o){return tn(e,t,"evidence",n,s,o)}function Es(e){return en(e,"letter")}function $s(e){return en(e,"report")}function Ts(e){var l;const t=e.flatMap(p=>p.violations),n=ne(t,void 0,e),s=Q(e),o=new Map;for(const p of t){const f=fe(p.ruleId,p.target.selector);o.has(f)||o.set(f,p)}const i=Array.from(o.values()).sort((p,f)=>{const a={critical:0,serious:1,moderate:2,minor:3};return(a[p.impact]??99)-(a[f.impact]??99)}),c=new Map;for(const p of i){const f=c.get(p.ruleId)??[];f.push(p),c.set(p.ruleId,f)}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:** ${s}`),r.push(`- **Lawsuit-target risk:** ${Mt[n.risk]} — ${Ut[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)`),n.coverage&&n.coverage.untestedCriteria.length>0?r.push(`- **WCAG conformance progress:** ${n.coverage.evaluated} of ${n.coverage.totalApplicable} criteria evaluated. Fixing the issues below will clear me of automated lawsuit-targeting tools. To make a formal claim that the site meets WCAG ${n.coverage.targetVersion} ${n.coverage.targetLevel}, ${n.coverage.untestedCriteria.length} more criteria need human review — see the "Guided Tests" workflows in the auditor side panel for these.`):n.coverage&&n.coverage.untestedCriteria.length===0&&r.push(`- **WCAG conformance:** All ${n.coverage.totalApplicable} applicable WCAG ${n.coverage.targetVersion} ${n.coverage.targetLevel} criteria have been evaluated. Fixing the issues below will produce a defensible conformance claim.`),r.push(""),r.push("## The issues, in plain language"),r.push(""),c.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(`
|
|
347
|
+
`);let d=1;for(const[p,f]of c.entries()){const a=(l=f.find(u=>u.target.opacityContext))==null?void 0:l.target.opacityContext,m=_t(p,a),h=f[0].impact;r.push(`### ${d}. [${h.toUpperCase()}] ${m.whatsWrong}`),r.push(""),r.push(`**Why this matters.** ${m.whyItMatters}`),r.push(""),r.push(`**Plain-language fix guidance.** ${m.howToFix}`),r.push(""),r.push(`**Where on the page (${f.length} ${f.length===1?"spot":"spots"}):**`);for(const u of f.slice(0,8))r.push(`- \`${u.target.selector}\``);f.length>8&&r.push(`- (and ${f.length-8} more spots)`),r.push(""),r.push(`*Technical reference for your developer:* axe-core rule \`${p}\` — full docs at https://dequeuniversity.com/rules/axe/4.11/${p}`),r.push(""),d++}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(`
|
|
348
|
+
`)}function en(e,t){const n=e.flatMap(a=>a.violations),s=ne(n,void 0,e),o=Q(e),i=new Date,c=new Map;for(const a of n){const m=fe(a.ruleId,a.target.selector);c.has(m)||c.set(m,a)}const r=Array.from(c.values()).sort((a,m)=>{const h={critical:0,serious:1,moderate:2,minor:3};return(h[a.impact]??99)-(h[m.impact]??99)}),d=new Map;for(const a of r){const m=d.get(a.ruleId)??[];m.push(a),d.set(a.ruleId,m)}const l=Array.from(d.entries()).map(([a,m])=>{var E;const h=(E=m.find(T=>T.target.opacityContext))==null?void 0:E.target.opacityContext,u=_t(a,h),w=m[0].impact,R=m.slice(0,8).map(T=>`<code>${y(T.target.selector)}</code>`).join(", "),x=m.length>8?` <em>(and ${m.length-8} more)</em>`:"";return`
|
|
349
|
+
<section class="rule">
|
|
350
|
+
<div class="rule-header">
|
|
351
|
+
<span class="impact impact-${y(w)}">${y(w)}</span>
|
|
352
|
+
<h3>${y(u.whatsWrong)}</h3>
|
|
353
|
+
</div>
|
|
354
|
+
<p class="why"><strong>Why this matters.</strong> ${y(u.whyItMatters)}</p>
|
|
355
|
+
<p class="how"><strong>How to fix.</strong> ${y(u.howToFix)}</p>
|
|
356
|
+
<p class="where">
|
|
357
|
+
<strong>Where on the page (${m.length} ${m.length===1?"spot":"spots"}):</strong> ${R}${x}
|
|
358
|
+
</p>
|
|
359
|
+
<details class="techy">
|
|
360
|
+
<summary>Technical reference for the developer</summary>
|
|
361
|
+
<p>axe-core rule ID: <code>${y(a)}</code>. Full rule documentation:
|
|
362
|
+
<a href="https://dequeuniversity.com/rules/axe/4.11/${y(a)}">dequeuniversity.com/rules/axe/4.11/${y(a)}</a>.</p>
|
|
363
|
+
</details>
|
|
364
|
+
</section>`}).join(""),p=t==="letter"?s.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.":s.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.":s.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.":s.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.",f=t==="letter"?"Accessibility check":"Accessibility report";return`<!doctype html>
|
|
365
|
+
<html lang="en">
|
|
366
|
+
<head>
|
|
367
|
+
<meta charset="utf-8" />
|
|
368
|
+
<title>${y(f)} — ${y(o)}</title>
|
|
369
|
+
<style>
|
|
370
|
+
body { font: 15px/1.6 -apple-system, system-ui, "Segoe UI", sans-serif; color: #0f172a; max-width: 720px; margin: 24px auto; padding: 0 24px 60px; }
|
|
371
|
+
h1 { font-size: 22px; margin: 0 0 4px; }
|
|
372
|
+
h2 { font-size: 16px; margin-top: 28px; }
|
|
373
|
+
h3 { font-size: 15px; margin: 0; flex: 1; }
|
|
374
|
+
.meta { color: #64748b; font-size: 13px; margin-bottom: 18px; }
|
|
375
|
+
.summary { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 14px 18px; margin: 18px 0; }
|
|
376
|
+
.summary .totals { display: flex; gap: 14px; flex-wrap: wrap; margin-top: 8px; font-size: 13px; }
|
|
377
|
+
.summary .totals span { color: #64748b; }
|
|
378
|
+
.summary .totals strong { color: #0f172a; }
|
|
379
|
+
.rule { border: 1px solid #e2e8f0; border-radius: 6px; padding: 14px 18px; margin: 14px 0; background: white; }
|
|
380
|
+
.rule-header { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
|
|
381
|
+
.impact { font-size: 11px; padding: 2px 8px; border-radius: 4px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; }
|
|
382
|
+
.impact-critical { background: #fee2e2; color: #991b1b; }
|
|
383
|
+
.impact-serious { background: #ffedd5; color: #9a3412; }
|
|
384
|
+
.impact-moderate { background: #fef9c3; color: #854d0e; }
|
|
385
|
+
.impact-minor { background: #dbeafe; color: #1e40af; }
|
|
386
|
+
.rule p { margin: 8px 0; }
|
|
387
|
+
.where { font-size: 13px; color: #475569; }
|
|
388
|
+
.where code { background: #f1f5f9; padding: 1px 4px; border-radius: 3px; font-size: 12px; }
|
|
389
|
+
details.techy { margin-top: 10px; font-size: 12px; color: #64748b; }
|
|
390
|
+
details.techy summary { cursor: pointer; color: #475569; }
|
|
391
|
+
footer { margin-top: 40px; padding-top: 16px; border-top: 1px solid #e2e8f0; font-size: 12px; color: #94a3b8; }
|
|
392
|
+
@media print { body { max-width: none; margin: 0; padding: 0.5in; } }
|
|
393
|
+
</style>
|
|
394
|
+
</head>
|
|
395
|
+
<body>
|
|
396
|
+
<h1>${y(f)} for ${y(o)}</h1>
|
|
397
|
+
<p class="meta">${t==="letter"?"Ran":"Audit run"} ${y(i.toLocaleString())} · ${r.length} issue${r.length===1?"":"s"} grouped into ${d.size} ${d.size===1?"category":"categories"}</p>
|
|
398
|
+
|
|
399
|
+
<p>${p}</p>
|
|
400
|
+
|
|
401
|
+
<div class="summary">
|
|
402
|
+
<p style="margin: 0;"><strong>${y(Mt[s.risk])}.</strong> ${y(Ut[s.risk])}</p>
|
|
403
|
+
<div class="totals">
|
|
404
|
+
${s.totals.critical>0?`<span><strong>${s.totals.critical}</strong> critical</span>`:""}
|
|
405
|
+
${s.totals.serious>0?`<span><strong>${s.totals.serious}</strong> serious</span>`:""}
|
|
406
|
+
${s.totals.moderate>0?`<span><strong>${s.totals.moderate}</strong> moderate</span>`:""}
|
|
407
|
+
${s.totals.minor>0?`<span><strong>${s.totals.minor}</strong> minor</span>`:""}
|
|
408
|
+
${s.totals.unique===0?"<span>No automated issues detected — manual review still recommended.</span>":""}
|
|
409
|
+
</div>
|
|
410
|
+
</div>
|
|
411
|
+
|
|
412
|
+
<h2>${t==="letter"?"What I found":"Detailed findings"}</h2>
|
|
413
|
+
${l.length>0?l:"<p>No automated issues detected on this page.</p>"}
|
|
414
|
+
|
|
415
|
+
${t==="letter"?`
|
|
416
|
+
<h2>What I'd like you to do</h2>
|
|
417
|
+
<ol>
|
|
418
|
+
<li>Take a look at the items above and let me know which ones you can address.</li>
|
|
419
|
+
<li>For the ones marked <strong>critical</strong> — those tend to be the issues that show up in ADA-related lawsuit complaints, so I'd prioritize those if you can.</li>
|
|
420
|
+
<li>If anything is unclear or you need more detail, the technical references inside each section link to the full rule documentation. The whole audit was generated by an automated tool that runs the same checks Deque, IBM, and Microsoft all use.</li>
|
|
421
|
+
</ol>
|
|
422
|
+
|
|
423
|
+
<p>Thanks for taking a look!</p>
|
|
424
|
+
`:`
|
|
425
|
+
<h2>Recommended next steps</h2>
|
|
426
|
+
<ol>
|
|
427
|
+
<li>Address <strong>critical</strong> findings first — these are the categories most commonly cited in ADA-related accessibility lawsuits.</li>
|
|
428
|
+
<li>Address <strong>serious</strong> findings during the next site update cycle.</li>
|
|
429
|
+
<li>Save or print this report for your records. It documents an automated audit was performed on ${y(i.toLocaleDateString())}.</li>
|
|
430
|
+
<li>Re-run the audit after fixes to verify resolution and catch any regressions.</li>
|
|
431
|
+
</ol>
|
|
432
|
+
`}
|
|
433
|
+
|
|
434
|
+
<footer>
|
|
435
|
+
<p>Generated by ${y(B)} on ${y(i.toISOString())}.
|
|
436
|
+
Automated audits typically catch around 30–50% of accessibility issues; some categories require manual review.</p>
|
|
437
|
+
</footer>
|
|
438
|
+
</body>
|
|
439
|
+
</html>`}function Cs(e){if(!e||e.length===0)return`
|
|
440
|
+
<h2 id="manual">5. Manual assessment results</h2>
|
|
441
|
+
<p style="font-size: 10pt; color: #475569;">
|
|
442
|
+
No manual workflows were completed for this audit. Automated audits cover ~30–50% of WCAG;
|
|
443
|
+
the Guided Tests workflows in the side panel cover the remainder (keyboard operability beyond
|
|
444
|
+
static checks, screen-reader semantics, focus management, forms behavior, page structure,
|
|
445
|
+
visual indicators, reflow / text spacing, hover/focus content, pointer interactions, time-based
|
|
446
|
+
behaviors, error prevention, consistency).
|
|
447
|
+
When manual workflows are completed, their results are integrated into both the executive
|
|
448
|
+
summary's compliance grade and this section's per-criterion findings.
|
|
449
|
+
</p>`;const t=new Map(e.map(r=>[r.workflowId,r])),n=ge.map(r=>Is(r,t.get(r.id))).filter(r=>r!==null).join("");let s=0,o=0,i=0,c=0;for(const r of ge){c+=r.steps.length;const d=t.get(r.id);if(d)for(const l of r.steps){const p=d.steps[l.id];p&&(i++,p.status==="fail"&&(l.severity==="required"?s++:o++))}}return`
|
|
450
|
+
<h2 id="manual">5. Manual assessment results</h2>
|
|
451
|
+
<p style="font-size: 10pt; color: #475569; margin-bottom: 8pt;">
|
|
452
|
+
Manual workflows performed by the auditor. ${i} of ${c} steps answered
|
|
453
|
+
across ${e.length} workflow${e.length===1?"":"s"}.
|
|
454
|
+
${s>0?`<strong style="color: #b91c1c;">${s} required step${s===1?"":"s"} failed</strong> — each is a confirmed WCAG breach contributing to the compliance grade above.`:"No required steps failed."}
|
|
455
|
+
${o>0?` ${o} advisory step${o===1?"":"s"} failed (best-practice findings).`:""}
|
|
456
|
+
</p>
|
|
457
|
+
${n}`}function Is(e,t){if(!t)return null;const n=[];for(const l of e.steps){const p=t.steps[l.id];p&&n.push({step:l,status:p.status,notes:p.notes})}if(n.length===0)return null;const s=n.filter(l=>l.status==="pass").length,o=n.filter(l=>l.status==="fail").length,i=n.filter(l=>l.status==="skip").length,c=n.filter(l=>l.status==="not-applicable").length,d=n.filter(l=>l.status==="fail").map(l=>`
|
|
458
|
+
<tr>
|
|
459
|
+
<td><span class="impact impact-${l.step.severity==="required"?"serious":"moderate"}">${y(l.step.severity)}</span></td>
|
|
460
|
+
<td>${l.step.wcag?`<code>${y(l.step.wcag)}</code>`:"—"}</td>
|
|
461
|
+
<td><strong>${y(l.step.question)}</strong>${l.notes?`<br/><span style="font-size: 9pt; color: #475569;">Auditor notes: ${y(l.notes)}</span>`:""}</td>
|
|
462
|
+
</tr>
|
|
463
|
+
`).join("");return`
|
|
464
|
+
<h3>${y(e.name)}</h3>
|
|
465
|
+
<p style="font-size: 10pt; color: #475569; margin: 0 0 6pt;">${y(e.blurb)}</p>
|
|
466
|
+
<p style="font-size: 10pt; margin: 0 0 6pt;">
|
|
467
|
+
<span class="cat-pass">${s} passed</span>
|
|
468
|
+
${o>0?` · <span class="cat-fail">${o} failed</span>`:""}
|
|
469
|
+
${i>0?` · <span style="color: #64748b;">${i} skipped</span>`:""}
|
|
470
|
+
${c>0?` · <span style="color: #64748b;">${c} N/A</span>`:""}
|
|
471
|
+
· ${n.length} of ${e.steps.length} answered
|
|
472
|
+
</p>
|
|
473
|
+
${o>0?`
|
|
474
|
+
<table>
|
|
475
|
+
<thead><tr><th style="width: 80pt;">Severity</th><th style="width: 70pt;">WCAG</th><th>Failed check + auditor notes</th></tr></thead>
|
|
476
|
+
<tbody>${d}</tbody>
|
|
477
|
+
</table>
|
|
478
|
+
`:""}`}function tn(e,t,n="defense",s,o,i){var b,O,U;const c=Ss[n],r=((b=e[0])==null?void 0:b.componentId)??"unknown",d=Q(e),l=e.reduce((k,v)=>k+v.durationMs,0),p=e.flatMap(k=>k.violations),f=s&&s.length>0?{runs:s,workflows:ge}:void 0,a=ne(p,f,e),m=new Date,h=new Map;for(const k of p){const v=fe(k.ruleId,k.target.selector),C=`${k.currentState.pseudoState} · ${k.currentState.theme} · ${k.currentState.direction}`,A=h.get(v);if(A){A._states.includes(C)||A._states.push(C);continue}h.set(v,{...k,_states:[C]})}const u=Array.from(h.values()).sort((k,v)=>{const C={critical:0,serious:1,moderate:2,minor:3};return(C[k.impact]??99)-(C[v.impact]??99)}),w=bs(e),R=new Map;for(const k of w)for(const v of k.violations){const C=fe(v.ruleId,v.target.selector);R.has(C)||R.set(C,v._idx)}const x=u.map((k,v)=>`
|
|
479
|
+
<tr>
|
|
480
|
+
<td>${v+1}</td>
|
|
481
|
+
<td><span class="impact impact-${y(k.impact)}">${y(k.impact)}</span></td>
|
|
482
|
+
<td><code>${y(k.ruleId)}</code></td>
|
|
483
|
+
<td>${y(k.wcagCriterion)} ${y(k.wcagLevel)}</td>
|
|
484
|
+
<td>${y(k.description)}</td>
|
|
485
|
+
<td><code class="sel">${y(k.target.selector)}</code></td>
|
|
486
|
+
<td>${y(k._states.join(", "))}</td>
|
|
487
|
+
</tr>`).join(""),E=new Set(p.map(k=>k.ruleId)),T=new Set(E);for(const k of e){const v=k.axeRulesEvaluated;if(v){for(const C of v.passed)T.add(C.ruleId);for(const C of v.inapplicable)T.add(C.ruleId);for(const C of v.incomplete)T.add(C.ruleId)}}const M=Qt.map(k=>{const{conformance:v,hits:C}=Zt(k,E,T);return`
|
|
488
|
+
<tr class="${v==="Supports"?"supports":v==="Partially Supports"?"partial":v==="Not Evaluated"?"not-evaluated":"na"}">
|
|
489
|
+
<td><code>${y(k.ref)}</code></td>
|
|
490
|
+
<td>${y(k.title)} <span class="lvl">(${k.level})</span></td>
|
|
491
|
+
<td>${v}</td>
|
|
492
|
+
<td>${C.length>0?`Detected violations of: ${C.map(g=>`<code>${y(g)}</code>`).join(", ")}`:v==="Not Applicable"?"Not addressable by automated audit; requires manual review.":"No automated violations detected."}</td>
|
|
493
|
+
</tr>`}).join("");return`<!doctype html>
|
|
494
|
+
<html lang="en">
|
|
495
|
+
<head>
|
|
496
|
+
<meta charset="utf-8" />
|
|
497
|
+
<title>${y(c.title)} — ${y(r)}</title>
|
|
498
|
+
<style>
|
|
499
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
500
|
+
body { font: 11pt/1.55 -apple-system, system-ui, "Segoe UI", sans-serif; color: #0f172a; max-width: 8in; margin: 0.5in auto; padding: 0 0.25in; }
|
|
501
|
+
h1 { font-size: 22pt; margin: 0 0 4pt; }
|
|
502
|
+
h2 { font-size: 14pt; margin: 24pt 0 8pt; padding-bottom: 4pt; border-bottom: 1px solid #cbd5e1; page-break-after: avoid; }
|
|
503
|
+
h3 { font-size: 12pt; margin: 14pt 0 6pt; page-break-after: avoid; }
|
|
504
|
+
.doc-meta { font-size: 10pt; color: #475569; margin-bottom: 12pt; }
|
|
505
|
+
.toc { background: #f8fafc; border: 1px solid #e2e8f0; padding: 10pt 14pt; border-radius: 4pt; margin: 14pt 0; }
|
|
506
|
+
.toc ol { margin: 4pt 0 0; padding-left: 20pt; }
|
|
507
|
+
.toc a { color: #1e40af; text-decoration: none; }
|
|
508
|
+
.exec { border: 1px solid #cbd5e1; border-radius: 4pt; overflow: hidden; margin: 12pt 0; display: flex; }
|
|
509
|
+
.exec-grade { width: 1.4in; display: flex; align-items: center; justify-content: center; color: white; font-size: 56pt; font-weight: 700; line-height: 1; }
|
|
510
|
+
.exec-body { flex: 1; padding: 14pt 18pt; }
|
|
511
|
+
.risk-tier { display: inline-block; padding: 2pt 10pt; border-radius: 12pt; font-size: 10pt; font-weight: 600; margin-bottom: 6pt; }
|
|
512
|
+
.categories { display: grid; grid-template-columns: 1fr 1fr; gap: 4pt 16pt; margin: 8pt 0; font-size: 10pt; }
|
|
513
|
+
.cat-pass { color: #15803d; }
|
|
514
|
+
.cat-fail { color: #b91c1c; font-weight: 600; }
|
|
515
|
+
.cat-unchecked { color: #b45309; }
|
|
516
|
+
table { width: 100%; border-collapse: collapse; margin: 6pt 0 10pt; font-size: 9.5pt; }
|
|
517
|
+
th, td { border: 1px solid #cbd5e1; padding: 4pt 7pt; text-align: left; vertical-align: top; }
|
|
518
|
+
th { background: #f1f5f9; }
|
|
519
|
+
td.sel, code { font-family: "SF Mono", Menlo, monospace; font-size: 9pt; word-break: break-all; }
|
|
520
|
+
.impact { font-size: 8.5pt; padding: 1pt 5pt; border-radius: 2pt; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; }
|
|
521
|
+
.impact-critical { background: #fee2e2; color: #991b1b; }
|
|
522
|
+
.impact-serious { background: #ffedd5; color: #9a3412; }
|
|
523
|
+
.impact-moderate { background: #fef9c3; color: #854d0e; }
|
|
524
|
+
.impact-minor { background: #dbeafe; color: #1e40af; }
|
|
525
|
+
tr.supports td:nth-child(3) { color: #15803d; font-weight: 600; }
|
|
526
|
+
tr.partial td:nth-child(3) { color: #b45309; font-weight: 600; }
|
|
527
|
+
tr.na td:nth-child(3) { color: #64748b; }
|
|
528
|
+
.lvl { color: #94a3b8; font-weight: 400; font-size: 9pt; }
|
|
529
|
+
.disclaimer { background: #fef3c7; border: 1px solid #f59e0b; padding: 10pt 14pt; border-radius: 3pt; margin: 16pt 0; font-size: 10pt; }
|
|
530
|
+
.evidence-state { margin: 12pt 0 18pt; page-break-inside: avoid; }
|
|
531
|
+
.evidence-state h3 { font-size: 11pt; margin-bottom: 4pt; }
|
|
532
|
+
.evidence-state .caption { font-size: 9.5pt; color: #475569; margin-bottom: 4pt; }
|
|
533
|
+
.evidence-frame { display: block; width: 100%; border: 1px solid #cbd5e1; }
|
|
534
|
+
.evidence-frame svg { display: block; width: 100%; height: auto; }
|
|
535
|
+
.evidence-frame svg rect.violation-mark { fill: rgba(220, 38, 38, 0.10); stroke: #dc2626; stroke-width: 3; }
|
|
536
|
+
.evidence-frame svg text.violation-num { font: bold 14px ui-monospace, Menlo, monospace; fill: white; }
|
|
537
|
+
.evidence-frame svg rect.label-bg { fill: #dc2626; }
|
|
538
|
+
.methodology { font-size: 10pt; }
|
|
539
|
+
.methodology dt { font-weight: 600; margin-top: 6pt; }
|
|
540
|
+
.methodology dd { margin: 0 0 4pt 0; }
|
|
541
|
+
footer { margin-top: 28pt; padding-top: 8pt; border-top: 1px solid #cbd5e1; font-size: 8.5pt; color: #64748b; }
|
|
542
|
+
@media print { body { margin: 0; max-width: none; } .toc { page-break-after: always; } table, tr { page-break-inside: avoid; } }
|
|
543
|
+
</style>
|
|
544
|
+
</head>
|
|
545
|
+
<body>
|
|
546
|
+
|
|
547
|
+
<h1>${y(c.title)}</h1>
|
|
548
|
+
<p class="doc-meta">
|
|
549
|
+
<strong>Subject:</strong> <code>${y(r)}</code> ·
|
|
550
|
+
<strong>URL:</strong> ${y(d)}<br/>
|
|
551
|
+
<strong>Audit date:</strong> ${y(m.toLocaleString())} ·
|
|
552
|
+
<strong>Audit engine:</strong> axe-core ${y(((O=e[0])==null?void 0:O.axeVersion)??"n/a")} ·
|
|
553
|
+
<strong>Tool:</strong> ${y(B)} v${y(te())}
|
|
554
|
+
</p>
|
|
555
|
+
|
|
556
|
+
<nav class="toc">
|
|
557
|
+
<strong>Contents</strong>
|
|
558
|
+
<ol>
|
|
559
|
+
<li><a href="#exec">Executive summary</a></li>
|
|
560
|
+
<li><a href="#methodology">Methodology & scope</a></li>
|
|
561
|
+
<li><a href="#findings">Detailed findings (automated)</a></li>
|
|
562
|
+
<li><a href="#evidence">Visual evidence (per-state screenshots)</a></li>
|
|
563
|
+
<li><a href="#manual">Manual assessment results</a></li>
|
|
564
|
+
<li><a href="#conformance">WCAG 2.1 conformance assessment</a></li>
|
|
565
|
+
<li><a href="#remediation">${n==="evidence"?"Recommended approach before legal action":"Remediation plan"}</a></li>
|
|
566
|
+
<li><a href="#disclaimer">Disclaimer & limitations</a></li>
|
|
567
|
+
</ol>
|
|
568
|
+
</nav>
|
|
569
|
+
|
|
570
|
+
<h2 id="exec">1. Executive summary</h2>
|
|
571
|
+
<div class="exec">
|
|
572
|
+
<div class="exec-grade" style="background: ${xs[a.letter]};">${a.letter}</div>
|
|
573
|
+
<div class="exec-body">
|
|
574
|
+
<span class="risk-tier" style="background: ${vs[a.risk]}; color: ${As[a.risk]};">
|
|
575
|
+
${y(ws[a.risk])}
|
|
576
|
+
</span>
|
|
577
|
+
<p style="margin: 0 0 6pt;"><strong>${a.totals.unique} unique violations detected</strong> across ${e.length} state combinations
|
|
578
|
+
(${a.totals.critical} critical · ${a.totals.serious} serious · ${a.totals.moderate} moderate · ${a.totals.minor} minor).</p>
|
|
579
|
+
<p style="margin: 0; font-size: 10pt; color: #475569;">${y(kn[a.risk])}</p>
|
|
580
|
+
</div>
|
|
581
|
+
</div>
|
|
582
|
+
${i&&(i.lead||i.body||i.closer)?`
|
|
583
|
+
<div style="margin-top: 12pt; padding: 12pt 14pt; border-left: 4px solid #2563eb; background: #f0f6ff; border-radius: 0 4pt 4pt 0;">
|
|
584
|
+
<p style="margin: 0 0 6pt; font-size: 10pt; color: #1e40af; text-transform: uppercase; letter-spacing: 0.04em;">
|
|
585
|
+
AI-generated narrative summary (${y(i.model)})
|
|
586
|
+
</p>
|
|
587
|
+
${i.lead?`<p style="margin: 0 0 8pt; font-size: 11.5pt; font-weight: 500;">${y(i.lead)}</p>`:""}
|
|
588
|
+
${i.body?i.body.split(/\n{2,}/).map(k=>`<p style="margin: 0 0 6pt;">${y(k.trim())}</p>`).join(`
|
|
589
|
+
`):""}
|
|
590
|
+
${i.closer?`<p style="margin: 8pt 0 0; font-style: italic;">${y(i.closer)}</p>`:""}
|
|
591
|
+
<p style="margin: 6pt 0 0; font-size: 9pt; color: #475569;">
|
|
592
|
+
This narrative was generated by an LLM from the audit's structured findings. The numerical
|
|
593
|
+
data in §1 above and the per-violation details in §3 are the authoritative record.
|
|
594
|
+
</p>
|
|
595
|
+
</div>`:""}
|
|
596
|
+
|
|
597
|
+
<h3>Per-category breakdown (top-6 lawsuit-magnet categories)</h3>
|
|
598
|
+
<div class="categories">
|
|
599
|
+
${a.categories.map(k=>{const v=k.status==="pass"?"✓":k.status==="fail"?"✗":"⚠",C=k.status==="pass"?"cat-pass":k.status==="fail"?"cat-fail":"cat-unchecked",A=k.status==="fail"?` (${k.violationCount})`:k.needsManualCheck?" — requires manual review":"";return`<div class="${C}">${v} ${y(k.label)}${y(A)}</div>`}).join("")}
|
|
600
|
+
</div>
|
|
601
|
+
|
|
602
|
+
<h2 id="methodology">2. Methodology & scope</h2>
|
|
603
|
+
<dl class="methodology">
|
|
604
|
+
<dt>Audit engine</dt>
|
|
605
|
+
<dd>axe-core ${y(((U=e[0])==null?void 0:U.axeVersion)??"n/a")} (industry-standard, used by Deque, IBM, Microsoft, and Google's Lighthouse).
|
|
606
|
+
Rule sets enabled: WCAG 2.0 A/AA, WCAG 2.1 A/AA, WCAG 2.2 A/AA, axe-core best-practice.</dd>
|
|
607
|
+
<dt>State coverage</dt>
|
|
608
|
+
<dd>${e.length} state combinations were audited, including pseudo-states (<code>:hover</code>, <code>:focus</code>, <code>:focus-visible</code>, <code>:active</code>, <code>:disabled</code>),
|
|
609
|
+
color-scheme themes (<code>prefers-color-scheme: dark</code>, <code>forced-colors: active</code>),
|
|
610
|
+
text direction (LTR / RTL), and viewport breakpoints (mobile / tablet / desktop).
|
|
611
|
+
Audits at non-default states are uncommon among automated tools and represent significant additional WCAG coverage relative to single-state audits.</dd>
|
|
612
|
+
<dt>Audit duration</dt>
|
|
613
|
+
<dd>${(l/1e3).toFixed(1)} seconds across ${e.length} states (${(l/Math.max(1,e.length)).toFixed(0)}ms per state).</dd>
|
|
614
|
+
<dt>Scope</dt>
|
|
615
|
+
<dd>Component / region: <code>${y(r)}</code></dd>
|
|
616
|
+
<dt>What automated audit covers</dt>
|
|
617
|
+
<dd>Approximately 30–50% of WCAG success criteria are addressable by automated testing. Categories addressable here: color contrast, image alt text, form labels, accessible names for buttons / links, document structure, ID uniqueness, ARIA validity.</dd>
|
|
618
|
+
<dt>What requires manual evaluation</dt>
|
|
619
|
+
<dd>Keyboard operability beyond static checks (focus traps, focus restoration after dismissal), screen-reader semantic correctness, color use beyond contrast, content meaning (alt text accuracy), and cognitive accessibility. The ${y(B)} Guided Tests workflows provide a structured manual-audit path for these areas.</dd>
|
|
620
|
+
</dl>
|
|
621
|
+
|
|
622
|
+
<h2 id="findings">3. Detailed findings</h2>
|
|
623
|
+
${u.length===0?"<p>No automated violations were detected during this audit.</p>":`<table>
|
|
624
|
+
<thead>
|
|
625
|
+
<tr><th>#</th><th>Impact</th><th>Rule</th><th>WCAG</th><th>Description</th><th>Selector</th><th>States</th></tr>
|
|
626
|
+
</thead>
|
|
627
|
+
<tbody>${x}</tbody>
|
|
628
|
+
</table>`}
|
|
629
|
+
|
|
630
|
+
<h2 id="evidence">4. Visual evidence (per-state screenshots)</h2>
|
|
631
|
+
${w.length===0?'<p style="font-size: 10pt; color: #64748b;">No per-state screenshots were captured for this audit (no states had violations, or screenshot capture failed).</p>':`<p class="caption">Top ${w.length} state${w.length===1?"":"s"} by violation count shown below. Red rectangles mark violation locations; numbers correspond to entries in §3.</p>`+w.map(k=>{const v=k.violations.filter(C=>C.target.boundingRect).map(C=>{const A=C.target.boundingRect,g=String(C._idx).length*8+14,S=22;return[`<rect class="violation-mark" x="${A.x}" y="${A.y}" width="${A.w}" height="${A.h}" />`,`<rect class="label-bg" x="${A.x}" y="${Math.max(0,A.y-S)}" width="${g}" height="${S}" rx="2" />`,`<text class="violation-num" x="${A.x+6}" y="${Math.max(0,A.y-S)+16}">${C._idx}</text>`].join("")}).join("");return`
|
|
632
|
+
<div class="evidence-state">
|
|
633
|
+
<h3>State: ${y(k.stateLabel)} (${k.violations.length} violation${k.violations.length===1?"":"s"})</h3>
|
|
634
|
+
<div class="evidence-frame">
|
|
635
|
+
<svg viewBox="0 0 ${vt} ${At}" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet">
|
|
636
|
+
<image href="${k.screenshotDataUrl}" x="0" y="0" width="${vt}" height="${At}" preserveAspectRatio="xMidYMid slice" />
|
|
637
|
+
${v}
|
|
638
|
+
</svg>
|
|
639
|
+
</div>
|
|
640
|
+
</div>`}).join("")}
|
|
641
|
+
|
|
642
|
+
${Cs(s)}
|
|
643
|
+
|
|
644
|
+
<h2 id="conformance">6. WCAG 2.1 conformance assessment</h2>
|
|
645
|
+
<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>
|
|
646
|
+
<table>
|
|
647
|
+
<thead><tr><th>Criterion</th><th>Title</th><th>Conformance</th><th>Notes</th></tr></thead>
|
|
648
|
+
<tbody>${M}</tbody>
|
|
649
|
+
</table>
|
|
650
|
+
|
|
651
|
+
${(()=>{const k=o!==void 0,v=k?"8":"7",C=k?"9":"8",A=k?`<h2 id="audit-history">7. Audit history</h2>
|
|
652
|
+
<p style="font-size: 10pt; margin-bottom: 8pt;">Chronological record of audits this user has run, with cryptographic hashes of each audit's identifying fields (component, page URL, grade, severity totals, axe version, timestamp, state-matrix size). Hashes are SHA-256 of canonical-JSON serialization, so any tampering with a logged audit's identity is detectable client-side. Entries marked <em>Anchored</em> additionally carry a third-party RFC 3161 trusted timestamp issued by the named TSA — those entries cannot be backdated.</p>
|
|
653
|
+
${o&&o.length>0?`<table>
|
|
654
|
+
<thead>
|
|
655
|
+
<tr>
|
|
656
|
+
<th>Captured</th>
|
|
657
|
+
<th>Component</th>
|
|
658
|
+
<th>Page</th>
|
|
659
|
+
<th>Grade</th>
|
|
660
|
+
<th>Crit</th>
|
|
661
|
+
<th>Sev</th>
|
|
662
|
+
<th>States</th>
|
|
663
|
+
<th>Hash (first 12)</th>
|
|
664
|
+
<th>Source</th>
|
|
665
|
+
</tr>
|
|
666
|
+
</thead>
|
|
667
|
+
<tbody>${o.map(S=>`
|
|
668
|
+
<tr>
|
|
669
|
+
<td style="white-space: nowrap;">${y(S.capturedAt)}</td>
|
|
670
|
+
<td><code>${y(S.componentId)}</code></td>
|
|
671
|
+
<td style="word-break: break-all;">${y(S.pageUrl)}</td>
|
|
672
|
+
<td><strong>${y(S.grade)}</strong></td>
|
|
673
|
+
<td>${S.totals.critical}</td>
|
|
674
|
+
<td>${S.totals.serious}</td>
|
|
675
|
+
<td>${S.statesAudited}</td>
|
|
676
|
+
<td><code title="${y(S.hash)}">${y(S.hash.slice(0,12))}…</code></td>
|
|
677
|
+
<td>${S.receipt?`RFC 3161 (${y(S.receipt.tsaName)})`:"Local"}</td>
|
|
678
|
+
</tr>`).join("")}
|
|
679
|
+
</tbody>
|
|
680
|
+
</table>`:'<p style="color: #64748b; font-style: italic;">No prior audits recorded — this is the first audit on record for this installation.</p>'}
|
|
681
|
+
`:"",g=n==="evidence"?`<h2 id="remediation">${v}. Recommended approach before legal action</h2>
|
|
682
|
+
<div style="background: #f0f9ff; border: 1px solid #38bdf8; padding: 12pt 16pt; border-radius: 4pt; font-size: 10.5pt;">
|
|
683
|
+
<p style="margin: 0 0 8pt;"><strong>Most businesses are unaware their digital properties have accessibility defects.</strong> Many will fix them once notified. A good-faith notice-and-cure approach is more likely to resolve the underlying accessibility barrier (which is the actual purpose of disability-rights law) and, where remediation does not occur, strengthens any subsequent legal claim by establishing both notice and a reasonable opportunity to comply.</p>
|
|
684
|
+
<p style="margin: 0 0 8pt;"><strong>Recommended steps before considering legal action:</strong></p>
|
|
685
|
+
<ol style="margin: 0 0 8pt; padding-left: 20pt;">
|
|
686
|
+
<li><strong>Send written notice to the site owner / business operator.</strong> Include this evidence bundle as an attachment. Identify yourself, the date you encountered the barrier, the specific access need affected (e.g., screen-reader use, keyboard-only navigation, low-vision contrast), and a reasonable timeframe for response (commonly 30–60 days, depending on jurisdiction and severity).</li>
|
|
687
|
+
<li><strong>Offer to discuss remediation.</strong> Many businesses don't have an accessibility lead — pointing them at the WCAG criteria in §5 of this document and the per-rule canonical fixes (axe-core's documentation, linked from each violation's "More info") gives them a starting point.</li>
|
|
688
|
+
<li><strong>Document the response (or lack of one).</strong> Save the original notice, any reply, dates, and any remediation activity (or absence of it).</li>
|
|
689
|
+
<li><strong>If after the notice period the business has not engaged or remediated</strong>, the documented good-faith notice combined with this evidence bundle materially strengthens an ADA Title III, EAA, EN 301 549, AODA, or analogous claim, depending on jurisdiction.</li>
|
|
690
|
+
</ol>
|
|
691
|
+
<p style="margin: 0; font-size: 9.5pt; color: #475569;">This guidance is general and not legal advice. Specific procedural requirements vary by jurisdiction (e.g., some US states require pre-suit notice under "Unruh Act" amendments; others do not). Consult qualified counsel about the procedural posture appropriate for your situation.</p>
|
|
692
|
+
</div>`:`<h2 id="remediation">${v}. Remediation plan</h2>
|
|
693
|
+
<p>Each violation in §3 has a documented canonical fix pattern. The remediation workflow we recommend:</p>
|
|
694
|
+
<ol>
|
|
695
|
+
<li>Findings are classified by severity. Address critical and serious findings first.</li>
|
|
696
|
+
<li>For each rule, ${y(B)} produces a structured fix prompt that can be processed by a code-fluent AI assistant operating against the codebase. The fix prompt includes per-violation context (selector, state, recipe), pre-flight safety checks (clean git working tree, atomic commits per fix), and explicit constraints (no scope creep, native HTML preferred over ARIA, fix state-specific failures in state-specific styles).</li>
|
|
697
|
+
<li>After remediation, re-run the audit to verify each violation is resolved without introducing regressions.</li>
|
|
698
|
+
<li>Manual workflows (keyboard, screen-reader, focus-management, forms) should be re-completed via the Guided Tests panel.</li>
|
|
699
|
+
<li>Once resolved, the audited state should be accepted as a baseline; future audits surface only NEW debt vs. that baseline.</li>
|
|
700
|
+
</ol>`;return`${A}
|
|
701
|
+
|
|
702
|
+
${g}
|
|
703
|
+
|
|
704
|
+
<h2 id="disclaimer">${C}. Disclaimer & limitations</h2>`})()}
|
|
705
|
+
<div class="disclaimer">
|
|
706
|
+
<p style="margin: 0 0 6pt;"><strong>${y(c.disclaimerHeading)}</strong></p>
|
|
707
|
+
<p style="margin: 0 0 6pt;">${c.disclaimerBody}</p>
|
|
708
|
+
<p style="margin: 0;">This document represents detected violations at the time and scope of audit. Subsequent changes to the audited resource may produce different results. The presence or absence of detected violations in this report is not, by itself, ${n==="defense"?"a defense against":"a determination of"} any specific accessibility claim, regulation, or legal action. This document is provided for ${y(c.documentLabel)} purposes.</p>
|
|
709
|
+
</div>
|
|
710
|
+
|
|
711
|
+
<footer>
|
|
712
|
+
Generated by ${y(B)} v${y(te())} on ${y(m.toISOString())}.
|
|
713
|
+
Audit run identifier: ${y(m.getTime().toString(36))}-${y(r.replace(/[^a-z0-9]/gi,"").slice(0,8))}.
|
|
714
|
+
</footer>
|
|
715
|
+
|
|
716
|
+
</body>
|
|
717
|
+
</html>`}async function Rs(){const t=(await chrome.storage.local.get("aiConfig")).aiConfig;return{...On,...t??{}}}async function xt(e,t,n){const s=await Rs();if(!s.enabled||!s.apiKey||e.length===0)return null;const o=e.flatMap(d=>d.violations),i=ne(o,void 0,e),c=i.categories.filter(d=>d.status==="fail").sort((d,l)=>l.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:c,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},s)}function Os(){return F("EXPORT_REQUEST",async e=>{if(e.format==="sarif")return{type:"EXPORT_RESPONSE",format:"sarif",content:JSON.stringify(us(e.results,e.delta),null,2)};if(e.format==="junit")return{type:"EXPORT_RESPONSE",format:"junit",content:ps(e.results,e.delta)};if(e.format==="html-print")return{type:"EXPORT_RESPONSE",format:"html-print",content:hs(e.results,e.delta)};if(e.format==="vpat")return{type:"EXPORT_RESPONSE",format:"vpat",content:fs(e.results,e.delta)};if(e.format==="ai-prompt")return{type:"EXPORT_RESPONSE",format:"ai-prompt",content:ms(e.results,e.delta,e.manualRuns,e.siteCrawlReport,e.dismissedKeys)};if(e.format==="defense-bundle"){const t=await lt(),n=await xt(e.results,"defense",t.length);return{type:"EXPORT_RESPONSE",format:"defense-bundle",content:tn(e.results,e.delta,"defense",e.manualRuns,t,n)}}if(e.format==="evidence-bundle"){const t=await lt(),n=await xt(e.results,"evidence",t.length);return{type:"EXPORT_RESPONSE",format:"evidence-bundle",content:ks(e.results,e.delta,e.manualRuns,t,n)}}return e.format==="developer-letter"?{type:"EXPORT_RESPONSE",format:"developer-letter",content:Es(e.results)}:e.format==="owner-report"?{type:"EXPORT_RESPONSE",format:"owner-report",content:$s(e.results)}:e.format==="ai-prompt-owner"?{type:"EXPORT_RESPONSE",format:"ai-prompt-owner",content:Ts(e.results)}:{type:"EXPORT_RESPONSE",format:"json",content:JSON.stringify(ds(e.results,e.delta),null,2)}})}const _s=W("support-messenger"),Ms="https://api.wcagcheckr.com/v1/products/wcagcheckr/support",We="support:rate-window",Us=5,Ps=10*60*1e3,Ds=2,Ns="wcagcheckr",St="storybook:lastDetected";async function Ls(e){if(!e)return;const t=await chrome.runtime.getPlatformInfo(),s=(await chrome.storage.local.get(St))[St];return{extensionVersion:chrome.runtime.getManifest().version,platform:t,storybookDetected:s!=null&&s.detected?s.version??"detected":"none",licenseTier:await Pt(),logTail:fn(gn())}}async function Fs(e=Date.now()){const n=((await chrome.storage.local.get(We))[We]??[]).filter(s=>e-s<Ps);return n.length>=Us?!1:(n.push(e),await chrome.storage.local.set({[We]:n}),!0)}async function nn(e,t=0){try{const n=await fetch(Ms,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!n.ok)throw new Error(`http ${n.status}`);const s=await n.json();if(!s.success)throw new Error(s.error??"server returned success=false");return{ticketRef:s.ticketRef??""}}catch(n){if(t<Ds){const s=(t+1)*1e3;return await new Promise(o=>setTimeout(o,s)),nn(e,t+1)}throw n}}function Gs(){return F("SUPPORT_MESSAGE_REQUEST",async e=>{if(!await Fs())return{type:"SUPPORT_MESSAGE_RESPONSE",success:!1,error:Se("NETWORK","Rate limit reached. Try again in 10 minutes.",!0)};try{const t=await Ls(e.includeContext);return{type:"SUPPORT_MESSAGE_RESPONSE",success:!0,ticketRef:(await nn({productSlug:Ns,subject:e.subject,body:e.body,replyEmail:e.replyEmail,context:t,extensionVersion:chrome.runtime.getManifest().version,timestamp:new Date().toISOString()})).ticketRef}}catch(t){_s.error("send failed",t);const n=t instanceof Error?t.message:String(t);return{type:"SUPPORT_MESSAGE_RESPONSE",success:!1,error:Se("NETWORK",n,!0)}}})}const an=W("ai-color-suggester");async function Ws(e,t,n=[]){if(!t.enabled||e.length===0)return{results:[],totalCostUsd:0,capExceeded:!1};const s=q(t);if(!s.ok)return{results:[],totalCostUsd:0,capExceeded:!1};const o=s.client,i=K(t.costCapUsd),c=[];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&&c.push({matchKey:r.matchKey,suggestions:d.candidates,reasoning:d.reasoning,costUsd:d.costUsd})}catch(d){an.warn(`color-fix suggestion failed for ${r.matchKey}`,d)}}return{results:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded}}async function js(){const e=await chrome.storage.local.get("aiConfig");return et(e.aiConfig)}function Vs(){return F("AI_COLOR_SUGGEST_REQUEST",async e=>{const t=await js();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:s,capExceeded:o}=await Ws(e.inputs,t,e.paletteHints);return{type:"AI_COLOR_SUGGEST_RESPONSE",results:n,totalCostUsd:s,capExceeded:o}}catch(n){return an.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 Te={A:0,B:1,C:2,D:3,F:4},kt={low:0,moderate:1,high:2,critical:3};function zs(e,t){return Te[e]>Te[t]?e:t}function Hs(e,t){return kt[e]>kt[t]?e:t}function Bs(e){const t=[];for(const n of e)for(const s of n.results)for(const o of s.violations)t.push({url:n.url,v:o});return t}function qs(e,t,n,s,o){const i=t.filter(x=>!x.error).length,c=t.filter(x=>!!x.error).length,r=t.map(x=>{if(x.error)return{url:x.url,grade:"F",risk:"critical",uniqueViolations:0,totals:{critical:0,serious:0,moderate:0,minor:0},durationMs:x.durationMs,error:x.error};const E=x.results.flatMap(M=>M.violations),T=ne(E);return{url:x.url,grade:T.letter,risk:T.risk,uniqueViolations:T.totals.unique,totals:{critical:T.totals.critical,serious:T.totals.serious,moderate:T.totals.moderate,minor:T.totals.minor},durationMs:x.durationMs}});r.sort((x,E)=>{const T=Te[E.grade]-Te[x.grade];return T!==0?T:E.uniqueViolations-x.uniqueViolations});let d="A",l="low";for(const x of r)d=zs(d,x.grade),l=Hs(l,x.risk);const p=Bs(t),f=x=>`${x.ruleId}::${x.target.selector}`,a=new Map;for(const{v:x}of p){const E=f(x);a.has(E)||a.set(E,x)}const m=a.size,h={critical:0,serious:0,moderate:0,minor:0};for(const x of a.values())h[x.impact]++;const u=new Map;for(const{url:x,v:E}of p){let T=u.get(E.ruleId);T||(T={ruleId:E.ruleId,description:E.description,impact:E.impact,urls:new Set,totalOccurrences:0},u.set(E.ruleId,T)),T.urls.add(x),T.totalOccurrences++}const w=Array.from(u.values()).map(x=>({ruleId:x.ruleId,description:x.description,impact:x.impact,urlsAffected:x.urls.size,totalOccurrences:x.totalOccurrences,sampleUrls:Array.from(x.urls).slice(0,5)})).sort((x,E)=>E.urlsAffected!==x.urlsAffected?E.urlsAffected-x.urlsAffected:E.totalOccurrences-x.totalOccurrences),R={A:0,B:0,C:0,D:0,F:0};for(const x of r)R[x.grade]++;return{startedAt:n,finishedAt:s,startUrl:e,pagesAudited:i,pagesFailed:c,totalDurationMs:o,siteGrade:d,siteRisk:l,totalUniqueViolations:m,totals:h,topViolations:w,pages:r,pagesByGrade:R}}function Ks(e){const t=new Map(e.breakpointPresets.map(s=>[s.id,s])),n=[];for(const s of e.breakpoints){const o=t.get(s);if(o)for(const i of e.directions)for(const c of e.themes){const r=e.ariaVariations.length===0?[null]:e.ariaVariations;for(const d of r)for(const l of e.pseudoStates)n.push({pseudoState:l,ariaVariation:d,theme:c,direction:i,breakpoint:o})}}return n}const Ys=W("ai-alt-text");async function Js(e,t,n={}){var f,a;if(!t.enabled||!t.enabledChecks.altText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const s=q(t);if(!s.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${s.reason}`]};const o=s.client,i=K(t.costCapUsd),c=[],r=[],d=Math.max(1,t.maxCandidatesPerCheck??10),l=e.slice(0,d),p=l.length;for(let m=0;m<l.length;m++){const h=l[m];if(!i.canCharge())break;try{if((f=n.signal)!=null&&f.aborted){r.push(`canceled after ${m}/${p} candidates`);break}const u=await o.judgeAltText({imageUrl:h.imageUrl,alt:h.alt,surroundingContext:h.surroundingContext,signal:n.signal});i.recordCharge(u.costUsd),(u.verdict==="fail"||u.verdict==="uncertain"&&u.confidence<.6)&&c.push(await Xs(h,u))}catch(u){if(u instanceof Error&&(u.name==="AbortError"||u.name==="ExternalAbortError")){r.push(`canceled after ${m}/${p} candidates`);break}const R=u instanceof Error?u.message:String(u);r.push(`${h.selector}: ${R}`),Ys.warn("alt-text judgment failed",u)}(a=n.onProgress)==null||a.call(n,m+1,p)}return{findings:c,totalCostUsd:i.state.spentUsd,capExceeded:i.state.exceeded,errors:r}}async function Xs(e,t){const n=t.verdict==="uncertain"?"ai-alt-uncertain":"ai-alt-misleading",s={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 Qe({ruleId:n,componentId:e.componentId,currentState:e.currentState,target:s}),i=t.verdict==="uncertain";return{ruleId:n,wcagCriterion:"wcag111",wcagLevel:"A",impact:i?"minor":"serious",description:i?"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:s,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},...i?{needsReview:!0}:{}}}const ce=W("ai-text");function le(e){if(e!=null&&e.aborted){const t=new Error("aborted");throw t.name="AbortError",t}}function de(e){return e instanceof Error&&(e.name==="AbortError"||e.name==="ExternalAbortError"||e.message==="aborted")}async function Qs(e,t,n,s={}){var a;if(!n.enabled||!n.enabledChecks.headings)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=q(n);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const i=o.client,c=K(n.costCapUsd),r=[],d=[],l=Math.max(1,n.maxCandidatesPerCheck??10),p=e.slice(0,l),f=p.length;for(let m=0;m<p.length;m++){const h=p[m];if(!c.canCharge())break;try{le(s.signal);const u=await i.judgeHeading({headingText:h.text,sectionContent:h.sectionContent,signal:s.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.6&&r.push(await ue("ai-heading-not-descriptive","wcag246","AA","moderate","AI judged this heading does not describe its section content.",h.selector,h.outerHTML,h.text,u,t))}catch(u){if(de(u)){d.push(`canceled after ${m}/${f} candidates`);break}const w=u instanceof Error?u.message:String(u);d.push(`${h.selector}: ${w}`),ce.warn("heading judgment failed",u)}(a=s.onProgress)==null||a.call(s,m+1,f)}return{findings:r,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:d}}async function Zs(e,t,n,s={}){var a;if(!n.enabled||!n.enabledChecks.sensory)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=q(n);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const i=o.client,c=K(n.costCapUsd),r=[],d=[],l=Math.max(1,n.maxCandidatesPerCheck??10),p=e.slice(0,l),f=p.length;for(let m=0;m<p.length;m++){const h=p[m];if(!c.canCharge())break;try{le(s.signal);const u=await i.judgeSensoryLanguage({instructionText:h.text,signal:s.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.6&&r.push(await ue("ai-sensory-instruction","wcag133","A","serious","AI flagged this instruction as relying on sensory characteristics (shape, color, location) without a programmatic identifier.",h.selector,h.outerHTML,h.text.slice(0,100),u,t))}catch(u){if(de(u)){d.push(`canceled after ${m}/${f} candidates`);break}const w=u instanceof Error?u.message:String(u);d.push(`${h.selector}: ${w}`),ce.warn("sensory judgment failed",u)}(a=s.onProgress)==null||a.call(s,m+1,f)}return{findings:r,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:d}}async function eo(e,t,n,s={}){var a;if(!n.enabled||!n.enabledChecks.aria)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=q(n);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const i=o.client,c=K(n.costCapUsd),r=[],d=[],l=Math.max(1,n.maxCandidatesPerCheck??10),p=e.slice(0,l),f=p.length;for(let m=0;m<p.length;m++){const h=p[m];if(!c.canCharge())break;try{le(s.signal);const u=await i.judgeAriaSemantics({elementHtml:h.outerHTML,computedRole:h.role,surroundingHtml:h.surroundingHtml,signal:s.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.65&&r.push(await ue("ai-aria-misuse","wcag412","A","serious",`AI judged role="${h.role}" is being used inappropriately on this element.`,h.selector,h.outerHTML,h.role,u,t))}catch(u){if(de(u)){d.push(`canceled after ${m}/${f} candidates`);break}const w=u instanceof Error?u.message:String(u);d.push(`${h.selector}: ${w}`),ce.warn("aria judgment failed",u)}(a=s.onProgress)==null||a.call(s,m+1,f)}return{findings:r,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:d}}async function to(e,t,n,s={}){var a;if(!n.enabled||!n.enabledChecks.genericLinkText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=q(n);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const i=o.client,c=K(n.costCapUsd),r=[],d=[],l=Math.max(1,n.maxCandidatesPerCheck??10),p=e.slice(0,l),f=p.length;for(let m=0;m<p.length;m++){const h=p[m];if(!c.canCharge())break;try{le(s.signal);const u=await i.judgeGenericLinkText({linkText:h.linkText,surroundingText:h.surroundingText,signal:s.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.6&&r.push(await ue("ai-generic-link-text","wcag244","A","serious","AI judged this link's purpose is not clear from its text or surrounding context.",h.selector,h.outerHTML,h.linkText,u,t))}catch(u){if(de(u)){d.push(`canceled after ${m}/${f} candidates`);break}const w=u instanceof Error?u.message:String(u);d.push(`${h.selector}: ${w}`),ce.warn("generic-link judgment failed",u)}(a=s.onProgress)==null||a.call(s,m+1,f)}return{findings:r,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:d}}async function no(e,t,n,s={}){var a;if(!n.enabled||!n.enabledChecks.wallOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=q(n);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const i=o.client,c=K(n.costCapUsd),r=[],d=[],l=Math.max(1,n.maxCandidatesPerCheck??10),p=e.slice(0,l),f=p.length;for(let m=0;m<p.length;m++){const h=p[m];if(!c.canCharge())break;try{le(s.signal);const u=await i.judgeWallOfText({blockText:h.blockText,structuralChildrenCount:h.structuralChildrenCount,signal:s.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.6&&r.push(await ue("ai-wall-of-text","wcag315","AAA","moderate","AI flagged this block as a wall of text with insufficient structural breaks (subheadings, lists, paragraphs).",h.selector,h.outerHTML,h.blockText.slice(0,80),u,t))}catch(u){if(de(u)){d.push(`canceled after ${m}/${f} candidates`);break}const w=u instanceof Error?u.message:String(u);d.push(`${h.selector}: ${w}`),ce.warn("wall-of-text judgment failed",u)}(a=s.onProgress)==null||a.call(s,m+1,f)}return{findings:r,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:d}}async function ao(e,t,n,s={}){var a;if(!n.enabled||!n.enabledChecks.languageMismatch)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=q(n);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const i=o.client,c=K(n.costCapUsd),r=[],d=[],l=Math.max(1,n.maxCandidatesPerCheck??10),p=e.slice(0,l),f=p.length;for(let m=0;m<p.length;m++){const h=p[m];if(!c.canCharge())break;try{le(s.signal);const u=await i.judgeLanguageMismatch({declaredLang:h.declaredLang,bodyTextSample:h.bodyTextSample,signal:s.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.7&&r.push(await ue("ai-language-mismatch","wcag311","A","serious",`AI judged the page's content does not match the declared lang="${h.declaredLang||"(none)"}".`,h.selector,h.outerHTML,h.bodyTextSample.slice(0,80),u,t))}catch(u){if(de(u)){d.push(`canceled after ${m}/${f} candidates`);break}const w=u instanceof Error?u.message:String(u);d.push(`${h.selector}: ${w}`),ce.warn("language-mismatch judgment failed",u)}(a=s.onProgress)==null||a.call(s,m+1,f)}return{findings:r,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:d}}async function ue(e,t,n,s,o,i,c,r,d,l){const p={selector:i,outerHTML:c.slice(0,500),failureSummary:`AI assessment: ${d.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:r.slice(0,100)||null,attrId:null,attrTestid:null},f=await Qe({ruleId:e,componentId:l.componentId,currentState:l.currentState,target:p});return{ruleId:e,wcagCriterion:t,wcagLevel:n,impact:s,description:o,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:p,componentId:l.componentId,currentState:l.currentState,axeVersion:l.axeVersion,detectedAt:new Date().toISOString(),matchKey:f,ai:{reasoning:d.reasoning,confidence:d.confidence,model:d.model,costUsd:d.costUsd}}}const sn=W("ai-vision");function on(e){if(e!=null&&e.aborted){const t=new Error("aborted");throw t.name="AbortError",t}}function rn(e){return e instanceof Error&&(e.name==="AbortError"||e.name==="ExternalAbortError"||e.message==="aborted")}async function so(e,t,n,s={}){var a;if(!n.enabled||!n.enabledChecks.imageOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=q(n);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${o.reason}`]};const i=o.client,c=K(n.costCapUsd),r=Math.max(1,n.imageOfTextMaxImages??5),d=[...e].sort((m,h)=>h.pixelArea-m.pixelArea).slice(0,r),l=d.length,p=[],f=[];for(let m=0;m<d.length;m++){const h=d[m];if(!c.canCharge())break;try{on(s.signal);const u=await i.judgeImageOfText({imageUrl:h.imageUrl,accessibleName:h.accessibleName,signal:s.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.65&&p.push(await cn("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.",h.selector,h.outerHTML,h.accessibleName??"",u,t))}catch(u){if(rn(u)){f.push(`canceled after ${m}/${l} candidates`);break}const w=u instanceof Error?u.message:String(u);f.push(`${h.selector}: ${w}`),sn.warn("image-of-text judgment failed",u)}(a=s.onProgress)==null||a.call(s,m+1,l)}return{findings:p,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:f}}async function oo(e,t,n,s={}){var a;if(!n.enabled||!n.enabledChecks.colorOnlyMeaning)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const o=q(n);if(!o.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[o.reason]};const i=o.client,c=K(n.costCapUsd),r=[],d=[],l=Math.max(1,n.maxCandidatesPerCheck??10),p=e.slice(0,l),f=p.length;for(let m=0;m<p.length;m++){const h=p[m];if(!c.canCharge())break;try{on(s.signal);const u=await i.judgeColorOnlyMeaning({elementHtml:h.elementHtml,contextDescription:h.contextDescription,signal:s.signal});c.recordCharge(u.costUsd),u.verdict==="fail"&&u.confidence>=.65&&r.push(await cn("ai-color-only-meaning","wcag141","A","serious","AI judged this region conveys information only through color (no text label, icon glyph, or shape difference).",h.selector,h.outerHTML,h.contextDescription,u,t))}catch(u){if(rn(u)){d.push(`canceled after ${m}/${f} candidates`);break}const w=u instanceof Error?u.message:String(u);d.push(w),sn.warn("color-only judgment failed",u)}(a=s.onProgress)==null||a.call(s,m+1,f)}return{findings:r,totalCostUsd:c.state.spentUsd,capExceeded:c.state.exceeded,errors:d}}async function cn(e,t,n,s,o,i,c,r,d,l){const p={selector:i,outerHTML:c.slice(0,500),failureSummary:`AI assessment: ${d.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:r.slice(0,100)||null,attrId:null,attrTestid:null},f=await Qe({ruleId:e,componentId:l.componentId,currentState:l.currentState,target:p});return{ruleId:e,wcagCriterion:t,wcagLevel:n,impact:s,description:o,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:p,componentId:l.componentId,currentState:l.currentState,axeVersion:l.axeVersion,detectedAt:new Date().toISOString(),matchKey:f,ai:{reasoning:d.reasoning,confidence:d.confidence,model:d.model,costUsd:d.costUsd}}}const Xe=W("ensure-content-script"),ro=1500,io=300;async function ln(e,t){if(await Et(e,t))return{ok:!0};const n=chrome.runtime.getManifest(),s=[];for(const o of n.content_scripts??[])Array.isArray(o.js)&&s.push(...o.js);if(s.length===0)return Xe.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:s})}catch(o){return co(o)}return await lo(io),await Et(e,t)?(Xe.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 Et(e,t){try{const n=t!==void 0?{frameId:t}:void 0,s=await Promise.race([chrome.tabs.sendMessage(e,{type:"PING_REQUEST"},n),new Promise((o,i)=>setTimeout(()=>i(new Error("ping timeout")),ro))]);return(s==null?void 0:s.type)==="PING_RESPONSE"}catch{return!1}}function co(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}:(Xe.warn("content-script injection failed",e),{ok:!1,reason:"inject-failed",detail:t})}function lo(e){return new Promise(t=>setTimeout(t,e))}function dn(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 je=W("forensic-anchor-client"),uo="https://api.wcagcheckr.com",po="wcagcheckr",ho=15e3;function fo(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 go(e,t){const n={auditHash:e.hash,pageUrl:e.pageUrl,capturedAt:e.capturedAt,...t?{licenseId:t}:{}},s=`${uo}/v1/products/${po}/forensic/anchor`;try{const o=await fetch(s,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n),signal:AbortSignal.timeout(ho)});if(!o.ok)return je.warn(`anchor HTTP ${o.status}; entry stays local-only`),null;const i=await o.json();return fo(i)?i:(je.warn("anchor returned malformed receipt; entry stays local-only",i),null)}catch(o){return je.warn("anchor request failed; entry stays local-only",o),null}}const $t="4.11.4";async function mo(e){try{return(await chrome.tabs.get(e)).url??""}catch{return""}}const I=W("flows");async function Ue(){const e=await Ze();if(!e)throw new Error("no audit target tab found");return e}async function yo(){var n,s,o,i,c;const e=await $e("stateMatrix",ze);return((n=e.pseudoStates)==null?void 0:n.length)>0&&((s=e.themes)==null?void 0:s.length)>0&&((o=e.directions)==null?void 0:o.length)>0&&((i=e.breakpoints)==null?void 0:i.length)>0&&((c=e.breakpointPresets)==null?void 0:c.length)>0?e:(I.warn("stored stateMatrix is invalid; using defaults"),ze)}async function bo(){const e=await $e("runsPerState",1);return typeof e!="number"||e<1||e>5?1:Math.floor(e)}async function wo(e,t,n){const s=t.themes.includes("light"),o=t.themes.includes("dark");if(!s||!o)return t;let i=null;try{i=await j(e,{type:"THEME_AWARENESS_REQUEST"},n)}catch{return t}return!i||i.hasUnreadableSheets?t:i.hasDarkModeCss&&!i.hasLightModeCss?(I.info("theme-axis prune: site is dark-only, skipping light pass"),{...t,themes:t.themes.filter(c=>c!=="light")}):i.hasLightModeCss&&!i.hasDarkModeCss?(I.info("theme-axis prune: site is light-only, skipping dark pass"),{...t,themes:t.themes.filter(c=>c!=="dark")}):t}function vo(e){if(e.length<=1)return e[0]??[];const t=new Map;return e.forEach((n,s)=>{for(const o of n){const i=`${o.ruleId}::${o.target.selector}`,c=t.get(i);c?c.runIndices.add(s):t.set(i,{v:o,runIndices:new Set([s])})}}),Array.from(t.values()).map(({v:n,runIndices:s})=>({...n,flakyAcrossRuns:s.size<e.length}))}async function be(e,t,n,s){var M;const o=await Ue(),i=await ln(o,n);if(!i.ok){D({type:"AUDIT_FAILED_EVENT",error:{code:i.reason==="restricted-url"?"RESTRICTED_URL":"CONTENT_SCRIPT_UNREACHABLE",message:dn(i),recoverable:i.reason!=="restricted-url"}});return}const c=s??await yo(),r=await wo(o,c,n),d=Ks(r),l=await bo(),p=await _n(`${await mo(o)}|${e}|${n??0}`),f=await Mn(r),a=await $e("axeDisabledRules",[]),m=await Un(a);let h=null;try{h=(await j(o,{type:"SCOPE_FINGERPRINT_REQUEST",selector:e},n)).fingerprint}catch(b){I.debug("scope fingerprint failed; skipping cache fast-path",b)}if(h){const b=await Pn(p);if(b&&b.fingerprint===h&&b.axeVersion===$t&&b.matrixConfigHash===f&&b.disabledRulesHash===m){I.info(`audit-cache HIT for ${b.componentId} — skipping matrix iteration`),D({type:"AUDIT_COMPLETE_EVENT",componentId:b.componentId,results:b.results,delta:b.delta}),D({type:"SCORECARD_UPDATED_EVENT"});return}}await Ie({sessionId:Re(),mode:"single-element",scope:e,startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"}),await ee(o,{type:"LIVE_REGIONS_ARM_REQUEST"},n).catch(()=>{}),await ee(o,{type:"FOCUS_TRACE_ARM_REQUEST"},n).catch(()=>{}),await j(o,{type:"WARMUP_REQUEST"},n).catch(()=>{});const u=[],w=[];let R="",x,E=!1,T=null;try{for(let b=0;b<d.length;b++){if(t.isCanceled()){I.info("single audit canceled by user");break}const O=d[b];try{const U=await Be(o,O,e,n);if(!U.success){x=(M=U.error)==null?void 0:M.message,I.warn(`state ${b+1}/${d.length} drive failed`,x);continue}O.breakpoint.id!==T&&(await j(o,{type:"WARMUP_REQUEST"},n).catch(()=>{}),T=O.breakpoint.id);const k=[];let v=null;for(let C=0;C<l;C++){const A=await j(o,{type:"AUDIT_REQUEST",selector:e,currentState:O},n);A.success&&A.result?(k.push(A.result.violations),v=A.result):A.error&&(x=A.error.message,I.warn(`state ${b+1}/${d.length} run ${C+1} failed`,x))}if(v&&k.length>0){E=!0;const C=vo(k),A=await ea(o,{quality:75})??void 0;let g,S;try{g=(await j(o,{type:"LIVE_REGIONS_DRAIN_REQUEST"},n)).announcements}catch{}try{S=(await j(o,{type:"FOCUS_TRACE_DRAIN_REQUEST"},n)).focusEvents}catch{}w.push({...v,violations:C,frameId:n,announcements:g,focusEvents:S,screenshotDataUrl:A}),u.push(...C),R=v.componentId}}catch(U){x=U instanceof Error?U.message:String(U),I.warn(`state ${b+1}/${d.length} threw`,U)}D({type:"AUDIT_PROGRESS_EVENT",current:b+1,total:d.length,currentState:O,componentId:R||void 0}),await Oe({})}if(E&&R){const b={pseudoState:"default",ariaVariation:null,theme:"light",direction:"ltr",breakpoint:{id:"desktop",label:"Desktop",width:1280,height:800,deviceScaleFactor:1,mobile:!1}};try{await Be(o,b,e,n),await new Promise(v=>setTimeout(v,200))}catch(v){I.debug("reference-state drive failed; analyzers will use last matrix state",v)}try{const v=await j(o,{type:"READING_ORDER_REQUEST",selector:e},n);v.issues.length>0&&w[0]&&(w[0].readingOrderIssues=v.issues,w[0].positionAnalysisCapturedAt=b)}catch(v){I.debug("reading-order analysis skipped",v)}try{const v=await j(o,{type:"TAB_ORDER_ANALYZE_REQUEST"},n);v.issues.length>0&&w[0]&&(w[0].tabOrderIssues=v.issues,w[0].positionAnalysisCapturedAt=b)}catch(v){I.debug("tab-order analysis skipped",v)}try{const v=await j(o,{type:"TYPOGRAPHY_ANALYZE_REQUEST",selector:e},n);v.issues.length>0&&w[0]&&(w[0].typographyIssues=v.issues)}catch(v){I.debug("typography analysis skipped",v)}try{const v=await j(o,{type:"CUSTOM_PROPERTY_REQUEST"},n);v.undefined.length>0&&w[0]&&(w[0].undefinedCustomProperties=v.undefined)}catch(v){I.debug("custom-property analysis skipped",v)}try{const v=await $e("aiConfig",{}),C=et(v);C.enabled&&C.apiKey&&await No(o,e,n,C,w,R,t)}catch(v){I.warn("ai augmentation skipped",v)}const O=w.flatMap(v=>v.announcements??[]),U=w.flatMap(v=>v.focusEvents??[]),k=await Bt(R,u,{announcements:O,focusEvents:U},c);try{const v=ne(u,void 0,w),C=w.reduce((g,S)=>g+S.durationMs,0),A=await En(w,v,C);if(A){const g=await Pt();if(Rn(g,"forensicAnchoring"))try{const S=await $n(),$=await go(A,S??void 0);$&&await Tn(A.componentId,A.capturedAt,$)}catch(S){I.warn("forensic anchoring failed (entry stays local-only)",S)}}}catch(v){I.warn("forensic log recording failed",v)}if(h)try{await Dn({lookupKey:p,fingerprint:h,axeVersion:$t,matrixConfigHash:f,disabledRulesHash:m,results:w,delta:k,componentId:R,cachedAt:Date.now()})}catch(v){I.warn("audit-cache write failed",v)}D({type:"AUDIT_COMPLETE_EVENT",componentId:R,results:w,delta:k})}else D({type:"AUDIT_FAILED_EVENT",error:{code:"AXE_FAILED",message:x?`All audits failed. Last error: ${x}`:"No state produced results. Selector may not exist in the audit target frame.",recoverable:!0}});await me()}catch(b){I.error("single audit failed",b),D({type:"AUDIT_FAILED_EVENT",componentId:R||void 0,error:vn(b)?b.payload:{code:"UNKNOWN",message:String(b),recoverable:!1}})}finally{await ee(o,{type:"LIVE_REGIONS_DISARM_REQUEST"},n).catch(()=>{}),await ee(o,{type:"FOCUS_TRACE_DISARM_REQUEST"},n).catch(()=>{}),await jt(o,e,n)}}async function Ao(e){var t;try{const s=(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(s)return{detected:!0,version:(t=s.result)==null?void 0:t.version,frameId:s.frameId}}catch(n){I.warn("storybook detection scripting failed",n)}return{detected:!1}}async function xo(e){const t=await Ue(),n=await Ro(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 s=n.frameId??0,o=n.kind==="storybook"?await ko(t,s):await To(t,s);if(!o.length){I.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 Ie({sessionId:Re(),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()){I.info(`${n.kind} iteration canceled by user`);break}try{if(n.kind==="storybook"){await Eo(t,s,i.id);const r=await ee(t,{type:"STORYBOOK_GET_STORY_TARGET_REQUEST",tabId:t},s);await be(r.selector,e,s)}else await Co(t,s,i.id),await be("#ladle-root",e,s)}catch(r){I.warn(`story ${i.id} failed; continuing with next`,r)}const c=await Dt();if((c==null?void 0:c.state)==="interrupted"){I.info("storybook iteration interrupted");break}await Oe({completedStories:[...(c==null?void 0:c.completedStories)??[],i.id]})}D({type:"SCORECARD_UPDATED_EVENT"}),await me()}async function So(e){const t=await Ue();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(s){I.warn("multi-frame discovery failed",s)}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 Ie({sessionId:Re(),mode:"all-frames",totalFrames:n.length,completedFrames:[],startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});for(const s of n){if(e.isCanceled()){I.info("all-frames iteration canceled");break}try{await be("html",e,s.frameId)}catch(i){I.warn(`frame ${s.frameId} (${s.url.slice(0,80)}) audit failed; continuing`,i)}const o=await Dt();if((o==null?void 0:o.state)==="interrupted"){I.info("all-frames iteration interrupted");break}await Oe({completedFrames:[...(o==null?void 0:o.completedFrames)??[],s.frameId]})}D({type:"SCORECARD_UPDATED_EVENT"}),await me()}async function ko(e,t){var n;try{return((n=(await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",func:async()=>{var d,l;const o=window.__STORYBOOK_PREVIEW__;if(!o)return[];const i=o.storyStoreValue??o.storyStore;if(!i)return[];typeof i.cacheAllCSFFiles=="function"&&await i.cacheAllCSFFiles();const c=((d=i.extract)==null?void 0:d.call(i))??((l=i.cachedCSFFiles)==null?void 0:l.call(i));if(!c)return[];const r=[];if(c instanceof Map)for(const[p,f]of c){const a=f;r.push({id:String(p),name:a.name??String(p),kind:a.title??a.kind??""})}else if(typeof c=="object"&&c!==null)for(const[p,f]of Object.entries(c)){const a=f;r.push({id:p,name:a.name??p,kind:a.title??a.kind??""})}return r}}))[0])==null?void 0:n.result)??[]}catch(s){return I.warn("listStoriesViaMainWorld failed",s),[]}}async function Eo(e,t,n){try{await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",args:[n,3e3],func:async(s,o)=>{const i=new URL(window.location.href);i.searchParams.set("id",s),i.searchParams.set("viewMode","story"),window.location.href!==i.href&&(window.history.pushState({},"",i.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(c=>{const r=setTimeout(()=>c(),o),d=()=>{clearTimeout(r),c()};window.addEventListener("storyrendered",d,{once:!0})})}})}catch(s){I.warn(`navigateToStoryViaMainWorld(${n}) failed`,s)}}async function $o(e){var t;try{const s=(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{const o=document.getElementById("ladle-root")!==null,i=document.title==="Ladle",c=!!document.querySelector('link[href*="ladle.css"]');return{detected:o&&(i||c),version:void 0}}})).find(o=>{var i;return(i=o.result)==null?void 0:i.detected});if(s)return{detected:!0,version:(t=s.result)==null?void 0:t.version,frameId:s.frameId}}catch(n){I.warn("ladle detection scripting failed",n)}return{detected:!1}}async function To(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 c=(await o.json()).stories??{};return Object.entries(c).map(([r,d])=>({id:r,name:d.name??r,kind:(d.levels??[]).join(" / ")}))}catch{return[]}}}))[0])==null?void 0:n.result)??[]}catch(s){return I.warn("listLadleStoriesViaMainWorld failed",s),[]}}async function Co(e,t,n){try{await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",args:[n,3e3],func:async(s,o)=>{const i=new URL(window.location.href);i.searchParams.set("story",s),window.location.href!==i.href&&(window.history.pushState({},"",i.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(c=>setTimeout(c,Math.min(o,500)))}})}catch(s){I.warn(`navigateToLadleStoryViaMainWorld(${n}) failed`,s)}}async function Io(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 Ro(e){const t=await Ao(e);if(t.detected)return{kind:"storybook",...t};const n=await $o(e);return n.detected?{kind:"ladle",...n}:await Io(e)?{kind:"bit",detected:!0}:{kind:null,detected:!1}}const Oo=200,_o=6e4;function Tt(e,t){try{return new URL(e).origin===new URL(t).origin}catch{return!1}}function Mo(e,t){try{const n=new URL(e,t);return n.hash="",n.toString()}catch{return null}}async function Uo(e,t,n){await chrome.tabs.update(e,{url:t}),await new Promise((s,o)=>{const i=setTimeout(()=>{chrome.tabs.onUpdated.removeListener(c),o(new Error(`navigation timeout (${n}ms): ${t}`))},n);function c(r,d){r===e&&d.status==="complete"&&(clearTimeout(i),chrome.tabs.onUpdated.removeListener(c),s())}chrome.tabs.onUpdated.addListener(c)}),await new Promise(s=>setTimeout(s,400))}async function Po(e){var t;try{return((t=(await chrome.scripting.executeScript({target:{tabId:e},func:()=>Array.from(document.querySelectorAll("a[href]")).map(s=>s.getAttribute("href")).filter(s=>typeof s=="string"&&!s.startsWith("javascript:"))}))[0])==null?void 0:t.result)??[]}catch(n){return I.debug("link discovery failed",n),[]}}async function Do(e,t,n){var a;const s=await Ue(),o=Math.min(t.maxPages??25,Oo),i=t.includeRegex?Ct(t.includeRegex):null,c=t.excludeRegex?Ct(t.excludeRegex):null,r=new Date().toISOString(),d=Date.now(),l=[e],p=new Set,f=[];await Ie({sessionId:Re(),mode:"storybook-all",scope:e,startedAt:r,lastProgressAt:new Date().toISOString(),state:"running"});try{for(;l.length>0&&p.size<o;){if(n.isCanceled()){I.info("site crawl canceled by user");break}const w=l.shift();if(!w||p.has(w)||!Tt(w,e)||i&&!i.test(w)||c&&c.test(w))continue;p.add(w);const R=p.size,x=Date.now();D({type:"SITE_CRAWL_PROGRESS_EVENT",current:R,total:Math.min(o,p.size+l.length),url:w,status:"auditing"});try{await Uo(s,w,_o);const E=await ln(s,0);if(!E.ok)throw new Error(dn(E));await j(s,{type:"WARMUP_REQUEST"},0).catch(()=>{});const T=await j(s,{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()-x;if(T.success&&T.result)f.push({url:w,results:[T.result],delta:null,componentId:T.result.componentId,durationMs:M}),D({type:"SITE_CRAWL_PROGRESS_EVENT",current:R,total:Math.min(o,p.size+l.length),url:w,status:"completed",violations:T.result.violations.length});else{const O=((a=T.error)==null?void 0:a.message)??"audit returned no result";f.push({url:w,results:[],delta:null,componentId:w,durationMs:M,error:O}),D({type:"SITE_CRAWL_PROGRESS_EVENT",current:R,total:Math.min(o,p.size+l.length),url:w,status:"failed",error:O})}const b=await Po(s);for(const O of b){const U=Mo(O,w);U&&Tt(U,e)&&!p.has(U)&&!l.includes(U)&&l.push(U)}}catch(E){const T=Date.now()-x,M=E instanceof Error?E.message:String(E);f.push({url:w,results:[],delta:null,componentId:w,durationMs:T,error:M}),D({type:"SITE_CRAWL_PROGRESS_EVENT",current:R,total:Math.min(o,p.size+l.length),url:w,status:"failed",error:M})}await Oe({})}const m=new Date().toISOString(),h=Date.now()-d,u=qs(e,f,r,m,h);D({type:"SITE_CRAWL_COMPLETE_EVENT",report:u}),await me()}catch(m){I.error("site crawl failed",m),D({type:"SITE_CRAWL_FAILED_EVENT",error:{code:"UNKNOWN",message:String(m),recoverable:!1}}),await me()}}function Ct(e){try{return new RegExp(e)}catch{return null}}const Ve=5*6e4,It=9e4,Rt={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 No(e,t,n,s,o,i,c){let r;try{r=await j(e,{type:"AI_CANDIDATES_REQUEST",selector:t},n)}catch(A){I.debug("ai candidates fetch failed",A);return}const d=o[0];if(!d)return;const l={componentId:i,currentState:d.state,axeVersion:d.axeVersion},p={at:new Date().toISOString(),componentId:i,pageUrl:d.pageUrl??t,enabledChecks:s.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"}};I.info("ai augmentation gate snapshot",JSON.stringify(p));try{await chrome.storage.local.set({aiDiagnosticLatest:p})}catch(A){I.warn("failed to persist ai diagnostic to storage.local",A)}try{const A=JSON.stringify(p,null,2),g=`data:application/json;charset=utf-8,${encodeURIComponent(A)}`,S=p.at.replace(/[:.]/g,"-"),$=await chrome.downloads.download({url:g,filename:`wcagcheckr-ai-diag-${S}.json`,saveAs:!1,conflictAction:"uniquify"});I.info(`ai diagnostic download started (id=${$})`)}catch(A){I.warn("failed to write ai diagnostic to Downloads (storage.local copy is still available under aiDiagnosticLatest)",A)}const f=[];if(s.enabledChecks.altText&&f.push({key:"altText",run:A=>{const g=r.images.map(S=>({imageUrl:S.imageUrl,alt:S.alt,selector:S.selector,outerHTML:S.outerHTML,surroundingContext:S.surroundingContext,...l}));return Js(g,s,A)}}),s.enabledChecks.headings&&f.push({key:"headings",run:A=>Qs(r.headings,l,s,A)}),s.enabledChecks.sensory&&f.push({key:"sensory",run:A=>Zs(r.instructions,l,s,A)}),s.enabledChecks.aria&&f.push({key:"aria",run:A=>eo(r.ariaElements,l,s,A)}),s.enabledChecks.imageOfText&&f.push({key:"imageOfText",run:A=>{const g=r.images.map(S=>({imageUrl:S.imageUrl,accessibleName:S.alt||void 0,selector:S.selector,outerHTML:S.outerHTML,pixelArea:S.pixelArea}));return so(g,l,s,A)}}),s.enabledChecks.colorOnlyMeaning&&f.push({key:"colorOnlyMeaning",run:A=>oo(r.colorOnlyRegions,l,s,A)}),s.enabledChecks.genericLinkText&&f.push({key:"genericLinkText",run:A=>to(r.links,l,s,A)}),s.enabledChecks.wallOfText&&f.push({key:"wallOfText",run:A=>no(r.longTextBlocks,l,s,A)}),s.enabledChecks.languageMismatch){const A=r.languageInfo;f.push({key:"languageMismatch",run:g=>ao(A?[A]:[],l,s,g)})}if(f.length===0)return;let a=0,m=0,h=!1;const u={};let w=0,R=0,x=0;const E=[];let T=!1,M=!1;function b(A,g){if(u[A]=(u[A]??0)+g.totalCostUsd,a+=g.totalCostUsd,m+=g.findings.length,h=h||g.capExceeded,w++,g.errors.length>0){x++;for(const S of g.errors)E.includes(S)||E.push(S)}else R++;I.info(`ai ${A}: ${g.findings.length} findings, $${g.totalCostUsd.toFixed(4)}, errs=${g.errors.length}`),g.capExceeded&&I.warn(`ai cost cap exceeded after $${g.totalCostUsd.toFixed(4)} during ${A}`)}const O=(async()=>{for(let A=0;A<f.length;A++){if(c!=null&&c.isCanceled()){M=!0;break}const g=f[A];D({type:"AI_AUGMENTATION_PROGRESS_EVENT",componentId:i,currentCheck:g.key,currentCheckLabel:Rt[g.key],current:A+1,total:f.length});const S=new AbortController,$=setTimeout(()=>S.abort(),It),N=c?setInterval(()=>{c.isCanceled()&&S.abort()},500):null;let P;try{P=await g.run({signal:S.signal,onProgress:(_,L)=>{L>0&&D({type:"AI_AUGMENTATION_PROGRESS_EVENT",componentId:i,currentCheck:g.key,currentCheckLabel:Rt[g.key],current:A+1,total:f.length,candidatesDone:_,candidatesTotal:L})}}),S.signal.aborted&&(c!=null&&c.isCanceled())?P.errors=[...P.errors,`${g.key} canceled by user`]:S.signal.aborted}catch(_){const L=_ instanceof Error?_.message:String(_),G=S.signal.aborted,pe=G&&(c!=null&&c.isCanceled())?`${g.key} canceled by user`:G?`${g.key} hit per-check ${It/1e3}s cap — analyzer aborted`:`${g.key} threw: ${L}`;P={findings:[],totalCostUsd:0,capExceeded:!1,errors:[pe]}}finally{clearTimeout($),N&&clearInterval(N)}P.findings.length>0&&(d.violations=[...d.violations,...P.findings]),b(g.key,P)}})(),U=new Promise(A=>{setTimeout(()=>A("timeout"),Ve)});await Promise.race([O.then(()=>"done"),U])==="timeout"&&(T=!0,I.warn(`ai augmentation hit global ${Ve/1e3}s timeout after ${w}/${f.length} checks`));let v;if(x>0||T||M){T?v=`AI augmentation timed out after ${Ve/1e3}s`:M?v="AI augmentation canceled":v=E[0]??"AI analyzer error";const A=R===0?"total":"partial";D({type:"AI_AUGMENTATION_FAILED_EVENT",componentId:i,severity:A,reason:v,checksAttempted:w,checksSucceeded:R,checksErrored:x,errorDetails:T||M?[v,...E.slice(0,4)]:E.slice(0,5)}),I.warn(`ai augmentation ${A} failure: ${x}/${w} checks errored — ${v}`)}(a>0||m>0||v)&&await Nn({at:new Date().toISOString(),pageUrl:d.pageUrl??t,componentId:i,totalCostUsd:a,byCheck:u,findingsCount:m,capExceeded:h,failureReason:v})}const V=W("service-worker");mn("service-worker");_a();var Ae,Ot;(Ot=(Ae=chrome.sidePanel)==null?void 0:Ae.setPanelBehavior)==null||Ot.call(Ae,{openPanelOnActionClick:!0}).catch(e=>V.warn("setPanelBehavior failed",e));ta();ga();Cn();ha().catch(e=>V.warn("initial cloud pull failed",e));(async()=>(await Ia(),Kt()))();Ra();Pa();Os();Gs();Vs();ls();self.addEventListener("unhandledrejection",e=>{const t=e.reason;yn(t instanceof Error?t:new Error(String(t)))});let Ce=!1;chrome.runtime.onConnect.addListener(e=>{if(e.name==="audit-keepalive"){V.debug("keepalive connected"),e.onDisconnect.addListener(async()=>{V.debug("keepalive disconnected"),await In()});return}if(e.name==="sidepanel-tracker"){Ce=!0,V.debug("chrome.sidePanel opened"),e.onDisconnect.addListener(async()=>{Ce=!1,V.debug("chrome.sidePanel closed — restoring in-page overlay"),await rt()});return}});let ot=!1;const ve={isCanceled:()=>ot};async function un(){const e=await Ze();if(e)try{await ee(e,{type:"SIDEBAR_HIDE_REQUEST"})}catch(t){V.warn("sidebar hide failed",t)}}async function rt(){const e=await Ze();if(e)try{await ee(e,{type:"SIDEBAR_SHOW_REQUEST"})}catch(t){V.warn("sidebar show failed",t)}}F("START_AUDIT",async e=>{ot=!1,await un();let t;if(e.mode==="single-element"){if(!e.scope)throw new Error("START_AUDIT single-element requires scope");t=be(e.scope,ve,e.frameId,e.matrixOverride)}else e.mode==="full-page"?t=be("html",ve,0,e.matrixOverride):e.mode==="all-frames"?t=So(ve):t=xo(ve);t.catch(n=>{V.error("audit failed",n);const s=n instanceof Error?n.message:String(n),o=/no audit target tab/i.test(s);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: ${s}`,recoverable:!0}})}).finally(()=>{Ce||rt()})});F("CANCEL_AUDIT",async()=>{ot=!0,V.info("cancel requested")});let it=!1;const Lo={isCanceled:()=>it};F("START_SITE_CRAWL",async e=>{it=!1,await un(),Do(e.startUrl,{maxPages:e.maxPages,includeRegex:e.includeRegex,excludeRegex:e.excludeRegex},Lo).catch(t=>V.error("site crawl failed",t)).finally(()=>{Ce||rt()})});F("CANCEL_SITE_CRAWL",async()=>{it=!0,V.info("site crawl cancel requested")});F("OPEN_SETTINGS",()=>{chrome.runtime.openOptionsPage()});V.info("service worker ready");
|