@wcag-checkr/ci 1.0.0-rc.15 → 1.0.0-rc.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{ErrorBoundary-BPz4qckm.js → ErrorBoundary-DUrFJm2h.js} +3 -3
- package/dist/assets/{ai-usage-log-DFkwAfmW.js → ai-usage-log-MwwSLQII.js} +1 -1
- package/dist/assets/{content-script.ts-D7yXcBUr.js → content-script.ts-D0-ItOIg.js} +1 -1
- package/dist/assets/{content-script.ts-loader-Cn8Y9Xod.js → content-script.ts-loader-B-Xa5S-H.js} +1 -1
- package/dist/assets/{crash-reporter-wxu43qbG.js → crash-reporter-Dc5lvxvY.js} +1 -1
- package/dist/assets/devtools-panel-CyMLmxwM.js +1 -0
- package/dist/assets/{forensic-log-B3iX62mE.js → forensic-log-h-BhJJva.js} +2 -2
- package/dist/assets/{main-CqDdt0Iq.js → options-BdQyLYSd.js} +1 -1
- package/dist/assets/{service-worker.ts-DaHvU8nE.js → service-worker.ts-BcYxqaRt.js} +68 -68
- package/dist/assets/side-panel-BCkOAc7F.js +1 -0
- package/dist/assets/{site-report-renderer-JH44v2hK.js → site-report-renderer-BxzO0FlE.js} +1 -1
- package/dist/assets/state-DFZV6oiO.js +1 -0
- package/dist/assets/{styles-kHMb1Lda.js → styles-C3k-Qle5.js} +1 -1
- package/dist/devtools/panel.html +6 -6
- package/dist/manifest.json +5 -5
- package/dist/options/options.html +7 -7
- package/dist/service-worker-loader.js +1 -1
- package/dist/side-panel/side-panel.html +9 -9
- package/package.json +1 -1
- package/dist/assets/devtools-panel-D2fL4guz.js +0 -1
- package/dist/assets/main-DyQfCbPM.js +0 -1
- package/dist/assets/options.html-jfjpxZBp.js +0 -1
- package/dist/assets/side-panel.html-DW1tssqQ.js +0 -1
- package/dist/assets/state-DnzwwNxZ.js +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import{c as G,o as N,b as D,r as Kt,g as Yt,s as Z,a as W,i as Jt,d as Xt}from"./crash-reporter-wxu43qbG.js";import{m as pe,d as Qt,T as Zt,a as Ze,c as Ne,i as en}from"./diff-D4sCAdXf.js";import{o as tn,l as et,a as ce,c as te,A as he,F as nn,g as wt,O as vt,b as xt,R as sn,d as At,s as Le,n as Ge,m as Fe,r as an,e as on,f as rn,h as fe,i as cn,j as We,k as ln,p as dn}from"./forensic-log-B3iX62mE.js";import{D as Re,i as un}from"./state-DnzwwNxZ.js";import{m as je,D as pn,a as hn}from"./ai-usage-log-DFkwAfmW.js";const fn=G("chrome-api"),gn="1.3";class mn{constructor(t){this.target=t}async attach(){await chrome.debugger.attach(this.target,gn)}async detach(){try{await chrome.debugger.detach(this.target)}catch(t){fn.debug("detach soft-failure",t)}}async send(t,s){return chrome.debugger.sendCommand(this.target,t,s)}}function yn(e){const t=e instanceof Error?e.message:String(e);return/Another debugger is already attached|Cannot attach to/i.test(t)}function bn(e){const t=e instanceof Error?e.message:String(e);return yn(e)?pe("DEBUGGER_BUSY","DevTools is open on this tab. Close it to run the audit.",!0,{raw:t}):pe("STATE_DRIVE_FAILED",t,!1)}function wn(e){const t=(s,a)=>e(s,a);return chrome.debugger.onDetach.addListener(t),()=>chrome.debugger.onDetach.removeListener(t)}const z=G("state-driver"),vn=60*1e3,Y=new Map,ge=new Map,oe=new Map,se=new Map,xn={default:[],hover:["hover"],focus:["focus"],"focus-visible":["focus","focus-visible"],active:["active"],disabled:[]};async function St(e){let t=Y.get(e);if(!t){t=new mn({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(s){try{await t.detach()}catch{}throw s}Y.set(e,t),oe.set(e,new Map),An(e),z.info("attached",{tabId:e})}return kn(e),t}function An(e){const t=(s,a,o)=>{var d,i;if(s.tabId!==e)return;const r=oe.get(e);if(r)if(a==="Runtime.executionContextCreated"){const u=o,c=u==null?void 0:u.context;c&&((d=c.auxData)!=null&&d.isDefault)&&((i=c.auxData)!=null&&i.frameId)&&r.set(c.auxData.frameId,c.id)}else if(a==="Runtime.executionContextDestroyed"){const u=o,c=u==null?void 0:u.executionContextId;if(c!==void 0)for(const[l,p]of r.entries())p===c&&r.delete(l)}else a==="Runtime.executionContextsCleared"&&r.clear()};chrome.debugger.onEvent.addListener(t)}async function Sn(e){const t=Y.get(e);if(Y.delete(e),oe.delete(e),se.delete(e),t)try{await t.detach()}catch{}}function kn(e){const t=ge.get(e);t&&clearTimeout(t),ge.set(e,setTimeout(()=>{ze(e).catch(s=>z.warn("idle detach failed",s))},vn))}async function ze(e){const t=Y.get(e);t&&(await t.detach(),Y.delete(e)),oe.delete(e),se.delete(e);const s=ge.get(e);s&&(clearTimeout(s),ge.delete(e))}function kt(e){const t=[{id:e.frame.id,url:e.frame.url}];for(const s of e.childFrames??[])t.push(...kt(s));return t}async function $n(e,t,s){var r;if(!s||s===0)return;const a=oe.get(t);if(!a)return;let o;try{o=(r=(await chrome.scripting.executeScript({target:{tabId:t,frameIds:[s]},func:()=>location.href}))[0])==null?void 0:r.result}catch(d){z.debug("frame URL lookup failed",d)}try{const d=await e.send("Page.getFrameTree"),i=kt(d.frameTree);if(o){const l=i.find(p=>p.url===o);if(l){const p=a.get(l.id);if(p!==void 0)return p}}const u=d.frameTree.frame.id,c=i.find(l=>l.id!==u);return c?a.get(c.id):void 0}catch(d){z.warn("Page.getFrameTree failed",d);return}}async function En(e,t,s){var d;const a={expression:`document.querySelector(${JSON.stringify(t)})`,objectGroup:"wcag-auditor",returnByValue:!1};s!==void 0&&(a.contextId=s);const r=(d=(await e.send("Runtime.evaluate",a)).result)==null?void 0:d.objectId;if(!r)return null;try{return(await e.send("DOM.requestNode",{objectId:r})).nodeId??null}finally{e.send("Runtime.releaseObject",{objectId:r}).catch(()=>{})}}async function $t(e){const t=se.get(e);if(!t)return;const s=Y.get(e);if(s){try{await s.send("CSS.forcePseudoState",{nodeId:t.nodeId,forcedPseudoClasses:[]})}catch(a){z.debug("forcePseudoState clear failed (node may be gone)",a)}se.delete(e)}}async function Oe(e,t,s,a){await chrome.scripting.executeScript({target:Ve(e,t),func:(o,r)=>{const d=document.querySelector(o);d&&(r?d.setAttribute("disabled",""):d.removeAttribute("disabled"))},args:[s,a]})}async function Et(e,t){await chrome.scripting.executeScript({target:Ve(e,t),func:()=>{const s=document.activeElement;s&&typeof s.blur=="function"&&s.blur()}})}async function Tt(e,t){await chrome.scripting.executeScript({target:{tabId:e,frameIds:[0]},func:s=>{document.documentElement.setAttribute("dir",s)},args:[t]})}async function Tn(e,t,s,a){await chrome.scripting.executeScript({target:Ve(e,t),func:(o,r)=>{const d=document.querySelector(o);if(d)for(const[i,u]of Object.entries(r))d.setAttribute(i,u)},args:[s,a]})}function Ve(e,t){return t!==void 0&&t!==0?{tabId:e,frameIds:[t]}:{tabId:e}}async function Cn(e,t,s,a){const o=await St(e);if(await $t(e),s){t.pseudoState==="disabled"?await Oe(e,a,s,!0):(await Oe(e,a,s,!1),await Et(e,a));const d=xn[t.pseudoState];if(d.length>0){const i=await $n(o,e,a),u=await En(o,s,i);if(u!==null)try{await o.send("CSS.forcePseudoState",{nodeId:u,forcedPseudoClasses:d}),se.set(e,{sessionTabId:e,nodeId:u})}catch(c){z.warn(`CSS.forcePseudoState failed for :${t.pseudoState}`,c)}else z.debug(`could not resolve nodeId for ${s} (frameId=${a})`)}}const r=[{name:"prefers-reduced-motion",value:"reduce"}];t.theme==="dark"?(r.push({name:"prefers-color-scheme",value:"dark"}),r.push({name:"forced-colors",value:"none"})):t.theme==="light"?(r.push({name:"prefers-color-scheme",value:"light"}),r.push({name:"forced-colors",value:"none"})):t.theme==="forced-colors-active"?r.push({name:"forced-colors",value:"active"}):t.theme==="forced-colors-none"&&r.push({name:"forced-colors",value:"none"}),await o.send("Emulation.setEmulatedMedia",{features:r}),await new Promise(d=>setTimeout(d,50)),await Tt(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&&s&&await Tn(e,a,s,t.ariaVariation.attributes)}async function In(e,t,s){const a=Y.get(e);if(a){if(await $t(e),t)try{await Oe(e,s,t,!1),await Et(e,s),await Tt(e,"ltr")}catch(o){z.warn("element-level reset partial failure",o)}try{await a.send("Emulation.setEmulatedMedia",{features:[]}),await a.send("Emulation.clearDeviceMetricsOverride")}catch(o){z.warn("reset partial failure",o)}await ze(e)}}async function _e(e,t,s,a){try{return await Cn(e,t,s,a),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 Sn(e),{type:"STATE_DRIVE_RESPONSE",success:!1,error:bn(o)}}}async function Ct(e,t,s){await In(e,t,s)}async function Rn(e,t={}){try{const s=await St(e),a={format:"jpeg",quality:t.quality??75};t.fullPage&&(a.captureBeyondViewport=!0);const o=await s.send("Page.captureScreenshot",a);return o!=null&&o.data?`data:image/jpeg;base64,${o.data}`:null}catch(s){return z.debug("Page.captureScreenshot failed",s),null}}function On(){var s;const e=[];if(e.push(N("STATE_DRIVE_REQUEST",a=>_e(a.tabId,a.state,a.scope,a.frameId))),e.push(N("STATE_RESET_REQUEST",a=>Ct(a.tabId))),(s=chrome.tabs)!=null&&s.onRemoved){const a=o=>{ze(o).catch(()=>{})};chrome.tabs.onRemoved.addListener(a),e.push(()=>chrome.tabs.onRemoved.removeListener(a))}const t=wn((a,o)=>{a.tabId!==void 0&&(Y.delete(a.tabId),oe.delete(a.tabId),se.delete(a.tabId),z.warn("unexpected detach",{tabId:a.tabId,reason:o}))});return e.push(t),z.info("handlers registered"),()=>e.forEach(a=>a())}const _n="wcag-component-auditor",Mn=1;let Ae=null;function we(){return Ae||(Ae=tn(_n,Mn,{upgrade(e,t){if(t<1){const s=e.createObjectStore("baselines",{keyPath:"componentId"});s.createIndex("byUrl","snapshotMeta.url"),s.createIndex("byDate","lastUpdated");const a=e.createObjectStore("audit-history",{keyPath:"auditId",autoIncrement:!0});a.createIndex("byComponentId","componentId"),a.createIndex("byDate","runDate")}}})),Ae}async function He(e){return(await we()).get("baselines",e)}async function It(e){await(await we()).put("baselines",e)}async function Un(e){await(await we()).delete("baselines",e)}async function Dn(){return(await we()).getAll("baselines")}const ne=G("cloud-sync"),tt="cloudSyncEnabled",Se="cloudSyncEndpoint",ke="licenseToken",Me="cloudSyncVersionCache";async function qe(){try{const e=await chrome.storage.local.get([tt,Se,ke]),t=e[tt]===!0,s=typeof e[Se]=="string"?e[Se]:"",a=typeof e[ke]=="string"?e[ke]:"";return!t||!s||!a?null:{endpoint:s.replace(/\/$/,""),token:a,enabled:t}}catch{return null}}async function Rt(){try{const t=(await chrome.storage.local.get(Me))[Me];return t&&typeof t=="object"?t:{}}catch{return{}}}async function me(e){try{await chrome.storage.local.set({[Me]:e})}catch{}}async function Pn(e,t,s){const a=await qe();if(!a)return!1;const o=await Rt(),r=o[e];try{const d=`${a.endpoint}/v1/products/wcagcheckr/baselines/${encodeURIComponent(e)}`,i=await fetch(d,{method:"PUT",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a.token}`},body:JSON.stringify({snapshot:t,axeVersion:s,expectedVersion:r})});if(i.status===409)return ne.warn(`baseline ${e} version conflict on push`,{expectedVersion:r}),delete o[e],await me(o),!1;if(!i.ok)return ne.warn("baseline push failed",{status:i.status}),!1;const u=await i.json();return typeof u.version=="number"&&(o[e]=u.version,await me(o)),!0}catch(d){return ne.debug("push network failure (offline?)",d),!1}}async function Nn(e){const t=await qe();if(!t)return!1;try{const s=`${t.endpoint}/v1/products/wcagcheckr/baselines/${encodeURIComponent(e)}`,a=await fetch(s,{method:"DELETE",headers:{Authorization:`Bearer ${t.token}`}});if(a.ok){const o=await Rt();delete o[e],await me(o)}return a.ok}catch(s){return ne.debug("delete network failure",s),!1}}async function Ln(){const e=await qe();if(!e)return[];try{const t=await fetch(`${e.endpoint}/v1/products/wcagcheckr/baselines`,{headers:{Authorization:`Bearer ${e.token}`}});if(!t.ok)return ne.warn("pull failed",{status:t.status}),[];const a=(await t.json()).items??[],o={};for(const r of a)o[r.componentId]=r.version;return await me(o),a}catch(t){return ne.debug("pull network failure",t),[]}}const Ot=G("baseline-store"),nt="componentIdAliases";async function ae(e,t){try{return await t()}catch(s){const a=s instanceof Error?s.message:String(s);throw Ot.error(`baseline IDB ${e} failed: ${a}`),new Zt({code:"BASELINE_DB_ERROR",message:`Baseline storage is unavailable (${e}: ${a}).`,recoverable:!1,details:a})}}async function ve(e){return((await chrome.storage.local.get(nt))[nt]??{})[e]??e}async function Gn(e){const t=await ve(e),s=await ae("get",()=>He(t));return s?{violations:s.violations,snapshotMeta:s.snapshotMeta}:null}async function Fn(e,t,s,a){const o=await ve(e),r=await ae("get-for-set",()=>He(o)),d=(r==null?void 0:r.seenOnUrls)??[],i=d.includes(s.url)?d:[...d,s.url];await ae("set",()=>It({componentId:o,violations:t,snapshotMeta:s,lastUpdated:new Date().toISOString(),axeVersion:s.axeVersion,announcements:a==null?void 0:a.announcements,focusEvents:a==null?void 0:a.focusEvents,seenOnUrls:i})),Pn(o,{violations:t,snapshotMeta:s},s.axeVersion).catch(()=>{})}function Wn(e,t){if(!e||!t||Ze(e)===Ze(t))return;const s=r=>{var d,i,u,c,l;return(((d=r.pseudoStates)==null?void 0:d.length)??0)*Math.max(1,((i=r.ariaVariations)==null?void 0:i.length)??0)*(((u=r.themes)==null?void 0:u.length)??0)*(((c=r.directions)==null?void 0:c.length)??0)*(((l=r.breakpoints)==null?void 0:l.length)??0)},a=s(e),o=s(t);return`Baseline was captured under a different state-matrix configuration (${a} states) than this run (${o} states). Per-state-instance deltas inflate when the matrix changes — the unique-violation deltas above are still reliable.`}async function _t(e,t,s,a){var u;const o=await ve(e),r=await ae("compare",()=>He(o));if(!r)return{new:t,persistent:[],fixed:[],newCount:t.length,persistentCount:0,fixedCount:0,baselineSnapshotMeta:null,comparedAt:new Date().toISOString(),newAnnouncements:s==null?void 0:s.announcements,newFocusEvents:s==null?void 0:s.focusEvents};const d=Qt(r.violations,t,r.snapshotMeta,{baselineAnnouncements:r.announcements,currentAnnouncements:s==null?void 0:s.announcements,baselineFocusEvents:r.focusEvents,currentFocusEvents:s==null?void 0:s.focusEvents}),i=Wn((u=r.snapshotMeta)==null?void 0:u.matrixConfig,a);return i&&(d.matrixMismatchWarning=i),d}async function jn(e){const t=await ve(e);await ae("delete",()=>Un(t)),Nn(t).catch(()=>{})}async function zn(){const e=await Ln();for(const t of e)await It({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 Vn(e){let t=await ae("list",()=>Dn());return e!=null&&e.url&&(t=t.filter(s=>s.snapshotMeta.url===e.url)),t.map(s=>{const a=(s.focusEvents??[]).filter(c=>c.isFocusReset).length,o=(s.announcements??[]).length,r=s.violations.filter(c=>c.ruleId==="target-size").length,d=s.violations.some(c=>c.ruleId==="color-contrast"&&c.currentState.pseudoState==="hover"),i=s.violations.filter(c=>c.impact==="critical").length,u=s.violations.filter(c=>c.impact==="serious").length;return{componentId:s.componentId,violationCount:s.violations.length,lastUpdated:s.lastUpdated,seenOnUrlsCount:(s.seenOnUrls??[]).length,metrics:{criticalCount:i,seriousCount:u,focusResetCount:a,announcementCount:o,targetSizeFailCount:r,hoverContrastFail:d}}})}function Hn(){const e=[];return e.push(N("BASELINE_GET",async t=>({type:"BASELINE_GET_RESPONSE",baseline:await Gn(t.componentId)}))),e.push(N("BASELINE_SET",async t=>{await Fn(t.componentId,t.violations,t.snapshotMeta,{announcements:t.announcements,focusEvents:t.focusEvents}),D({type:"SCORECARD_UPDATED_EVENT"})})),e.push(N("BASELINE_COMPARE",async t=>({type:"BASELINE_COMPARE_RESPONSE",delta:await _t(t.componentId,t.currentViolations,{announcements:t.announcements,focusEvents:t.focusEvents},t.currentMatrix)}))),e.push(N("BASELINE_DELETE",async t=>{await jn(t.componentId),D({type:"SCORECARD_UPDATED_EVENT"})})),e.push(N("BASELINE_LIST",async t=>({type:"BASELINE_LIST_RESPONSE",items:await Vn(t.filter)}))),Ot.info("handlers registered"),()=>e.forEach(t=>t())}const qn=G("settings-store"),st=1,at="__schemaVersion",Ue={stateMatrix:Re,componentIdAliases:{},ignorePatterns:[]};async function Be(e,t){return(await chrome.storage.local.get(e))[e]??t}async function Bn(){const e=await chrome.storage.local.get();if(e[at]===st)return;const t={[at]:st};for(const[s,a]of Object.entries(Ue))s in e||(t[s]=a);await chrome.storage.local.set(t),qn.info("defaults ensured")}const Kn=["__","inflight:","license:","support:"];function Yn(e){return!Kn.some(t=>e.startsWith(t))}function Jn(){const e=[];return e.push(N("SETTINGS_GET",async t=>({type:"SETTINGS_RESPONSE",data:(await chrome.storage.local.get(t.key))[t.key]??Ue[t.key]}))),e.push(N("SETTINGS_SET",async t=>{await chrome.storage.local.set({[t.key]:t.value})})),e.push(N("SETTINGS_GET_ALL",async()=>{const t=await chrome.storage.local.get(),s=Object.fromEntries(Object.entries(t).filter(([a])=>Yn(a)));return{type:"SETTINGS_RESPONSE",data:{...Ue,...s}}})),()=>e.forEach(t=>t())}const Xn={input:3/1e6,output:15/1e6},Qn={input:1/1e6,output:5/1e6},Zn="claude-sonnet-4-6",es="https://api.anthropic.com/v1/messages",ts="2023-06-01";function ns(e){const t=e.model||Zn,s=t.includes("haiku")?Qn:Xn;return{providerId:"anthropic",async judgeAltText({imageUrl:a,alt:o,surroundingContext:r}){const d=await it(a);if(!d)return rt("Could not fetch image to verify alt text",0,t);const i=os(o,r),u=await V({apiKey:e.apiKey,model:t,maxTokens:300,messages:[{role:"user",content:[{type:"image",source:{type:"base64",media_type:d.mediaType,data:d.data}},{type:"text",text:i}]}]});return K(u,s)},async judgeHeading({headingText:a,sectionContent:o}){const r=await V({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:is(a,o)}]}]});return K(r,s)},async judgeSensoryLanguage({instructionText:a}){const o=await V({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:rs(a)}]}]});return K(o,s)},async judgeAriaSemantics({elementHtml:a,computedRole:o,surroundingHtml:r}){const d=await V({apiKey:e.apiKey,model:t,maxTokens:350,messages:[{role:"user",content:[{type:"text",text:cs(a,o,r)}]}]});return K(d,s)},async judgeImageOfText({imageUrl:a,accessibleName:o}){const r=await it(a);if(!r)return rt("Could not fetch image to inspect for embedded text",0,t);const d=await V({apiKey:e.apiKey,model:t,maxTokens:300,messages:[{role:"user",content:[{type:"image",source:{type:"base64",media_type:r.mediaType,data:r.data}},{type:"text",text:ls(o)}]}]});return K(d,s)},async judgeColorOnlyMeaning({elementHtml:a,contextDescription:o}){const r=await V({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:ds(a,o)}]}]});return K(r,s)},async judgeGenericLinkText({linkText:a,surroundingText:o}){const r=await V({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:us(a,o)}]}]});return K(r,s)},async judgeWallOfText({blockText:a,structuralChildrenCount:o}){const r=await V({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:ps(a,o)}]}]});return K(r,s)},async judgeLanguageMismatch({declaredLang:a,bodyTextSample:o}){const r=await V({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:hs(a,o)}]}]});return K(r,s)},async suggestColorFix(a){const o=await V({apiKey:e.apiKey,model:t,maxTokens:600,messages:[{role:"user",content:[{type:"text",text:fs(a)}]}]});return ms(o,s)},async generateExecutiveSummary(a){const o=await V({apiKey:e.apiKey,model:t,maxTokens:800,messages:[{role:"user",content:[{type:"text",text:gs(a)}]}]});return ys(o,s)}}}const ot=3e4,ss=1e4;async function V(e){var a;const t=new AbortController,s=setTimeout(()=>t.abort(),ot);try{const o=await fetch(es,{method:"POST",headers:{"x-api-key":e.apiKey,"anthropic-version":ts,"content-type":"application/json","anthropic-dangerous-direct-browser-access":"true"},body:JSON.stringify({model:e.model,max_tokens:e.maxTokens,messages:e.messages}),signal:t.signal});if(!o.ok){const d=((a=(await o.json().catch(()=>({}))).error)==null?void 0:a.message)??`${o.status} ${o.statusText}`;throw new Error(`Anthropic API: ${d}`)}return await o.json()}catch(o){throw o instanceof Error&&o.name==="AbortError"?new Error(`Anthropic API: request timed out after ${ot/1e3}s`):o}finally{clearTimeout(s)}}async function it(e){var a,o;const t=new AbortController,s=setTimeout(()=>t.abort(),ss);try{const r=await fetch(e,{signal:t.signal});if(!r.ok)return null;const d=((o=(a=r.headers.get("content-type"))==null?void 0:a.split(";")[0])==null?void 0:o.trim())??"image/png";if(!d.startsWith("image/"))return null;const i=await r.arrayBuffer();let u="";const c=new Uint8Array(i),l=32768;for(let p=0;p<c.length;p+=l)u+=String.fromCharCode(...c.slice(p,p+l));return{data:btoa(u),mediaType:d}}catch{return null}finally{clearTimeout(s)}}function K(e,t){var r;const s=((r=e.content.find(d=>d.type==="text"))==null?void 0:r.text)??"",a=as(s),o=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;return{verdict:a.verdict,reasoning:a.reasoning,confidence:a.confidence,costUsd:o,model:e.model}}function as(e){const s=e.replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/);if(!s)return{verdict:"uncertain",reasoning:"Model returned no parseable JSON",confidence:0};try{const a=JSON.parse(s[0]),o=a.verdict==="pass"||a.verdict==="fail"?a.verdict:"uncertain",r=(a.reasoning??"").trim().slice(0,600),d=typeof a.confidence=="number"?a.confidence:.5,i=Math.max(0,Math.min(1,d));return{verdict:o,reasoning:r,confidence:i}}catch{return{verdict:"uncertain",reasoning:"Model returned invalid JSON",confidence:0}}}function rt(e,t,s){return{verdict:"uncertain",reasoning:e,confidence:0,costUsd:t,model:s}}const J=`
|
|
1
|
+
import{c as G,o as N,e as D,f as tn,g as nn,b as Z,d as W,i as sn,h as an}from"./crash-reporter-Dc5lvxvY.js";import{m as he,d as on,T as rn,a as nt,c as Fe,i as cn}from"./diff-D4sCAdXf.js";import{o as ln,i as st,l as ce,h as te,A as fe,F as dn,k as kt,O as Et,j as $t,R as un,n as Tt,p as We,q as je,r as ze,t as pn,g as hn,u as fn,f as ge,e as gn,a as Ve,v as mn,w as yn}from"./forensic-log-h-BhJJva.js";import{s as pe,T as at,a as Oe,i as bn}from"./state-DFZV6oiO.js";import{m as He,D as wn,a as vn}from"./ai-usage-log-MwwSLQII.js";const xn=G("chrome-api"),An="1.3";class Sn{constructor(t){this.target=t}async attach(){await chrome.debugger.attach(this.target,An)}async detach(){try{await chrome.debugger.detach(this.target)}catch(t){xn.debug("detach soft-failure",t)}}async send(t,s){return chrome.debugger.sendCommand(this.target,t,s)}}function kn(e){const t=e instanceof Error?e.message:String(e);return/Another debugger is already attached|Cannot attach to/i.test(t)}function En(e){const t=e instanceof Error?e.message:String(e);return kn(e)?he("DEBUGGER_BUSY","DevTools is open on this tab. Close it to run the audit.",!0,{raw:t}):he("STATE_DRIVE_FAILED",t,!1)}function $n(e){const t=(s,a)=>e(s,a);return chrome.debugger.onDetach.addListener(t),()=>chrome.debugger.onDetach.removeListener(t)}const z=G("state-driver"),Tn=60*1e3,Y=new Map,me=new Map,oe=new Map,se=new Map,Cn={default:[],hover:["hover"],focus:["focus"],"focus-visible":["focus","focus-visible"],active:["active"],disabled:[]};async function Ct(e){let t=Y.get(e);if(!t){t=new Sn({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(s){try{await t.detach()}catch{}throw s}Y.set(e,t),oe.set(e,new Map),In(e),z.info("attached",{tabId:e})}return On(e),t}function In(e){const t=(s,a,o)=>{var d,i;if(s.tabId!==e)return;const r=oe.get(e);if(r)if(a==="Runtime.executionContextCreated"){const u=o,c=u==null?void 0:u.context;c&&((d=c.auxData)!=null&&d.isDefault)&&((i=c.auxData)!=null&&i.frameId)&&r.set(c.auxData.frameId,c.id)}else if(a==="Runtime.executionContextDestroyed"){const u=o,c=u==null?void 0:u.executionContextId;if(c!==void 0)for(const[l,p]of r.entries())p===c&&r.delete(l)}else a==="Runtime.executionContextsCleared"&&r.clear()};chrome.debugger.onEvent.addListener(t)}async function Rn(e){const t=Y.get(e);if(Y.delete(e),oe.delete(e),se.delete(e),t)try{await t.detach()}catch{}}function On(e){const t=me.get(e);t&&clearTimeout(t),me.set(e,setTimeout(()=>{qe(e).catch(s=>z.warn("idle detach failed",s))},Tn))}async function qe(e){const t=Y.get(e);t&&(await t.detach(),Y.delete(e)),oe.delete(e),se.delete(e);const s=me.get(e);s&&(clearTimeout(s),me.delete(e))}function It(e){const t=[{id:e.frame.id,url:e.frame.url}];for(const s of e.childFrames??[])t.push(...It(s));return t}async function _n(e,t,s){var r;if(!s||s===0)return;const a=oe.get(t);if(!a)return;let o;try{o=(r=(await chrome.scripting.executeScript({target:{tabId:t,frameIds:[s]},func:()=>location.href}))[0])==null?void 0:r.result}catch(d){z.debug("frame URL lookup failed",d)}try{const d=await e.send("Page.getFrameTree"),i=It(d.frameTree);if(o){const l=i.find(p=>p.url===o);if(l){const p=a.get(l.id);if(p!==void 0)return p}}const u=d.frameTree.frame.id,c=i.find(l=>l.id!==u);return c?a.get(c.id):void 0}catch(d){z.warn("Page.getFrameTree failed",d);return}}async function Mn(e,t,s){var d;const a={expression:`document.querySelector(${JSON.stringify(t)})`,objectGroup:"wcag-auditor",returnByValue:!1};s!==void 0&&(a.contextId=s);const r=(d=(await e.send("Runtime.evaluate",a)).result)==null?void 0:d.objectId;if(!r)return null;try{return(await e.send("DOM.requestNode",{objectId:r})).nodeId??null}finally{e.send("Runtime.releaseObject",{objectId:r}).catch(()=>{})}}async function Rt(e){const t=se.get(e);if(!t)return;const s=Y.get(e);if(s){try{await s.send("CSS.forcePseudoState",{nodeId:t.nodeId,forcedPseudoClasses:[]})}catch(a){z.debug("forcePseudoState clear failed (node may be gone)",a)}se.delete(e)}}async function _e(e,t,s,a){await chrome.scripting.executeScript({target:Be(e,t),func:(o,r)=>{const d=document.querySelector(o);d&&(r?d.setAttribute("disabled",""):d.removeAttribute("disabled"))},args:[s,a]})}async function Ot(e,t){await chrome.scripting.executeScript({target:Be(e,t),func:()=>{const s=document.activeElement;s&&typeof s.blur=="function"&&s.blur()}})}async function _t(e,t){await chrome.scripting.executeScript({target:{tabId:e,frameIds:[0]},func:s=>{document.documentElement.setAttribute("dir",s)},args:[t]})}async function Un(e,t,s,a){await chrome.scripting.executeScript({target:Be(e,t),func:(o,r)=>{const d=document.querySelector(o);if(d)for(const[i,u]of Object.entries(r))d.setAttribute(i,u)},args:[s,a]})}function Be(e,t){return t!==void 0&&t!==0?{tabId:e,frameIds:[t]}:{tabId:e}}async function Dn(e,t,s,a){const o=await Ct(e);if(await Rt(e),s){t.pseudoState==="disabled"?await _e(e,a,s,!0):(await _e(e,a,s,!1),await Ot(e,a));const d=Cn[t.pseudoState];if(d.length>0){const i=await _n(o,e,a),u=await Mn(o,s,i);if(u!==null)try{await o.send("CSS.forcePseudoState",{nodeId:u,forcedPseudoClasses:d}),se.set(e,{sessionTabId:e,nodeId:u})}catch(c){z.warn(`CSS.forcePseudoState failed for :${t.pseudoState}`,c)}else z.debug(`could not resolve nodeId for ${s} (frameId=${a})`)}}const r=[{name:"prefers-reduced-motion",value:"reduce"}];t.theme==="dark"?(r.push({name:"prefers-color-scheme",value:"dark"}),r.push({name:"forced-colors",value:"none"})):t.theme==="light"?(r.push({name:"prefers-color-scheme",value:"light"}),r.push({name:"forced-colors",value:"none"})):t.theme==="forced-colors-active"?r.push({name:"forced-colors",value:"active"}):t.theme==="forced-colors-none"&&r.push({name:"forced-colors",value:"none"}),await o.send("Emulation.setEmulatedMedia",{features:r}),await new Promise(d=>setTimeout(d,50)),await _t(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&&s&&await Un(e,a,s,t.ariaVariation.attributes)}async function Pn(e,t,s){const a=Y.get(e);if(a){if(await Rt(e),t)try{await _e(e,s,t,!1),await Ot(e,s),await _t(e,"ltr")}catch(o){z.warn("element-level reset partial failure",o)}try{await a.send("Emulation.setEmulatedMedia",{features:[]}),await a.send("Emulation.clearDeviceMetricsOverride")}catch(o){z.warn("reset partial failure",o)}await qe(e)}}async function Me(e,t,s,a){try{return await Dn(e,t,s,a),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 Rn(e),{type:"STATE_DRIVE_RESPONSE",success:!1,error:En(o)}}}async function Mt(e,t,s){await Pn(e,t,s)}async function Nn(e,t={}){try{const s=await Ct(e),a={format:"jpeg",quality:t.quality??75};t.fullPage&&(a.captureBeyondViewport=!0);const o=await s.send("Page.captureScreenshot",a);return o!=null&&o.data?`data:image/jpeg;base64,${o.data}`:null}catch(s){return z.debug("Page.captureScreenshot failed",s),null}}function Ln(){var s;const e=[];if(e.push(N("STATE_DRIVE_REQUEST",a=>Me(a.tabId,a.state,a.scope,a.frameId))),e.push(N("STATE_RESET_REQUEST",a=>Mt(a.tabId))),(s=chrome.tabs)!=null&&s.onRemoved){const a=o=>{qe(o).catch(()=>{})};chrome.tabs.onRemoved.addListener(a),e.push(()=>chrome.tabs.onRemoved.removeListener(a))}const t=$n((a,o)=>{a.tabId!==void 0&&(Y.delete(a.tabId),oe.delete(a.tabId),se.delete(a.tabId),z.warn("unexpected detach",{tabId:a.tabId,reason:o}))});return e.push(t),z.info("handlers registered"),()=>e.forEach(a=>a())}const Gn="wcag-component-auditor",Fn=1;let Se=null;function ve(){return Se||(Se=ln(Gn,Fn,{upgrade(e,t){if(t<1){const s=e.createObjectStore("baselines",{keyPath:"componentId"});s.createIndex("byUrl","snapshotMeta.url"),s.createIndex("byDate","lastUpdated");const a=e.createObjectStore("audit-history",{keyPath:"auditId",autoIncrement:!0});a.createIndex("byComponentId","componentId"),a.createIndex("byDate","runDate")}}})),Se}async function Ke(e){return(await ve()).get("baselines",e)}async function Ut(e){await(await ve()).put("baselines",e)}async function Wn(e){await(await ve()).delete("baselines",e)}async function jn(){return(await ve()).getAll("baselines")}const ne=G("cloud-sync"),ot="cloudSyncEnabled",ke="cloudSyncEndpoint",Ee="licenseToken",Ue="cloudSyncVersionCache";async function Ye(){try{const e=await chrome.storage.local.get([ot,ke,Ee]),t=e[ot]===!0,s=typeof e[ke]=="string"?e[ke]:"",a=typeof e[Ee]=="string"?e[Ee]:"";return!t||!s||!a?null:{endpoint:s.replace(/\/$/,""),token:a,enabled:t}}catch{return null}}async function Dt(){try{const t=(await chrome.storage.local.get(Ue))[Ue];return t&&typeof t=="object"?t:{}}catch{return{}}}async function ye(e){try{await chrome.storage.local.set({[Ue]:e})}catch{}}async function zn(e,t,s){const a=await Ye();if(!a)return!1;const o=await Dt(),r=o[e];try{const d=`${a.endpoint}/v1/products/wcagcheckr/baselines/${encodeURIComponent(e)}`,i=await fetch(d,{method:"PUT",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a.token}`},body:JSON.stringify({snapshot:t,axeVersion:s,expectedVersion:r})});if(i.status===409)return ne.warn(`baseline ${e} version conflict on push`,{expectedVersion:r}),delete o[e],await ye(o),!1;if(!i.ok)return ne.warn("baseline push failed",{status:i.status}),!1;const u=await i.json();return typeof u.version=="number"&&(o[e]=u.version,await ye(o)),!0}catch(d){return ne.debug("push network failure (offline?)",d),!1}}async function Vn(e){const t=await Ye();if(!t)return!1;try{const s=`${t.endpoint}/v1/products/wcagcheckr/baselines/${encodeURIComponent(e)}`,a=await fetch(s,{method:"DELETE",headers:{Authorization:`Bearer ${t.token}`}});if(a.ok){const o=await Dt();delete o[e],await ye(o)}return a.ok}catch(s){return ne.debug("delete network failure",s),!1}}async function Hn(){const e=await Ye();if(!e)return[];try{const t=await fetch(`${e.endpoint}/v1/products/wcagcheckr/baselines`,{headers:{Authorization:`Bearer ${e.token}`}});if(!t.ok)return ne.warn("pull failed",{status:t.status}),[];const a=(await t.json()).items??[],o={};for(const r of a)o[r.componentId]=r.version;return await ye(o),a}catch(t){return ne.debug("pull network failure",t),[]}}const Pt=G("baseline-store"),it="componentIdAliases";async function ae(e,t){try{return await t()}catch(s){const a=s instanceof Error?s.message:String(s);throw Pt.error(`baseline IDB ${e} failed: ${a}`),new rn({code:"BASELINE_DB_ERROR",message:`Baseline storage is unavailable (${e}: ${a}).`,recoverable:!1,details:a})}}async function xe(e){return((await chrome.storage.local.get(it))[it]??{})[e]??e}async function qn(e){const t=await xe(e),s=await ae("get",()=>Ke(t));return s?{violations:s.violations,snapshotMeta:s.snapshotMeta}:null}async function Bn(e,t,s,a){const o=await xe(e),r=await ae("get-for-set",()=>Ke(o)),d=(r==null?void 0:r.seenOnUrls)??[],i=d.includes(s.url)?d:[...d,s.url];await ae("set",()=>Ut({componentId:o,violations:t,snapshotMeta:s,lastUpdated:new Date().toISOString(),axeVersion:s.axeVersion,announcements:a==null?void 0:a.announcements,focusEvents:a==null?void 0:a.focusEvents,seenOnUrls:i})),zn(o,{violations:t,snapshotMeta:s},s.axeVersion).catch(()=>{})}function Kn(e,t){if(!e||!t||nt(e)===nt(t))return;const s=r=>{var d,i,u,c,l;return(((d=r.pseudoStates)==null?void 0:d.length)??0)*Math.max(1,((i=r.ariaVariations)==null?void 0:i.length)??0)*(((u=r.themes)==null?void 0:u.length)??0)*(((c=r.directions)==null?void 0:c.length)??0)*(((l=r.breakpoints)==null?void 0:l.length)??0)},a=s(e),o=s(t);return`Baseline was captured under a different state-matrix configuration (${a} states) than this run (${o} states). Per-state-instance deltas inflate when the matrix changes — the unique-violation deltas above are still reliable.`}async function Nt(e,t,s,a){var u;const o=await xe(e),r=await ae("compare",()=>Ke(o));if(!r)return{new:t,persistent:[],fixed:[],newCount:t.length,persistentCount:0,fixedCount:0,baselineSnapshotMeta:null,comparedAt:new Date().toISOString(),newAnnouncements:s==null?void 0:s.announcements,newFocusEvents:s==null?void 0:s.focusEvents};const d=on(r.violations,t,r.snapshotMeta,{baselineAnnouncements:r.announcements,currentAnnouncements:s==null?void 0:s.announcements,baselineFocusEvents:r.focusEvents,currentFocusEvents:s==null?void 0:s.focusEvents}),i=Kn((u=r.snapshotMeta)==null?void 0:u.matrixConfig,a);return i&&(d.matrixMismatchWarning=i),d}async function Yn(e){const t=await xe(e);await ae("delete",()=>Wn(t)),Vn(t).catch(()=>{})}async function Jn(){const e=await Hn();for(const t of e)await Ut({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 Xn(e){let t=await ae("list",()=>jn());return e!=null&&e.url&&(t=t.filter(s=>s.snapshotMeta.url===e.url)),t.map(s=>{const a=(s.focusEvents??[]).filter(c=>c.isFocusReset).length,o=(s.announcements??[]).length,r=s.violations.filter(c=>c.ruleId==="target-size").length,d=s.violations.some(c=>c.ruleId==="color-contrast"&&c.currentState.pseudoState==="hover"),i=s.violations.filter(c=>c.impact==="critical").length,u=s.violations.filter(c=>c.impact==="serious").length;return{componentId:s.componentId,violationCount:s.violations.length,lastUpdated:s.lastUpdated,seenOnUrlsCount:(s.seenOnUrls??[]).length,metrics:{criticalCount:i,seriousCount:u,focusResetCount:a,announcementCount:o,targetSizeFailCount:r,hoverContrastFail:d}}})}function Qn(){const e=[];return e.push(N("BASELINE_GET",async t=>({type:"BASELINE_GET_RESPONSE",baseline:await qn(t.componentId)}))),e.push(N("BASELINE_SET",async t=>{await Bn(t.componentId,t.violations,t.snapshotMeta,{announcements:t.announcements,focusEvents:t.focusEvents}),D({type:"SCORECARD_UPDATED_EVENT"})})),e.push(N("BASELINE_COMPARE",async t=>({type:"BASELINE_COMPARE_RESPONSE",delta:await Nt(t.componentId,t.currentViolations,{announcements:t.announcements,focusEvents:t.focusEvents},t.currentMatrix)}))),e.push(N("BASELINE_DELETE",async t=>{await Yn(t.componentId),D({type:"SCORECARD_UPDATED_EVENT"})})),e.push(N("BASELINE_LIST",async t=>({type:"BASELINE_LIST_RESPONSE",items:await Xn(t.filter)}))),Pt.info("handlers registered"),()=>e.forEach(t=>t())}const De="tierConfig:cache",Zn=60*60*1e3,es=24*60*60*1e3,ts=7*24*60*60*1e3;async function ns(){return(await chrome.storage.local.get(De))[De]??null}async function ss(e){await chrome.storage.local.set({[De]:e})}function as(e,t=Date.now()){const s=t-e.fetchedAt;return s<Zn?"fresh":s<es?"stale":s<ts?"grace":"expired"}function os(e,t){const s={...e},a=E=>{if(typeof E=="number")return E===-1?1/0:E},o=E=>typeof E=="boolean"?E:void 0,r=E=>{if(E==="all"||E==="configurable"||E==="default-only")return E},d=a(t.maxComponents);d!==void 0&&(s.maxComponents=d);const i=a(t.maxBaselines);i!==void 0&&(s.maxBaselines=i);const u=r(t.stateMatrix);u!==void 0&&(s.stateMatrix=u);const c=o(t.storybookAutoIterate);c!==void 0&&(s.storybookAutoIterate=c);const l=o(t.exportJson);l!==void 0&&(s.exportJson=l);const p=o(t.exportSarif);p!==void 0&&(s.exportSarif=p);const n=o(t.exportJunit);n!==void 0&&(s.exportJunit=n);const v=o(t.cloudSync);v!==void 0&&(s.cloudSync=v);const k=o(t.forensicAnchoring);return k!==void 0&&(s.forensicAnchoring=k),s}function is(e,t){var a;if(e==="trial")return((a=t.trial)==null?void 0:a.features)??null;const s=t.plans.find(o=>o.code===e||o.code.startsWith(e+"-"));return(s==null?void 0:s.features)??null}const Pe=G("tier-config-client"),rs="wcagcheckr",cs=`https://api.wcagcheckr.com/v1/products/${rs}/tier-config`,ls=1e4,rt="tier-config-refresh",ds=60,us=["trial","free","solo","team"];function Lt(e){const t={};for(const s of us){const a=is(s,e);t[s]=a?os(at[s],a):{...at[s]}}return t}async function ps(){const e=await ns();if(!e){pe(null);return}if(as(e)==="expired"){Pe.warn("cached tier-config is expired (>7d); using hardcoded defaults"),pe(null);return}pe(Lt(e.config))}async function Gt(){try{const e=await fetch(cs,{signal:AbortSignal.timeout(ls),headers:{accept:"application/json"}});if(!e.ok)throw new Error(`http ${e.status}`);const t=await e.json();await ss({fetchedAt:Date.now(),config:t}),pe(Lt(t)),Pe.info("tier-config refreshed from server")}catch(e){Pe.warn("tier-config refresh failed; keeping prior cache",e)}}function hs(){chrome.alarms.create(rt,{periodInMinutes:ds}),chrome.alarms.onAlarm.addListener(e=>{e.name===rt&&Gt()})}const fs=G("settings-store"),ct=1,lt="__schemaVersion",Ne={stateMatrix:Oe,componentIdAliases:{},ignorePatterns:[]};async function Je(e,t){return(await chrome.storage.local.get(e))[e]??t}async function gs(){const e=await chrome.storage.local.get();if(e[lt]===ct)return;const t={[lt]:ct};for(const[s,a]of Object.entries(Ne))s in e||(t[s]=a);await chrome.storage.local.set(t),fs.info("defaults ensured")}const ms=["__","inflight:","license:","support:"];function ys(e){return!ms.some(t=>e.startsWith(t))}function bs(){const e=[];return e.push(N("SETTINGS_GET",async t=>({type:"SETTINGS_RESPONSE",data:(await chrome.storage.local.get(t.key))[t.key]??Ne[t.key]}))),e.push(N("SETTINGS_SET",async t=>{await chrome.storage.local.set({[t.key]:t.value})})),e.push(N("SETTINGS_GET_ALL",async()=>{const t=await chrome.storage.local.get(),s=Object.fromEntries(Object.entries(t).filter(([a])=>ys(a)));return{type:"SETTINGS_RESPONSE",data:{...Ne,...s}}})),()=>e.forEach(t=>t())}const ws={input:3/1e6,output:15/1e6},vs={input:1/1e6,output:5/1e6},xs="claude-sonnet-4-6",As="https://api.anthropic.com/v1/messages",Ss="2023-06-01";function ks(e){const t=e.model||xs,s=t.includes("haiku")?vs:ws;return{providerId:"anthropic",async judgeAltText({imageUrl:a,alt:o,surroundingContext:r}){const d=await ut(a);if(!d)return pt("Could not fetch image to verify alt text",0,t);const i=Ts(o,r),u=await V({apiKey:e.apiKey,model:t,maxTokens:300,messages:[{role:"user",content:[{type:"image",source:{type:"base64",media_type:d.mediaType,data:d.data}},{type:"text",text:i}]}]});return K(u,s)},async judgeHeading({headingText:a,sectionContent:o}){const r=await V({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:Cs(a,o)}]}]});return K(r,s)},async judgeSensoryLanguage({instructionText:a}){const o=await V({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:Is(a)}]}]});return K(o,s)},async judgeAriaSemantics({elementHtml:a,computedRole:o,surroundingHtml:r}){const d=await V({apiKey:e.apiKey,model:t,maxTokens:350,messages:[{role:"user",content:[{type:"text",text:Rs(a,o,r)}]}]});return K(d,s)},async judgeImageOfText({imageUrl:a,accessibleName:o}){const r=await ut(a);if(!r)return pt("Could not fetch image to inspect for embedded text",0,t);const d=await V({apiKey:e.apiKey,model:t,maxTokens:300,messages:[{role:"user",content:[{type:"image",source:{type:"base64",media_type:r.mediaType,data:r.data}},{type:"text",text:Os(o)}]}]});return K(d,s)},async judgeColorOnlyMeaning({elementHtml:a,contextDescription:o}){const r=await V({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:_s(a,o)}]}]});return K(r,s)},async judgeGenericLinkText({linkText:a,surroundingText:o}){const r=await V({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:Ms(a,o)}]}]});return K(r,s)},async judgeWallOfText({blockText:a,structuralChildrenCount:o}){const r=await V({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:Us(a,o)}]}]});return K(r,s)},async judgeLanguageMismatch({declaredLang:a,bodyTextSample:o}){const r=await V({apiKey:e.apiKey,model:t,maxTokens:250,messages:[{role:"user",content:[{type:"text",text:Ds(a,o)}]}]});return K(r,s)},async suggestColorFix(a){const o=await V({apiKey:e.apiKey,model:t,maxTokens:600,messages:[{role:"user",content:[{type:"text",text:Ps(a)}]}]});return Ls(o,s)},async generateExecutiveSummary(a){const o=await V({apiKey:e.apiKey,model:t,maxTokens:800,messages:[{role:"user",content:[{type:"text",text:Ns(a)}]}]});return Gs(o,s)}}}const dt=3e4,Es=1e4;async function V(e){var a;const t=new AbortController,s=setTimeout(()=>t.abort(),dt);try{const o=await fetch(As,{method:"POST",headers:{"x-api-key":e.apiKey,"anthropic-version":Ss,"content-type":"application/json","anthropic-dangerous-direct-browser-access":"true"},body:JSON.stringify({model:e.model,max_tokens:e.maxTokens,messages:e.messages}),signal:t.signal});if(!o.ok){const d=((a=(await o.json().catch(()=>({}))).error)==null?void 0:a.message)??`${o.status} ${o.statusText}`;throw new Error(`Anthropic API: ${d}`)}return await o.json()}catch(o){throw o instanceof Error&&o.name==="AbortError"?new Error(`Anthropic API: request timed out after ${dt/1e3}s`):o}finally{clearTimeout(s)}}async function ut(e){var a,o;const t=new AbortController,s=setTimeout(()=>t.abort(),Es);try{const r=await fetch(e,{signal:t.signal});if(!r.ok)return null;const d=((o=(a=r.headers.get("content-type"))==null?void 0:a.split(";")[0])==null?void 0:o.trim())??"image/png";if(!d.startsWith("image/"))return null;const i=await r.arrayBuffer();let u="";const c=new Uint8Array(i),l=32768;for(let p=0;p<c.length;p+=l)u+=String.fromCharCode(...c.slice(p,p+l));return{data:btoa(u),mediaType:d}}catch{return null}finally{clearTimeout(s)}}function K(e,t){var r;const s=((r=e.content.find(d=>d.type==="text"))==null?void 0:r.text)??"",a=$s(s),o=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;return{verdict:a.verdict,reasoning:a.reasoning,confidence:a.confidence,costUsd:o,model:e.model}}function $s(e){const s=e.replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/);if(!s)return{verdict:"uncertain",reasoning:"Model returned no parseable JSON",confidence:0};try{const a=JSON.parse(s[0]),o=a.verdict==="pass"||a.verdict==="fail"?a.verdict:"uncertain",r=(a.reasoning??"").trim().slice(0,600),d=typeof a.confidence=="number"?a.confidence:.5,i=Math.max(0,Math.min(1,d));return{verdict:o,reasoning:r,confidence:i}}catch{return{verdict:"uncertain",reasoning:"Model returned invalid JSON",confidence:0}}}function pt(e,t,s){return{verdict:"uncertain",reasoning:e,confidence:0,costUsd:t,model:s}}const J=`
|
|
2
2
|
Respond ONLY with a single JSON object — no preamble, no markdown:
|
|
3
3
|
{ "verdict": "pass" | "fail" | "uncertain", "reasoning": "1-3 sentences", "confidence": 0.0-1.0 }
|
|
4
|
-
`.trim();function
|
|
4
|
+
`.trim();function Ts(e,t){return`You are an accessibility auditor verifying alt text correctness for screen-reader users (WCAG 1.1.1).
|
|
5
5
|
|
|
6
6
|
Image is attached. Current alt text: ${e?`"${e}"`:"(empty / missing)"}
|
|
7
7
|
${t?`
|
|
@@ -12,7 +12,7 @@ Judge whether the alt text accurately represents the image's content or function
|
|
|
12
12
|
- "fail" = alt is wrong, misleading, generic ("image", "photo"), filename-style ("img-1234.jpg"), or omitted on a meaningful image
|
|
13
13
|
- "uncertain" = you can't tell from the image alone whether the alt is appropriate
|
|
14
14
|
|
|
15
|
-
${J}`}function
|
|
15
|
+
${J}`}function Cs(e,t){return`You are an accessibility auditor verifying that headings describe their sections (WCAG 2.4.6).
|
|
16
16
|
|
|
17
17
|
Heading text: "${e}"
|
|
18
18
|
Section content beneath it (first 800 chars): "${t.slice(0,800)}"
|
|
@@ -22,7 +22,7 @@ Judge whether the heading actually describes what the section contains:
|
|
|
22
22
|
- "fail" = heading is non-descriptive ("Section 1", "Welcome", "More info"), unrelated to the content, or generic placeholder
|
|
23
23
|
- "uncertain" = section content is too short or ambiguous to judge
|
|
24
24
|
|
|
25
|
-
${J}`}function
|
|
25
|
+
${J}`}function Is(e){return`You are an accessibility auditor checking instructions for sensory-characteristic dependencies (WCAG 1.3.3).
|
|
26
26
|
|
|
27
27
|
Instruction text: "${e.slice(0,800)}"
|
|
28
28
|
|
|
@@ -31,7 +31,7 @@ WCAG 1.3.3 says instructions must NOT rely solely on shape, size, color, visual
|
|
|
31
31
|
- "pass" = instruction uses programmatic identifiers (button label, link text) — sensory cues are fine when accompanied by a name
|
|
32
32
|
- "uncertain" = ambiguous whether a sensory descriptor is the only cue
|
|
33
33
|
|
|
34
|
-
${J}`}function
|
|
34
|
+
${J}`}function Rs(e,t,s){return`You are an accessibility auditor verifying ARIA role appropriateness (WCAG 4.1.2).
|
|
35
35
|
|
|
36
36
|
Element (truncated): ${e.slice(0,600)}
|
|
37
37
|
Computed role: ${t}
|
|
@@ -42,7 +42,7 @@ Judge whether the role is semantically appropriate for this element's apparent p
|
|
|
42
42
|
- "fail" = role is misused — e.g., role="button" on a div with no event handlers, role="dialog" on a non-modal overlay, role="navigation" on a non-nav block
|
|
43
43
|
- "uncertain" = can't tell the element's true behavior from this static snapshot
|
|
44
44
|
|
|
45
|
-
${J}`}function
|
|
45
|
+
${J}`}function Os(e){return`You are an accessibility auditor checking for WCAG 1.4.5 (Images of Text) compliance.
|
|
46
46
|
|
|
47
47
|
WCAG 1.4.5 says: if text can be presented using actual text instead of a baked-in image, it should be. Exceptions: logos, decorative use, or text that's essential to a specific visual presentation (e.g., a screenshot of code).
|
|
48
48
|
|
|
@@ -54,7 +54,7 @@ Judge whether the image bakes substantial text into the raster:
|
|
|
54
54
|
- "pass" = the image is purely visual (photo, illustration, icon) with no substantial baked-in text, OR the baked-in text is a logo / brand mark / unavoidable visual artifact (code screenshot, equation, etc.).
|
|
55
55
|
- "uncertain" = the image is too small / low-resolution to tell, or the text is partial/ambiguous.
|
|
56
56
|
|
|
57
|
-
${J}`}function
|
|
57
|
+
${J}`}function _s(e,t){return`You are an accessibility auditor checking for WCAG 1.4.1 (Use of Color) compliance.
|
|
58
58
|
|
|
59
59
|
WCAG 1.4.1 says: color must not be the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element. There must always be a parallel non-color signal — a text label, an icon shape, a position cue, etc.
|
|
60
60
|
|
|
@@ -68,7 +68,7 @@ Judge whether the element conveys meaning ONLY through color:
|
|
|
68
68
|
- "pass" = the element has a visible text label, an icon child, an aria-label, OR the color is decorative — any non-color signal that a color-blind / grayscale user could detect.
|
|
69
69
|
- "uncertain" = markup is ambiguous (e.g. the contextDescription says "has icon child" but the icon may itself be color-only).
|
|
70
70
|
|
|
71
|
-
${J}`}function
|
|
71
|
+
${J}`}function Ms(e,t){return`You are an accessibility auditor checking for WCAG 2.4.4 (Link Purpose in Context).
|
|
72
72
|
|
|
73
73
|
WCAG 2.4.4 says: the purpose of each link must be determinable from the link text alone, OR from the link text together with its programmatically-determined context (the surrounding sentence / paragraph / list item).
|
|
74
74
|
|
|
@@ -80,7 +80,7 @@ Judge whether a screen-reader user activating "links list" navigation could unde
|
|
|
80
80
|
- "pass" = link text alone is descriptive, OR the surrounding context unambiguously clarifies the destination (e.g., "Pricing details are available — learn more →" makes "learn more" passable).
|
|
81
81
|
- "uncertain" = link text is borderline-generic and context is ambiguous.
|
|
82
82
|
|
|
83
|
-
${J}`}function
|
|
83
|
+
${J}`}function Us(e,t){return`You are an accessibility auditor checking for WCAG 3.1.5 (Reading Level) and broader cognitive-accessibility issues.
|
|
84
84
|
|
|
85
85
|
A "wall of text" is body content that exceeds reasonable read-without-fatigue size with no structural breaks (subheadings, lists, paragraphs, blockquotes). Cognitively-disabled users + speed-skim readers can't scan it.
|
|
86
86
|
|
|
@@ -92,7 +92,7 @@ Judge whether this block presents a wall-of-text problem:
|
|
|
92
92
|
- "pass" = block is short enough OR has adequate structural breaks (paragraphs, sub-headings, lists) for scannable reading.
|
|
93
93
|
- "uncertain" = block is borderline (~200-300 words with minimal structure).
|
|
94
94
|
|
|
95
|
-
${J}`}function
|
|
95
|
+
${J}`}function Ds(e,t){return`You are an accessibility auditor checking for WCAG 3.1.1 (Language of Page) and 3.1.2 (Language of Parts).
|
|
96
96
|
|
|
97
97
|
The page declares its default human language via the HTML \`lang\` attribute. Screen readers use that to pick the correct pronunciation engine. If the declared language doesn't match the actual content, screen-reader output is unintelligible.
|
|
98
98
|
|
|
@@ -104,7 +104,7 @@ Judge whether the declared language matches the content:
|
|
|
104
104
|
- "pass" = declared language matches the content's primary language.
|
|
105
105
|
- "uncertain" = content is too short, too multilingual, or in a language closely related to the declared one.
|
|
106
106
|
|
|
107
|
-
${J}`}function
|
|
107
|
+
${J}`}function Ps(e){const t=e.targetLevel==="AAA"?e.fontSize>=18.66||e.fontSize>=14&&e.fontWeight>=700?"4.5":"7.0":e.fontSize>=18.66||e.fontSize>=14&&e.fontWeight>=700?"3.0":"4.5";return`You are a color accessibility expert. Suggest replacement colors that fix a contrast failure while preserving design intent.
|
|
108
108
|
|
|
109
109
|
Current pair (FAILING):
|
|
110
110
|
- Foreground: ${e.foreground}
|
|
@@ -131,7 +131,7 @@ Respond ONLY with a JSON object — no preamble, no markdown:
|
|
|
131
131
|
{ "foreground": "#hexHEX", "background": "#hexHEX", "contrast": 4.6, "rationale": "1 sentence" }
|
|
132
132
|
],
|
|
133
133
|
"reasoning": "1-3 sentences explaining your overall approach or why no suggestions are possible"
|
|
134
|
-
}`}function
|
|
134
|
+
}`}function Ns(e){return`You are an accessibility auditor writing the executive summary for an automated audit report.
|
|
135
135
|
|
|
136
136
|
Audience: ${e.framing==="defense"?"an internal stakeholder team (engineering + legal + risk-management)":e.framing==="evidence"?"an attorney or accessibility advocate considering a notice-and-cure step":"a non-technical site owner who needs to understand their accessibility status"}.
|
|
137
137
|
Framing: ${e.framing} report.
|
|
@@ -162,31 +162,31 @@ Respond ONLY with a JSON object — no preamble, no markdown:
|
|
|
162
162
|
"lead": "2-3 sentence headline framing",
|
|
163
163
|
"body": "2-3 paragraphs of risk explanation + audit-date framing + ongoing-diligence context if applicable. ~150-300 words total.",
|
|
164
164
|
"closer": "1-2 sentence closing line"
|
|
165
|
-
}`}function
|
|
165
|
+
}`}function Ls(e,t){var d;const o=(((d=e.content.find(i=>i.type==="text"))==null?void 0:d.text)??"").replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/),r=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:r,model:e.model};try{const i=JSON.parse(o[0]),u=(i.candidates??[]).slice(0,3).map(c=>({foreground:typeof c.foreground=="string"?c.foreground:"",background:typeof c.background=="string"?c.background:"",contrast:typeof c.contrast=="number"?c.contrast:0,rationale:typeof c.rationale=="string"?c.rationale:""})).filter(c=>c.foreground&&c.background);return{verdict:i.verdict==="suggested"&&u.length>0?"suggested":"no-suggestion",candidates:u,reasoning:typeof i.reasoning=="string"?i.reasoning:"",costUsd:r,model:e.model}}catch{return{verdict:"error",candidates:[],reasoning:"Could not parse color suggestions",costUsd:r,model:e.model}}}function Gs(e,t){var d;const s=((d=e.content.find(i=>i.type==="text"))==null?void 0:d.text)??"",o=s.replace(/```(?:json)?/g,"").trim().match(/\{[\s\S]*\}/),r=e.usage.input_tokens*t.input+e.usage.output_tokens*t.output;if(!o)return{lead:"",body:s||"Summary generation failed.",closer:"",costUsd:r,model:e.model};try{const i=JSON.parse(o[0]);return{lead:typeof i.lead=="string"?i.lead:"",body:typeof i.body=="string"?i.body:"",closer:typeof i.closer=="string"?i.closer:"",costUsd:r,model:e.model}}catch{return{lead:"",body:s,closer:"",costUsd:r,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:ks({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 B(e){const t={capUsd:Math.max(0,e),spentUsd:0,exceeded:!1};return{state:t,canCharge(){return!t.exceeded&&t.spentUsd<t.capUsd},recordCharge(s){t.spentUsd+=Math.max(0,s),t.spentUsd>=t.capUsd&&(t.exceeded=!0)}}}const $e=G("ai-compliance-summarizer");async function Ft(e,t){if(!t.enabled)return null;const s=q(t);if(!s.ok)return $e.debug(`AI client unavailable: ${s.reason}`),null;const a=B(t.costCapUsd);if(!a.canCharge())return null;try{const o=await s.client.generateExecutiveSummary(e);return a.recordCharge(o.costUsd),!o.lead&&!o.body?($e.debug("AI summary returned empty"),null):o}catch(o){return $e.warn("compliance-summary generation failed",o),null}}async function Fs(){const e=await chrome.storage.local.get("aiConfig");return He(e.aiConfig)}function Ws(){return N("AI_COMPLIANCE_SUMMARY_REQUEST",async e=>{const t=await Fs();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 s=await Ft(e.input,t);return{type:"AI_COMPLIANCE_SUMMARY_RESPONSE",summary:s,unavailableReason:s?void 0:"AI summary generation returned no result."}})}const H="wcag-component-auditor";function ee(){try{return chrome.runtime.getManifest().version}catch{return"0.0.0"}}function X(e){const t=e[0];return t?t.pageUrl?t.pageUrl:t.scope&&/^https?:\/\//.test(t.scope)?t.scope:t.scope?`(selector: ${t.scope})`:"unknown":"unknown"}function js(e,t){return{schemaVersion:1,generatedAt:new Date().toISOString(),tool:{name:H,version:ee()},results:e,delta:t}}function ht(e){return e==="critical"||e==="serious"?"error":e==="moderate"?"warning":"note"}function zs(e,t){const s=t?t.new:e.flatMap(i=>i.violations),a=new Set,o=[];for(const i of s)a.has(i.ruleId)||(a.add(i.ruleId),o.push({id:i.ruleId,shortDescription:{text:i.description||i.ruleId},helpUri:i.helpUrl||void 0,defaultConfiguration:{level:ht(i.impact)},properties:{tags:[i.wcagCriterion,`wcag-${i.wcagLevel}`]}}));const r=X(e),d=s.map(i=>({ruleId:i.ruleId,level:ht(i.impact),message:{text:`${i.description}`+(i.target.failureSummary?`
|
|
166
166
|
|
|
167
|
-
${i.target.failureSummary}`:"")},locations:[{physicalLocation:{artifactLocation:{uri:r&&/^https?:\/\//.test(r)?`${r}#${i.target.selector}`:i.target.selector?`selector://${i.target.selector}`:"unknown"},region:{snippet:{text:i.target.outerHTML}}},logicalLocations:[{name:i.componentId,kind:"module"}]}],properties:{componentId:i.componentId,wcagCriterion:i.wcagCriterion,wcagLevel:i.wcagLevel,pseudoState:i.currentState.pseudoState,theme:i.currentState.theme,direction:i.currentState.direction,breakpoint:i.currentState.breakpoint.id,detectedAt:i.detectedAt}}));return{$schema:"https://json.schemastore.org/sarif-2.1.0.json",version:"2.1.0",runs:[{tool:{driver:{name:H,version:ee(),informationUri:"https://wcagcheckr.com",rules:o}},results:d}]}}function
|
|
168
|
-
WCAG: ${
|
|
167
|
+
${i.target.failureSummary}`:"")},locations:[{physicalLocation:{artifactLocation:{uri:r&&/^https?:\/\//.test(r)?`${r}#${i.target.selector}`:i.target.selector?`selector://${i.target.selector}`:"unknown"},region:{snippet:{text:i.target.outerHTML}}},logicalLocations:[{name:i.componentId,kind:"module"}]}],properties:{componentId:i.componentId,wcagCriterion:i.wcagCriterion,wcagLevel:i.wcagLevel,pseudoState:i.currentState.pseudoState,theme:i.currentState.theme,direction:i.currentState.direction,breakpoint:i.currentState.breakpoint.id,detectedAt:i.detectedAt}}));return{$schema:"https://json.schemastore.org/sarif-2.1.0.json",version:"2.1.0",runs:[{tool:{driver:{name:H,version:ee(),informationUri:"https://wcagcheckr.com",rules:o}},results:d}]}}function Wt(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function Q(e){return Wt(e).replace(/\n/g," ").replace(/\r/g," ")}function Vs(e,t){const s=!!t,a=c=>s?c.violations.filter(l=>t.new.some(p=>p.matchKey===l.matchKey)):c.violations,o=e.reduce((c,l)=>c+Math.max(1,l.violations.length),0),r=e.reduce((c,l)=>c+a(l).length,0),d=e.reduce((c,l)=>c+l.durationMs,0)/1e3,i=[];i.push('<?xml version="1.0" encoding="UTF-8"?>');const u=`${H} — ${X(e)}`;i.push(`<testsuites name="${Q(u)}" tests="${o}" failures="${r}" time="${d.toFixed(3)}">`);for(const c of e){const l=`${c.state.pseudoState}_${c.state.theme}_${c.state.direction}_${c.state.breakpoint.id}`,p=`${c.componentId}::${l}`,n=a(c),v=Math.max(1,c.violations.length);if(i.push(` <testsuite name="${Q(p)}" tests="${v}" failures="${n.length}" time="${(c.durationMs/1e3).toFixed(3)}">`),c.violations.length===0)i.push(` <testcase classname="${Q(p)}" name="no-violations" time="${(c.durationMs/1e3).toFixed(3)}" />`);else for(const k of c.violations){const E=n.some(S=>S.matchKey===k.matchKey),$=`classname="${Q(p)}" name="${Q(k.ruleId)}" time="0"`;if(!E){i.push(` <testcase ${$} />`);continue}const T=`${k.impact} - ${k.description}`,g=`Selector: ${k.target.selector}
|
|
168
|
+
WCAG: ${k.wcagCriterion} (${k.wcagLevel})
|
|
169
169
|
State: ${l}
|
|
170
|
-
`+(
|
|
171
|
-
`:"")+(
|
|
172
|
-
${
|
|
173
|
-
`)}function f(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function
|
|
174
|
-
<article class="violation impact-${f(y.impact)}${
|
|
170
|
+
`+(k.helpUrl?`Help: ${k.helpUrl}
|
|
171
|
+
`:"")+(k.target.failureSummary?`
|
|
172
|
+
${k.target.failureSummary}`:"");i.push(` <testcase ${$}>`),i.push(` <failure message="${Q(T)}" type="${Q(k.ruleId)}">${Wt(g)}</failure>`),i.push(" </testcase>")}i.push(" </testsuite>")}return i.push("</testsuites>"),i.join(`
|
|
173
|
+
`)}function f(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function Hs(e,t){var T,g,S,x,w;const s=((T=e[0])==null?void 0:T.componentId)??"unknown",a=X(e),o=e.reduce((y,C)=>y+C.durationMs,0),r=e.flatMap(y=>y.violations),d=new Map;for(const y of r){const C=`${y.ruleId}::${y.target.selector}`,m=`:${y.currentState.pseudoState} · ${y.currentState.theme} · ${y.currentState.direction}`,I=d.get(C);if(I){I._states.includes(m)||I._states.push(m);continue}d.set(C,{...y,_states:[m]})}const i=Array.from(d.values()),u={critical:[],serious:[],moderate:[],minor:[]};for(const y of i)(u[y.impact]??u.moderate).push(y);const c=new Set(((g=t==null?void 0:t.new)==null?void 0:g.map(y=>`${y.ruleId}::${y.target.selector}`))??[]),l=new Set(((S=t==null?void 0:t.persistent)==null?void 0:S.map(y=>`${y.ruleId}::${y.target.selector}`))??[]),p=new Set(((x=t==null?void 0:t.fixed)==null?void 0:x.map(y=>`${y.ruleId}::${y.target.selector}`))??[]);let n=0,v=0;for(const y of i){const C=`${y.ruleId}::${y.target.selector}`;l.has(C)?v++:c.has(C)&&n++}let k=0;for(const y of p)!l.has(y)&&!c.has(y)&&k++;const E=y=>{const C=c.has(`${y.ruleId}::${y.target.selector}`);return`
|
|
174
|
+
<article class="violation impact-${f(y.impact)}${C?" is-new":""}">
|
|
175
175
|
<header>
|
|
176
176
|
<span class="rule">${f(y.ruleId)}</span>
|
|
177
177
|
<span class="impact">${f(y.impact)}</span>
|
|
178
178
|
<span class="wcag">${f(y.wcagCriterion)} ${f(y.wcagLevel)}</span>
|
|
179
|
-
${
|
|
179
|
+
${C?'<span class="new-badge">NEW vs baseline</span>':""}
|
|
180
180
|
</header>
|
|
181
181
|
<p class="desc">${f(y.description)}</p>
|
|
182
182
|
<p class="meta"><strong>Selector:</strong> <code>${f(y.target.selector)}</code></p>
|
|
183
183
|
<p class="meta"><strong>Found in state(s):</strong> ${f(y._states.join(", "))}</p>
|
|
184
184
|
<pre class="snippet"><code>${f(y.target.outerHTML)}</code></pre>
|
|
185
185
|
${y.helpUrl?`<p class="help"><a href="${f(y.helpUrl)}">Reference: ${f(y.helpUrl)}</a></p>`:""}
|
|
186
|
-
</article>`}
|
|
186
|
+
</article>`},$=(y,C)=>C.length===0?"":`
|
|
187
187
|
<section class="impact-section impact-${f(y)}">
|
|
188
|
-
<h2>${f(y[0].toUpperCase()+y.slice(1))} (${
|
|
189
|
-
${
|
|
188
|
+
<h2>${f(y[0].toUpperCase()+y.slice(1))} (${C.length})</h2>
|
|
189
|
+
${C.map(E).join("")}
|
|
190
190
|
</section>`;return`<!doctype html>
|
|
191
191
|
<html lang="en">
|
|
192
192
|
<head>
|
|
@@ -267,7 +267,7 @@ ${E.target.failureSummary}`:"");i.push(` <testcase ${k}>`),i.push(` <fai
|
|
|
267
267
|
<p class="summary">
|
|
268
268
|
<span style="color: #dc2626;"><strong>NEW:</strong> ${n}</span>
|
|
269
269
|
<span><strong>Persistent:</strong> ${v}</span>
|
|
270
|
-
<span style="color: #15803d;"><strong>Fixed since baseline:</strong> ${
|
|
270
|
+
<span style="color: #15803d;"><strong>Fixed since baseline:</strong> ${k}</span>
|
|
271
271
|
</p>
|
|
272
272
|
<p class="summary" style="font-size: 9pt; color: #64748b;">
|
|
273
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.
|
|
@@ -277,21 +277,21 @@ ${E.target.failureSummary}`:"");i.push(` <testcase ${k}>`),i.push(` <fai
|
|
|
277
277
|
Tip: use your browser's print dialog (Ctrl+P / Cmd+P) → "Save as PDF" to produce a PDF file.
|
|
278
278
|
</p>
|
|
279
279
|
</header>
|
|
280
|
-
${
|
|
281
|
-
${
|
|
282
|
-
${
|
|
283
|
-
${
|
|
280
|
+
${$("critical",u.critical)}
|
|
281
|
+
${$("serious",u.serious)}
|
|
282
|
+
${$("moderate",u.moderate)}
|
|
283
|
+
${$("minor",u.minor)}
|
|
284
284
|
<footer>
|
|
285
285
|
Generated by ${H} v${ee()}. Full audit — all detected violations included, deduped across states. Items flagged "NEW vs baseline" were not present in the most-recent accepted baseline.
|
|
286
286
|
</footer>
|
|
287
287
|
</body>
|
|
288
|
-
</html>`}const
|
|
288
|
+
</html>`}const jt=[{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){if(e.rules.length===0)return{conformance:"Not Applicable",hits:[]};const s=e.rules.filter(a=>t.has(a));return{conformance:s.length>0?"Partially Supports":"Supports",hits:s}}function qs(e,t){var u,c;const s=((u=e[0])==null?void 0:u.componentId)??"unknown",a=X(e),o=e.flatMap(l=>l.violations),r=new Set(o.map(l=>l.ruleId)),d=l=>{const{conformance:p,hits:n}=zt(l,r);return`
|
|
289
289
|
<tr class="${p==="Supports"?"supports":p==="Partially Supports"?"partial":"na"}">
|
|
290
290
|
<td class="ref">${f(l.ref)}</td>
|
|
291
291
|
<td class="title">${f(l.title)} <span class="level">(${l.level})</span></td>
|
|
292
292
|
<td class="conf">${p}</td>
|
|
293
|
-
<td class="notes">${n.length>0?`Auto-detected violations of: ${n.map(
|
|
294
|
-
</tr>`},i=l=>{const p=
|
|
293
|
+
<td class="notes">${n.length>0?`Auto-detected violations of: ${n.map(k=>`<code>${f(k)}</code>`).join(", ")}. Manual review recommended.`:p==="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>`},i=l=>{const p=jt.filter(n=>n.level===l).map(d).join("");return`
|
|
295
295
|
<h2>WCAG 2.1 Level ${l}</h2>
|
|
296
296
|
<table>
|
|
297
297
|
<thead>
|
|
@@ -341,19 +341,19 @@ ${E.target.failureSummary}`:"");i.push(` <testcase ${k}>`),i.push(` <fai
|
|
|
341
341
|
Generated by ${H} v${ee()}. Conformance assessed against all violations detected during the audit (not delta-filtered).
|
|
342
342
|
</footer>
|
|
343
343
|
</body>
|
|
344
|
-
</html>`}function $s(e){const t=[],s=e.target.outerHTML;if(e.ruleId==="image-alt"||e.ruleId==="input-image-alt"||e.ruleId==="area-alt"||e.ruleId==="svg-img-alt"||e.ruleId==="object-alt"){const a=/\bsrc\s*=\s*"([^"]+)"|\bsrc\s*=\s*'([^']+)'/i.exec(s);a&&t.push({label:"Image source",value:`\`${a[1]??a[2]}\` — if you have vision capability, view this image and propose alt text. Use \`alt=""\` if it's decorative.`});const o=/<title>([^<]+)<\/title>/i.exec(s);o&&t.push({label:"Inline SVG <title>",value:o[1]})}if(e.ruleId==="button-name"||e.ruleId==="empty-button"||e.ruleId==="input-button-name"){const a=/\baria-label\s*=\s*"([^"]+)"/i.exec(s);a&&t.push({label:"Existing aria-label",value:a[1]});const o=/<svg[^>]*>[\s\S]*?<title>([^<]+)<\/title>/i.exec(s);o&&t.push({label:"SVG <title> inside",value:o[1]});const r=/<(?:i|svg|span)[^>]*class\s*=\s*"([^"]*(?:icon|fa-|material-icon)[^"]*)"/i.exec(s);r&&t.push({label:"Icon class hint",value:`\`${r[1]}\` — name suggests intent`})}if(e.ruleId==="link-name"||e.ruleId==="empty-link"){const a=/\bhref\s*=\s*"([^"]+)"|\bhref\s*=\s*'([^']+)'/i.exec(s);a&&t.push({label:"Link href",value:`\`${a[1]??a[2]}\` — destination may suggest the right link text`});const o=/\baria-label\s*=\s*"([^"]+)"/i.exec(s);o&&t.push({label:"Existing aria-label",value:o[1]})}if(e.ruleId==="label"||e.ruleId==="select-name"||e.ruleId==="aria-input-field-name"){const a=/\bname\s*=\s*"([^"]+)"|\bname\s*=\s*'([^']+)'/i.exec(s);a&&t.push({label:"Field name attribute",value:`\`${a[1]??a[2]}\` — often hints at the intended label`});const o=/\bplaceholder\s*=\s*"([^"]+)"|\bplaceholder\s*=\s*'([^']+)'/i.exec(s);o&&t.push({label:"Placeholder text (visible)",value:o[1]??o[2]});const r=/\btype\s*=\s*"([^"]+)"|\btype\s*=\s*'([^']+)'/i.exec(s);r&&t.push({label:"Input type",value:`\`${r[1]??r[2]}\``});const d=/\bautocomplete\s*=\s*"([^"]+)"|\bautocomplete\s*=\s*'([^']+)'/i.exec(s);d&&t.push({label:"Autocomplete",value:d[1]??d[2]})}return t}function Es(e,t,s,a,o){var y,T,m,C,I,U;const r=new Set(o??[]),d=((y=e[0])==null?void 0:y.componentId)??"unknown",i=X(e),u=!!(t&&t.baselineSnapshotMeta),c=e.flatMap(h=>h.violations),l=new Map;for(const h of c){const b=h.ruleId.startsWith("ai-")?`${h.ruleId}::${h.target.selector}::${(h.target.outerHTML??"").slice(0,200)}`:ce(h.ruleId,h.target.selector),A=`${h.currentState.pseudoState} · ${h.currentState.theme} · ${h.currentState.direction} · ${h.currentState.breakpoint.id}`,M=l.get(b);if(M){M._states.includes(A)||M._states.push(A),M._instanceSelectors.includes(h.target.selector)||M._instanceSelectors.push(h.target.selector);continue}l.set(b,{...h,_states:[A],_instanceSelectors:[h.target.selector]})}const p=Array.from(l.values()).sort((h,b)=>{const A={critical:0,serious:1,moderate:2,minor:3};return(A[h.impact]??99)-(A[b.impact]??99)}),n=[];n.push("# Accessibility Fix Request"),n.push(""),n.push(`You are a senior accessibility engineer pairing with the user on their codebase. The component below has ${p.length} unique WCAG violation${p.length===1?"":"s"} detected by an automated audit running axe-core ${((T=e[0])==null?void 0:T.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.`),n.push("");const v=te(c);n.push("## Context"),n.push(""),n.push(`- **Component / scope:** \`${d}\``),n.push(`- **Audited URL:** ${i}`),n.push("- **WCAG target:** WCAG 2.1 AA + WCAG 2.2 AA + best-practice rules"),n.push("- **Source filter:** Full audit — every detected violation"),n.push(`- **Compliance posture:** Grade **${v.letter}** · **${v.risk.toUpperCase()} risk** (${v.totals.critical} critical · ${v.totals.serious} serious · ${v.totals.moderate} moderate · ${v.totals.minor} minor). ${v.risk==="critical"||v.risk==="high"?"Treat as triage priority — these violations represent meaningful lawsuit exposure under ADA Title III, EAA, and EN 301 549.":v.risk==="moderate"?"Should be addressed in the current sprint cycle; not lawsuit-emergency but real exposure.":"Lower exposure; still worth fixing but not blocking other work."}`),u&&t&&n.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.`),n.push(""),n.push("## Before you start (safety)"),n.push(""),n.push("Do these in order before making any code changes:"),n.push(""),n.push("1. **Verify a clean git working tree** (`git status`). If there are uncommitted changes, ask the user whether to commit, stash, or proceed."),n.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`."),n.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>`."),n.push("4. **Show diffs before applying** when your tooling supports it (Cursor / Copilot Chat agents). Never silently overwrite files."),n.push("5. **Do not bump dependencies** to resolve accessibility issues — fixes should be code changes, not package upgrades."),n.push(""),n.push("## Ask me these questions first"),n.push(""),n.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."),n.push(""),n.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."),n.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?"),n.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.'),n.push("4. **Files or directories that are off-limits?** Generated code, vendored libraries, third-party widgets, lockfiles — confirm what you must NOT touch."),n.push("5. **For color-contrast fixes: prefer adjusting foreground or background?** Brand color preservation is usually a stakeholder call, not a developer call."),n.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?"),n.push(""),n.push("## Decision rules (when ambiguity surfaces during fixes)"),n.push(""),n.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.'),n.push("- **State-specific failures get state-specific fixes.** A `:hover`-only contrast failure goes in the `:hover` rule, not the default selector."),n.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."),n.push("- **When the fix would alter visible design** (color shifts, layout changes, content additions), pause and confirm with the user before applying."),n.push(""),n.push("## ⛔ Do not dismiss findings without empirical verification"),n.push(""),n.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.'),n.push(""),n.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.'),n.push(""),n.push("**To dismiss any finding as a false positive, you must do all of the following:**"),n.push(""),n.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)."),n.push("2. **Inspect the flagged elements directly** — DevTools, real `getBoundingClientRect()` coordinates, real DOM source order."),n.push("3. **Compare your empirical observation to the auditor's claim.** Does the visual layout actually contradict the DOM order, or not?"),n.push("4. **If you conclude false positive, document why** — include the empirical evidence (coordinates, screenshots, DOM excerpt) so the user can audit your reasoning."),n.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").`),n.push(""),n.push("**What does NOT count as verification:**"),n.push(""),n.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)"),n.push('- Pattern-matching the finding shape ("this looks like a perfect arithmetic flip therefore it must be an RTL artifact")'),n.push(`- Reading the auditor's false-positive list and concluding your case "looks like one of those"`),n.push(`- Deciding it's "probably fine" because the structure looks reasonable on a quick read`),n.push(""),n.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."),n.push(""),n.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."),n.push(""),n.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."),n.push(""),n.push("## Content-decision rules (alt text, button labels, link text)"),n.push(""),n.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:"),n.push(""),n.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=""`.'),n.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.'),n.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."),n.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."),n.push("5. **Always show your proposed copy before applying** — let the user approve, edit, or reject."),n.push(""),n.push("## Constraints"),n.push(""),n.push("- Don't fix violations that aren't in this list (no scope creep)."),n.push(`- Don't reformat or reorder unrelated code. No "while I'm here" cleanup.`),n.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.`),n.push("- Don't add new dependencies, configs, or build steps."),n.push("- Add inline comments only where the fix's rationale is non-obvious; otherwise skip comments."),n.push("- Preserve existing functionality and visual design wherever possible."),n.push(""),n.push("## How to use this prompt"),n.push(""),n.push("Paste this into Claude / Cursor / Copilot Chat with your codebase open. The AI should:"),n.push('1. **First**: walk through the "Before you start" + "Ask me these questions" sections with the user.'),n.push("2. **Then**: identify the source files containing each violating element (use the selectors + outerHTML below)."),n.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."),n.push("4. **Finally**: produce one atomic commit per fix, with a clear message."),n.push(""),n.push("---"),n.push("");const E=(((m=e[0])==null?void 0:m.readingOrderIssues)??[]).filter(h=>!r.has(`reading-order::${h.selector}`)),R=(C=e[0])==null?void 0:C.positionAnalysisCapturedAt,k=R?`${R.breakpoint.width}×${R.breakpoint.height} (${R.breakpoint.label}), ${R.direction.toUpperCase()}, ${R.theme} theme, ${R.pseudoState} pseudo-state`:"(reference state unavailable — findings reflect last matrix state)";if(E.length>0){n.push("## Reading-order concerns (DOM ≠ visual order)"),n.push(""),n.push(`_Positions captured at: **${k}**. 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._`),n.push(""),n.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. ${E.length} element${E.length===1?" has":"s have"} a notable gap between their DOM position and their visual position:`),n.push(""),n.push("| DOM index | Visual index | Selector | Text |"),n.push("|---|---|---|---|");for(const h of E){const b=h.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");n.push(`| ${h.domIndex} | ${h.visualIndex} | \`${h.selector}\` | ${b} |`)}n.push(""),n.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."),n.push(""),n.push("**False-positive cases — these are the ONLY legitimate reasons to skip a flagged element:**"),n.push(""),n.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.'),n.push('2. **Skip-links** ("Skip to main content") — visually positioned late but DOM-early on purpose.'),n.push("3. **Caption-first patterns** — captions placed before their image in DOM so SR users get context before the alt text."),n.push("4. **Modal / popover triggers** — the trigger button is DOM-near the modal's portal mount point, which is often `<body>` end."),n.push(""),n.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.'),n.push(""),n.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."),n.push(""),n.push("---"),n.push("")}const $=(((I=e[0])==null?void 0:I.tabOrderIssues)??[]).filter(h=>!r.has(`tab-order::${h.selector}`));if($.length>0){const h=$.filter(A=>A.flag==="visual"||A.flag==="both").length,b=$.filter(A=>A.flag==="tabindex"||A.flag==="both").length;n.push("## Tab-order concerns (keyboard focus sequence ≠ visual order) — WCAG 2.4.3 Focus Order (A)"),n.push(""),n.push(`_Positions captured at: **${k}**. 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._`),n.push(""),n.push(`Keyboard users tab through the page in one order; sighted users scan in another. axe-core does not detect this. ${$.length} focusable element${$.length===1?" has":"s have"} a notable divergence. Of these: ${h} ${h===1?"has":"have"} a visual-vs-tab mismatch (focus jumps around the page); ${b} ${b===1?"is":"are"} caused by positive \`tabindex\` reordering DOM source.`),n.push(""),n.push("| Tab pos | Visual pos | DOM pos | Flag | Selector | Text |"),n.push("|---|---|---|---|---|---|");for(const A of $){const M=A.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");n.push(`| ${A.tabPosition} | ${A.visualPosition} | ${A.domPosition} | ${A.flag} | \`${A.selector}\` | ${M} |`)}n.push(""),n.push("**How to fix:**"),n.push(""),n.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."),n.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.'),n.push("- **`flag: both`** — both conditions present. Fix the DOM order first, then remove the positive `tabindex`."),n.push(""),n.push("**False-positive cases — these are the ONLY legitimate reasons to skip a flagged element:**"),n.push(""),n.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.'),n.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."),n.push("3. **Dropdown menus / popovers** — a button DOM-far from its portal-rendered menu may produce false positives when the menu is open."),n.push(""),n.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.`),n.push(""),n.push("Surface candidates to the user; ask which are intended; only fix the rest."),n.push(""),n.push("---"),n.push("")}const g=e.flatMap(h=>h.announcements??[]),x=e.flatMap(h=>h.focusEvents??[]).filter(h=>h.isFocusReset);if(g.length>0||x.length>0){if(n.push("## Runtime behavioral observations"),n.push(""),g.length>0){const h=g.filter(A=>A.politeness==="assertive").length;n.push(`**Live-region announcements** (${g.length} total, ${h} 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:`),n.push("");const b=g.slice(0,8);for(const A of b){const M=A.text.replace(/\|/g,"\\|").replace(/\n/g," ").slice(0,120);n.push(`- \`[${A.politeness}${A.role?`/${A.role}`:""}]\` ${M}`)}g.length>8&&n.push(`- (and ${g.length-8} more)`),n.push(""),n.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)."),n.push("")}if(x.length>0){n.push(`**Focus resets to body** (${x.length} occurrence${x.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.`),n.push("");for(const h of x.slice(0,5))n.push(`- \`${h.fromSelector??"(none)"}\` → \`${h.toSelector}\``);x.length>5&&n.push(`- (and ${x.length-5} more)`),n.push(""),n.push("Common cause: an element is removed from the DOM (e.g., modal closes) without `.focus()` being called on a sensible destination."),n.push("")}n.push("---"),n.push("")}const w=((U=e[0])==null?void 0:U.undefinedCustomProperties)??[];if(w.length>0){n.push("## Undefined CSS custom properties (cascade root cause)"),n.push(""),n.push(`${w.length} CSS custom propert${w.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.`),n.push(""),n.push("| Custom property | References | First sample sites |"),n.push("|---|---|---|");for(const h of w){const b=h.sampleSites.map(A=>`\`${A.selector} { ${A.property} }\``).join("; ");n.push(`| \`${h.name}\` | ${h.referenceCount} | ${b} |`)}n.push(""),n.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."),n.push(""),n.push("---"),n.push("")}if(s&&s.length>0){n.push("## Manual checks already completed (Guided Tests)"),n.push(""),n.push(`The consultant has run ${s.length} Intelligent Guided Test workflow${s.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.`),n.push(""),n.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."),n.push("");const h=he;n.push("| Workflow | Status | Notes |"),n.push("|---|---|---|");for(const P of s){const _=h.find(xe=>xe.id===P.workflowId);if(!_)continue;let L=0,F=0,le=0;for(const xe of _.steps){const de=P.steps[xe.id];de&&(de.status==="pass"?L++:de.status==="fail"?F++:de.status==="skip"&&le++)}const Qe=L+F+le,qt=F>0?`❌ ${F} failed`:le===Qe?"➖ skipped":`✓ ${L}/${Qe} passed`,Bt=`${L}p · ${F}f · ${le}s of ${_.steps.length} steps`;n.push(`| ${_.name} | ${qt} | ${Bt} |`)}n.push("");const b=[];for(const P of s){const _=h.find(L=>L.id===P.workflowId);if(_)for(const L of _.steps){const F=P.steps[L.id];(F==null?void 0:F.status)==="fail"&&b.push({workflow:_.name,stepTitle:L.question,notes:F.notes})}}if(b.length>0){n.push("**Failed manual checks** (these are real WCAG failures the consultant verified by hand):"),n.push("");for(const P of b)n.push(`- **${P.workflow} → ${P.stepTitle}**${P.notes?`: ${P.notes}`:""}`);n.push(""),n.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."),n.push("")}const A=new Set(s.map(P=>P.workflowId)),M=h.filter(P=>!A.has(P.id));M.length>0&&(n.push(`**Not yet manually verified** (${M.length} workflow${M.length===1?"":"s"}): `+M.map(P=>P.name).join(", ")+". These cover WCAG criteria automation cannot detect; ask the consultant whether to run them before declaring the audit complete."),n.push("")),n.push("---"),n.push("")}if(a&&a.pagesAudited>1){const h=(()=>{try{return new URL(i).origin}catch{return null}})(),b=(()=>{try{return new URL(a.startUrl).origin}catch{return null}})();if(h&&b&&h===b){if(n.push("## Site-wide context (from a recent site crawl)"),n.push(""),n.push(`A recent crawl of ${a.pagesAudited} page${a.pagesAudited===1?"":"s"} on this origin produced grade **${a.siteGrade}** site-wide with **${a.totalUniqueViolations}** unique violations. Use this context for WCAG 3.2.3 (Consistent Navigation) and 3.2.4 (Consistent Identification) which require cross-page comparison.`),n.push(""),a.topViolations.length>0){n.push("**Most-frequent violations across the site** (rules to prioritize because they affect multiple pages):"),n.push("");for(const A of a.topViolations.slice(0,6))n.push(`- \`${A.ruleId}\` — ${A.impact} — found on ${A.urlsAffected} page${A.urlsAffected===1?"":"s"} (${A.totalOccurrences} total instances)`);n.push(""),n.push("Fixing one of these rules in shared components / layouts is leverage — one fix resolves N pages."),n.push("")}n.push("**Manual cross-page checks the AI should walk through:**"),n.push(""),n.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."),n.push('- **3.2.4 Consistent Identification** — verify components with identical functionality (logo "home" link, search button, menu toggle) have identical accessible names across pages.'),n.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."),n.push(""),n.push("---"),n.push("")}}return n.push("## Violations to fix"),n.push(""),p.forEach((h,b)=>{const A=nn[h.ruleId],M=h._instanceSelectors.length;if(n.push(`### ${b+1}. \`${h.ruleId}\` — ${h.impact} — WCAG ${h.wcagCriterion} ${h.wcagLevel}${M>1?` (${M} instances)`:""}`),n.push(""),n.push(`**Description:** ${h.description}`),n.push(""),M>1){n.push(`**Affects ${M} spots — fix the rule once, applies to all:**`);for(const L of h._instanceSelectors.slice(0,8))n.push(`- \`${L}\``);h._instanceSelectors.length>8&&n.push(`- (and ${h._instanceSelectors.length-8} more)`)}else n.push(`**Selector:** \`${h.target.selector}\``);n.push(""),n.push(`**Found in state(s):** ${h._states.join(" / ")}`),n.push("");const P=$s(h);if(P.length>0){for(const L of P)n.push(`**${L.label}:** ${L.value}`);n.push("")}h.target.failureSummary&&(n.push(`**Diagnostic:** ${h.target.failureSummary.replace(/\n+/g," ")}`),n.push("")),h.ai&&(n.push(`**🤖 AI-verified finding** (model: ${h.ai.model}, confidence: ${(h.ai.confidence*100).toFixed(0)}%)`),n.push(`> ${h.ai.reasoning}`),n.push(""));const _=h.target.cascadeChain;if(_&&(_.color||_.backgroundColor)){if(n.push("**CSS cascade chain (authored → rendered):**"),_.color){const L=_.color.vars.length>0?` (vars: ${_.color.vars.map(F=>`\`${F}\``).join(", ")})`:"";n.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(F=>`\`${F}\``).join(", ")})`:"";n.push(`- \`background-color\`: \`${_.backgroundColor.authored}\` → \`${_.backgroundColor.rendered}\`${L}${_.backgroundColor.ruleSelector?` — set in rule \`${_.backgroundColor.ruleSelector}\``:""}`)}n.push("")}n.push("**Element HTML:**"),n.push("```html"),n.push(h.target.outerHTML),n.push("```"),n.push(""),A?(n.push(`**Recipe:** ${A.summary}`),A.snippet&&(n.push(""),n.push("```"+(A.snippetLang??"")),n.push(A.snippet),n.push("```"))):n.push(`**Recipe:** No curated recipe for this rule — apply the standard WCAG ${h.wcagCriterion} guidance.`),h.helpUrl&&(n.push(""),n.push(`**More info:** ${h.helpUrl}`)),n.push(""),n.push("---"),n.push("")}),n.push("## Manual verification checklist — non-automatable WCAG 2.1 / 2.2 AA criteria"),n.push(""),n.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)."),n.push(""),n.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.'),n.push(""),n.push("### Perceivable"),n.push(""),n.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."),n.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.'),n.push("- [ ] **1.2.3 Audio Description / Media Alternative Prerecorded (A)** — prerecorded video has either audio description or a full text alternative."),n.push("- [ ] **1.2.4 Captions Live (AA)** — live audio content (livestream, real-time meeting) has captions. Usually N/A for static sites."),n.push("- [ ] **1.2.5 Audio Description Prerecorded (AA)** — prerecorded video has audio description for visual-only information."),n.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."),n.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."),n.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."),n.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)."),n.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."),n.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)."),n.push(""),n.push("### Operable"),n.push(""),n.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."),n.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.'),n.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.'),n.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."),n.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."),$.length>0?n.push("- [x] **2.4.3 Focus Order (A)** — ALREADY FLAGGED above (`Tab-order concerns` section). Confirm fixes are in place."):n.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)."),n.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."),n.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."),n.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."),n.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."),n.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."),n.push(""),n.push("### Understandable"),n.push(""),n.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."),n.push("- [ ] **3.2.2 On Input (A)** — changing the value of any form control does NOT trigger context change unless the user was warned. Common offender: `<select onChange={navigate}>` — must include adjacent submit button or warn the user."),a&&a.pagesAudited>1?(n.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."),n.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).")):(n.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."),n.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.')),n.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."),n.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."),n.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."),n.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)."),n.push(""),n.push("### Robust"),n.push(""),g.length>0||x.length>0?n.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."):n.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.'),n.push(""),n.push("### Manual checks STILL TO DO"),n.push(""),n.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).`),n.push(""),n.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.'),n.push(""),n.push("---"),n.push(""),n.push("## When you're done — verification"),n.push(""),n.push("Before reporting completion, do all of the following:"),n.push(""),n.push(`1. **Confirm all ${p.length} violation${p.length===1?"":"s"} above are addressed.** Walk back through the list with the user, ticking each one off.`),n.push("2. **Re-run the auditor** (or whatever method we agreed in question 6 above). Verify zero NEW violations were introduced by the fixes."),n.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."`),n.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."),n.push("5. **Commit log review** — surface the list of commits you produced. Each should be atomic and revertable."),n.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."),n.push(""),n.push("## Success criteria"),n.push(""),n.push("- All "+p.length+" violation"+(p.length===1?"":"s")+" above are resolved or explicitly deferred with reason."),n.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.'),n.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.)'),n.push("- No new violations introduced."),n.push("- State-specific behavior preserved (`:hover`, `:focus`, dark-mode, RTL, breakpoints)."),n.push("- One atomic commit per fix; clean revertable history."),n.push("- No unrelated code reformatted, no dependencies bumped."),n.push("- Diff is reviewable in under 10 minutes per fix."),n.push(""),n.push(`_Generated by ${H} v${ee()}._`),n.join(`
|
|
345
|
-
`)}const
|
|
346
|
-
`);let u=1;for(const[l,p]of d.entries()){const n=(c=p.find(
|
|
347
|
-
`)}function
|
|
344
|
+
</html>`}function Bs(e){const t=[],s=e.target.outerHTML;if(e.ruleId==="image-alt"||e.ruleId==="input-image-alt"||e.ruleId==="area-alt"||e.ruleId==="svg-img-alt"||e.ruleId==="object-alt"){const a=/\bsrc\s*=\s*"([^"]+)"|\bsrc\s*=\s*'([^']+)'/i.exec(s);a&&t.push({label:"Image source",value:`\`${a[1]??a[2]}\` — if you have vision capability, view this image and propose alt text. Use \`alt=""\` if it's decorative.`});const o=/<title>([^<]+)<\/title>/i.exec(s);o&&t.push({label:"Inline SVG <title>",value:o[1]})}if(e.ruleId==="button-name"||e.ruleId==="empty-button"||e.ruleId==="input-button-name"){const a=/\baria-label\s*=\s*"([^"]+)"/i.exec(s);a&&t.push({label:"Existing aria-label",value:a[1]});const o=/<svg[^>]*>[\s\S]*?<title>([^<]+)<\/title>/i.exec(s);o&&t.push({label:"SVG <title> inside",value:o[1]});const r=/<(?:i|svg|span)[^>]*class\s*=\s*"([^"]*(?:icon|fa-|material-icon)[^"]*)"/i.exec(s);r&&t.push({label:"Icon class hint",value:`\`${r[1]}\` — name suggests intent`})}if(e.ruleId==="link-name"||e.ruleId==="empty-link"){const a=/\bhref\s*=\s*"([^"]+)"|\bhref\s*=\s*'([^']+)'/i.exec(s);a&&t.push({label:"Link href",value:`\`${a[1]??a[2]}\` — destination may suggest the right link text`});const o=/\baria-label\s*=\s*"([^"]+)"/i.exec(s);o&&t.push({label:"Existing aria-label",value:o[1]})}if(e.ruleId==="label"||e.ruleId==="select-name"||e.ruleId==="aria-input-field-name"){const a=/\bname\s*=\s*"([^"]+)"|\bname\s*=\s*'([^']+)'/i.exec(s);a&&t.push({label:"Field name attribute",value:`\`${a[1]??a[2]}\` — often hints at the intended label`});const o=/\bplaceholder\s*=\s*"([^"]+)"|\bplaceholder\s*=\s*'([^']+)'/i.exec(s);o&&t.push({label:"Placeholder text (visible)",value:o[1]??o[2]});const r=/\btype\s*=\s*"([^"]+)"|\btype\s*=\s*'([^']+)'/i.exec(s);r&&t.push({label:"Input type",value:`\`${r[1]??r[2]}\``});const d=/\bautocomplete\s*=\s*"([^"]+)"|\bautocomplete\s*=\s*'([^']+)'/i.exec(s);d&&t.push({label:"Autocomplete",value:d[1]??d[2]})}return t}function Ks(e,t,s,a,o){var y,C,m,I,R,U;const r=new Set(o??[]),d=((y=e[0])==null?void 0:y.componentId)??"unknown",i=X(e),u=!!(t&&t.baselineSnapshotMeta),c=e.flatMap(h=>h.violations),l=new Map;for(const h of c){const b=h.ruleId.startsWith("ai-")?`${h.ruleId}::${h.target.selector}::${(h.target.outerHTML??"").slice(0,200)}`:ce(h.ruleId,h.target.selector),A=`${h.currentState.pseudoState} · ${h.currentState.theme} · ${h.currentState.direction} · ${h.currentState.breakpoint.id}`,M=l.get(b);if(M){M._states.includes(A)||M._states.push(A),M._instanceSelectors.includes(h.target.selector)||M._instanceSelectors.push(h.target.selector);continue}l.set(b,{...h,_states:[A],_instanceSelectors:[h.target.selector]})}const p=Array.from(l.values()).sort((h,b)=>{const A={critical:0,serious:1,moderate:2,minor:3};return(A[h.impact]??99)-(A[b.impact]??99)}),n=[];n.push("# Accessibility Fix Request"),n.push(""),n.push(`You are a senior accessibility engineer pairing with the user on their codebase. The component below has ${p.length} unique WCAG violation${p.length===1?"":"s"} detected by an automated audit running axe-core ${((C=e[0])==null?void 0:C.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.`),n.push("");const v=te(c);n.push("## Context"),n.push(""),n.push(`- **Component / scope:** \`${d}\``),n.push(`- **Audited URL:** ${i}`),n.push("- **WCAG target:** WCAG 2.1 AA + WCAG 2.2 AA + best-practice rules"),n.push("- **Source filter:** Full audit — every detected violation"),n.push(`- **Compliance posture:** Grade **${v.letter}** · **${v.risk.toUpperCase()} risk** (${v.totals.critical} critical · ${v.totals.serious} serious · ${v.totals.moderate} moderate · ${v.totals.minor} minor). ${v.risk==="critical"||v.risk==="high"?"Treat as triage priority — these violations represent meaningful lawsuit exposure under ADA Title III, EAA, and EN 301 549.":v.risk==="moderate"?"Should be addressed in the current sprint cycle; not lawsuit-emergency but real exposure.":"Lower exposure; still worth fixing but not blocking other work."}`),u&&t&&n.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.`),n.push(""),n.push("## Before you start (safety)"),n.push(""),n.push("Do these in order before making any code changes:"),n.push(""),n.push("1. **Verify a clean git working tree** (`git status`). If there are uncommitted changes, ask the user whether to commit, stash, or proceed."),n.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`."),n.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>`."),n.push("4. **Show diffs before applying** when your tooling supports it (Cursor / Copilot Chat agents). Never silently overwrite files."),n.push("5. **Do not bump dependencies** to resolve accessibility issues — fixes should be code changes, not package upgrades."),n.push(""),n.push("## Ask me these questions first"),n.push(""),n.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."),n.push(""),n.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."),n.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?"),n.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.'),n.push("4. **Files or directories that are off-limits?** Generated code, vendored libraries, third-party widgets, lockfiles — confirm what you must NOT touch."),n.push("5. **For color-contrast fixes: prefer adjusting foreground or background?** Brand color preservation is usually a stakeholder call, not a developer call."),n.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?"),n.push(""),n.push("## Decision rules (when ambiguity surfaces during fixes)"),n.push(""),n.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.'),n.push("- **State-specific failures get state-specific fixes.** A `:hover`-only contrast failure goes in the `:hover` rule, not the default selector."),n.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."),n.push("- **When the fix would alter visible design** (color shifts, layout changes, content additions), pause and confirm with the user before applying."),n.push(""),n.push("## ⛔ Do not dismiss findings without empirical verification"),n.push(""),n.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.'),n.push(""),n.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.'),n.push(""),n.push("**To dismiss any finding as a false positive, you must do all of the following:**"),n.push(""),n.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)."),n.push("2. **Inspect the flagged elements directly** — DevTools, real `getBoundingClientRect()` coordinates, real DOM source order."),n.push("3. **Compare your empirical observation to the auditor's claim.** Does the visual layout actually contradict the DOM order, or not?"),n.push("4. **If you conclude false positive, document why** — include the empirical evidence (coordinates, screenshots, DOM excerpt) so the user can audit your reasoning."),n.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").`),n.push(""),n.push("**What does NOT count as verification:**"),n.push(""),n.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)"),n.push('- Pattern-matching the finding shape ("this looks like a perfect arithmetic flip therefore it must be an RTL artifact")'),n.push(`- Reading the auditor's false-positive list and concluding your case "looks like one of those"`),n.push(`- Deciding it's "probably fine" because the structure looks reasonable on a quick read`),n.push(""),n.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."),n.push(""),n.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."),n.push(""),n.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."),n.push(""),n.push("## Content-decision rules (alt text, button labels, link text)"),n.push(""),n.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:"),n.push(""),n.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=""`.'),n.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.'),n.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."),n.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."),n.push("5. **Always show your proposed copy before applying** — let the user approve, edit, or reject."),n.push(""),n.push("## Constraints"),n.push(""),n.push("- Don't fix violations that aren't in this list (no scope creep)."),n.push(`- Don't reformat or reorder unrelated code. No "while I'm here" cleanup.`),n.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.`),n.push("- Don't add new dependencies, configs, or build steps."),n.push("- Add inline comments only where the fix's rationale is non-obvious; otherwise skip comments."),n.push("- Preserve existing functionality and visual design wherever possible."),n.push(""),n.push("## How to use this prompt"),n.push(""),n.push("Paste this into Claude / Cursor / Copilot Chat with your codebase open. The AI should:"),n.push('1. **First**: walk through the "Before you start" + "Ask me these questions" sections with the user.'),n.push("2. **Then**: identify the source files containing each violating element (use the selectors + outerHTML below)."),n.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."),n.push("4. **Finally**: produce one atomic commit per fix, with a clear message."),n.push(""),n.push("---"),n.push("");const k=(((m=e[0])==null?void 0:m.readingOrderIssues)??[]).filter(h=>!r.has(`reading-order::${h.selector}`)),E=(I=e[0])==null?void 0:I.positionAnalysisCapturedAt,$=E?`${E.breakpoint.width}×${E.breakpoint.height} (${E.breakpoint.label}), ${E.direction.toUpperCase()}, ${E.theme} theme, ${E.pseudoState} pseudo-state`:"(reference state unavailable — findings reflect last matrix state)";if(k.length>0){n.push("## Reading-order concerns (DOM ≠ visual order)"),n.push(""),n.push(`_Positions captured at: **${$}**. 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._`),n.push(""),n.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. ${k.length} element${k.length===1?" has":"s have"} a notable gap between their DOM position and their visual position:`),n.push(""),n.push("| DOM index | Visual index | Selector | Text |"),n.push("|---|---|---|---|");for(const h of k){const b=h.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");n.push(`| ${h.domIndex} | ${h.visualIndex} | \`${h.selector}\` | ${b} |`)}n.push(""),n.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."),n.push(""),n.push("**False-positive cases — these are the ONLY legitimate reasons to skip a flagged element:**"),n.push(""),n.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.'),n.push('2. **Skip-links** ("Skip to main content") — visually positioned late but DOM-early on purpose.'),n.push("3. **Caption-first patterns** — captions placed before their image in DOM so SR users get context before the alt text."),n.push("4. **Modal / popover triggers** — the trigger button is DOM-near the modal's portal mount point, which is often `<body>` end."),n.push(""),n.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.'),n.push(""),n.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."),n.push(""),n.push("---"),n.push("")}const T=(((R=e[0])==null?void 0:R.tabOrderIssues)??[]).filter(h=>!r.has(`tab-order::${h.selector}`));if(T.length>0){const h=T.filter(A=>A.flag==="visual"||A.flag==="both").length,b=T.filter(A=>A.flag==="tabindex"||A.flag==="both").length;n.push("## Tab-order concerns (keyboard focus sequence ≠ visual order) — WCAG 2.4.3 Focus Order (A)"),n.push(""),n.push(`_Positions captured at: **${$}**. 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._`),n.push(""),n.push(`Keyboard users tab through the page in one order; sighted users scan in another. axe-core does not detect this. ${T.length} focusable element${T.length===1?" has":"s have"} a notable divergence. Of these: ${h} ${h===1?"has":"have"} a visual-vs-tab mismatch (focus jumps around the page); ${b} ${b===1?"is":"are"} caused by positive \`tabindex\` reordering DOM source.`),n.push(""),n.push("| Tab pos | Visual pos | DOM pos | Flag | Selector | Text |"),n.push("|---|---|---|---|---|---|");for(const A of T){const M=A.textSnippet.replace(/\|/g,"\\|").replace(/\n/g," ");n.push(`| ${A.tabPosition} | ${A.visualPosition} | ${A.domPosition} | ${A.flag} | \`${A.selector}\` | ${M} |`)}n.push(""),n.push("**How to fix:**"),n.push(""),n.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."),n.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.'),n.push("- **`flag: both`** — both conditions present. Fix the DOM order first, then remove the positive `tabindex`."),n.push(""),n.push("**False-positive cases — these are the ONLY legitimate reasons to skip a flagged element:**"),n.push(""),n.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.'),n.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."),n.push("3. **Dropdown menus / popovers** — a button DOM-far from its portal-rendered menu may produce false positives when the menu is open."),n.push(""),n.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.`),n.push(""),n.push("Surface candidates to the user; ask which are intended; only fix the rest."),n.push(""),n.push("---"),n.push("")}const g=e.flatMap(h=>h.announcements??[]),x=e.flatMap(h=>h.focusEvents??[]).filter(h=>h.isFocusReset);if(g.length>0||x.length>0){if(n.push("## Runtime behavioral observations"),n.push(""),g.length>0){const h=g.filter(A=>A.politeness==="assertive").length;n.push(`**Live-region announcements** (${g.length} total, ${h} 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:`),n.push("");const b=g.slice(0,8);for(const A of b){const M=A.text.replace(/\|/g,"\\|").replace(/\n/g," ").slice(0,120);n.push(`- \`[${A.politeness}${A.role?`/${A.role}`:""}]\` ${M}`)}g.length>8&&n.push(`- (and ${g.length-8} more)`),n.push(""),n.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)."),n.push("")}if(x.length>0){n.push(`**Focus resets to body** (${x.length} occurrence${x.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.`),n.push("");for(const h of x.slice(0,5))n.push(`- \`${h.fromSelector??"(none)"}\` → \`${h.toSelector}\``);x.length>5&&n.push(`- (and ${x.length-5} more)`),n.push(""),n.push("Common cause: an element is removed from the DOM (e.g., modal closes) without `.focus()` being called on a sensible destination."),n.push("")}n.push("---"),n.push("")}const w=((U=e[0])==null?void 0:U.undefinedCustomProperties)??[];if(w.length>0){n.push("## Undefined CSS custom properties (cascade root cause)"),n.push(""),n.push(`${w.length} CSS custom propert${w.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.`),n.push(""),n.push("| Custom property | References | First sample sites |"),n.push("|---|---|---|");for(const h of w){const b=h.sampleSites.map(A=>`\`${A.selector} { ${A.property} }\``).join("; ");n.push(`| \`${h.name}\` | ${h.referenceCount} | ${b} |`)}n.push(""),n.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."),n.push(""),n.push("---"),n.push("")}if(s&&s.length>0){n.push("## Manual checks already completed (Guided Tests)"),n.push(""),n.push(`The consultant has run ${s.length} Intelligent Guided Test workflow${s.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.`),n.push(""),n.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."),n.push("");const h=fe;n.push("| Workflow | Status | Notes |"),n.push("|---|---|---|");for(const P of s){const _=h.find(Ae=>Ae.id===P.workflowId);if(!_)continue;let L=0,F=0,le=0;for(const Ae of _.steps){const de=P.steps[Ae.id];de&&(de.status==="pass"?L++:de.status==="fail"?F++:de.status==="skip"&&le++)}const tt=L+F+le,Zt=F>0?`❌ ${F} failed`:le===tt?"➖ skipped":`✓ ${L}/${tt} passed`,en=`${L}p · ${F}f · ${le}s of ${_.steps.length} steps`;n.push(`| ${_.name} | ${Zt} | ${en} |`)}n.push("");const b=[];for(const P of s){const _=h.find(L=>L.id===P.workflowId);if(_)for(const L of _.steps){const F=P.steps[L.id];(F==null?void 0:F.status)==="fail"&&b.push({workflow:_.name,stepTitle:L.question,notes:F.notes})}}if(b.length>0){n.push("**Failed manual checks** (these are real WCAG failures the consultant verified by hand):"),n.push("");for(const P of b)n.push(`- **${P.workflow} → ${P.stepTitle}**${P.notes?`: ${P.notes}`:""}`);n.push(""),n.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."),n.push("")}const A=new Set(s.map(P=>P.workflowId)),M=h.filter(P=>!A.has(P.id));M.length>0&&(n.push(`**Not yet manually verified** (${M.length} workflow${M.length===1?"":"s"}): `+M.map(P=>P.name).join(", ")+". These cover WCAG criteria automation cannot detect; ask the consultant whether to run them before declaring the audit complete."),n.push("")),n.push("---"),n.push("")}if(a&&a.pagesAudited>1){const h=(()=>{try{return new URL(i).origin}catch{return null}})(),b=(()=>{try{return new URL(a.startUrl).origin}catch{return null}})();if(h&&b&&h===b){if(n.push("## Site-wide context (from a recent site crawl)"),n.push(""),n.push(`A recent crawl of ${a.pagesAudited} page${a.pagesAudited===1?"":"s"} on this origin produced grade **${a.siteGrade}** site-wide with **${a.totalUniqueViolations}** unique violations. Use this context for WCAG 3.2.3 (Consistent Navigation) and 3.2.4 (Consistent Identification) which require cross-page comparison.`),n.push(""),a.topViolations.length>0){n.push("**Most-frequent violations across the site** (rules to prioritize because they affect multiple pages):"),n.push("");for(const A of a.topViolations.slice(0,6))n.push(`- \`${A.ruleId}\` — ${A.impact} — found on ${A.urlsAffected} page${A.urlsAffected===1?"":"s"} (${A.totalOccurrences} total instances)`);n.push(""),n.push("Fixing one of these rules in shared components / layouts is leverage — one fix resolves N pages."),n.push("")}n.push("**Manual cross-page checks the AI should walk through:**"),n.push(""),n.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."),n.push('- **3.2.4 Consistent Identification** — verify components with identical functionality (logo "home" link, search button, menu toggle) have identical accessible names across pages.'),n.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."),n.push(""),n.push("---"),n.push("")}}return n.push("## Violations to fix"),n.push(""),p.forEach((h,b)=>{const A=dn[h.ruleId],M=h._instanceSelectors.length;if(n.push(`### ${b+1}. \`${h.ruleId}\` — ${h.impact} — WCAG ${h.wcagCriterion} ${h.wcagLevel}${M>1?` (${M} instances)`:""}`),n.push(""),n.push(`**Description:** ${h.description}`),n.push(""),M>1){n.push(`**Affects ${M} spots — fix the rule once, applies to all:**`);for(const L of h._instanceSelectors.slice(0,8))n.push(`- \`${L}\``);h._instanceSelectors.length>8&&n.push(`- (and ${h._instanceSelectors.length-8} more)`)}else n.push(`**Selector:** \`${h.target.selector}\``);n.push(""),n.push(`**Found in state(s):** ${h._states.join(" / ")}`),n.push("");const P=Bs(h);if(P.length>0){for(const L of P)n.push(`**${L.label}:** ${L.value}`);n.push("")}h.target.failureSummary&&(n.push(`**Diagnostic:** ${h.target.failureSummary.replace(/\n+/g," ")}`),n.push("")),h.ai&&(n.push(`**🤖 AI-verified finding** (model: ${h.ai.model}, confidence: ${(h.ai.confidence*100).toFixed(0)}%)`),n.push(`> ${h.ai.reasoning}`),n.push(""));const _=h.target.cascadeChain;if(_&&(_.color||_.backgroundColor)){if(n.push("**CSS cascade chain (authored → rendered):**"),_.color){const L=_.color.vars.length>0?` (vars: ${_.color.vars.map(F=>`\`${F}\``).join(", ")})`:"";n.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(F=>`\`${F}\``).join(", ")})`:"";n.push(`- \`background-color\`: \`${_.backgroundColor.authored}\` → \`${_.backgroundColor.rendered}\`${L}${_.backgroundColor.ruleSelector?` — set in rule \`${_.backgroundColor.ruleSelector}\``:""}`)}n.push("")}n.push("**Element HTML:**"),n.push("```html"),n.push(h.target.outerHTML),n.push("```"),n.push(""),A?(n.push(`**Recipe:** ${A.summary}`),A.snippet&&(n.push(""),n.push("```"+(A.snippetLang??"")),n.push(A.snippet),n.push("```"))):n.push(`**Recipe:** No curated recipe for this rule — apply the standard WCAG ${h.wcagCriterion} guidance.`),h.helpUrl&&(n.push(""),n.push(`**More info:** ${h.helpUrl}`)),n.push(""),n.push("---"),n.push("")}),n.push("## Manual verification checklist — non-automatable WCAG 2.1 / 2.2 AA criteria"),n.push(""),n.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)."),n.push(""),n.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.'),n.push(""),n.push("### Perceivable"),n.push(""),n.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."),n.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.'),n.push("- [ ] **1.2.3 Audio Description / Media Alternative Prerecorded (A)** — prerecorded video has either audio description or a full text alternative."),n.push("- [ ] **1.2.4 Captions Live (AA)** — live audio content (livestream, real-time meeting) has captions. Usually N/A for static sites."),n.push("- [ ] **1.2.5 Audio Description Prerecorded (AA)** — prerecorded video has audio description for visual-only information."),n.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."),n.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."),n.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."),n.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)."),n.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."),n.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)."),n.push(""),n.push("### Operable"),n.push(""),n.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."),n.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.'),n.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.'),n.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."),n.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."),T.length>0?n.push("- [x] **2.4.3 Focus Order (A)** — ALREADY FLAGGED above (`Tab-order concerns` section). Confirm fixes are in place."):n.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)."),n.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."),n.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."),n.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."),n.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."),n.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."),n.push(""),n.push("### Understandable"),n.push(""),n.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."),n.push("- [ ] **3.2.2 On Input (A)** — changing the value of any form control does NOT trigger context change unless the user was warned. Common offender: `<select onChange={navigate}>` — must include adjacent submit button or warn the user."),a&&a.pagesAudited>1?(n.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."),n.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).")):(n.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."),n.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.')),n.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."),n.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."),n.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."),n.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)."),n.push(""),n.push("### Robust"),n.push(""),g.length>0||x.length>0?n.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."):n.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.'),n.push(""),n.push("### Manual checks STILL TO DO"),n.push(""),n.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).`),n.push(""),n.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.'),n.push(""),n.push("---"),n.push(""),n.push("## When you're done — verification"),n.push(""),n.push("Before reporting completion, do all of the following:"),n.push(""),n.push(`1. **Confirm all ${p.length} violation${p.length===1?"":"s"} above are addressed.** Walk back through the list with the user, ticking each one off.`),n.push("2. **Re-run the auditor** (or whatever method we agreed in question 6 above). Verify zero NEW violations were introduced by the fixes."),n.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."`),n.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."),n.push("5. **Commit log review** — surface the list of commits you produced. Each should be atomic and revertable."),n.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."),n.push(""),n.push("## Success criteria"),n.push(""),n.push("- All "+p.length+" violation"+(p.length===1?"":"s")+" above are resolved or explicitly deferred with reason."),n.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.'),n.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.)'),n.push("- No new violations introduced."),n.push("- State-specific behavior preserved (`:hover`, `:focus`, dark-mode, RTL, breakpoints)."),n.push("- One atomic commit per fix; clean revertable history."),n.push("- No unrelated code reformatted, no dependencies bumped."),n.push("- Diff is reviewable in under 10 minutes per fix."),n.push(""),n.push(`_Generated by ${H} v${ee()}._`),n.join(`
|
|
345
|
+
`)}const Ys=5;function Js(e){const t=new Map;let s=0;for(const a of e){if(!a.screenshotDataUrl||a.violations.length===0)continue;const o=`${a.state.pseudoState} · ${a.state.theme} · ${a.state.direction} · ${a.state.breakpoint.id}`,r=o;t.has(r)||t.set(r,{stateLabel:o,screenshotDataUrl:a.screenshotDataUrl,violations:[]});const d=t.get(r);for(const i of a.violations)s++,d.violations.push({...i,_idx:s})}return Array.from(t.values()).sort((a,o)=>o.violations.length-a.violations.length).slice(0,Ys)}const ft=1280,gt=800,Xs={low:"Low risk",moderate:"Moderate risk",high:"High risk",critical:"Critical risk"},Qs={low:"#d1fae5",moderate:"#fef3c7",high:"#fed7aa",critical:"#fecaca"},Zs={low:"#065f46",moderate:"#92400e",high:"#9a3412",critical:"#991b1b"},ea={A:"#10b981",B:"#84cc16",C:"#eab308",D:"#f97316",F:"#dc2626"},ta={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 na(e,t,s,a,o){return Ht(e,t,"evidence",s,a,o)}function sa(e){return Vt(e,"letter")}function aa(e){return Vt(e,"report")}function oa(e){var c;const t=e.flatMap(l=>l.violations),s=te(t),a=X(e),o=new Map;for(const l of t){const p=ce(l.ruleId,l.target.selector);o.has(p)||o.set(p,l)}const r=Array.from(o.values()).sort((l,p)=>{const n={critical:0,serious:1,moderate:2,minor:3};return(n[l.impact]??99)-(n[p.impact]??99)}),d=new Map;for(const l of r){const p=d.get(l.ruleId)??[];p.push(l),d.set(l.ruleId,p)}const i=[];if(i.push("# Help me fix accessibility issues on my website"),i.push(""),i.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."),i.push(""),i.push("## What I need from you"),i.push(""),i.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."),i.push("2. **Then, for each issue below, tell me clearly:**"),i.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"),i.push(" - Or whether **my developer needs to do this** (code-level fix) — and what to ask them to do"),i.push("3. **Prioritize ruthlessly.** I want to start with the issues that:"),i.push(" - Are most likely to come up in an ADA-related lawsuit (critical-impact items)"),i.push(" - Are easiest for me to fix without involving a developer"),i.push("4. **Don't bury me in jargon.** If you have to use a technical term, explain it in plain English first."),i.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."),i.push(""),i.push("## Audit summary"),i.push(""),i.push(`- **Page audited:** ${a}`),i.push(`- **Lawsuit-target risk:** ${Et[s.risk]} — ${$t[s.risk]}`),i.push(`- **Issues found:** ${s.totals.unique} unique problems (${s.totals.critical} critical · ${s.totals.serious} serious · ${s.totals.moderate} moderate · ${s.totals.minor} minor)`),i.push(""),i.push("## The issues, in plain language"),i.push(""),d.size===0)return i.push("No automated issues were detected. Note: automated checks only catch about 30–50% of accessibility problems. Manual testing covers the rest."),i.join(`
|
|
346
|
+
`);let u=1;for(const[l,p]of d.entries()){const n=(c=p.find(E=>E.target.opacityContext))==null?void 0:c.target.opacityContext,v=kt(l,n),k=p[0].impact;i.push(`### ${u}. [${k.toUpperCase()}] ${v.whatsWrong}`),i.push(""),i.push(`**Why this matters.** ${v.whyItMatters}`),i.push(""),i.push(`**Plain-language fix guidance.** ${v.howToFix}`),i.push(""),i.push(`**Where on the page (${p.length} ${p.length===1?"spot":"spots"}):**`);for(const E of p.slice(0,8))i.push(`- \`${E.target.selector}\``);p.length>8&&i.push(`- (and ${p.length-8} more spots)`),i.push(""),i.push(`*Technical reference for your developer:* axe-core rule \`${l}\` — full docs at https://dequeuniversity.com/rules/axe/4.11/${l}`),i.push(""),u++}return i.push("---"),i.push(""),i.push("## How to use this output"),i.push(""),i.push("Walk through this list with me one issue at a time. For each:"),i.push("1. Help me understand the issue with a real example of who it affects."),i.push("2. Tell me clearly if I can fix it myself, and if so, walk me through it for my platform."),i.push("3. If I need a developer, give me a short paragraph I can paste into an email or message to them."),i.push("4. Help me prioritize — I have limited time and want to fix the riskiest things first."),i.push(""),i.push("Start by asking me which platform I use."),i.join(`
|
|
347
|
+
`)}function Vt(e,t){const s=e.flatMap(n=>n.violations),a=te(s),o=X(e),r=new Date,d=new Map;for(const n of s){const v=ce(n.ruleId,n.target.selector);d.has(v)||d.set(v,n)}const i=Array.from(d.values()).sort((n,v)=>{const k={critical:0,serious:1,moderate:2,minor:3};return(k[n.impact]??99)-(k[v.impact]??99)}),u=new Map;for(const n of i){const v=u.get(n.ruleId)??[];v.push(n),u.set(n.ruleId,v)}const c=Array.from(u.entries()).map(([n,v])=>{var S;const k=(S=v.find(x=>x.target.opacityContext))==null?void 0:S.target.opacityContext,E=kt(n,k),$=v[0].impact,T=v.slice(0,8).map(x=>`<code>${f(x.target.selector)}</code>`).join(", "),g=v.length>8?` <em>(and ${v.length-8} more)</em>`:"";return`
|
|
348
348
|
<section class="rule">
|
|
349
349
|
<div class="rule-header">
|
|
350
|
-
<span class="impact impact-${f(
|
|
351
|
-
<h3>${f(
|
|
350
|
+
<span class="impact impact-${f($)}">${f($)}</span>
|
|
351
|
+
<h3>${f(E.whatsWrong)}</h3>
|
|
352
352
|
</div>
|
|
353
|
-
<p class="why"><strong>Why this matters.</strong> ${f(
|
|
354
|
-
<p class="how"><strong>How to fix.</strong> ${f(
|
|
353
|
+
<p class="why"><strong>Why this matters.</strong> ${f(E.whyItMatters)}</p>
|
|
354
|
+
<p class="how"><strong>How to fix.</strong> ${f(E.howToFix)}</p>
|
|
355
355
|
<p class="where">
|
|
356
|
-
<strong>Where on the page (${v.length} ${v.length===1?"spot":"spots"}):</strong> ${
|
|
356
|
+
<strong>Where on the page (${v.length} ${v.length===1?"spot":"spots"}):</strong> ${T}${g}
|
|
357
357
|
</p>
|
|
358
358
|
<details class="techy">
|
|
359
359
|
<summary>Technical reference for the developer</summary>
|
|
@@ -398,7 +398,7 @@ ${E.target.failureSummary}`:"");i.push(` <testcase ${k}>`),i.push(` <fai
|
|
|
398
398
|
<p>${l}</p>
|
|
399
399
|
|
|
400
400
|
<div class="summary">
|
|
401
|
-
<p style="margin: 0;"><strong>${f(
|
|
401
|
+
<p style="margin: 0;"><strong>${f(Et[a.risk])}.</strong> ${f($t[a.risk])}</p>
|
|
402
402
|
<div class="totals">
|
|
403
403
|
${a.totals.critical>0?`<span><strong>${a.totals.critical}</strong> critical</span>`:""}
|
|
404
404
|
${a.totals.serious>0?`<span><strong>${a.totals.serious}</strong> serious</span>`:""}
|
|
@@ -435,7 +435,7 @@ ${t==="letter"?`
|
|
|
435
435
|
Automated audits typically catch around 30–50% of accessibility issues; some categories require manual review.</p>
|
|
436
436
|
</footer>
|
|
437
437
|
</body>
|
|
438
|
-
</html>`}function
|
|
438
|
+
</html>`}function ia(e){if(!e||e.length===0)return`
|
|
439
439
|
<h2 id="manual">5. Manual assessment results</h2>
|
|
440
440
|
<p style="font-size: 10pt; color: #475569;">
|
|
441
441
|
No manual workflows were completed for this audit. Automated audits cover ~30–50% of WCAG;
|
|
@@ -445,7 +445,7 @@ ${t==="letter"?`
|
|
|
445
445
|
behaviors, error prevention, consistency).
|
|
446
446
|
When manual workflows are completed, their results are integrated into both the executive
|
|
447
447
|
summary's compliance grade and this section's per-criterion findings.
|
|
448
|
-
</p>`;const t=new Map(e.map(i=>[i.workflowId,i])),s=
|
|
448
|
+
</p>`;const t=new Map(e.map(i=>[i.workflowId,i])),s=fe.map(i=>ra(i,t.get(i.id))).filter(i=>i!==null).join("");let a=0,o=0,r=0,d=0;for(const i of fe){d+=i.steps.length;const u=t.get(i.id);if(u)for(const c of i.steps){const l=u.steps[c.id];l&&(r++,l.status==="fail"&&(c.severity==="required"?a++:o++))}}return`
|
|
449
449
|
<h2 id="manual">5. Manual assessment results</h2>
|
|
450
450
|
<p style="font-size: 10pt; color: #475569; margin-bottom: 8pt;">
|
|
451
451
|
Manual workflows performed by the auditor. ${r} of ${d} steps answered
|
|
@@ -453,7 +453,7 @@ ${t==="letter"?`
|
|
|
453
453
|
${a>0?`<strong style="color: #b91c1c;">${a} required step${a===1?"":"s"} failed</strong> — each is a confirmed WCAG breach contributing to the compliance grade above.`:"No required steps failed."}
|
|
454
454
|
${o>0?` ${o} advisory step${o===1?"":"s"} failed (best-practice findings).`:""}
|
|
455
455
|
</p>
|
|
456
|
-
${s}`}function
|
|
456
|
+
${s}`}function ra(e,t){if(!t)return null;const s=[];for(const u of e.steps){const c=t.steps[u.id];c&&s.push({step:u,status:c.status,notes:c.notes})}if(s.length===0)return null;const a=s.filter(u=>u.status==="pass").length,o=s.filter(u=>u.status==="fail").length,r=s.filter(u=>u.status==="skip").length,i=s.filter(u=>u.status==="fail").map(u=>`
|
|
457
457
|
<tr>
|
|
458
458
|
<td><span class="impact impact-${u.step.severity==="required"?"serious":"moderate"}">${f(u.step.severity)}</span></td>
|
|
459
459
|
<td>${u.step.wcag?`<code>${f(u.step.wcag)}</code>`:"—"}</td>
|
|
@@ -473,21 +473,21 @@ ${o>0?`
|
|
|
473
473
|
<thead><tr><th style="width: 80pt;">Severity</th><th style="width: 70pt;">WCAG</th><th>Failed check + auditor notes</th></tr></thead>
|
|
474
474
|
<tbody>${i}</tbody>
|
|
475
475
|
</table>
|
|
476
|
-
`:""}`}function
|
|
476
|
+
`:""}`}function Ht(e,t,s="defense",a,o,r){var w,y,C;const d=ta[s],i=((w=e[0])==null?void 0:w.componentId)??"unknown",u=X(e),c=e.reduce((m,I)=>m+I.durationMs,0),l=e.flatMap(m=>m.violations),p=a&&a.length>0?{runs:a,workflows:fe}:void 0,n=te(l,p),v=new Date,k=new Map;for(const m of l){const I=ce(m.ruleId,m.target.selector),R=`${m.currentState.pseudoState} · ${m.currentState.theme} · ${m.currentState.direction}`,U=k.get(I);if(U){U._states.includes(R)||U._states.push(R);continue}k.set(I,{...m,_states:[R]})}const E=Array.from(k.values()).sort((m,I)=>{const R={critical:0,serious:1,moderate:2,minor:3};return(R[m.impact]??99)-(R[I.impact]??99)}),$=Js(e),T=new Map;for(const m of $)for(const I of m.violations){const R=ce(I.ruleId,I.target.selector);T.has(R)||T.set(R,I._idx)}const g=E.map((m,I)=>`
|
|
477
477
|
<tr>
|
|
478
|
-
<td>${
|
|
478
|
+
<td>${I+1}</td>
|
|
479
479
|
<td><span class="impact impact-${f(m.impact)}">${f(m.impact)}</span></td>
|
|
480
480
|
<td><code>${f(m.ruleId)}</code></td>
|
|
481
481
|
<td>${f(m.wcagCriterion)} ${f(m.wcagLevel)}</td>
|
|
482
482
|
<td>${f(m.description)}</td>
|
|
483
483
|
<td><code class="sel">${f(m.target.selector)}</code></td>
|
|
484
484
|
<td>${f(m._states.join(", "))}</td>
|
|
485
|
-
</tr>`).join(""),S=new Set(l.map(m=>m.ruleId)),x=
|
|
486
|
-
<tr class="${
|
|
485
|
+
</tr>`).join(""),S=new Set(l.map(m=>m.ruleId)),x=jt.map(m=>{const{conformance:I,hits:R}=zt(m,S);return`
|
|
486
|
+
<tr class="${I==="Supports"?"supports":I==="Partially Supports"?"partial":"na"}">
|
|
487
487
|
<td><code>${f(m.ref)}</code></td>
|
|
488
488
|
<td>${f(m.title)} <span class="lvl">(${m.level})</span></td>
|
|
489
|
-
<td>${
|
|
490
|
-
<td>${
|
|
489
|
+
<td>${I}</td>
|
|
490
|
+
<td>${R.length>0?`Detected violations of: ${R.map(h=>`<code>${f(h)}</code>`).join(", ")}`:I==="Not Applicable"?"Not addressable by automated audit; requires manual review.":"No automated violations detected."}</td>
|
|
491
491
|
</tr>`}).join("");return`<!doctype html>
|
|
492
492
|
<html lang="en">
|
|
493
493
|
<head>
|
|
@@ -567,14 +567,14 @@ ${o>0?`
|
|
|
567
567
|
|
|
568
568
|
<h2 id="exec">1. Executive summary</h2>
|
|
569
569
|
<div class="exec">
|
|
570
|
-
<div class="exec-grade" style="background: ${
|
|
570
|
+
<div class="exec-grade" style="background: ${ea[n.letter]};">${n.letter}</div>
|
|
571
571
|
<div class="exec-body">
|
|
572
|
-
<span class="risk-tier" style="background: ${
|
|
573
|
-
${f(
|
|
572
|
+
<span class="risk-tier" style="background: ${Qs[n.risk]}; color: ${Zs[n.risk]};">
|
|
573
|
+
${f(Xs[n.risk])}
|
|
574
574
|
</span>
|
|
575
575
|
<p style="margin: 0 0 6pt;"><strong>${n.totals.unique} unique violations detected</strong> across ${e.length} state combinations
|
|
576
576
|
(${n.totals.critical} critical · ${n.totals.serious} serious · ${n.totals.moderate} moderate · ${n.totals.minor} minor).</p>
|
|
577
|
-
<p style="margin: 0; font-size: 10pt; color: #475569;">${f(
|
|
577
|
+
<p style="margin: 0; font-size: 10pt; color: #475569;">${f(un[n.risk])}</p>
|
|
578
578
|
</div>
|
|
579
579
|
</div>
|
|
580
580
|
${r&&(r.lead||r.body||r.closer)?`
|
|
@@ -594,13 +594,13 @@ ${r&&(r.lead||r.body||r.closer)?`
|
|
|
594
594
|
|
|
595
595
|
<h3>Per-category breakdown (top-6 lawsuit-magnet categories)</h3>
|
|
596
596
|
<div class="categories">
|
|
597
|
-
${n.categories.map(m=>{const
|
|
597
|
+
${n.categories.map(m=>{const I=m.status==="pass"?"✓":m.status==="fail"?"✗":"⚠",R=m.status==="pass"?"cat-pass":m.status==="fail"?"cat-fail":"cat-unchecked",U=m.status==="fail"?` (${m.violationCount})`:m.needsManualCheck?" — requires manual review":"";return`<div class="${R}">${I} ${f(m.label)}${f(U)}</div>`}).join("")}
|
|
598
598
|
</div>
|
|
599
599
|
|
|
600
600
|
<h2 id="methodology">2. Methodology & scope</h2>
|
|
601
601
|
<dl class="methodology">
|
|
602
602
|
<dt>Audit engine</dt>
|
|
603
|
-
<dd>axe-core ${f(((
|
|
603
|
+
<dd>axe-core ${f(((C=e[0])==null?void 0:C.axeVersion)??"n/a")} (industry-standard, used by Deque, IBM, Microsoft, and Google's Lighthouse).
|
|
604
604
|
Rule sets enabled: WCAG 2.0 A/AA, WCAG 2.1 A/AA, WCAG 2.2 A/AA, axe-core best-practice.</dd>
|
|
605
605
|
<dt>State coverage</dt>
|
|
606
606
|
<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>),
|
|
@@ -618,7 +618,7 @@ ${r&&(r.lead||r.body||r.closer)?`
|
|
|
618
618
|
</dl>
|
|
619
619
|
|
|
620
620
|
<h2 id="findings">3. Detailed findings</h2>
|
|
621
|
-
${
|
|
621
|
+
${E.length===0?"<p>No automated violations were detected during this audit.</p>":`<table>
|
|
622
622
|
<thead>
|
|
623
623
|
<tr><th>#</th><th>Impact</th><th>Rule</th><th>WCAG</th><th>Description</th><th>Selector</th><th>States</th></tr>
|
|
624
624
|
</thead>
|
|
@@ -626,18 +626,18 @@ ${R.length===0?"<p>No automated violations were detected during this audit.</p>"
|
|
|
626
626
|
</table>`}
|
|
627
627
|
|
|
628
628
|
<h2 id="evidence">4. Visual evidence (per-state screenshots)</h2>
|
|
629
|
-
${
|
|
629
|
+
${$.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 ${$.length} state${$.length===1?"":"s"} by violation count shown below. Red rectangles mark violation locations; numbers correspond to entries in §3.</p>`+$.map(m=>{const I=m.violations.filter(R=>R.target.boundingRect).map(R=>{const U=R.target.boundingRect,h=String(R._idx).length*8+14,b=22;return[`<rect class="violation-mark" x="${U.x}" y="${U.y}" width="${U.w}" height="${U.h}" />`,`<rect class="label-bg" x="${U.x}" y="${Math.max(0,U.y-b)}" width="${h}" height="${b}" rx="2" />`,`<text class="violation-num" x="${U.x+6}" y="${Math.max(0,U.y-b)+16}">${R._idx}</text>`].join("")}).join("");return`
|
|
630
630
|
<div class="evidence-state">
|
|
631
631
|
<h3>State: ${f(m.stateLabel)} (${m.violations.length} violation${m.violations.length===1?"":"s"})</h3>
|
|
632
632
|
<div class="evidence-frame">
|
|
633
|
-
<svg viewBox="0 0 ${
|
|
634
|
-
<image href="${m.screenshotDataUrl}" x="0" y="0" width="${
|
|
635
|
-
${
|
|
633
|
+
<svg viewBox="0 0 ${ft} ${gt}" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet">
|
|
634
|
+
<image href="${m.screenshotDataUrl}" x="0" y="0" width="${ft}" height="${gt}" preserveAspectRatio="xMidYMid slice" />
|
|
635
|
+
${I}
|
|
636
636
|
</svg>
|
|
637
637
|
</div>
|
|
638
638
|
</div>`}).join("")}
|
|
639
639
|
|
|
640
|
-
${
|
|
640
|
+
${ia(a)}
|
|
641
641
|
|
|
642
642
|
<h2 id="conformance">6. WCAG 2.1 conformance assessment</h2>
|
|
643
643
|
<p style="font-size: 10pt;">Per-criterion conformance based on automated detection. Conformance values follow the VPAT 2.5-INT vocabulary.${t&&t.baselineSnapshotMeta?" Comparison against an accepted baseline is available; see Section 6.":""}</p>
|
|
@@ -646,7 +646,7 @@ ${Ls(a)}
|
|
|
646
646
|
<tbody>${x}</tbody>
|
|
647
647
|
</table>
|
|
648
648
|
|
|
649
|
-
${(()=>{const m=o!==void 0,
|
|
649
|
+
${(()=>{const m=o!==void 0,I=m?"8":"7",R=m?"9":"8",U=m?`<h2 id="audit-history">7. Audit history</h2>
|
|
650
650
|
<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>
|
|
651
651
|
${o&&o.length>0?`<table>
|
|
652
652
|
<thead>
|
|
@@ -676,7 +676,7 @@ ${o&&o.length>0?`<table>
|
|
|
676
676
|
</tr>`).join("")}
|
|
677
677
|
</tbody>
|
|
678
678
|
</table>`:'<p style="color: #64748b; font-style: italic;">No prior audits recorded — this is the first audit on record for this installation.</p>'}
|
|
679
|
-
`:"",h=s==="evidence"?`<h2 id="remediation">${
|
|
679
|
+
`:"",h=s==="evidence"?`<h2 id="remediation">${I}. Recommended approach before legal action</h2>
|
|
680
680
|
<div style="background: #f0f9ff; border: 1px solid #38bdf8; padding: 12pt 16pt; border-radius: 4pt; font-size: 10.5pt;">
|
|
681
681
|
<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>
|
|
682
682
|
<p style="margin: 0 0 8pt;"><strong>Recommended steps before considering legal action:</strong></p>
|
|
@@ -687,7 +687,7 @@ ${o&&o.length>0?`<table>
|
|
|
687
687
|
<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>
|
|
688
688
|
</ol>
|
|
689
689
|
<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>
|
|
690
|
-
</div>`:`<h2 id="remediation">${
|
|
690
|
+
</div>`:`<h2 id="remediation">${I}. Remediation plan</h2>
|
|
691
691
|
<p>Each violation in §3 has a documented canonical fix pattern. The remediation workflow we recommend:</p>
|
|
692
692
|
<ol>
|
|
693
693
|
<li>Findings are classified by severity. Address critical and serious findings first.</li>
|
|
@@ -699,7 +699,7 @@ ${o&&o.length>0?`<table>
|
|
|
699
699
|
|
|
700
700
|
${h}
|
|
701
701
|
|
|
702
|
-
<h2 id="disclaimer">${
|
|
702
|
+
<h2 id="disclaimer">${R}. Disclaimer & limitations</h2>`})()}
|
|
703
703
|
<div class="disclaimer">
|
|
704
704
|
<p style="margin: 0 0 6pt;"><strong>${f(d.disclaimerHeading)}</strong></p>
|
|
705
705
|
<p style="margin: 0 0 6pt;">${d.disclaimerBody}</p>
|
|
@@ -712,4 +712,4 @@ ${h}
|
|
|
712
712
|
</footer>
|
|
713
713
|
|
|
714
714
|
</body>
|
|
715
|
-
</html>`}async function Fs(){const t=(await chrome.storage.local.get("aiConfig")).aiConfig;return{...pn,...t??{}}}async function ut(e,t,s){const a=await Fs();if(!a.enabled||!a.apiKey||e.length===0)return null;const o=e.flatMap(u=>u.violations),r=te(o),d=r.categories.filter(u=>u.status==="fail").sort((u,c)=>c.violationCount-u.violationCount).slice(0,3).map(u=>u.label),i=e[0];return Mt({framing:t,riskTier:r.risk,grade:r.letter,totals:{critical:r.totals.critical,serious:r.totals.serious,moderate:r.totals.moderate,minor:r.totals.minor,unique:r.totals.unique},riskDrivers:d,pageUrl:(i==null?void 0:i.pageUrl)??(i==null?void 0:i.scope)??"",auditDate:(i==null?void 0:i.startedAt)??new Date().toISOString(),priorAuditCount:s},a)}function Ws(){return N("EXPORT_REQUEST",async e=>{if(e.format==="sarif")return{type:"EXPORT_RESPONSE",format:"sarif",content:JSON.stringify(xs(e.results,e.delta),null,2)};if(e.format==="junit")return{type:"EXPORT_RESPONSE",format:"junit",content:As(e.results,e.delta)};if(e.format==="html-print")return{type:"EXPORT_RESPONSE",format:"html-print",content:Ss(e.results,e.delta)};if(e.format==="vpat")return{type:"EXPORT_RESPONSE",format:"vpat",content:ks(e.results,e.delta)};if(e.format==="ai-prompt")return{type:"EXPORT_RESPONSE",format:"ai-prompt",content:Es(e.results,e.delta,e.manualRuns,e.siteCrawlReport,e.dismissedKeys)};if(e.format==="defense-bundle"){const t=await et(),s=await ut(e.results,"defense",t.length);return{type:"EXPORT_RESPONSE",format:"defense-bundle",content:Lt(e.results,e.delta,"defense",e.manualRuns,t,s)}}if(e.format==="evidence-bundle"){const t=await et(),s=await ut(e.results,"evidence",t.length);return{type:"EXPORT_RESPONSE",format:"evidence-bundle",content:Us(e.results,e.delta,e.manualRuns,t,s)}}return e.format==="developer-letter"?{type:"EXPORT_RESPONSE",format:"developer-letter",content:Ds(e.results)}:e.format==="owner-report"?{type:"EXPORT_RESPONSE",format:"owner-report",content:Ps(e.results)}:e.format==="ai-prompt-owner"?{type:"EXPORT_RESPONSE",format:"ai-prompt-owner",content:Ns(e.results)}:{type:"EXPORT_RESPONSE",format:"json",content:JSON.stringify(vs(e.results,e.delta),null,2)}})}const js=G("support-messenger"),zs="https://api.wcagcheckr.com/v1/products/wcagcheckr/support",Ee="support:rate-window",Vs=5,Hs=10*60*1e3,qs=2,Bs="wcagcheckr",pt="storybook:lastDetected";async function Ks(e){if(!e)return;const t=await chrome.runtime.getPlatformInfo(),a=(await chrome.storage.local.get(pt))[pt];return{extensionVersion:chrome.runtime.getManifest().version,platform:t,storybookDetected:a!=null&&a.detected?a.version??"detected":"none",licenseTier:await At(),logTail:Kt(Yt())}}async function Ys(e=Date.now()){const s=((await chrome.storage.local.get(Ee))[Ee]??[]).filter(a=>e-a<Hs);return s.length>=Vs?!1:(s.push(e),await chrome.storage.local.set({[Ee]:s}),!0)}async function Gt(e,t=0){try{const s=await fetch(zs,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!s.ok)throw new Error(`http ${s.status}`);const a=await s.json();if(!a.success)throw new Error(a.error??"server returned success=false");return{ticketRef:a.ticketRef??""}}catch(s){if(t<qs){const a=(t+1)*1e3;return await new Promise(o=>setTimeout(o,a)),Gt(e,t+1)}throw s}}function Js(){return N("SUPPORT_MESSAGE_REQUEST",async e=>{if(!await Ys())return{type:"SUPPORT_MESSAGE_RESPONSE",success:!1,error:pe("NETWORK","Rate limit reached. Try again in 10 minutes.",!0)};try{const t=await Ks(e.includeContext);return{type:"SUPPORT_MESSAGE_RESPONSE",success:!0,ticketRef:(await Gt({productSlug:Bs,subject:e.subject,body:e.body,replyEmail:e.replyEmail,context:t,extensionVersion:chrome.runtime.getManifest().version,timestamp:new Date().toISOString()})).ticketRef}}catch(t){js.error("send failed",t);const s=t instanceof Error?t.message:String(t);return{type:"SUPPORT_MESSAGE_RESPONSE",success:!1,error:pe("NETWORK",s,!0)}}})}const Ft=G("ai-color-suggester");async function Xs(e,t,s=[]){if(!t.enabled||e.length===0)return{results:[],totalCostUsd:0,capExceeded:!1};const a=q(t);if(!a.ok)return{results:[],totalCostUsd:0,capExceeded:!1};const o=a.client,r=B(t.costCapUsd),d=[];for(const i of e){if(!r.canCharge())break;try{const u=await o.suggestColorFix({foreground:i.foreground,background:i.background,fontSize:i.fontSize,fontWeight:i.fontWeight,targetLevel:i.targetLevel,paletteHints:s});r.recordCharge(u.costUsd),u.verdict==="suggested"&&u.candidates.length>0&&d.push({matchKey:i.matchKey,suggestions:u.candidates,reasoning:u.reasoning,costUsd:u.costUsd})}catch(u){Ft.warn(`color-fix suggestion failed for ${i.matchKey}`,u)}}return{results:d,totalCostUsd:r.state.spentUsd,capExceeded:r.state.exceeded}}async function Qs(){const e=await chrome.storage.local.get("aiConfig");return je(e.aiConfig)}function Zs(){return N("AI_COLOR_SUGGEST_REQUEST",async e=>{const t=await Qs();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:s,totalCostUsd:a,capExceeded:o}=await Xs(e.inputs,t,e.paletteHints);return{type:"AI_COLOR_SUGGEST_RESPONSE",results:s,totalCostUsd:a,capExceeded:o}}catch(s){return Ft.warn("color-suggest handler failed",s),{type:"AI_COLOR_SUGGEST_RESPONSE",results:[],totalCostUsd:0,capExceeded:!1,unavailableReason:s instanceof Error?s.message:String(s)}}})}const ye={A:0,B:1,C:2,D:3,F:4},ht={low:0,moderate:1,high:2,critical:3};function ea(e,t){return ye[e]>ye[t]?e:t}function ta(e,t){return ht[e]>ht[t]?e:t}function na(e){const t=[];for(const s of e)for(const a of s.results)for(const o of a.violations)t.push({url:s.url,v:o});return t}function sa(e,t,s,a,o){const r=t.filter(g=>!g.error).length,d=t.filter(g=>!!g.error).length,i=t.map(g=>{if(g.error)return{url:g.url,grade:"F",risk:"critical",uniqueViolations:0,totals:{critical:0,serious:0,moderate:0,minor:0},durationMs:g.durationMs,error:g.error};const S=g.results.flatMap(w=>w.violations),x=te(S);return{url:g.url,grade:x.letter,risk:x.risk,uniqueViolations:x.totals.unique,totals:{critical:x.totals.critical,serious:x.totals.serious,moderate:x.totals.moderate,minor:x.totals.minor},durationMs:g.durationMs}});i.sort((g,S)=>{const x=ye[S.grade]-ye[g.grade];return x!==0?x:S.uniqueViolations-g.uniqueViolations});let u="A",c="low";for(const g of i)u=ea(u,g.grade),c=ta(c,g.risk);const l=na(t),p=g=>`${g.ruleId}::${g.target.selector}`,n=new Map;for(const{v:g}of l){const S=p(g);n.has(S)||n.set(S,g)}const v=n.size,E={critical:0,serious:0,moderate:0,minor:0};for(const g of n.values())E[g.impact]++;const R=new Map;for(const{url:g,v:S}of l){let x=R.get(S.ruleId);x||(x={ruleId:S.ruleId,description:S.description,impact:S.impact,urls:new Set,totalOccurrences:0},R.set(S.ruleId,x)),x.urls.add(g),x.totalOccurrences++}const k=Array.from(R.values()).map(g=>({ruleId:g.ruleId,description:g.description,impact:g.impact,urlsAffected:g.urls.size,totalOccurrences:g.totalOccurrences,sampleUrls:Array.from(g.urls).slice(0,5)})).sort((g,S)=>S.urlsAffected!==g.urlsAffected?S.urlsAffected-g.urlsAffected:S.totalOccurrences-g.totalOccurrences),$={A:0,B:0,C:0,D:0,F:0};for(const g of i)$[g.grade]++;return{startedAt:s,finishedAt:a,startUrl:e,pagesAudited:r,pagesFailed:d,totalDurationMs:o,siteGrade:u,siteRisk:c,totalUniqueViolations:v,totals:E,topViolations:k,pages:i,pagesByGrade:$}}function aa(e){const t=new Map(e.breakpointPresets.map(a=>[a.id,a])),s=[];for(const a of e.breakpoints){const o=t.get(a);if(o)for(const r of e.directions)for(const d of e.themes){const i=e.ariaVariations.length===0?[null]:e.ariaVariations;for(const u of i)for(const c of e.pseudoStates)s.push({pseudoState:c,ariaVariation:u,theme:d,direction:r,breakpoint:o})}}return s}const oa=G("ai-alt-text");async function ia(e,t){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 a=s.client,o=B(t.costCapUsd),r=[],d=[],i=Math.max(1,t.maxCandidatesPerCheck??10);for(const u of e.slice(0,i)){if(!o.canCharge())break;try{const c=await a.judgeAltText({imageUrl:u.imageUrl,alt:u.alt,surroundingContext:u.surroundingContext});o.recordCharge(c.costUsd),(c.verdict==="fail"||c.verdict==="uncertain"&&c.confidence<.6)&&r.push(await ra(u,c))}catch(c){const l=c instanceof Error?c.message:String(c);d.push(`${u.selector}: ${l}`),oa.warn("alt-text judgment failed",c)}}return{findings:r,totalCostUsd:o.state.spentUsd,capExceeded:o.state.exceeded,errors:d}}async function ra(e,t){const s=t.verdict==="uncertain"?"ai-alt-uncertain":"ai-alt-misleading",a={selector:e.selector,outerHTML:e.outerHTML.slice(0,500),failureSummary:`AI assessment: ${t.reasoning}`,tagName:"IMG",role:null,accessibleName:e.alt,textSnippet:null,attrId:null,attrTestid:null},o=await Ne({ruleId:s,componentId:e.componentId,currentState:e.currentState,target:a});return{ruleId:s,wcagCriterion:"wcag111",wcagLevel:"A",impact:t.verdict==="uncertain"?"minor":"serious",description:t.verdict==="uncertain"?"AI couldn't verify whether this alt text is appropriate — flag for human review.":"AI judged the alt text to be misleading, generic, or otherwise inappropriate.",helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/non-text-content.html",target:a,componentId:e.componentId,currentState:e.currentState,axeVersion:e.axeVersion,detectedAt:new Date().toISOString(),matchKey:o,ai:{reasoning:t.reasoning,confidence:t.confidence,model:t.model,costUsd:t.costUsd}}}const ie=G("ai-text");async function ca(e,t,s){if(!s.enabled||!s.enabledChecks.headings)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=q(s);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,r=B(s.costCapUsd),d=[],i=[],u=Math.max(1,s.maxCandidatesPerCheck??10);for(const c of e.slice(0,u)){if(!r.canCharge())break;try{const l=await o.judgeHeading({headingText:c.text,sectionContent:c.sectionContent});r.recordCharge(l.costUsd),l.verdict==="fail"&&l.confidence>=.6&&d.push(await re("ai-heading-not-descriptive","wcag246","AA","moderate","AI judged this heading does not describe its section content.",c.selector,c.outerHTML,c.text,l,t))}catch(l){const p=l instanceof Error?l.message:String(l);i.push(`${c.selector}: ${p}`),ie.warn("heading judgment failed",l)}}return{findings:d,totalCostUsd:r.state.spentUsd,capExceeded:r.state.exceeded,errors:i}}async function la(e,t,s){if(!s.enabled||!s.enabledChecks.sensory)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=q(s);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,r=B(s.costCapUsd),d=[],i=[],u=Math.max(1,s.maxCandidatesPerCheck??10);for(const c of e.slice(0,u)){if(!r.canCharge())break;try{const l=await o.judgeSensoryLanguage({instructionText:c.text});r.recordCharge(l.costUsd),l.verdict==="fail"&&l.confidence>=.6&&d.push(await re("ai-sensory-instruction","wcag133","A","serious","AI flagged this instruction as relying on sensory characteristics (shape, color, location) without a programmatic identifier.",c.selector,c.outerHTML,c.text.slice(0,100),l,t))}catch(l){const p=l instanceof Error?l.message:String(l);i.push(`${c.selector}: ${p}`),ie.warn("sensory judgment failed",l)}}return{findings:d,totalCostUsd:r.state.spentUsd,capExceeded:r.state.exceeded,errors:i}}async function da(e,t,s){if(!s.enabled||!s.enabledChecks.aria)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=q(s);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,r=B(s.costCapUsd),d=[],i=[],u=Math.max(1,s.maxCandidatesPerCheck??10);for(const c of e.slice(0,u)){if(!r.canCharge())break;try{const l=await o.judgeAriaSemantics({elementHtml:c.outerHTML,computedRole:c.role,surroundingHtml:c.surroundingHtml});r.recordCharge(l.costUsd),l.verdict==="fail"&&l.confidence>=.65&&d.push(await re("ai-aria-misuse","wcag412","A","serious",`AI judged role="${c.role}" is being used inappropriately on this element.`,c.selector,c.outerHTML,c.role,l,t))}catch(l){const p=l instanceof Error?l.message:String(l);i.push(`${c.selector}: ${p}`),ie.warn("aria judgment failed",l)}}return{findings:d,totalCostUsd:r.state.spentUsd,capExceeded:r.state.exceeded,errors:i}}async function ua(e,t,s){if(!s.enabled||!s.enabledChecks.genericLinkText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=q(s);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,r=B(s.costCapUsd),d=[],i=[],u=Math.max(1,s.maxCandidatesPerCheck??10);for(const c of e.slice(0,u)){if(!r.canCharge())break;try{const l=await o.judgeGenericLinkText({linkText:c.linkText,surroundingText:c.surroundingText});r.recordCharge(l.costUsd),l.verdict==="fail"&&l.confidence>=.6&&d.push(await re("ai-generic-link-text","wcag244","A","serious","AI judged this link's purpose is not clear from its text or surrounding context.",c.selector,c.outerHTML,c.linkText,l,t))}catch(l){const p=l instanceof Error?l.message:String(l);i.push(`${c.selector}: ${p}`),ie.warn("generic-link judgment failed",l)}}return{findings:d,totalCostUsd:r.state.spentUsd,capExceeded:r.state.exceeded,errors:i}}async function pa(e,t,s){if(!s.enabled||!s.enabledChecks.wallOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=q(s);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,r=B(s.costCapUsd),d=[],i=[],u=Math.max(1,s.maxCandidatesPerCheck??10);for(const c of e.slice(0,u)){if(!r.canCharge())break;try{const l=await o.judgeWallOfText({blockText:c.blockText,structuralChildrenCount:c.structuralChildrenCount});r.recordCharge(l.costUsd),l.verdict==="fail"&&l.confidence>=.6&&d.push(await re("ai-wall-of-text","wcag315","AAA","moderate","AI flagged this block as a wall of text with insufficient structural breaks (subheadings, lists, paragraphs).",c.selector,c.outerHTML,c.blockText.slice(0,80),l,t))}catch(l){const p=l instanceof Error?l.message:String(l);i.push(`${c.selector}: ${p}`),ie.warn("wall-of-text judgment failed",l)}}return{findings:d,totalCostUsd:r.state.spentUsd,capExceeded:r.state.exceeded,errors:i}}async function ha(e,t,s){if(!s.enabled||!s.enabledChecks.languageMismatch)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=q(s);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,r=B(s.costCapUsd),d=[],i=[],u=Math.max(1,s.maxCandidatesPerCheck??10);for(const c of e.slice(0,u)){if(!r.canCharge())break;try{const l=await o.judgeLanguageMismatch({declaredLang:c.declaredLang,bodyTextSample:c.bodyTextSample});r.recordCharge(l.costUsd),l.verdict==="fail"&&l.confidence>=.7&&d.push(await re("ai-language-mismatch","wcag311","A","serious",`AI judged the page's content does not match the declared lang="${c.declaredLang||"(none)"}".`,c.selector,c.outerHTML,c.bodyTextSample.slice(0,80),l,t))}catch(l){const p=l instanceof Error?l.message:String(l);i.push(`${c.selector}: ${p}`),ie.warn("language-mismatch judgment failed",l)}}return{findings:d,totalCostUsd:r.state.spentUsd,capExceeded:r.state.exceeded,errors:i}}async function re(e,t,s,a,o,r,d,i,u,c){const l={selector:r,outerHTML:d.slice(0,500),failureSummary:`AI assessment: ${u.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:i.slice(0,100)||null,attrId:null,attrTestid:null},p=await Ne({ruleId:e,componentId:c.componentId,currentState:c.currentState,target:l});return{ruleId:e,wcagCriterion:t,wcagLevel:s,impact:a,description:o,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:l,componentId:c.componentId,currentState:c.currentState,axeVersion:c.axeVersion,detectedAt:new Date().toISOString(),matchKey:p,ai:{reasoning:u.reasoning,confidence:u.confidence,model:u.model,costUsd:u.costUsd}}}const Wt=G("ai-vision");async function fa(e,t,s){if(!s.enabled||!s.enabledChecks.imageOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=q(s);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,r=B(s.costCapUsd),d=Math.max(1,s.imageOfTextMaxImages??5),i=[...e].sort((l,p)=>p.pixelArea-l.pixelArea).slice(0,d),u=[],c=[];for(const l of i){if(!r.canCharge())break;try{const p=await o.judgeImageOfText({imageUrl:l.imageUrl,accessibleName:l.accessibleName});r.recordCharge(p.costUsd),p.verdict==="fail"&&p.confidence>=.65&&u.push(await jt("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.",l.selector,l.outerHTML,l.accessibleName??"",p,t))}catch(p){const n=p instanceof Error?p.message:String(p);c.push(`${l.selector}: ${n}`),Wt.warn("image-of-text judgment failed",p)}}return{findings:u,totalCostUsd:r.state.spentUsd,capExceeded:r.state.exceeded,errors:c}}async function ga(e,t,s){if(!s.enabled||!s.enabledChecks.colorOnlyMeaning)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=q(s);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[a.reason]};const o=a.client,r=B(s.costCapUsd),d=[],i=[],u=Math.max(1,s.maxCandidatesPerCheck??10);for(const c of e.slice(0,u)){if(!r.canCharge())break;try{const l=await o.judgeColorOnlyMeaning({elementHtml:c.elementHtml,contextDescription:c.contextDescription});r.recordCharge(l.costUsd),l.verdict==="fail"&&l.confidence>=.65&&d.push(await jt("ai-color-only-meaning","wcag141","A","serious","AI judged this region conveys information only through color (no text label, icon glyph, or shape difference).",c.selector,c.outerHTML,c.contextDescription,l,t))}catch(l){const p=l instanceof Error?l.message:String(l);i.push(p),Wt.warn("color-only judgment failed",l)}}return{findings:d,totalCostUsd:r.state.spentUsd,capExceeded:r.state.exceeded,errors:i}}async function jt(e,t,s,a,o,r,d,i,u,c){const l={selector:r,outerHTML:d.slice(0,500),failureSummary:`AI assessment: ${u.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:i.slice(0,100)||null,attrId:null,attrTestid:null},p=await Ne({ruleId:e,componentId:c.componentId,currentState:c.currentState,target:l});return{ruleId:e,wcagCriterion:t,wcagLevel:s,impact:a,description:o,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:l,componentId:c.componentId,currentState:c.currentState,axeVersion:c.axeVersion,detectedAt:new Date().toISOString(),matchKey:p,ai:{reasoning:u.reasoning,confidence:u.confidence,model:u.model,costUsd:u.costUsd}}}const De=G("ensure-content-script"),ma=1500,ya=300;async function zt(e,t){if(await ft(e,t))return{ok:!0};const s=chrome.runtime.getManifest(),a=[];for(const o of s.content_scripts??[])Array.isArray(o.js)&&a.push(...o.js);if(a.length===0)return De.error("manifest declares no content_scripts"),{ok:!1,reason:"inject-failed",detail:"no content_scripts declared in manifest"};try{const o=t!==void 0?{tabId:e,frameIds:[t]}:{tabId:e,allFrames:!0};await chrome.scripting.executeScript({target:o,files:a})}catch(o){return ba(o)}return await wa(ya),await ft(e,t)?(De.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 ft(e,t){try{const s=t!==void 0?{frameId:t}:void 0,a=await Promise.race([chrome.tabs.sendMessage(e,{type:"PING_REQUEST"},s),new Promise((o,r)=>setTimeout(()=>r(new Error("ping timeout")),ma))]);return(a==null?void 0:a.type)==="PING_RESPONSE"}catch{return!1}}function ba(e){const t=e instanceof Error?e.message:String(e);return/cannot access contents of (?:the page|url)/i.test(t)||/chrome:\/\//i.test(t)||/chrome-extension:\/\//i.test(t)||/devtools:\/\//i.test(t)||/chrome\.google\.com\/webstore/i.test(t)||/the extensions gallery/i.test(t)?{ok:!1,reason:"restricted-url",detail:t}:/no tab with id/i.test(t)||/tab .* was closed/i.test(t)?{ok:!1,reason:"tab-gone",detail:t}:(De.warn("content-script injection failed",e),{ok:!1,reason:"inject-failed",detail:t})}function wa(e){return new Promise(t=>setTimeout(t,e))}function Vt(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 Te=G("forensic-anchor-client"),va="https://api.wcagcheckr.com",xa="wcagcheckr",Aa=15e3;function Sa(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 ka(e,t){const s={auditHash:e.hash,pageUrl:e.pageUrl,capturedAt:e.capturedAt,...t?{licenseId:t}:{}},a=`${va}/v1/products/${xa}/forensic/anchor`;try{const o=await fetch(a,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s),signal:AbortSignal.timeout(Aa)});if(!o.ok)return Te.warn(`anchor HTTP ${o.status}; entry stays local-only`),null;const r=await o.json();return Sa(r)?r:(Te.warn("anchor returned malformed receipt; entry stays local-only",r),null)}catch(o){return Te.warn("anchor request failed; entry stays local-only",o),null}}const O=G("flows");async function Ke(){const e=await We();if(!e)throw new Error("no audit target tab found");return e}async function $a(){var s,a,o,r,d;const e=await Be("stateMatrix",Re);return((s=e.pseudoStates)==null?void 0:s.length)>0&&((a=e.themes)==null?void 0:a.length)>0&&((o=e.directions)==null?void 0:o.length)>0&&((r=e.breakpoints)==null?void 0:r.length)>0&&((d=e.breakpointPresets)==null?void 0:d.length)>0?e:(O.warn("stored stateMatrix is invalid; using defaults"),Re)}async function Ea(){const e=await Be("runsPerState",1);return typeof e!="number"||e<1||e>5?1:Math.floor(e)}async function Ta(e,t,s){const a=t.themes.includes("light"),o=t.themes.includes("dark");if(!a||!o)return t;let r=null;try{r=await W(e,{type:"THEME_AWARENESS_REQUEST"},s)}catch{return t}return!r||r.hasUnreadableSheets?t:r.hasDarkModeCss&&!r.hasLightModeCss?(O.info("theme-axis prune: site is dark-only, skipping light pass"),{...t,themes:t.themes.filter(d=>d!=="light")}):r.hasLightModeCss&&!r.hasDarkModeCss?(O.info("theme-axis prune: site is light-only, skipping dark pass"),{...t,themes:t.themes.filter(d=>d!=="dark")}):t}function Ca(e){if(e.length<=1)return e[0]??[];const t=new Map;return e.forEach((s,a)=>{for(const o of s){const r=`${o.ruleId}::${o.target.selector}`,d=t.get(r);d?d.runIndices.add(a):t.set(r,{v:o,runIndices:new Set([a])})}}),Array.from(t.values()).map(({v:s,runIndices:a})=>({...s,flakyAcrossRuns:a.size<e.length}))}async function Pe(e,t,s,a){var k;const o=await Ke(),r=await zt(o,s);if(!r.ok){D({type:"AUDIT_FAILED_EVENT",error:{code:r.reason==="restricted-url"?"RESTRICTED_URL":"CONTENT_SCRIPT_UNREACHABLE",message:Vt(r),recoverable:r.reason!=="restricted-url"}});return}const d=a??await $a(),i=await Ta(o,d,s),u=aa(i),c=await Ea();await Le({sessionId:Ge(),mode:"single-element",scope:e,startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"}),await Z(o,{type:"LIVE_REGIONS_ARM_REQUEST"},s).catch(()=>{}),await Z(o,{type:"FOCUS_TRACE_ARM_REQUEST"},s).catch(()=>{}),await W(o,{type:"WARMUP_REQUEST"},s).catch(()=>{});const l=[],p=[];let n="",v,E=!1,R=null;try{for(let $=0;$<u.length;$++){if(t.isCanceled()){O.info("single audit canceled by user");break}const g=u[$];try{const S=await _e(o,g,e,s);if(!S.success){v=(k=S.error)==null?void 0:k.message,O.warn(`state ${$+1}/${u.length} drive failed`,v);continue}g.breakpoint.id!==R&&(await W(o,{type:"WARMUP_REQUEST"},s).catch(()=>{}),R=g.breakpoint.id);const x=[];let w=null;for(let y=0;y<c;y++){const T=await W(o,{type:"AUDIT_REQUEST",selector:e,currentState:g},s);T.success&&T.result?(x.push(T.result.violations),w=T.result):T.error&&(v=T.error.message,O.warn(`state ${$+1}/${u.length} run ${y+1} failed`,v))}if(w&&x.length>0){E=!0;const y=Ca(x),T=await Rn(o,{quality:75})??void 0;let m,C;try{m=(await W(o,{type:"LIVE_REGIONS_DRAIN_REQUEST"},s)).announcements}catch{}try{C=(await W(o,{type:"FOCUS_TRACE_DRAIN_REQUEST"},s)).focusEvents}catch{}p.push({...w,violations:y,frameId:s,announcements:m,focusEvents:C,screenshotDataUrl:T}),l.push(...y),n=w.componentId}}catch(S){v=S instanceof Error?S.message:String(S),O.warn(`state ${$+1}/${u.length} threw`,S)}D({type:"AUDIT_PROGRESS_EVENT",current:$+1,total:u.length,currentState:g,componentId:n||void 0}),await Fe({})}if(E&&n){const $={pseudoState:"default",ariaVariation:null,theme:"light",direction:"ltr",breakpoint:{id:"desktop",label:"Desktop",width:1280,height:800,deviceScaleFactor:1,mobile:!1}};try{await _e(o,$,e,s),await new Promise(w=>setTimeout(w,200))}catch(w){O.debug("reference-state drive failed; analyzers will use last matrix state",w)}try{const w=await W(o,{type:"READING_ORDER_REQUEST",selector:e},s);w.issues.length>0&&p[0]&&(p[0].readingOrderIssues=w.issues,p[0].positionAnalysisCapturedAt=$)}catch(w){O.debug("reading-order analysis skipped",w)}try{const w=await W(o,{type:"TAB_ORDER_ANALYZE_REQUEST"},s);w.issues.length>0&&p[0]&&(p[0].tabOrderIssues=w.issues,p[0].positionAnalysisCapturedAt=$)}catch(w){O.debug("tab-order analysis skipped",w)}try{const w=await W(o,{type:"TYPOGRAPHY_ANALYZE_REQUEST",selector:e},s);w.issues.length>0&&p[0]&&(p[0].typographyIssues=w.issues)}catch(w){O.debug("typography analysis skipped",w)}try{const w=await W(o,{type:"CUSTOM_PROPERTY_REQUEST"},s);w.undefined.length>0&&p[0]&&(p[0].undefinedCustomProperties=w.undefined)}catch(w){O.debug("custom-property analysis skipped",w)}try{const w=await Be("aiConfig",{}),y=je(w);y.enabled&&y.apiKey&&await Fa(o,e,s,y,p,n,t)}catch(w){O.warn("ai augmentation skipped",w)}const g=p.flatMap(w=>w.announcements??[]),S=p.flatMap(w=>w.focusEvents??[]),x=await _t(n,l,{announcements:g,focusEvents:S},d);try{const w=te(l),y=p.reduce((m,C)=>m+C.durationMs,0),T=await an(p,w,y);if(T){const m=await At();if(un(m,"forensicAnchoring"))try{const C=await on(),I=await ka(T,C??void 0);I&&await rn(T.componentId,T.capturedAt,I)}catch(C){O.warn("forensic anchoring failed (entry stays local-only)",C)}}}catch(w){O.warn("forensic log recording failed",w)}D({type:"AUDIT_COMPLETE_EVENT",componentId:n,results:p,delta:x})}else D({type:"AUDIT_FAILED_EVENT",error:{code:"AXE_FAILED",message:v?`All audits failed. Last error: ${v}`:"No state produced results. Selector may not exist in the audit target frame.",recoverable:!0}});await fe()}catch($){O.error("single audit failed",$),D({type:"AUDIT_FAILED_EVENT",componentId:n||void 0,error:en($)?$.payload:{code:"UNKNOWN",message:String($),recoverable:!1}})}finally{await Z(o,{type:"LIVE_REGIONS_DISARM_REQUEST"},s).catch(()=>{}),await Z(o,{type:"FOCUS_TRACE_DISARM_REQUEST"},s).catch(()=>{}),await Ct(o,e,s)}}async function Ia(e){var t;try{const a=(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{var r;const o=window;return{detected:typeof o.__STORYBOOK_PREVIEW__=="object",version:(r=o.__STORYBOOK_PREVIEW__)==null?void 0:r.version}}})).find(o=>{var r;return(r=o.result)==null?void 0:r.detected});if(a)return{detected:!0,version:(t=a.result)==null?void 0:t.version,frameId:a.frameId}}catch(s){O.warn("storybook detection scripting failed",s)}return{detected:!1}}async function Ra(e){const t=await Ke(),s=await Ia(t);if(await chrome.storage.local.set({"storybook:lastDetected":{detected:s.detected,version:s.version,detectedAt:new Date().toISOString()}}),!s.detected){D({type:"AUDIT_FAILED_EVENT",error:{code:"STORYBOOK_NOT_DETECTED",message:"Storybook not detected on this tab. Open a Storybook page (URL with iframe.html or storybook-static) and try again.",recoverable:!0}});return}const a=s.frameId??0,o=await Oa(t,a);if(!o.length){O.warn("storybook detected but no stories returned"),D({type:"AUDIT_FAILED_EVENT",error:{code:"STORYBOOK_NOT_DETECTED",message:"Storybook detected but no stories were returned. Try reloading the page.",recoverable:!0}});return}await Le({sessionId:Ge(),mode:"storybook-all",totalStories:o.length,completedStories:[],startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});for(const r of o){if(e.isCanceled()){O.info("storybook iteration canceled by user");break}try{await _a(t,a,r.id);const i=await Z(t,{type:"STORYBOOK_GET_STORY_TARGET_REQUEST",tabId:t},a);await Pe(i.selector,e,a)}catch(i){O.warn(`story ${r.id} failed; continuing with next`,i)}const d=await cn();if((d==null?void 0:d.state)==="interrupted"){O.info("storybook iteration interrupted");break}await Fe({completedStories:[...(d==null?void 0:d.completedStories)??[],r.id]})}D({type:"SCORECARD_UPDATED_EVENT"}),await fe()}async function Oa(e,t){var s;try{return((s=(await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",func:async()=>{var u,c;const o=window.__STORYBOOK_PREVIEW__;if(!o)return[];const r=o.storyStoreValue??o.storyStore;if(!r)return[];typeof r.cacheAllCSFFiles=="function"&&await r.cacheAllCSFFiles();const d=((u=r.extract)==null?void 0:u.call(r))??((c=r.cachedCSFFiles)==null?void 0:c.call(r));if(!d)return[];const i=[];if(d instanceof Map)for(const[l,p]of d){const n=p;i.push({id:String(l),name:n.name??String(l),kind:n.title??n.kind??""})}else if(typeof d=="object"&&d!==null)for(const[l,p]of Object.entries(d)){const n=p;i.push({id:l,name:n.name??l,kind:n.title??n.kind??""})}return i}}))[0])==null?void 0:s.result)??[]}catch(a){return O.warn("listStoriesViaMainWorld failed",a),[]}}async function _a(e,t,s){try{await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",args:[s,3e3],func:async(a,o)=>{const r=new URL(window.location.href);r.searchParams.set("id",a),r.searchParams.set("viewMode","story"),window.location.href!==r.href&&(window.history.pushState({},"",r.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(d=>{const i=setTimeout(()=>d(),o),u=()=>{clearTimeout(i),d()};window.addEventListener("storyrendered",u,{once:!0})})}})}catch(a){O.warn(`navigateToStoryViaMainWorld(${s}) failed`,a)}}const Ma=200,Ua=6e4;function gt(e,t){try{return new URL(e).origin===new URL(t).origin}catch{return!1}}function Da(e,t){try{const s=new URL(e,t);return s.hash="",s.toString()}catch{return null}}async function Pa(e,t,s){await chrome.tabs.update(e,{url:t}),await new Promise((a,o)=>{const r=setTimeout(()=>{chrome.tabs.onUpdated.removeListener(d),o(new Error(`navigation timeout (${s}ms): ${t}`))},s);function d(i,u){i===e&&u.status==="complete"&&(clearTimeout(r),chrome.tabs.onUpdated.removeListener(d),a())}chrome.tabs.onUpdated.addListener(d)}),await new Promise(a=>setTimeout(a,400))}async function Na(e){var t;try{return((t=(await chrome.scripting.executeScript({target:{tabId:e},func:()=>Array.from(document.querySelectorAll("a[href]")).map(a=>a.getAttribute("href")).filter(a=>typeof a=="string"&&!a.startsWith("javascript:"))}))[0])==null?void 0:t.result)??[]}catch(s){return O.debug("link discovery failed",s),[]}}async function La(e,t,s){var n;const a=await Ke(),o=Math.min(t.maxPages??25,Ma),r=t.includeRegex?mt(t.includeRegex):null,d=t.excludeRegex?mt(t.excludeRegex):null,i=new Date().toISOString(),u=Date.now(),c=[e],l=new Set,p=[];await Le({sessionId:Ge(),mode:"storybook-all",scope:e,startedAt:i,lastProgressAt:new Date().toISOString(),state:"running"});try{for(;c.length>0&&l.size<o;){if(s.isCanceled()){O.info("site crawl canceled by user");break}const k=c.shift();if(!k||l.has(k)||!gt(k,e)||r&&!r.test(k)||d&&d.test(k))continue;l.add(k);const $=l.size,g=Date.now();D({type:"SITE_CRAWL_PROGRESS_EVENT",current:$,total:Math.min(o,l.size+c.length),url:k,status:"auditing"});try{await Pa(a,k,Ua);const S=await zt(a,0);if(!S.ok)throw new Error(Vt(S));await W(a,{type:"WARMUP_REQUEST"},0).catch(()=>{});const x=await W(a,{type:"AUDIT_REQUEST",selector:"html",currentState:{pseudoState:"default",ariaVariation:null,theme:"light",direction:"ltr",breakpoint:{id:"desktop",label:"Desktop",width:1280,height:800,deviceScaleFactor:1,mobile:!1}}},0),w=Date.now()-g;if(x.success&&x.result)p.push({url:k,results:[x.result],delta:null,componentId:x.result.componentId,durationMs:w}),D({type:"SITE_CRAWL_PROGRESS_EVENT",current:$,total:Math.min(o,l.size+c.length),url:k,status:"completed",violations:x.result.violations.length});else{const T=((n=x.error)==null?void 0:n.message)??"audit returned no result";p.push({url:k,results:[],delta:null,componentId:k,durationMs:w,error:T}),D({type:"SITE_CRAWL_PROGRESS_EVENT",current:$,total:Math.min(o,l.size+c.length),url:k,status:"failed",error:T})}const y=await Na(a);for(const T of y){const m=Da(T,k);m&>(m,e)&&!l.has(m)&&!c.includes(m)&&c.push(m)}}catch(S){const x=Date.now()-g,w=S instanceof Error?S.message:String(S);p.push({url:k,results:[],delta:null,componentId:k,durationMs:x,error:w}),D({type:"SITE_CRAWL_PROGRESS_EVENT",current:$,total:Math.min(o,l.size+c.length),url:k,status:"failed",error:w})}await Fe({})}const v=new Date().toISOString(),E=Date.now()-u,R=sa(e,p,i,v,E);D({type:"SITE_CRAWL_COMPLETE_EVENT",report:R}),await fe()}catch(v){O.error("site crawl failed",v),D({type:"SITE_CRAWL_FAILED_EVENT",error:{code:"UNKNOWN",message:String(v),recoverable:!1}}),await fe()}}function mt(e){try{return new RegExp(e)}catch{return null}}const Ce=3*6e4,yt=6e4,Ga={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 Fa(e,t,s,a,o,r,d){let i;try{i=await W(e,{type:"AI_CANDIDATES_REQUEST",selector:t},s)}catch(h){O.debug("ai candidates fetch failed",h);return}const u=o[0];if(!u)return;const c={componentId:r,currentState:u.state,axeVersion:u.axeVersion},l={at:new Date().toISOString(),componentId:r,pageUrl:u.pageUrl??t,enabledChecks:a.enabledChecks,candidateCounts:{images:i.images.length,headings:i.headings.length,instructions:i.instructions.length,ariaElements:i.ariaElements.length,links:i.links.length,longTextBlocks:i.longTextBlocks.length,colorOnlyRegions:i.colorOnlyRegions.length,languageInfo:i.languageInfo?"present":"null"}};O.info("ai augmentation gate snapshot",JSON.stringify(l));try{await chrome.storage.local.set({aiDiagnosticLatest:l})}catch(h){O.warn("failed to persist ai diagnostic to storage.local",h)}try{const h=JSON.stringify(l,null,2),b=`data:application/json;charset=utf-8,${encodeURIComponent(h)}`,A=l.at.replace(/[:.]/g,"-"),M=await chrome.downloads.download({url:b,filename:`wcagcheckr-ai-diag-${A}.json`,saveAs:!1,conflictAction:"uniquify"});O.info(`ai diagnostic download started (id=${M})`)}catch(h){O.warn("failed to write ai diagnostic to Downloads (storage.local copy is still available under aiDiagnosticLatest)",h)}const p=[];if(a.enabledChecks.altText&&p.push({key:"altText",run:()=>{const h=i.images.map(b=>({imageUrl:b.imageUrl,alt:b.alt,selector:b.selector,outerHTML:b.outerHTML,surroundingContext:b.surroundingContext,...c}));return ia(h,a)}}),a.enabledChecks.headings&&p.push({key:"headings",run:()=>ca(i.headings,c,a)}),a.enabledChecks.sensory&&p.push({key:"sensory",run:()=>la(i.instructions,c,a)}),a.enabledChecks.aria&&p.push({key:"aria",run:()=>da(i.ariaElements,c,a)}),a.enabledChecks.imageOfText&&p.push({key:"imageOfText",run:()=>{const h=i.images.map(b=>({imageUrl:b.imageUrl,accessibleName:b.alt||void 0,selector:b.selector,outerHTML:b.outerHTML,pixelArea:b.pixelArea}));return fa(h,c,a)}}),a.enabledChecks.colorOnlyMeaning&&p.push({key:"colorOnlyMeaning",run:()=>ga(i.colorOnlyRegions,c,a)}),a.enabledChecks.genericLinkText&&p.push({key:"genericLinkText",run:()=>ua(i.links,c,a)}),a.enabledChecks.wallOfText&&p.push({key:"wallOfText",run:()=>pa(i.longTextBlocks,c,a)}),a.enabledChecks.languageMismatch){const h=i.languageInfo;p.push({key:"languageMismatch",run:()=>ha(h?[h]:[],c,a)})}if(p.length===0)return;let n=0,v=0,E=!1;const R={};let k=0,$=0,g=0;const S=[];let x=!1,w=!1;function y(h,b){if(R[h]=(R[h]??0)+b.totalCostUsd,n+=b.totalCostUsd,v+=b.findings.length,E=E||b.capExceeded,k++,b.errors.length>0){g++;for(const A of b.errors)S.includes(A)||S.push(A)}else $++;O.info(`ai ${h}: ${b.findings.length} findings, $${b.totalCostUsd.toFixed(4)}, errs=${b.errors.length}`),b.capExceeded&&O.warn(`ai cost cap exceeded after $${b.totalCostUsd.toFixed(4)} during ${h}`)}const T=(async()=>{for(let h=0;h<p.length;h++){if(d!=null&&d.isCanceled()){w=!0;break}const b=p[h];D({type:"AI_AUGMENTATION_PROGRESS_EVENT",componentId:r,currentCheck:b.key,currentCheckLabel:Ga[b.key],current:h+1,total:p.length});const A=await Promise.race([b.run(),new Promise(M=>setTimeout(()=>M({findings:[],totalCostUsd:0,capExceeded:!1,errors:[`${b.key} timed out after ${yt/1e3}s — single check hung; moving on`]}),yt))]);A.findings.length>0&&(u.violations=[...u.violations,...A.findings]),y(b.key,A)}})(),m=new Promise(h=>{setTimeout(()=>h("timeout"),Ce)});await Promise.race([T.then(()=>"done"),m])==="timeout"&&(x=!0,O.warn(`ai augmentation hit global ${Ce/1e3}s timeout after ${k}/${p.length} checks`));let I;if(g>0||x||w){x?I=`AI augmentation timed out after ${Ce/1e3}s`:w?I="AI augmentation canceled":I=S[0]??"AI analyzer error";const h=$===0?"total":"partial";D({type:"AI_AUGMENTATION_FAILED_EVENT",componentId:r,severity:h,reason:I,checksAttempted:k,checksSucceeded:$,checksErrored:g,errorDetails:x||w?[I,...S.slice(0,4)]:S.slice(0,5)}),O.warn(`ai augmentation ${h} failure: ${g}/${k} checks errored — ${I}`)}(n>0||v>0||I)&&await hn({at:new Date().toISOString(),pageUrl:u.pageUrl??t,componentId:r,totalCostUsd:n,byCheck:R,findingsCount:v,capExceeded:E,failureReason:I})}const j=G("service-worker");Jt("service-worker");Bn();var ue,bt;(bt=(ue=chrome.sidePanel)==null?void 0:ue.setPanelBehavior)==null||bt.call(ue,{openPanelOnActionClick:!0}).catch(e=>j.warn("setPanelBehavior failed",e));On();Hn();ln();zn().catch(e=>j.warn("initial cloud pull failed",e));Jn();Ws();Js();Zs();ws();self.addEventListener("unhandledrejection",e=>{const t=e.reason;Xt(t instanceof Error?t:new Error(String(t)))});let be=!1;chrome.runtime.onConnect.addListener(e=>{if(e.name==="audit-keepalive"){j.debug("keepalive connected"),e.onDisconnect.addListener(async()=>{j.debug("keepalive disconnected"),await dn()});return}if(e.name==="sidepanel-tracker"){be=!0,j.debug("chrome.sidePanel opened"),e.onDisconnect.addListener(async()=>{be=!1,j.debug("chrome.sidePanel closed — restoring in-page overlay"),await Je()});return}});let Ye=!1;const Ie={isCanceled:()=>Ye};async function Ht(){const e=await We();if(e)try{await Z(e,{type:"SIDEBAR_HIDE_REQUEST"})}catch(t){j.warn("sidebar hide failed",t)}}async function Je(){const e=await We();if(e)try{await Z(e,{type:"SIDEBAR_SHOW_REQUEST"})}catch(t){j.warn("sidebar show failed",t)}}N("START_AUDIT",async e=>{Ye=!1,await Ht();let t;if(e.mode==="single-element"){if(!e.scope)throw new Error("START_AUDIT single-element requires scope");t=Pe(e.scope,Ie,e.frameId,e.matrixOverride)}else e.mode==="full-page"?t=Pe("html",Ie,0,e.matrixOverride):t=Ra(Ie);t.catch(s=>{j.error("audit failed",s);const a=s instanceof Error?s.message:String(s),o=/no audit target tab/i.test(a);D({type:"AUDIT_FAILED_EVENT",error:{code:o?"RESTRICTED_URL":"UNKNOWN",message:o?"No audit target tab found. Open a regular http(s) page (not chrome:// or about:blank) and try again.":`Audit failed: ${a}`,recoverable:!0}})}).finally(()=>{be||Je()})});N("CANCEL_AUDIT",async()=>{Ye=!0,j.info("cancel requested")});let Xe=!1;const Wa={isCanceled:()=>Xe};N("START_SITE_CRAWL",async e=>{Xe=!1,await Ht(),La(e.startUrl,{maxPages:e.maxPages,includeRegex:e.includeRegex,excludeRegex:e.excludeRegex},Wa).catch(t=>j.error("site crawl failed",t)).finally(()=>{be||Je()})});N("CANCEL_SITE_CRAWL",async()=>{Xe=!0,j.info("site crawl cancel requested")});N("OPEN_SETTINGS",()=>{chrome.runtime.openOptionsPage()});j.info("service worker ready");
|
|
715
|
+
</html>`}async function ca(){const t=(await chrome.storage.local.get("aiConfig")).aiConfig;return{...wn,...t??{}}}async function mt(e,t,s){const a=await ca();if(!a.enabled||!a.apiKey||e.length===0)return null;const o=e.flatMap(u=>u.violations),r=te(o),d=r.categories.filter(u=>u.status==="fail").sort((u,c)=>c.violationCount-u.violationCount).slice(0,3).map(u=>u.label),i=e[0];return Ft({framing:t,riskTier:r.risk,grade:r.letter,totals:{critical:r.totals.critical,serious:r.totals.serious,moderate:r.totals.moderate,minor:r.totals.minor,unique:r.totals.unique},riskDrivers:d,pageUrl:(i==null?void 0:i.pageUrl)??(i==null?void 0:i.scope)??"",auditDate:(i==null?void 0:i.startedAt)??new Date().toISOString(),priorAuditCount:s},a)}function la(){return N("EXPORT_REQUEST",async e=>{if(e.format==="sarif")return{type:"EXPORT_RESPONSE",format:"sarif",content:JSON.stringify(zs(e.results,e.delta),null,2)};if(e.format==="junit")return{type:"EXPORT_RESPONSE",format:"junit",content:Vs(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:qs(e.results,e.delta)};if(e.format==="ai-prompt")return{type:"EXPORT_RESPONSE",format:"ai-prompt",content:Ks(e.results,e.delta,e.manualRuns,e.siteCrawlReport,e.dismissedKeys)};if(e.format==="defense-bundle"){const t=await st(),s=await mt(e.results,"defense",t.length);return{type:"EXPORT_RESPONSE",format:"defense-bundle",content:Ht(e.results,e.delta,"defense",e.manualRuns,t,s)}}if(e.format==="evidence-bundle"){const t=await st(),s=await mt(e.results,"evidence",t.length);return{type:"EXPORT_RESPONSE",format:"evidence-bundle",content:na(e.results,e.delta,e.manualRuns,t,s)}}return e.format==="developer-letter"?{type:"EXPORT_RESPONSE",format:"developer-letter",content:sa(e.results)}:e.format==="owner-report"?{type:"EXPORT_RESPONSE",format:"owner-report",content:aa(e.results)}:e.format==="ai-prompt-owner"?{type:"EXPORT_RESPONSE",format:"ai-prompt-owner",content:oa(e.results)}:{type:"EXPORT_RESPONSE",format:"json",content:JSON.stringify(js(e.results,e.delta),null,2)}})}const da=G("support-messenger"),ua="https://api.wcagcheckr.com/v1/products/wcagcheckr/support",Te="support:rate-window",pa=5,ha=10*60*1e3,fa=2,ga="wcagcheckr",yt="storybook:lastDetected";async function ma(e){if(!e)return;const t=await chrome.runtime.getPlatformInfo(),a=(await chrome.storage.local.get(yt))[yt];return{extensionVersion:chrome.runtime.getManifest().version,platform:t,storybookDetected:a!=null&&a.detected?a.version??"detected":"none",licenseTier:await Tt(),logTail:tn(nn())}}async function ya(e=Date.now()){const s=((await chrome.storage.local.get(Te))[Te]??[]).filter(a=>e-a<ha);return s.length>=pa?!1:(s.push(e),await chrome.storage.local.set({[Te]:s}),!0)}async function qt(e,t=0){try{const s=await fetch(ua,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)});if(!s.ok)throw new Error(`http ${s.status}`);const a=await s.json();if(!a.success)throw new Error(a.error??"server returned success=false");return{ticketRef:a.ticketRef??""}}catch(s){if(t<fa){const a=(t+1)*1e3;return await new Promise(o=>setTimeout(o,a)),qt(e,t+1)}throw s}}function ba(){return N("SUPPORT_MESSAGE_REQUEST",async e=>{if(!await ya())return{type:"SUPPORT_MESSAGE_RESPONSE",success:!1,error:he("NETWORK","Rate limit reached. Try again in 10 minutes.",!0)};try{const t=await ma(e.includeContext);return{type:"SUPPORT_MESSAGE_RESPONSE",success:!0,ticketRef:(await qt({productSlug:ga,subject:e.subject,body:e.body,replyEmail:e.replyEmail,context:t,extensionVersion:chrome.runtime.getManifest().version,timestamp:new Date().toISOString()})).ticketRef}}catch(t){da.error("send failed",t);const s=t instanceof Error?t.message:String(t);return{type:"SUPPORT_MESSAGE_RESPONSE",success:!1,error:he("NETWORK",s,!0)}}})}const Bt=G("ai-color-suggester");async function wa(e,t,s=[]){if(!t.enabled||e.length===0)return{results:[],totalCostUsd:0,capExceeded:!1};const a=q(t);if(!a.ok)return{results:[],totalCostUsd:0,capExceeded:!1};const o=a.client,r=B(t.costCapUsd),d=[];for(const i of e){if(!r.canCharge())break;try{const u=await o.suggestColorFix({foreground:i.foreground,background:i.background,fontSize:i.fontSize,fontWeight:i.fontWeight,targetLevel:i.targetLevel,paletteHints:s});r.recordCharge(u.costUsd),u.verdict==="suggested"&&u.candidates.length>0&&d.push({matchKey:i.matchKey,suggestions:u.candidates,reasoning:u.reasoning,costUsd:u.costUsd})}catch(u){Bt.warn(`color-fix suggestion failed for ${i.matchKey}`,u)}}return{results:d,totalCostUsd:r.state.spentUsd,capExceeded:r.state.exceeded}}async function va(){const e=await chrome.storage.local.get("aiConfig");return He(e.aiConfig)}function xa(){return N("AI_COLOR_SUGGEST_REQUEST",async e=>{const t=await va();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:s,totalCostUsd:a,capExceeded:o}=await wa(e.inputs,t,e.paletteHints);return{type:"AI_COLOR_SUGGEST_RESPONSE",results:s,totalCostUsd:a,capExceeded:o}}catch(s){return Bt.warn("color-suggest handler failed",s),{type:"AI_COLOR_SUGGEST_RESPONSE",results:[],totalCostUsd:0,capExceeded:!1,unavailableReason:s instanceof Error?s.message:String(s)}}})}const be={A:0,B:1,C:2,D:3,F:4},bt={low:0,moderate:1,high:2,critical:3};function Aa(e,t){return be[e]>be[t]?e:t}function Sa(e,t){return bt[e]>bt[t]?e:t}function ka(e){const t=[];for(const s of e)for(const a of s.results)for(const o of a.violations)t.push({url:s.url,v:o});return t}function Ea(e,t,s,a,o){const r=t.filter(g=>!g.error).length,d=t.filter(g=>!!g.error).length,i=t.map(g=>{if(g.error)return{url:g.url,grade:"F",risk:"critical",uniqueViolations:0,totals:{critical:0,serious:0,moderate:0,minor:0},durationMs:g.durationMs,error:g.error};const S=g.results.flatMap(w=>w.violations),x=te(S);return{url:g.url,grade:x.letter,risk:x.risk,uniqueViolations:x.totals.unique,totals:{critical:x.totals.critical,serious:x.totals.serious,moderate:x.totals.moderate,minor:x.totals.minor},durationMs:g.durationMs}});i.sort((g,S)=>{const x=be[S.grade]-be[g.grade];return x!==0?x:S.uniqueViolations-g.uniqueViolations});let u="A",c="low";for(const g of i)u=Aa(u,g.grade),c=Sa(c,g.risk);const l=ka(t),p=g=>`${g.ruleId}::${g.target.selector}`,n=new Map;for(const{v:g}of l){const S=p(g);n.has(S)||n.set(S,g)}const v=n.size,k={critical:0,serious:0,moderate:0,minor:0};for(const g of n.values())k[g.impact]++;const E=new Map;for(const{url:g,v:S}of l){let x=E.get(S.ruleId);x||(x={ruleId:S.ruleId,description:S.description,impact:S.impact,urls:new Set,totalOccurrences:0},E.set(S.ruleId,x)),x.urls.add(g),x.totalOccurrences++}const $=Array.from(E.values()).map(g=>({ruleId:g.ruleId,description:g.description,impact:g.impact,urlsAffected:g.urls.size,totalOccurrences:g.totalOccurrences,sampleUrls:Array.from(g.urls).slice(0,5)})).sort((g,S)=>S.urlsAffected!==g.urlsAffected?S.urlsAffected-g.urlsAffected:S.totalOccurrences-g.totalOccurrences),T={A:0,B:0,C:0,D:0,F:0};for(const g of i)T[g.grade]++;return{startedAt:s,finishedAt:a,startUrl:e,pagesAudited:r,pagesFailed:d,totalDurationMs:o,siteGrade:u,siteRisk:c,totalUniqueViolations:v,totals:k,topViolations:$,pages:i,pagesByGrade:T}}function $a(e){const t=new Map(e.breakpointPresets.map(a=>[a.id,a])),s=[];for(const a of e.breakpoints){const o=t.get(a);if(o)for(const r of e.directions)for(const d of e.themes){const i=e.ariaVariations.length===0?[null]:e.ariaVariations;for(const u of i)for(const c of e.pseudoStates)s.push({pseudoState:c,ariaVariation:u,theme:d,direction:r,breakpoint:o})}}return s}const Ta=G("ai-alt-text");async function Ca(e,t){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 a=s.client,o=B(t.costCapUsd),r=[],d=[],i=Math.max(1,t.maxCandidatesPerCheck??10);for(const u of e.slice(0,i)){if(!o.canCharge())break;try{const c=await a.judgeAltText({imageUrl:u.imageUrl,alt:u.alt,surroundingContext:u.surroundingContext});o.recordCharge(c.costUsd),(c.verdict==="fail"||c.verdict==="uncertain"&&c.confidence<.6)&&r.push(await Ia(u,c))}catch(c){const l=c instanceof Error?c.message:String(c);d.push(`${u.selector}: ${l}`),Ta.warn("alt-text judgment failed",c)}}return{findings:r,totalCostUsd:o.state.spentUsd,capExceeded:o.state.exceeded,errors:d}}async function Ia(e,t){const s=t.verdict==="uncertain"?"ai-alt-uncertain":"ai-alt-misleading",a={selector:e.selector,outerHTML:e.outerHTML.slice(0,500),failureSummary:`AI assessment: ${t.reasoning}`,tagName:"IMG",role:null,accessibleName:e.alt,textSnippet:null,attrId:null,attrTestid:null},o=await Fe({ruleId:s,componentId:e.componentId,currentState:e.currentState,target:a});return{ruleId:s,wcagCriterion:"wcag111",wcagLevel:"A",impact:t.verdict==="uncertain"?"minor":"serious",description:t.verdict==="uncertain"?"AI couldn't verify whether this alt text is appropriate — flag for human review.":"AI judged the alt text to be misleading, generic, or otherwise inappropriate.",helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/non-text-content.html",target:a,componentId:e.componentId,currentState:e.currentState,axeVersion:e.axeVersion,detectedAt:new Date().toISOString(),matchKey:o,ai:{reasoning:t.reasoning,confidence:t.confidence,model:t.model,costUsd:t.costUsd}}}const ie=G("ai-text");async function Ra(e,t,s){if(!s.enabled||!s.enabledChecks.headings)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=q(s);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,r=B(s.costCapUsd),d=[],i=[],u=Math.max(1,s.maxCandidatesPerCheck??10);for(const c of e.slice(0,u)){if(!r.canCharge())break;try{const l=await o.judgeHeading({headingText:c.text,sectionContent:c.sectionContent});r.recordCharge(l.costUsd),l.verdict==="fail"&&l.confidence>=.6&&d.push(await re("ai-heading-not-descriptive","wcag246","AA","moderate","AI judged this heading does not describe its section content.",c.selector,c.outerHTML,c.text,l,t))}catch(l){const p=l instanceof Error?l.message:String(l);i.push(`${c.selector}: ${p}`),ie.warn("heading judgment failed",l)}}return{findings:d,totalCostUsd:r.state.spentUsd,capExceeded:r.state.exceeded,errors:i}}async function Oa(e,t,s){if(!s.enabled||!s.enabledChecks.sensory)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=q(s);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,r=B(s.costCapUsd),d=[],i=[],u=Math.max(1,s.maxCandidatesPerCheck??10);for(const c of e.slice(0,u)){if(!r.canCharge())break;try{const l=await o.judgeSensoryLanguage({instructionText:c.text});r.recordCharge(l.costUsd),l.verdict==="fail"&&l.confidence>=.6&&d.push(await re("ai-sensory-instruction","wcag133","A","serious","AI flagged this instruction as relying on sensory characteristics (shape, color, location) without a programmatic identifier.",c.selector,c.outerHTML,c.text.slice(0,100),l,t))}catch(l){const p=l instanceof Error?l.message:String(l);i.push(`${c.selector}: ${p}`),ie.warn("sensory judgment failed",l)}}return{findings:d,totalCostUsd:r.state.spentUsd,capExceeded:r.state.exceeded,errors:i}}async function _a(e,t,s){if(!s.enabled||!s.enabledChecks.aria)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=q(s);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,r=B(s.costCapUsd),d=[],i=[],u=Math.max(1,s.maxCandidatesPerCheck??10);for(const c of e.slice(0,u)){if(!r.canCharge())break;try{const l=await o.judgeAriaSemantics({elementHtml:c.outerHTML,computedRole:c.role,surroundingHtml:c.surroundingHtml});r.recordCharge(l.costUsd),l.verdict==="fail"&&l.confidence>=.65&&d.push(await re("ai-aria-misuse","wcag412","A","serious",`AI judged role="${c.role}" is being used inappropriately on this element.`,c.selector,c.outerHTML,c.role,l,t))}catch(l){const p=l instanceof Error?l.message:String(l);i.push(`${c.selector}: ${p}`),ie.warn("aria judgment failed",l)}}return{findings:d,totalCostUsd:r.state.spentUsd,capExceeded:r.state.exceeded,errors:i}}async function Ma(e,t,s){if(!s.enabled||!s.enabledChecks.genericLinkText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=q(s);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,r=B(s.costCapUsd),d=[],i=[],u=Math.max(1,s.maxCandidatesPerCheck??10);for(const c of e.slice(0,u)){if(!r.canCharge())break;try{const l=await o.judgeGenericLinkText({linkText:c.linkText,surroundingText:c.surroundingText});r.recordCharge(l.costUsd),l.verdict==="fail"&&l.confidence>=.6&&d.push(await re("ai-generic-link-text","wcag244","A","serious","AI judged this link's purpose is not clear from its text or surrounding context.",c.selector,c.outerHTML,c.linkText,l,t))}catch(l){const p=l instanceof Error?l.message:String(l);i.push(`${c.selector}: ${p}`),ie.warn("generic-link judgment failed",l)}}return{findings:d,totalCostUsd:r.state.spentUsd,capExceeded:r.state.exceeded,errors:i}}async function Ua(e,t,s){if(!s.enabled||!s.enabledChecks.wallOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=q(s);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,r=B(s.costCapUsd),d=[],i=[],u=Math.max(1,s.maxCandidatesPerCheck??10);for(const c of e.slice(0,u)){if(!r.canCharge())break;try{const l=await o.judgeWallOfText({blockText:c.blockText,structuralChildrenCount:c.structuralChildrenCount});r.recordCharge(l.costUsd),l.verdict==="fail"&&l.confidence>=.6&&d.push(await re("ai-wall-of-text","wcag315","AAA","moderate","AI flagged this block as a wall of text with insufficient structural breaks (subheadings, lists, paragraphs).",c.selector,c.outerHTML,c.blockText.slice(0,80),l,t))}catch(l){const p=l instanceof Error?l.message:String(l);i.push(`${c.selector}: ${p}`),ie.warn("wall-of-text judgment failed",l)}}return{findings:d,totalCostUsd:r.state.spentUsd,capExceeded:r.state.exceeded,errors:i}}async function Da(e,t,s){if(!s.enabled||!s.enabledChecks.languageMismatch)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=q(s);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,r=B(s.costCapUsd),d=[],i=[],u=Math.max(1,s.maxCandidatesPerCheck??10);for(const c of e.slice(0,u)){if(!r.canCharge())break;try{const l=await o.judgeLanguageMismatch({declaredLang:c.declaredLang,bodyTextSample:c.bodyTextSample});r.recordCharge(l.costUsd),l.verdict==="fail"&&l.confidence>=.7&&d.push(await re("ai-language-mismatch","wcag311","A","serious",`AI judged the page's content does not match the declared lang="${c.declaredLang||"(none)"}".`,c.selector,c.outerHTML,c.bodyTextSample.slice(0,80),l,t))}catch(l){const p=l instanceof Error?l.message:String(l);i.push(`${c.selector}: ${p}`),ie.warn("language-mismatch judgment failed",l)}}return{findings:d,totalCostUsd:r.state.spentUsd,capExceeded:r.state.exceeded,errors:i}}async function re(e,t,s,a,o,r,d,i,u,c){const l={selector:r,outerHTML:d.slice(0,500),failureSummary:`AI assessment: ${u.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:i.slice(0,100)||null,attrId:null,attrTestid:null},p=await Fe({ruleId:e,componentId:c.componentId,currentState:c.currentState,target:l});return{ruleId:e,wcagCriterion:t,wcagLevel:s,impact:a,description:o,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:l,componentId:c.componentId,currentState:c.currentState,axeVersion:c.axeVersion,detectedAt:new Date().toISOString(),matchKey:p,ai:{reasoning:u.reasoning,confidence:u.confidence,model:u.model,costUsd:u.costUsd}}}const Kt=G("ai-vision");async function Pa(e,t,s){if(!s.enabled||!s.enabledChecks.imageOfText)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=q(s);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[`AI client unavailable: ${a.reason}`]};const o=a.client,r=B(s.costCapUsd),d=Math.max(1,s.imageOfTextMaxImages??5),i=[...e].sort((l,p)=>p.pixelArea-l.pixelArea).slice(0,d),u=[],c=[];for(const l of i){if(!r.canCharge())break;try{const p=await o.judgeImageOfText({imageUrl:l.imageUrl,accessibleName:l.accessibleName});r.recordCharge(p.costUsd),p.verdict==="fail"&&p.confidence>=.65&&u.push(await Yt("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.",l.selector,l.outerHTML,l.accessibleName??"",p,t))}catch(p){const n=p instanceof Error?p.message:String(p);c.push(`${l.selector}: ${n}`),Kt.warn("image-of-text judgment failed",p)}}return{findings:u,totalCostUsd:r.state.spentUsd,capExceeded:r.state.exceeded,errors:c}}async function Na(e,t,s){if(!s.enabled||!s.enabledChecks.colorOnlyMeaning)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[]};const a=q(s);if(!a.ok)return{findings:[],totalCostUsd:0,capExceeded:!1,errors:[a.reason]};const o=a.client,r=B(s.costCapUsd),d=[],i=[],u=Math.max(1,s.maxCandidatesPerCheck??10);for(const c of e.slice(0,u)){if(!r.canCharge())break;try{const l=await o.judgeColorOnlyMeaning({elementHtml:c.elementHtml,contextDescription:c.contextDescription});r.recordCharge(l.costUsd),l.verdict==="fail"&&l.confidence>=.65&&d.push(await Yt("ai-color-only-meaning","wcag141","A","serious","AI judged this region conveys information only through color (no text label, icon glyph, or shape difference).",c.selector,c.outerHTML,c.contextDescription,l,t))}catch(l){const p=l instanceof Error?l.message:String(l);i.push(p),Kt.warn("color-only judgment failed",l)}}return{findings:d,totalCostUsd:r.state.spentUsd,capExceeded:r.state.exceeded,errors:i}}async function Yt(e,t,s,a,o,r,d,i,u,c){const l={selector:r,outerHTML:d.slice(0,500),failureSummary:`AI assessment: ${u.reasoning}`,tagName:"AI",role:null,accessibleName:null,textSnippet:i.slice(0,100)||null,attrId:null,attrTestid:null},p=await Fe({ruleId:e,componentId:c.componentId,currentState:c.currentState,target:l});return{ruleId:e,wcagCriterion:t,wcagLevel:s,impact:a,description:o,helpUrl:"https://www.w3.org/WAI/WCAG21/Understanding/",target:l,componentId:c.componentId,currentState:c.currentState,axeVersion:c.axeVersion,detectedAt:new Date().toISOString(),matchKey:p,ai:{reasoning:u.reasoning,confidence:u.confidence,model:u.model,costUsd:u.costUsd}}}const Le=G("ensure-content-script"),La=1500,Ga=300;async function Jt(e,t){if(await wt(e,t))return{ok:!0};const s=chrome.runtime.getManifest(),a=[];for(const o of s.content_scripts??[])Array.isArray(o.js)&&a.push(...o.js);if(a.length===0)return Le.error("manifest declares no content_scripts"),{ok:!1,reason:"inject-failed",detail:"no content_scripts declared in manifest"};try{const o=t!==void 0?{tabId:e,frameIds:[t]}:{tabId:e,allFrames:!0};await chrome.scripting.executeScript({target:o,files:a})}catch(o){return Fa(o)}return await Wa(Ga),await wt(e,t)?(Le.info("content script injected on demand",{tabId:e,frameId:t}),{ok:!0}):{ok:!1,reason:"unresponsive-after-inject",detail:"content script injected but did not register handlers — likely Content Security Policy on this page is blocking it"}}async function wt(e,t){try{const s=t!==void 0?{frameId:t}:void 0,a=await Promise.race([chrome.tabs.sendMessage(e,{type:"PING_REQUEST"},s),new Promise((o,r)=>setTimeout(()=>r(new Error("ping timeout")),La))]);return(a==null?void 0:a.type)==="PING_RESPONSE"}catch{return!1}}function Fa(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}:(Le.warn("content-script injection failed",e),{ok:!1,reason:"inject-failed",detail:t})}function Wa(e){return new Promise(t=>setTimeout(t,e))}function Xt(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 Ce=G("forensic-anchor-client"),ja="https://api.wcagcheckr.com",za="wcagcheckr",Va=15e3;function Ha(e){if(typeof e!="object"||e===null)return!1;const t=e;return t.schemaVersion!==1&&t.schemaVersion!==2||!(typeof t.anchoredAt=="string"&&typeof t.tsaName=="string"&&typeof t.rfc3161TokenBase64=="string"&&t.rfc3161TokenBase64.length>0&&typeof t.serverSignatureBase64=="string"&&t.serverSignatureBase64.length>0&&typeof t.serverKeyFingerprint=="string"&&t.serverKeyFingerprint.length>0)?!1:t.schemaVersion===2?typeof t.prevAuditHash=="string"&&/^[0-9a-f]{64}$/.test(t.prevAuditHash):!0}async function qa(e,t){const s={auditHash:e.hash,pageUrl:e.pageUrl,capturedAt:e.capturedAt,...t?{licenseId:t}:{}},a=`${ja}/v1/products/${za}/forensic/anchor`;try{const o=await fetch(a,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(s),signal:AbortSignal.timeout(Va)});if(!o.ok)return Ce.warn(`anchor HTTP ${o.status}; entry stays local-only`),null;const r=await o.json();return Ha(r)?r:(Ce.warn("anchor returned malformed receipt; entry stays local-only",r),null)}catch(o){return Ce.warn("anchor request failed; entry stays local-only",o),null}}const O=G("flows");async function Xe(){const e=await Ve();if(!e)throw new Error("no audit target tab found");return e}async function Ba(){var s,a,o,r,d;const e=await Je("stateMatrix",Oe);return((s=e.pseudoStates)==null?void 0:s.length)>0&&((a=e.themes)==null?void 0:a.length)>0&&((o=e.directions)==null?void 0:o.length)>0&&((r=e.breakpoints)==null?void 0:r.length)>0&&((d=e.breakpointPresets)==null?void 0:d.length)>0?e:(O.warn("stored stateMatrix is invalid; using defaults"),Oe)}async function Ka(){const e=await Je("runsPerState",1);return typeof e!="number"||e<1||e>5?1:Math.floor(e)}async function Ya(e,t,s){const a=t.themes.includes("light"),o=t.themes.includes("dark");if(!a||!o)return t;let r=null;try{r=await W(e,{type:"THEME_AWARENESS_REQUEST"},s)}catch{return t}return!r||r.hasUnreadableSheets?t:r.hasDarkModeCss&&!r.hasLightModeCss?(O.info("theme-axis prune: site is dark-only, skipping light pass"),{...t,themes:t.themes.filter(d=>d!=="light")}):r.hasLightModeCss&&!r.hasDarkModeCss?(O.info("theme-axis prune: site is light-only, skipping dark pass"),{...t,themes:t.themes.filter(d=>d!=="dark")}):t}function Ja(e){if(e.length<=1)return e[0]??[];const t=new Map;return e.forEach((s,a)=>{for(const o of s){const r=`${o.ruleId}::${o.target.selector}`,d=t.get(r);d?d.runIndices.add(a):t.set(r,{v:o,runIndices:new Set([a])})}}),Array.from(t.values()).map(({v:s,runIndices:a})=>({...s,flakyAcrossRuns:a.size<e.length}))}async function Ge(e,t,s,a){var $;const o=await Xe(),r=await Jt(o,s);if(!r.ok){D({type:"AUDIT_FAILED_EVENT",error:{code:r.reason==="restricted-url"?"RESTRICTED_URL":"CONTENT_SCRIPT_UNREACHABLE",message:Xt(r),recoverable:r.reason!=="restricted-url"}});return}const d=a??await Ba(),i=await Ya(o,d,s),u=$a(i),c=await Ka();await We({sessionId:je(),mode:"single-element",scope:e,startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"}),await Z(o,{type:"LIVE_REGIONS_ARM_REQUEST"},s).catch(()=>{}),await Z(o,{type:"FOCUS_TRACE_ARM_REQUEST"},s).catch(()=>{}),await W(o,{type:"WARMUP_REQUEST"},s).catch(()=>{});const l=[],p=[];let n="",v,k=!1,E=null;try{for(let T=0;T<u.length;T++){if(t.isCanceled()){O.info("single audit canceled by user");break}const g=u[T];try{const S=await Me(o,g,e,s);if(!S.success){v=($=S.error)==null?void 0:$.message,O.warn(`state ${T+1}/${u.length} drive failed`,v);continue}g.breakpoint.id!==E&&(await W(o,{type:"WARMUP_REQUEST"},s).catch(()=>{}),E=g.breakpoint.id);const x=[];let w=null;for(let y=0;y<c;y++){const C=await W(o,{type:"AUDIT_REQUEST",selector:e,currentState:g},s);C.success&&C.result?(x.push(C.result.violations),w=C.result):C.error&&(v=C.error.message,O.warn(`state ${T+1}/${u.length} run ${y+1} failed`,v))}if(w&&x.length>0){k=!0;const y=Ja(x),C=await Nn(o,{quality:75})??void 0;let m,I;try{m=(await W(o,{type:"LIVE_REGIONS_DRAIN_REQUEST"},s)).announcements}catch{}try{I=(await W(o,{type:"FOCUS_TRACE_DRAIN_REQUEST"},s)).focusEvents}catch{}p.push({...w,violations:y,frameId:s,announcements:m,focusEvents:I,screenshotDataUrl:C}),l.push(...y),n=w.componentId}}catch(S){v=S instanceof Error?S.message:String(S),O.warn(`state ${T+1}/${u.length} threw`,S)}D({type:"AUDIT_PROGRESS_EVENT",current:T+1,total:u.length,currentState:g,componentId:n||void 0}),await ze({})}if(k&&n){const T={pseudoState:"default",ariaVariation:null,theme:"light",direction:"ltr",breakpoint:{id:"desktop",label:"Desktop",width:1280,height:800,deviceScaleFactor:1,mobile:!1}};try{await Me(o,T,e,s),await new Promise(w=>setTimeout(w,200))}catch(w){O.debug("reference-state drive failed; analyzers will use last matrix state",w)}try{const w=await W(o,{type:"READING_ORDER_REQUEST",selector:e},s);w.issues.length>0&&p[0]&&(p[0].readingOrderIssues=w.issues,p[0].positionAnalysisCapturedAt=T)}catch(w){O.debug("reading-order analysis skipped",w)}try{const w=await W(o,{type:"TAB_ORDER_ANALYZE_REQUEST"},s);w.issues.length>0&&p[0]&&(p[0].tabOrderIssues=w.issues,p[0].positionAnalysisCapturedAt=T)}catch(w){O.debug("tab-order analysis skipped",w)}try{const w=await W(o,{type:"TYPOGRAPHY_ANALYZE_REQUEST",selector:e},s);w.issues.length>0&&p[0]&&(p[0].typographyIssues=w.issues)}catch(w){O.debug("typography analysis skipped",w)}try{const w=await W(o,{type:"CUSTOM_PROPERTY_REQUEST"},s);w.undefined.length>0&&p[0]&&(p[0].undefinedCustomProperties=w.undefined)}catch(w){O.debug("custom-property analysis skipped",w)}try{const w=await Je("aiConfig",{}),y=He(w);y.enabled&&y.apiKey&&await co(o,e,s,y,p,n,t)}catch(w){O.warn("ai augmentation skipped",w)}const g=p.flatMap(w=>w.announcements??[]),S=p.flatMap(w=>w.focusEvents??[]),x=await Nt(n,l,{announcements:g,focusEvents:S},d);try{const w=te(l),y=p.reduce((m,I)=>m+I.durationMs,0),C=await pn(p,w,y);if(C){const m=await Tt();if(bn(m,"forensicAnchoring"))try{const I=await hn(),R=await qa(C,I??void 0);R&&await fn(C.componentId,C.capturedAt,R)}catch(I){O.warn("forensic anchoring failed (entry stays local-only)",I)}}}catch(w){O.warn("forensic log recording failed",w)}D({type:"AUDIT_COMPLETE_EVENT",componentId:n,results:p,delta:x})}else D({type:"AUDIT_FAILED_EVENT",error:{code:"AXE_FAILED",message:v?`All audits failed. Last error: ${v}`:"No state produced results. Selector may not exist in the audit target frame.",recoverable:!0}});await ge()}catch(T){O.error("single audit failed",T),D({type:"AUDIT_FAILED_EVENT",componentId:n||void 0,error:cn(T)?T.payload:{code:"UNKNOWN",message:String(T),recoverable:!1}})}finally{await Z(o,{type:"LIVE_REGIONS_DISARM_REQUEST"},s).catch(()=>{}),await Z(o,{type:"FOCUS_TRACE_DISARM_REQUEST"},s).catch(()=>{}),await Mt(o,e,s)}}async function Xa(e){var t;try{const a=(await chrome.scripting.executeScript({target:{tabId:e,allFrames:!0},world:"MAIN",func:()=>{var r;const o=window;return{detected:typeof o.__STORYBOOK_PREVIEW__=="object",version:(r=o.__STORYBOOK_PREVIEW__)==null?void 0:r.version}}})).find(o=>{var r;return(r=o.result)==null?void 0:r.detected});if(a)return{detected:!0,version:(t=a.result)==null?void 0:t.version,frameId:a.frameId}}catch(s){O.warn("storybook detection scripting failed",s)}return{detected:!1}}async function Qa(e){const t=await Xe(),s=await Xa(t);if(await chrome.storage.local.set({"storybook:lastDetected":{detected:s.detected,version:s.version,detectedAt:new Date().toISOString()}}),!s.detected){D({type:"AUDIT_FAILED_EVENT",error:{code:"STORYBOOK_NOT_DETECTED",message:"Storybook not detected on this tab. Open a Storybook page (URL with iframe.html or storybook-static) and try again.",recoverable:!0}});return}const a=s.frameId??0,o=await Za(t,a);if(!o.length){O.warn("storybook detected but no stories returned"),D({type:"AUDIT_FAILED_EVENT",error:{code:"STORYBOOK_NOT_DETECTED",message:"Storybook detected but no stories were returned. Try reloading the page.",recoverable:!0}});return}await We({sessionId:je(),mode:"storybook-all",totalStories:o.length,completedStories:[],startedAt:new Date().toISOString(),lastProgressAt:new Date().toISOString(),state:"running"});for(const r of o){if(e.isCanceled()){O.info("storybook iteration canceled by user");break}try{await eo(t,a,r.id);const i=await Z(t,{type:"STORYBOOK_GET_STORY_TARGET_REQUEST",tabId:t},a);await Ge(i.selector,e,a)}catch(i){O.warn(`story ${r.id} failed; continuing with next`,i)}const d=await gn();if((d==null?void 0:d.state)==="interrupted"){O.info("storybook iteration interrupted");break}await ze({completedStories:[...(d==null?void 0:d.completedStories)??[],r.id]})}D({type:"SCORECARD_UPDATED_EVENT"}),await ge()}async function Za(e,t){var s;try{return((s=(await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",func:async()=>{var u,c;const o=window.__STORYBOOK_PREVIEW__;if(!o)return[];const r=o.storyStoreValue??o.storyStore;if(!r)return[];typeof r.cacheAllCSFFiles=="function"&&await r.cacheAllCSFFiles();const d=((u=r.extract)==null?void 0:u.call(r))??((c=r.cachedCSFFiles)==null?void 0:c.call(r));if(!d)return[];const i=[];if(d instanceof Map)for(const[l,p]of d){const n=p;i.push({id:String(l),name:n.name??String(l),kind:n.title??n.kind??""})}else if(typeof d=="object"&&d!==null)for(const[l,p]of Object.entries(d)){const n=p;i.push({id:l,name:n.name??l,kind:n.title??n.kind??""})}return i}}))[0])==null?void 0:s.result)??[]}catch(a){return O.warn("listStoriesViaMainWorld failed",a),[]}}async function eo(e,t,s){try{await chrome.scripting.executeScript({target:{tabId:e,frameIds:[t]},world:"MAIN",args:[s,3e3],func:async(a,o)=>{const r=new URL(window.location.href);r.searchParams.set("id",a),r.searchParams.set("viewMode","story"),window.location.href!==r.href&&(window.history.pushState({},"",r.href),window.dispatchEvent(new PopStateEvent("popstate"))),await new Promise(d=>{const i=setTimeout(()=>d(),o),u=()=>{clearTimeout(i),d()};window.addEventListener("storyrendered",u,{once:!0})})}})}catch(a){O.warn(`navigateToStoryViaMainWorld(${s}) failed`,a)}}const to=200,no=6e4;function vt(e,t){try{return new URL(e).origin===new URL(t).origin}catch{return!1}}function so(e,t){try{const s=new URL(e,t);return s.hash="",s.toString()}catch{return null}}async function ao(e,t,s){await chrome.tabs.update(e,{url:t}),await new Promise((a,o)=>{const r=setTimeout(()=>{chrome.tabs.onUpdated.removeListener(d),o(new Error(`navigation timeout (${s}ms): ${t}`))},s);function d(i,u){i===e&&u.status==="complete"&&(clearTimeout(r),chrome.tabs.onUpdated.removeListener(d),a())}chrome.tabs.onUpdated.addListener(d)}),await new Promise(a=>setTimeout(a,400))}async function oo(e){var t;try{return((t=(await chrome.scripting.executeScript({target:{tabId:e},func:()=>Array.from(document.querySelectorAll("a[href]")).map(a=>a.getAttribute("href")).filter(a=>typeof a=="string"&&!a.startsWith("javascript:"))}))[0])==null?void 0:t.result)??[]}catch(s){return O.debug("link discovery failed",s),[]}}async function io(e,t,s){var n;const a=await Xe(),o=Math.min(t.maxPages??25,to),r=t.includeRegex?xt(t.includeRegex):null,d=t.excludeRegex?xt(t.excludeRegex):null,i=new Date().toISOString(),u=Date.now(),c=[e],l=new Set,p=[];await We({sessionId:je(),mode:"storybook-all",scope:e,startedAt:i,lastProgressAt:new Date().toISOString(),state:"running"});try{for(;c.length>0&&l.size<o;){if(s.isCanceled()){O.info("site crawl canceled by user");break}const $=c.shift();if(!$||l.has($)||!vt($,e)||r&&!r.test($)||d&&d.test($))continue;l.add($);const T=l.size,g=Date.now();D({type:"SITE_CRAWL_PROGRESS_EVENT",current:T,total:Math.min(o,l.size+c.length),url:$,status:"auditing"});try{await ao(a,$,no);const S=await Jt(a,0);if(!S.ok)throw new Error(Xt(S));await W(a,{type:"WARMUP_REQUEST"},0).catch(()=>{});const x=await W(a,{type:"AUDIT_REQUEST",selector:"html",currentState:{pseudoState:"default",ariaVariation:null,theme:"light",direction:"ltr",breakpoint:{id:"desktop",label:"Desktop",width:1280,height:800,deviceScaleFactor:1,mobile:!1}}},0),w=Date.now()-g;if(x.success&&x.result)p.push({url:$,results:[x.result],delta:null,componentId:x.result.componentId,durationMs:w}),D({type:"SITE_CRAWL_PROGRESS_EVENT",current:T,total:Math.min(o,l.size+c.length),url:$,status:"completed",violations:x.result.violations.length});else{const C=((n=x.error)==null?void 0:n.message)??"audit returned no result";p.push({url:$,results:[],delta:null,componentId:$,durationMs:w,error:C}),D({type:"SITE_CRAWL_PROGRESS_EVENT",current:T,total:Math.min(o,l.size+c.length),url:$,status:"failed",error:C})}const y=await oo(a);for(const C of y){const m=so(C,$);m&&vt(m,e)&&!l.has(m)&&!c.includes(m)&&c.push(m)}}catch(S){const x=Date.now()-g,w=S instanceof Error?S.message:String(S);p.push({url:$,results:[],delta:null,componentId:$,durationMs:x,error:w}),D({type:"SITE_CRAWL_PROGRESS_EVENT",current:T,total:Math.min(o,l.size+c.length),url:$,status:"failed",error:w})}await ze({})}const v=new Date().toISOString(),k=Date.now()-u,E=Ea(e,p,i,v,k);D({type:"SITE_CRAWL_COMPLETE_EVENT",report:E}),await ge()}catch(v){O.error("site crawl failed",v),D({type:"SITE_CRAWL_FAILED_EVENT",error:{code:"UNKNOWN",message:String(v),recoverable:!1}}),await ge()}}function xt(e){try{return new RegExp(e)}catch{return null}}const Ie=3*6e4,At=6e4,ro={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 co(e,t,s,a,o,r,d){let i;try{i=await W(e,{type:"AI_CANDIDATES_REQUEST",selector:t},s)}catch(h){O.debug("ai candidates fetch failed",h);return}const u=o[0];if(!u)return;const c={componentId:r,currentState:u.state,axeVersion:u.axeVersion},l={at:new Date().toISOString(),componentId:r,pageUrl:u.pageUrl??t,enabledChecks:a.enabledChecks,candidateCounts:{images:i.images.length,headings:i.headings.length,instructions:i.instructions.length,ariaElements:i.ariaElements.length,links:i.links.length,longTextBlocks:i.longTextBlocks.length,colorOnlyRegions:i.colorOnlyRegions.length,languageInfo:i.languageInfo?"present":"null"}};O.info("ai augmentation gate snapshot",JSON.stringify(l));try{await chrome.storage.local.set({aiDiagnosticLatest:l})}catch(h){O.warn("failed to persist ai diagnostic to storage.local",h)}try{const h=JSON.stringify(l,null,2),b=`data:application/json;charset=utf-8,${encodeURIComponent(h)}`,A=l.at.replace(/[:.]/g,"-"),M=await chrome.downloads.download({url:b,filename:`wcagcheckr-ai-diag-${A}.json`,saveAs:!1,conflictAction:"uniquify"});O.info(`ai diagnostic download started (id=${M})`)}catch(h){O.warn("failed to write ai diagnostic to Downloads (storage.local copy is still available under aiDiagnosticLatest)",h)}const p=[];if(a.enabledChecks.altText&&p.push({key:"altText",run:()=>{const h=i.images.map(b=>({imageUrl:b.imageUrl,alt:b.alt,selector:b.selector,outerHTML:b.outerHTML,surroundingContext:b.surroundingContext,...c}));return Ca(h,a)}}),a.enabledChecks.headings&&p.push({key:"headings",run:()=>Ra(i.headings,c,a)}),a.enabledChecks.sensory&&p.push({key:"sensory",run:()=>Oa(i.instructions,c,a)}),a.enabledChecks.aria&&p.push({key:"aria",run:()=>_a(i.ariaElements,c,a)}),a.enabledChecks.imageOfText&&p.push({key:"imageOfText",run:()=>{const h=i.images.map(b=>({imageUrl:b.imageUrl,accessibleName:b.alt||void 0,selector:b.selector,outerHTML:b.outerHTML,pixelArea:b.pixelArea}));return Pa(h,c,a)}}),a.enabledChecks.colorOnlyMeaning&&p.push({key:"colorOnlyMeaning",run:()=>Na(i.colorOnlyRegions,c,a)}),a.enabledChecks.genericLinkText&&p.push({key:"genericLinkText",run:()=>Ma(i.links,c,a)}),a.enabledChecks.wallOfText&&p.push({key:"wallOfText",run:()=>Ua(i.longTextBlocks,c,a)}),a.enabledChecks.languageMismatch){const h=i.languageInfo;p.push({key:"languageMismatch",run:()=>Da(h?[h]:[],c,a)})}if(p.length===0)return;let n=0,v=0,k=!1;const E={};let $=0,T=0,g=0;const S=[];let x=!1,w=!1;function y(h,b){if(E[h]=(E[h]??0)+b.totalCostUsd,n+=b.totalCostUsd,v+=b.findings.length,k=k||b.capExceeded,$++,b.errors.length>0){g++;for(const A of b.errors)S.includes(A)||S.push(A)}else T++;O.info(`ai ${h}: ${b.findings.length} findings, $${b.totalCostUsd.toFixed(4)}, errs=${b.errors.length}`),b.capExceeded&&O.warn(`ai cost cap exceeded after $${b.totalCostUsd.toFixed(4)} during ${h}`)}const C=(async()=>{for(let h=0;h<p.length;h++){if(d!=null&&d.isCanceled()){w=!0;break}const b=p[h];D({type:"AI_AUGMENTATION_PROGRESS_EVENT",componentId:r,currentCheck:b.key,currentCheckLabel:ro[b.key],current:h+1,total:p.length});const A=await Promise.race([b.run(),new Promise(M=>setTimeout(()=>M({findings:[],totalCostUsd:0,capExceeded:!1,errors:[`${b.key} timed out after ${At/1e3}s — single check hung; moving on`]}),At))]);A.findings.length>0&&(u.violations=[...u.violations,...A.findings]),y(b.key,A)}})(),m=new Promise(h=>{setTimeout(()=>h("timeout"),Ie)});await Promise.race([C.then(()=>"done"),m])==="timeout"&&(x=!0,O.warn(`ai augmentation hit global ${Ie/1e3}s timeout after ${$}/${p.length} checks`));let R;if(g>0||x||w){x?R=`AI augmentation timed out after ${Ie/1e3}s`:w?R="AI augmentation canceled":R=S[0]??"AI analyzer error";const h=T===0?"total":"partial";D({type:"AI_AUGMENTATION_FAILED_EVENT",componentId:r,severity:h,reason:R,checksAttempted:$,checksSucceeded:T,checksErrored:g,errorDetails:x||w?[R,...S.slice(0,4)]:S.slice(0,5)}),O.warn(`ai augmentation ${h} failure: ${g}/${$} checks errored — ${R}`)}(n>0||v>0||R)&&await vn({at:new Date().toISOString(),pageUrl:u.pageUrl??t,componentId:r,totalCostUsd:n,byCheck:E,findingsCount:v,capExceeded:k,failureReason:R})}const j=G("service-worker");sn("service-worker");gs();var ue,St;(St=(ue=chrome.sidePanel)==null?void 0:ue.setPanelBehavior)==null||St.call(ue,{openPanelOnActionClick:!0}).catch(e=>j.warn("setPanelBehavior failed",e));Ln();Qn();mn();Jn().catch(e=>j.warn("initial cloud pull failed",e));(async()=>(await ps(),Gt()))();hs();bs();la();ba();xa();Ws();self.addEventListener("unhandledrejection",e=>{const t=e.reason;an(t instanceof Error?t:new Error(String(t)))});let we=!1;chrome.runtime.onConnect.addListener(e=>{if(e.name==="audit-keepalive"){j.debug("keepalive connected"),e.onDisconnect.addListener(async()=>{j.debug("keepalive disconnected"),await yn()});return}if(e.name==="sidepanel-tracker"){we=!0,j.debug("chrome.sidePanel opened"),e.onDisconnect.addListener(async()=>{we=!1,j.debug("chrome.sidePanel closed — restoring in-page overlay"),await Ze()});return}});let Qe=!1;const Re={isCanceled:()=>Qe};async function Qt(){const e=await Ve();if(e)try{await Z(e,{type:"SIDEBAR_HIDE_REQUEST"})}catch(t){j.warn("sidebar hide failed",t)}}async function Ze(){const e=await Ve();if(e)try{await Z(e,{type:"SIDEBAR_SHOW_REQUEST"})}catch(t){j.warn("sidebar show failed",t)}}N("START_AUDIT",async e=>{Qe=!1,await Qt();let t;if(e.mode==="single-element"){if(!e.scope)throw new Error("START_AUDIT single-element requires scope");t=Ge(e.scope,Re,e.frameId,e.matrixOverride)}else e.mode==="full-page"?t=Ge("html",Re,0,e.matrixOverride):t=Qa(Re);t.catch(s=>{j.error("audit failed",s);const a=s instanceof Error?s.message:String(s),o=/no audit target tab/i.test(a);D({type:"AUDIT_FAILED_EVENT",error:{code:o?"RESTRICTED_URL":"UNKNOWN",message:o?"No audit target tab found. Open a regular http(s) page (not chrome:// or about:blank) and try again.":`Audit failed: ${a}`,recoverable:!0}})}).finally(()=>{we||Ze()})});N("CANCEL_AUDIT",async()=>{Qe=!0,j.info("cancel requested")});let et=!1;const lo={isCanceled:()=>et};N("START_SITE_CRAWL",async e=>{et=!1,await Qt(),io(e.startUrl,{maxPages:e.maxPages,includeRegex:e.includeRegex,excludeRegex:e.excludeRegex},lo).catch(t=>j.error("site crawl failed",t)).finally(()=>{we||Ze()})});N("CANCEL_SITE_CRAWL",async()=>{et=!0,j.info("site crawl cancel requested")});N("OPEN_SETTINGS",()=>{chrome.runtime.openOptionsPage()});j.info("service worker ready");
|