linear-grab 0.24.0 → 0.24.1

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.
@@ -270,7 +270,7 @@ of the other sections.`,hD="- FIRST, self-orient in the repo checkout (skip any
270
270
  `);f(e=>`${e?`${e}\n`:``}${t}\n`),uj(`capture`)});let y=async()=>{m(!0);try{let{block:e,failed:t}=await f$();e&&f(t=>`${t?`${t}\n`:``}${e}\n`),t.length&&f(e=>`${e}\n<!-- upload issues: ${t.join(` · `)} -->\n`)}finally{m(!1)}},b=vr(()=>({mutationFn:e=>IO(e.id,e.text),onSuccess:()=>{f(``),l()}})),x=vr(()=>({mutationFn:e=>LO(e.id,e.model),onSuccess:t=>{e.setQueryData([`bridge-tasks`],e=>e?.map(e=>e.id===t.id?t:e)),l()}})),S=vr(()=>({mutationFn:e=>zO(e),onSuccess:l})),C,w=!0,E=()=>{C&&(w=C.scrollTop+C.clientHeight>=C.scrollHeight-48)};O(()=>{let e=s.data?.tail?.length??0;r(),!(!C||!e||!w)&&requestAnimationFrame(()=>{C&&(C.scrollTop=C.scrollHeight)})});let[ee,te]=T(null),A=async e=>{try{await navigator.clipboard.writeText(YO(e)),te(e.id),setTimeout(()=>te(null),1800)}catch{}},ne=e=>e===`running`?`var(--color-accent)`:e===`done`?`var(--color-success)`:e===`stopped`?`var(--color-warn)`:`var(--color-danger)`,re=e=>{let t=e.line.kind,n=k(()=>S$(e.line.text));return t===`user`?(()=>{var e=V1(),t=e.firstChild.nextSibling;return D(()=>t.innerHTML=n()),e})():t===`assistant`||t===`result`?(()=>{var e=H1(),r=e.firstChild,i=r.nextSibling;return st(e,`bg-surface rounded-lg border px-2.5 py-1.5 ${t===`result`?`border-success/40`:`border-border`}`),st(r,`mb-0.5 text-[9.5px] font-semibold tracking-wide uppercase ${t===`result`?`text-success`:`text-text-faint`}`),P(r,t===`result`?`Result`:`Claude`),D(()=>i.innerHTML=n()),e})():(()=>{var n=U1();return st(n,`font-mono px-1 text-[10px] leading-relaxed break-words ${t===`subagent`?`text-warn`:t===`stderr`?`text-danger`:`text-text-faint`}`),P(n,()=>e.line.text),n})()};return(()=>{var e=K1(),a=e.firstChild;a.firstChild;var l=a.nextSibling;return P(a,j(M,{get when(){return t.data?.ok},get fallback(){return j(zj,{class:`text-danger`,children:`bridge offline`})},get children(){return[j(M,{get when(){return C0(t.data.version,`0.15.1`)},get children(){return j(zj,{class:`text-warn`,title:`Ctrl+C the old process, then: npx linear-grab-bridge`,get children(){return[`v`,$e(()=>t.data.version),` outdated — restart`]}})}}),(()=>{var e=W1(),r=e.firstChild,i=r.nextSibling;return i.nextSibling,P(e,()=>t.data.active,r),P(e,()=>{let e=(n.data??[]).reduce((e,t)=>e+(t.usage?.costUsd??0),0);return e>0?` · Σ $${e.toFixed(2)}`:``},i),P(e,()=>t.data.cwd.split(`/`).slice(-2).join(`/`),null),D(()=>it(e,`title`,t.data?.cwd)),e})()]}}),null),P(l,j(M,{get when(){return t.data?.ok},get fallback(){return j(Vj,{title:`Bridge not running`,get children(){return[`Start it in your repo's terminal:`,q1(),`Then delegate from the Draft tab ("Local Claude Code") or watch tasks here. Sessions stay interactive — send follow-ups, switch models, interrupt, resume in your terminal. History persists across restarts.`]}})},get children(){return j(M,{get when(){return(n.data??[]).length>0},get fallback(){return j(Vj,{title:`No local tasks yet`,children:`Create an issue in Draft with "Delegate to → Local Claude Code", and it appears here with live status and a conversation view.`})},get children(){var e=G1();return P(e,j(Ge,{get each(){return n.data},children:e=>(()=>{var t=l0(),n=t.firstChild,a=n.firstChild,l=a.nextSibling,m=n.nextSibling,h=m.nextSibling,g=h.nextSibling;return P(n,j(M,{get when(){return e().status===`running`},get fallback(){return(()=>{var t=u0();return D(n=>dt(t,`background`,ne(e().status))),t})()},get children(){return j(Aj,{})}}),a),P(a,()=>e().title),P(l,()=>Wj(e().startedAt)),P(m,()=>e().lastText),P(h,j(zj,{get color(){return ne(e().status)},get children(){return e().status}}),null),P(h,j(zj,{get children(){return e().model??`default model`}}),null),P(h,j(M,{get when(){return e().pendingModel},get children(){return j(zj,{class:`text-warn`,get children(){return[`→ `,$e(()=>e().pendingModel),` next msg`]}})}}),null),P(h,j(M,{get when(){return e().subagents>0},get children(){return j(zj,{class:`text-warn`,get children(){return[`⛓ `,$e(()=>e().subagents),` subagents`]}})}}),null),P(h,j(M,{get when(){return e().usage},get children(){var t=J1(),n=t.firstChild.nextSibling;return n.nextSibling,P(t,()=>w0(e().usage.contextTokens),n),P(t,()=>w0(e().usage.outputTokens),null),P(t,(()=>{var t=$e(()=>!!e().usage.costUsd);return()=>t()?` · $${e().usage.costUsd.toFixed(2)}`:``})(),null),t}}),null),P(t,j(M,{get when(){return o(e())},children:e=>(()=>{var t=d0();return P(t,j(zj,{get children(){return e().state.name}}),null),P(t,j(Ij,{get href(){return e().url},children:`Open in Linear`}),null),t})()}),g),P(t,j(M,{get when(){return $e(()=>!!e().worktree)()&&!e().worktree.removed},get children(){var t=Y1(),n=t.firstChild;return P(t,j(zj,{class:`text-warn`,children:`⎇ worktree`}),n),P(n,()=>e().worktree.branch),P(t,j(M,{get when(){return e().status!==`running`},get children(){return j(kj,{variant:`ghost`,class:`ml-auto h-6 shrink-0 px-2 text-[11px]`,get loading(){return S.isPending},get title(){return`Remove the worktree at ${e().worktree.path} — the branch (and PR) survive`},onClick:()=>S.mutate(e().id),children:`Remove`})}}),null),D(()=>it(n,`title`,e().worktree.path)),t}}),g),P(g,j(Nj,{class:`h-7 w-auto min-w-0 flex-1 text-[11px]`,get value(){return e().pendingModel??e().model??``},onChange:t=>x.mutate({id:e().id,model:t.currentTarget.value}),title:`Model — applies from the next message`,get children(){return[j(We,{each:S0,children:t=>(()=>{var n=f0();return P(n,()=>t.label),D(()=>n.selected=(e().pendingModel??e().model??``)===t.id),D(()=>n.value=t.id),n})()}),j(M,{get when(){return!S0.some(t=>t.id===(e().pendingModel??e().model??``))},get children(){var t=X1();return P(t,()=>e().pendingModel??e().model),D(()=>t.value=e().pendingModel??e().model??``),t}})]}}),null),P(g,j(M,{get when(){return $e(()=>e().status!==`running`)()&&e().sessionId},get children(){return j(kj,{variant:`ghost`,class:`size-7 px-0`,get loading(){return b.isPending},get title(){return`Resume — continues this session${e().pendingModel?` on ${e().pendingModel}`:``}`},"aria-label":`Resume session`,onClick:()=>b.mutate({id:e().id,text:`Continue where you left off. If the work is already complete, finish any remaining closeout steps (PR, Linear update, announcement).`}),get children(){return Z1()}})}}),null),P(g,j(M,{get when(){return o(e())},children:e=>j(kj,{variant:`ghost`,class:`size-7 px-0`,title:`Open the issue's Activity thread`,onClick:()=>oj(`activity`,e().id),get children(){return p0()}})}),null),P(g,j(kj,{variant:`ghost`,class:`size-7 px-0`,get disabled(){return!e().sessionId},get title(){return $e(()=>!!e().sessionId)()?`Copy resume command · session ${e().sessionId.slice(0,8)}…`:`No session yet`},onClick:()=>void A(e()),get children(){return j(M,{get when(){return ee()===e().id},get fallback(){return m0()},get children(){return Q1()}})}}),null),P(g,j(kj,{variant:`ghost`,class:`size-7 px-0`,get title(){return r()===e().id?`Hide thread`:`Open thread — chat & changes`},onClick:()=>i(r()===e().id?null:e().id),get children(){return $1()}}),null),P(g,j(M,{get when(){return e().status===`running`},get children(){return j(kj,{variant:`danger`,class:`size-7 px-0`,get loading(){return u.isPending},title:`Interrupt — the session stays resumable`,onClick:()=>u.mutate(e().id),get children(){return e0()}})}}),null),P(t,j(M,{get when(){return r()===e().id},get children(){return[j(M,{get when(){return $e(()=>!!c.data)()&&(c.data.files.length||c.data.untracked.length||c.data.prs.length)},get children(){var e=n0(),t=e.firstChild,n=t.firstChild.nextSibling,r=n.firstChild;r.firstChild;var i=r.nextSibling.nextSibling;return i.firstChild,P(t,j(M,{get when(){return c.data.branch},get children(){var e=t0();return P(e,()=>c.data.branch),e}}),n),P(r,()=>c.data.totalAdded,null),P(i,()=>c.data.totalDeleted,null),P(e,j(We,{get each(){return c.data.files},children:e=>(()=>{var t=_0(),n=t.firstChild,r=n.nextSibling;return P(n,()=>e.path),P(r,j(M,{get when(){return!e.binary},get fallback(){return v0()},get children(){return[(()=>{var t=h0();return t.firstChild,P(t,()=>e.added,null),t})(),` `,(()=>{var t=g0();return t.firstChild,P(t,()=>e.deleted,null),t})()]}})),D(()=>it(n,`title`,e.path)),t})()}),null),P(e,j(We,{get each(){return c.data.untracked},children:e=>(()=>{var t=y0(),n=t.firstChild;return it(n,`title`,e),P(n,e),t})()}),null),P(e,j(We,{get each(){return c.data.prs},children:e=>(()=>{var t=b0(),n=t.firstChild;return P(t,j(zj,{class:`text-accent`,get children(){return[`PR · `,$e(()=>e.state.toLowerCase())]}}),n),P(n,()=>e.title),D(t=>{var r=e.url,i=e.title;return r!==t.e&&it(n,`href`,t.e=r),i!==t.t&&it(n,`title`,t.t=i),t},{e:void 0,t:void 0}),t})()}),null),e}}),(()=>{var e=r0();return e.addEventListener(`scroll`,E),pt(e=>{C=e,w=!0},e),P(e,j(M,{get when(){return(s.data?.tail??[]).length>0},get fallback(){return x0()},get children(){return j(Ge,{get each(){return s.data?.tail??[]},children:e=>j(re,{get line(){return e()}})})}})),e})(),(()=>{var t=c0(),n=t.firstChild,r=n.firstChild;return P(t,j(Mj,{rows:2,get placeholder(){return e().status===`running`?`Message the working agent — it queues into the live session…`:`Follow-up — resumes the session…`},get value(){return d()},onInput:e=>f(e.currentTarget.value)}),n),P(r,(()=>{var t=$e(()=>!!e().alive);return()=>t()?`live session`:e().sessionId?`resumes on send`:`no session`})()),P(n,j(kj,{variant:`ghost`,class:`size-7 shrink-0 px-0`,title:`Pick elements on the page — refs drop into this message`,"aria-label":`Pick element`,onClick:v,get children(){return i0()}}),null),P(n,j(kj,{variant:`ghost`,class:`size-7 shrink-0 px-0`,get loading(){return p()},title:`Attach captures — element refs, screenshots, region shots, recording GIF`,"aria-label":`Attach captures`,onClick:()=>void y(),get children(){return a0()}}),null),P(n,j(kj,{variant:`ghost`,class:`size-7 shrink-0 px-0`,get disabled(){return $e(()=>!e().alive)()&&!e().sessionId},title:`Compact the session — frees context (sends /compact)`,"aria-label":`Compact session`,onClick:()=>b.mutate({id:e().id,text:`/compact`}),get children(){return o0()}}),null),P(n,j(kj,{variant:`primary`,class:`shrink-0`,get loading(){return b.isPending},get disabled(){return!d().trim()||!e().alive&&!e().sessionId},onClick:()=>b.mutate({id:e().id,text:d().trim()}),get children(){return s0()}}),null),t})()]}}),null),t})()})),e}})}})),e})()}Br();var E0=N(`<span class="text-text-faint text-[10.5px] tabular-nums">/<!> from your issues`),D0=N(`<div class="flex flex-col gap-1.5">`),O0=N(`<div class="flex h-full flex-col"><div class="border-border flex shrink-0 items-center justify-between border-b px-3 py-2"><span class="text-text text-[12px] font-semibold">Pull requests</span></div><div class="border-border flex shrink-0 items-center gap-1.5 border-b px-3 py-1.5"><div class="bg-surface-2 border-border flex shrink-0 rounded-md border p-0.5"></div></div><div class="min-h-0 flex-1 overflow-y-auto pt-2 pb-3 pl-2 pr-3">`),k0=N(`<span class="text-warn text-[10.5px]"title="Merge states come from gh via the bridge">states unknown — restart the bridge`),A0=N(`<button>`),j0=N(`<span class="text-success text-[11px]">✓`),M0=N(`<span class="text-[12px] leading-none">✓`),N0=N(`<svg width=13 height=13 viewBox="0 0 16 16"fill=none stroke=currentColor stroke-width=1.5 stroke-linecap=round aria-hidden><circle cx=8 cy=8 r=6></circle><circle cx=8 cy=8 r=1.6 fill=currentColor stroke=none>`),P0=N(`<span class="text-success text-[12px] leading-none">✓`),F0=N(`<div class="bg-surface border-border flex flex-col gap-1.5 rounded-lg border p-2.5"><a target=_blank rel=noreferrer class="text-accent truncate text-[12.5px] font-medium hover:underline"></a><div class="flex min-w-0 items-center gap-1.5"><span class="font-mono text-text-dim shrink-0 text-[11px]"></span><span class="text-text-dim truncate text-[11.5px]"></span><span class="text-text-faint ml-auto shrink-0 text-[10.5px] tabular-nums"></span></div><div class="flex min-w-0 flex-wrap items-center gap-1.5"><div class="ml-auto flex shrink-0 items-center gap-1">`),I0=N(`<svg width=12 height=12 viewBox="0 0 16 16"fill=none stroke=currentColor stroke-width=1.5 stroke-linecap=round stroke-linejoin=round aria-hidden><path d="M8 2 14 5.5 8 9 2 5.5 8 2Z"></path><path d="M2 10.5 8 14l6-3.5">`),L0=N(`<svg width=13 height=13 viewBox="0 0 16 16"fill=none stroke=currentColor stroke-width=1.5 stroke-linecap=round aria-hidden><circle cx=4 cy=3.5 r=1.5></circle><circle cx=4 cy=12.5 r=1.5></circle><circle cx=12 cy=8 r=1.5></circle><path d="M4 5v6M4 6.5c0 2 2 3 4.5 3h2">`),R0=N(`<svg width=13 height=13 viewBox="0 0 16 16"fill=none stroke=currentColor stroke-width=1.5 stroke-linejoin=round aria-hidden><rect x=5.5 y=5.5 width=9 height=9 rx=1.5></rect><path d="M10.5 5.5v-2a2 2 0 0 0-2-2h-5a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h2">`),z0=/github\.com\/[^/]+\/[^/]+\/pull\/\d+/i;function B0(e){let t=_r(()=>({queryKey:[`my-issues`],queryFn:fO,refetchInterval:15e3})),n=k(()=>(t.data??[]).flatMap(e=>(e.attachments??[]).filter(e=>z0.test(e.url)).map(t=>({issue:e,attachment:t})))),r=_r(()=>({queryKey:[`bridge-tasks`],queryFn:NO,refetchInterval:1e4,retry:0})),i=_r(()=>({queryKey:[`pr-status`,n().map(e=>e.attachment.url).sort().join(`|`)],queryFn:()=>HO(n().map(e=>e.attachment.url)),refetchInterval:3e4,retry:0,enabled:n().length>0})),[a,o]=T(new Set),s=e=>a().has(e.attachment.url)?`MERGED`:i.data?.statuses[e.attachment.url],c=e=>!!r.data?.some(t=>t.title.startsWith(e.issue.identifier)),[l,u]=T(`all`),[d,f]=T(``),p=k(()=>{let e=d().trim().toLowerCase();return n().filter(t=>{let n=t.issue.state.type,r=s(t);return!(l()===`review`&&(n!==`started`||r===`MERGED`||r===`CLOSED`)||l()===`open`&&(r===`MERGED`||r===`CLOSED`||!(n===`started`||n===`unstarted`||n===`backlog`))||l()===`done`&&!(n===`completed`||r===`MERGED`||r===`CLOSED`)||e&&![t.attachment.title,t.issue.title,t.issue.identifier].join(` `).toLowerCase().includes(e))})}),m=_r(()=>({queryKey:[`settings`],queryFn:Tr})),h=()=>m.data?.stagingBranch?.trim()||`staging`,[g,_]=T(null),[v,y]=T(new Set),[b,x]=T(null),S=async e=>{_(e),x(null);try{await UO(e,h()),y(t=>new Set(t).add(e))}catch(e){x(e instanceof Error?e.message:String(e))}finally{_(null)}},[C,w]=T(null),[E,O]=T(null),ee=async e=>{O(e);try{await qO(e),o(t=>new Set(t).add(e))}catch{}finally{O(null)}},te=async e=>{try{await navigator.clipboard.writeText(e.attachment.url),w(e.attachment.id),setTimeout(()=>w(null),1500)}catch{}};return(()=>{var r=O0(),o=r.firstChild;o.firstChild;var m=o.nextSibling,_=m.firstChild,y=m.nextSibling;return P(o,j(M,{get when(){return!i.isError},get fallback(){return k0()},get children(){var e=E0(),t=e.firstChild,r=t.nextSibling;return r.nextSibling,P(e,()=>p().length,t),P(e,()=>n().length,r),e}}),null),P(_,j(We,{each:[{id:`all`,label:`All`},{id:`review`,label:`Review`},{id:`open`,label:`Open`},{id:`done`,label:`Done`}],children:e=>(()=>{var t=A0();return t.$$click=()=>u(e.id),P(t,()=>e.label),D(()=>st(t,`rounded-[5px] px-2 py-0.5 text-[11px] font-medium transition-colors ${l()===e.id?`bg-surface-3 text-text`:`text-text-dim hover:text-text cursor-pointer`}`)),t})()})),P(m,j(jj,{class:`h-6 min-w-0 flex-1 text-[11px]`,placeholder:`Search PRs…`,get value(){return d()},onInput:e=>f(e.currentTarget.value)}),null),P(y,j(M,{get when(){return p().length>0},get fallback(){return j(Vj,{title:`No PRs yet`,get children(){return $e(()=>!!t.isError)()?`Connect Linear in Settings.`:n().length>0?`No PRs match the current filter.`:`PRs opened by delegated agents appear here as soon as they link back to your issues.`}})},get children(){var t=D0();return P(t,j(We,{get each(){return p()},children:t=>(()=>{var n=F0(),r=n.firstChild,o=r.nextSibling,l=o.firstChild,u=l.nextSibling,d=u.nextSibling,f=o.nextSibling,p=f.firstChild;return P(r,()=>t.attachment.title||t.attachment.url),P(n,j(M,{get when(){return i.data?.previews[t.attachment.url]},get children(){return j(Ij,{get href(){return i.data.previews[t.attachment.url]},class:`shrink-0 self-start`,title:`Vercel deploy preview of this PR's branch`,children:`Preview`})}}),o),P(o,j(Bj,{get color(){return t.issue.state.color}}),l),P(l,()=>t.issue.identifier),P(u,()=>t.issue.title),P(d,()=>Wj(t.issue.updatedAt)),P(f,j(zj,{get children(){return t.issue.state.name}}),p),P(f,j(M,{get when(){return t.issue.delegate},get children(){return j(zj,{class:`text-accent`,get children(){return[j(Pj,{}),` `,$e(()=>t.issue.delegate.displayName)]}})}}),p),P(f,j(M,{get when(){return c(t)},get children(){return j(zj,{class:`text-warn`,get children(){return[j(Fj,{}),` local`]}})}}),p),P(f,j(Ij,{get href(){return t.issue.url},class:`shrink-0`,title:`Open the issue in Linear`,children:`Linear`}),p),P(p,j(M,{get when(){return $e(()=>!!s(t))()&&s(t)!==`OPEN`},get children(){return j(zj,{get class(){return s(t)===`MERGED`?`text-success`:`text-text-faint`},get children(){return s(t)===`MERGED`?`✓ merged`:`closed`}})}}),null),P(p,j(kj,{variant:`ghost`,class:`size-7 shrink-0 px-0`,get loading(){return g()===t.attachment.url},get disabled(){return v().has(t.attachment.url)},get title(){return $e(()=>!!v().has(t.attachment.url))()?`On ${h()} — staging preview deploying`:`Merge into ${h()} — deploys the staging preview domain`},"aria-label":`Merge to staging`,onClick:()=>void S(t.attachment.url),get children(){return j(M,{get when(){return v().has(t.attachment.url)},get fallback(){return I0()},get children(){return j0()}})}}),null),P(p,j(kj,{variant:`primary`,class:`size-7 px-0`,get classList(){return{hidden:!!s(t)&&s(t)!==`OPEN`}},get loading(){return E()===t.attachment.url},get disabled(){return a().has(t.attachment.url)},get title(){return a().has(t.attachment.url)?`Merged`:`Squash-merge this PR (bridge gh)`},"aria-label":`Merge PR`,onClick:()=>void ee(t.attachment.url),get children(){return j(M,{get when(){return a().has(t.attachment.url)},get fallback(){return L0()},get children(){return M0()}})}}),null),P(p,j(kj,{variant:`ghost`,class:`size-7 px-0`,title:`Open the issue's Activity thread`,"aria-label":`Open issue activity`,onClick:()=>{oj(`activity`,t.issue.id),e.onOpenIssue()},get children(){return N0()}}),null),P(p,j(kj,{variant:`ghost`,class:`size-7 px-0`,title:`Copy PR URL`,"aria-label":`Copy PR URL`,onClick:()=>void te(t),get children(){return j(M,{get when(){return C()===t.attachment.id},get fallback(){return R0()},get children(){return P0()}})}}),null),P(f,j(M,{get when(){return v().has(t.attachment.url)},get children(){return j(u$,{get url(){return t.attachment.url},get base(){return h()}})}}),null),D(e=>{var n=t.attachment.url,i=t.attachment.title;return n!==e.e&&it(r,`href`,e.e=n),i!==e.t&&it(r,`title`,e.t=i),e},{e:void 0,t:void 0}),n})()})),t}})),r})()}rt([`click`]);var V0=[`display`,`align-items`,`justify-content`,`gap`,`padding`,`margin`,`width`,`height`,`font-size`,`font-weight`,`line-height`,`letter-spacing`,`color`,`background-color`,`border-radius`,`border-width`,`border-color`,`box-shadow`,`opacity`,`transform`,`transition-property`,`transition-duration`],H0=new Set([`color`,`background-color`,`border-color`]),U0={margin:new Set([`0px`]),padding:new Set([`0px`]),gap:new Set([`normal`,`0px`]),"box-shadow":new Set([`none`]),transform:new Set([`none`]),"border-width":new Set([`0px`]),"border-radius":new Set([`0px`]),"letter-spacing":new Set([`normal`]),opacity:new Set([`1`]),"transition-property":new Set([`all`,`none`]),"transition-duration":new Set([`0s`]),width:new Set([`auto`]),height:new Set([`auto`])},W0=null;function G0(e){try{return W0||(W0=document.createElement(`span`),W0.style.display=`none`,document.body.appendChild(W0)),W0.style.color=``,W0.style.color=e,W0.style.color?getComputedStyle(W0).color:null}catch{return null}}var K0=null;function q0(){if(K0)return K0;let e=new Map,t=getComputedStyle(document.documentElement),n=new Set;for(let r of Array.from(document.styleSheets)){let i;try{i=r.cssRules}catch{continue}for(let r of Array.from(i)){let i=r.style;if(i)for(let r=0;r<i.length;r++){let a=i.item(r);if(!a.startsWith(`--`)||n.has(a))continue;n.add(a);let o=t.getPropertyValue(a).trim()||i.getPropertyValue(a).trim();if(!o||o.length>80)continue;let s=G0(o);s&&!e.has(s)&&e.set(s,a)}}}return K0=e,e}function J0(e){let t=e.getBoundingClientRect();if(t.width<2&&t.height<2)return!1;let n=getComputedStyle(e);return n.display!==`none`&&n.visibility!==`hidden`}function Y0(e){let t=[],n=e.getAttribute(`role`);n&&t.push(n);for(let n of[`data-state`,`data-highlighted`,`aria-expanded`,`aria-selected`,`data-active`]){let r=e.getAttribute(n);r!=null&&t.push(`${n.replace(/^(data|aria)-/,``)}=${r||`true`}`)}return t.join(` `)}async function X0(){try{return(await Promise.resolve().then(()=>(RZ(),AZ))).getGlobalApi()?.getDisplayName??null}catch{return null}}function Z0(e,t){let n=null;try{n=t?.(e)??null}catch{}let r=Y0(e),i=n||e.tagName.toLowerCase();return r?`${i} [${r}]`:i}function Q0(e,t){let n=getComputedStyle(e),r={},i={};for(let e of V0){let a=n.getPropertyValue(e).trim();if(!(!a||U0[e]?.has(a)))if(H0.has(e)){let n=G0(a);if(n===`rgba(0, 0, 0, 0)`)continue;let o=n?t.get(n):void 0;o&&(i[e]=o),r[e]=a}else r[e]=a}return{styles:r,tok:i}}var $0=40;async function e2(e){let t=q0(),n=await X0(),r=[],i=new Map,a=e=>{if(r.length>=$0||!J0(e))return;let o=typeof e.className==`string`?e.className:``,s=`${e.tagName}|${o}`,c=i.get(s);if(c)c.count+=1;else{let{styles:a,tok:c}=Q0(e,t),l={label:Z0(e,n),count:1,classes:o,styles:a,tokens:c};i.set(s,l),r.push(l)}for(let t of Array.from(e.children))a(t)};return a(e),{title:r[0]?.label??e.tagName.toLowerCase(),pageUrl:location.href,extractedAt:Date.now(),nodes:r,states:[]}}var t2={active:!1,msLeft:0,total:0,byTrigger:{}},n2=new Set;function r2(e){t2={...t2,...e};for(let e of n2)e(t2)}function i2(e){return n2.add(e),e(t2),()=>void n2.delete(e)}var a2=null;function o2(){a2?.()}function s2(e){return e===`hover`||e===`focus`?e:`open`}async function c2(e,t,n){let r=q0(),i=await X0(),a=[],o=new Set,s=new Map,c=e=>Q0(e,r).styles,l=e=>{if(!(s.size>=120)){s.set(e,c(e));for(let t of Array.from(e.children))l(t)}};l(e);let u=(e,t)=>{let n=s.get(e);if(!n)return;let r=c(e),l={};for(let e of new Set([...Object.keys(n),...Object.keys(r)])){let t=n[e]??`(unset)`,i=r[e]??`(unset)`;t!==i&&(l[e]={from:t,to:i})}if(Object.keys(l).length===0)return;let u=Z0(e,i),d=`${t}|${u}|${Object.keys(l).join(`,`)}`;if(o.has(d))return;o.add(d),a.push({trigger:t,label:u,changed:l});let f=s2(t);r2({total:t2.total+1,byTrigger:{...t2.byTrigger,[f]:(t2.byTrigger[f]??0)+1}})},d=e=>{let t=e.target;t instanceof Element&&s.has(t)&&setTimeout(()=>u(t,`hover`),50)},f=e=>{let t=e.target;t instanceof Element&&s.has(t)&&setTimeout(()=>u(t,`focus`),50)};e.addEventListener(`pointerover`,d,!0),e.addEventListener(`focusin`,f,!0);let p=new MutationObserver(e=>{for(let t of e){if(t.type!==`attributes`||!(t.target instanceof Element))continue;let e=t.target,n=t.attributeName??``,r=e.getAttribute(n)??``;setTimeout(()=>u(e,`attr:${n}=${r||`true`}`),50)}});p.observe(e,{subtree:!0,attributes:!0,attributeFilter:[`class`,`data-state`,`data-highlighted`,`aria-expanded`,`aria-selected`,`open`]});let m=new MutationObserver(t=>{for(let n of t)for(let t of Array.from(n.addedNodes))!(t instanceof Element)||e.contains(t)||t.id===`linear-grab-root`||t.closest?.(`#linear-grab-root`)||setTimeout(()=>{if(!t.isConnected||!J0(t))return;let{styles:e}=Q0(t,r);if(Object.keys(e).length===0)return;let n=Z0(t,i),s=`appeared|${n}`;if(o.has(s))return;o.add(s);let c={};for(let[t,n]of Object.entries(e))c[t]={from:`(absent)`,to:n};a.push({trigger:`appeared (portal/open)`,label:n,changed:c}),r2({total:t2.total+1,byTrigger:{...t2.byTrigger,open:(t2.byTrigger.open??0)+1}})},50)});m.observe(document.body,{childList:!0,subtree:!1}),r2({active:!0,msLeft:t,total:0,byTrigger:{}});let h=setInterval(()=>{let e=t-(Date.now()-g);n?.(Math.max(0,e)),r2({msLeft:Math.max(0,e)})},250),g=Date.now();return await new Promise(e=>{let n=setTimeout(e,t);a2=()=>{clearTimeout(n),e()}}),a2=null,r2({active:!1,msLeft:0}),clearInterval(h),e.removeEventListener(`pointerover`,d,!0),e.removeEventListener(`focusin`,f,!0),p.disconnect(),m.disconnect(),a}function l2(e){let t=[`# Design genome: ${e.title}`,`Extracted from ${e.pageUrl}`,``,`## Structure`];for(let n of e.nodes){t.push(`### ${n.label}${n.count>1?` ×${n.count}`:``}`),n.classes&&t.push(`classes: \`${n.classes}\``);let e=Object.entries(n.styles).map(([e,t])=>{let r=n.tokens[e];return`${e}: ${t}${r?` ← var(${r})`:``}`});e.length&&t.push("```",...e,"```")}if(e.states.length){t.push(``,`## Interaction states`);for(let n of e.states)t.push(`### ${n.label} — ${n.trigger}`),t.push("```",...Object.entries(n.changed).map(([e,t])=>`${e}: ${t.from} → ${t.to}`),"```")}return t.join(`
271
271
  `)}function u2(){return new Promise(e=>{let t=document.createElement(`div`);t.style.cssText=`position:fixed;z-index:2147483647;pointer-events:none;border:1.5px solid #5e6ad2;border-radius:4px;background:rgba(94,106,210,0.08);display:none;`,document.body.appendChild(t);let n=null,r=e=>{let t=document.elementFromPoint(e.clientX,e.clientY);return!t||t.closest(`#linear-grab-root`)?null:t},i=e=>{let i=r(e);if(n=i,!i){t.style.display=`none`;return}let a=i.getBoundingClientRect();t.style.display=`block`,t.style.left=`${a.left-2}px`,t.style.top=`${a.top-2}px`,t.style.width=`${a.width+4}px`,t.style.height=`${a.height+4}px`},a=n=>{t.remove(),window.removeEventListener(`mousemove`,i,!0),window.removeEventListener(`click`,o,!0),window.removeEventListener(`keydown`,s,!0),e(n)},o=e=>{e.preventDefault(),e.stopPropagation(),a(n)},s=e=>{e.key===`Escape`&&(e.stopPropagation(),a(null))};window.addEventListener(`mousemove`,i,!0),window.addEventListener(`click`,o,!0),window.addEventListener(`keydown`,s,!0)})}function d2(e){return e.split(`,`).map(e=>{let t=e.trim();return t.endsWith(`ms`)?parseFloat(t)||0:t.endsWith(`s`)?(parseFloat(t)||0)*1e3:0})}var f2=new Set(`width.height.min-width.min-height.max-width.max-height.top.left.right.bottom.inset.margin.margin-top.margin-right.margin-bottom.margin-left.padding.padding-top.padding-right.padding-bottom.padding-left.font-size.line-height.letter-spacing.box-shadow.filter.background-position.grid-template-columns.grid-template-rows.flex-basis.gap`.split(`.`));function p2(e){return e.includes(`all`)?[`all (includes layout/paint props)`]:e.filter(e=>f2.has(e))}function m2(e){let t=h2(e);if(t===`ease-in`||t===`ease-in-out`)return t;let n=t.match(/cubic-bezier\(\s*([\d.]+)\s*,\s*(-?[\d.]+)/);if(n){let e=parseFloat(n[1]),r=parseFloat(n[2]);if(e>=.4&&r<e/2)return t}return null}function h2(e){let t=0;for(let n=0;n<e.length;n++){let r=e[n];if(r===`(`)t++;else if(r===`)`)t--;else if(r===`,`&&t===0)return e.slice(0,n).trim()}return e.trim()}var g2=`[data-slot*="content"], [role="menu"], [role="dialog"], [role="listbox"]`,_2=new Set([`width`,`height`,`flex-basis`,`grid-template-columns`,`grid-template-rows`,`grid-template-areas`]);function v2(e,t){if(e instanceof SVGElement)return!0;try{if(e.closest(`svg`))return!0}catch{}try{if(e.closest(g2))return!0}catch{}try{if(e.closest(`[data-panel]`))return!t||t.length===0||t.every(e=>_2.has(e))}catch{}return!1}function y2(e){let t=e.id?`#${e.id}`:``,n=typeof e.className==`string`&&e.className?`.${e.className.split(/\s+/).filter(Boolean).slice(0,2).join(`.`)}`:``;return`${e.tagName.toLowerCase()}${t}${n}`}var b2=200;function x2(e){if(typeof e.className!=`string`)return;let t=e.className.split(/\s+/).filter(e=>/^(transition|duration-|delay-|ease-)/.test(e));return t.length?t.join(` `):void 0}function S2(e,t,n,r,i,a){let o=[];return n>0&&o.push(`remove the ${Math.round(n)}ms transition-delay`),(r?.includes(`transition-all`)||e.includes(`all`))&&o.push(`scope 'transition-all' to the intended properties (e.g. transition-colors)`),a?.length&&o.push(`JANK: transitions LAYOUT/PAINT props (${a.join(`, `)}) — reflow/repaint every frame; animate transform/opacity instead (box-shadow → animate a pseudo-element's opacity)`),i&&o.push(`easing '${i}' starts slow — §42 bans ease-in/ease-in-out on UI, use ease-out`),t>500?o.push(`${Math.round(t)}ms is over the 500ms HARD CAP for UI feedback`):t>300?o.push(`${Math.round(t)}ms is over the 300ms UI cap`):t>100&&o.push(`shorten ${Math.round(t)}ms → ≤100ms`),o.push(`make PRESS feedback instant: 'active:transition-none' (and data-[state=…]:transition-none for toggles) — animate only hover in/out`),o.join(`; `)}async function C2(){try{return(await Promise.resolve().then(()=>(RZ(),AZ))).getGlobalApi()??null}catch{return null}}async function w2(e){let t=await C2(),n=null,r=null;try{n=t?.getDisplayName?.(e)??null}catch{}try{let n=await t?.getSource?.(e);n?.filePath&&(r=`${n.filePath}${n.lineNumber==null?``:`:${n.lineNumber}`}`)}catch{}return{component:n,source:r}}var T2=[],E2=null;function D2(e){T2.push(e),E2??=setTimeout(()=>{E2=null;let e=T2.slice(0,50).map(e=>({kind:`css-slowdown`,...e}));T2=[],JO().then(t=>fetch(`${t}/scan/events`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({events:e}),keepalive:!0})).catch(()=>{})},1500)}var O2=[],k2=new Set;function A2(e){let t=e.mode===`live`?O2.find(t=>t.mode===`live`&&t.element===e.element&&(t.classHint??``)===(e.classHint??``)&&t.properties.join()===e.properties.join()):void 0;t?(t.count+=1,t.at=e.at,t.sinceInputMs=e.sinceInputMs,O2=[...O2]):O2=[e,...O2].slice(0,100);for(let e of k2)e(O2);D2(e)}function j2(e){return k2.add(e),e(O2),()=>void k2.delete(e)}function M2(){O2=[];for(let e of k2)e(O2)}var N2=!0;try{N2=localStorage.getItem(`linear-grab:css-watch`)!==`0`}catch{}var P2=new Set;function F2(){return N2}function I2(e){N2=e;try{localStorage.setItem(`linear-grab:css-watch`,e?`1`:`0`)}catch{}for(let t of P2)t(e)}function L2(e){return P2.add(e),e(N2),()=>void P2.delete(e)}var R2=!1;function z2(){if(R2||typeof window>`u`)return;R2=!0;let e=0,t=()=>{e=performance.now()};for(let e of[`pointerdown`,`pointerup`,`keydown`])window.addEventListener(e,t,{capture:!0,passive:!0});let n=new Map,r=new Map;window.addEventListener(`transitionrun`,t=>{if(!N2)return;let i=performance.now()-e;if(i>b2)return;let a=t.target;if(!(a instanceof Element)||a.closest(`#linear-grab-root`)||a===document.documentElement||a===document.body||t.propertyName.startsWith(`--`))return;let o=getComputedStyle(a),s=o.transitionProperty.split(`,`).map(e=>e.trim());if(v2(a,s))return;let c=d2(o.transitionDuration),l=d2(o.transitionDelay),u=Math.max(0,s.indexOf(t.propertyName)),d=c[u%c.length]??c[0]??0,f=l[u%l.length]??l[0]??0;if(d+f<=100)return;let p=r.get(a);if(p){p.props.add(t.propertyName),p.duration=Math.max(p.duration,d),p.delay=Math.max(p.delay,f);return}let m={props:new Set([t.propertyName]),duration:d,delay:f,since:i,timer:setTimeout(()=>{r.delete(a);let e=y2(a),t=Date.now();if(t-(n.get(e)??0)<5e3)return;n.set(e,t);let i=x2(a),o=[...m.props],s=a.isConnected?m2(getComputedStyle(a).transitionTimingFunction):null,c=p2(o);w2(a).then(({component:n,source:r})=>{A2({mode:`live`,page:location.pathname,element:e,component:n,source:r,properties:o,durationMs:Math.round(m.duration),delayMs:Math.round(m.delay),sinceInputMs:Math.round(m.since),count:1,classHint:i,easing:s??void 0,jankProps:c.length?c:void 0,suggestion:S2(o,m.duration,m.delay,i,s,c),at:t})})},300)};r.set(a,m)},{capture:!0,passive:!0})}var B2=`button, a, input, select, textarea, [role], [tabindex], [data-state], [aria-expanded]`;async function V2(){let e=new Map,t=Array.from(document.querySelectorAll(B2)).slice(0,3e3);for(let n of t){if(n.closest(`#linear-grab-root`))continue;let t=n.getBoundingClientRect();if(t.width<2&&t.height<2)continue;let r=getComputedStyle(n);if(r.cursor!==`pointer`&&!n.matches(`button, a, input, select, textarea`))continue;let i=r.transitionProperty.split(`,`).map(e=>e.trim());if(v2(n,i))continue;let a=d2(r.transitionDuration),o=d2(r.transitionDelay),s=Math.max(...a,0)+Math.max(...o,0);if(s<=100)continue;let c=x2(n),l=`${n.tagName}|${c??i.join()}|${Math.round(s)}`,u=e.get(l);if(u){u.finding.count+=1;continue}let d=m2(r.transitionTimingFunction),f=p2(i);e.set(l,{example:n,finding:{mode:`audit`,page:location.pathname,element:y2(n),component:null,source:null,properties:i,durationMs:Math.round(Math.max(...a,0)),delayMs:Math.round(Math.max(...o,0)),count:1,classHint:c,easing:d??void 0,jankProps:f.length?f:void 0,suggestion:S2(i,Math.max(...a,0),Math.max(...o,0),c,d,f),at:Date.now()}})}let n=[];for(let{finding:t,example:r}of e.values()){let{component:e,source:i}=await w2(r);t.component=e,t.source=i,n.push(t)}n.sort((e,t)=>t.durationMs+t.delayMs-(e.durationMs+e.delayMs)||t.count-e.count),O2=[...n,...O2.filter(e=>e.mode===`live`)].slice(0,100);for(let e of k2)e(O2);for(let e of n)D2(e);return n}function H2(e){return[`Below is a CSS-slowdown report from my running app (live-captured on real input + full-page audit).`,`Goal: ALL interaction feedback must be instant. Rules:`,``,`1. Most of these come from SHARED design-system variants — fix the VARIANT`,` DEFINITIONS, not the call sites. Grep for the exact class recipes in the`," findings' `classes:` lines (e.g. `transition-all`, `transition-colors duration-150`).","2. Press/toggle state changes get NO transition: add `active:transition-none`"," and `data-[state=on]:transition-none` / `data-[state=open]:transition-none`.",` Hover in/out may keep a transition but shorten it: duration-150 → duration-100`,` (duration-75 for small controls like tabs and icon buttons).`,"3. Replace every `transition-all` with the scoped variant (transition-colors /",` transition-transform / transition-opacity — per what actually changes).`,`3b. Findings marked ⚠ layout/paint: NEVER transition width/height/top/left/`,` margin/padding/box-shadow — they reflow or repaint every frame. Use`,` transform: translate/scale, and animate a pseudo-element's opacity for shadows.`,`3c. Findings marked ⚠ easing: never ease-in OR ease-in-out on UI (§42 bans`,` both) — replace with ease-out (house curve: cubic-bezier(.2,0,.1,1), the`,` theme default).`,`3d. Press must be instant GLOBALLY: keep the unlayered §42 rule in globals.css`," — `button:active, a:active, [role=button/tab/menuitem/option]:active"," { transition-duration: 0s }` — so pointer-down feedback snaps and only the",` release animates back. If a control still animates on press, that global`,` rule is missing or overridden.`,`4. Where a finding's source looks like a generic fallback (one file:line repeated`," for many components), locate the code by the `classes:` line instead.",`5. Do NOT touch entry/exit animations of popovers/menus or icon micro-motion`,` (chevron rotate etc.) — that is intentional; only input FEEDBACK must be instant.`,`6. PROOF REQUIRED: after the change I will re-run the audit — it must show zero`,` interactive elements ≥100ms and clicking must produce no live findings.`,` Do not claim done without stating what you changed per variant.`,``,`---`,``,U2(e)].join(`
272
272
  `)}function U2(e){let t=[`# CSS slowdowns on ${location.host}${location.pathname}`,`Threshold: 100ms (interaction feedback should be instant).`,``];for(let n of e)t.push(`## ${n.component??n.element}${n.count>1?` ×${n.count}`:``} — ${n.durationMs}ms${n.delayMs?` +${n.delayMs}ms delay`:``}`),n.source&&t.push(`- source: ${n.source}`),t.push(`- transitions: ${n.properties.join(`, `)}`),n.classHint&&t.push(`- classes: \`${n.classHint}\``),n.jankProps?.length&&t.push(`- ⚠ layout/paint transition (reflows every frame): ${n.jankProps.join(`, `)}`),n.easing&&t.push(`- ⚠ easing: ${n.easing} (§42 bans ease-in/ease-in-out — use ease-out)`),n.sinceInputMs!=null&&t.push(`- fired ${n.sinceInputMs}ms after user input (live capture)`),t.push(`- fix: ${n.suggestion}`,``);return t.join(`
273
- `)}function W2(e,t,n){return{ruleId:e.id,part:e.part,severity:e.severity,description:e.description,selector:y2(t),evidence:n,el:typeof WeakRef<`u`?new WeakRef(t):void 0}}function G2(e){return e.getBoundingClientRect()}function K2(e){return e.split(`/`)[0].trim().split(/\s+/).filter(Boolean).map(e=>e.endsWith(`px`)?parseFloat(e):NaN)}function q2(e){let t=e.match(/rgba?\(([^)]+)\)/);return t?t[1].split(`,`).map(e=>e.trim()).join(`,`):null}function J2(e,t){let n=t,r=n.maskImage||n.webkitMaskImage||``;if(r&&r!==`none`)return r;let i=(e.getAttribute(`style`)??``).match(/(?:^|;|\s)(?:-webkit-)?mask-image\s*:\s*([^;]+)/i);return i&&i[1].trim()&&i[1].trim()!==`none`?i[1].trim():``}var Y2=new Set([0,8,12,16]),X2={light:q2(`rgb(240, 240, 241)`),dark:q2(`rgb(35, 35, 37)`)},Z2=new Set([`#50e3c2`,`#ee0000`,`#f5a623`]),Q2=`button, a, [role="button"], [role="tab"], [role="menuitem"], [role="option"], input, textarea, select`;function $2(e){let t=[],n=e=>{for(let r of Array.from(e)){let e=r;if(e.cssRules&&e.cssRules.length){n(e.cssRules);continue}let i=r.selectorText;i&&i.includes(`:hover`)&&t.push(i.replace(/:hover/g,``))}};for(let t of Array.from(e.styleSheets))try{n(t.cssRules)}catch{}return t}function e4(e,t){for(let n of t)if(n.trim())try{if(e.matches(n))return!0}catch{}return!1}function t4(e){return!e.matches(`button, [role="button"]`)||e.querySelectorAll(`svg`).length===0?!1:(e.textContent??``).trim().length===0}function n4(e){let t=e.parentElement;for(;t;){try{let e=r4(t).overflowY;if(e===`auto`||e===`scroll`)return t}catch{}t=t.parentElement}return null}var r4=e=>getComputedStyle(e),i4={id:`radius-vocabulary`,part:`§40`,severity:`error`,description:`§40: radius vocabulary is {8, 12, 16, full} — never rounded-md/[4px]/[6px]/[10px]. Off-vocabulary radius.`,check(e){let t=[];for(let n of e.els){let r=e.css(n).borderRadius;if(!r||r===`0px`)continue;let i=K2(r);if(i.some(e=>Number.isNaN(e)))continue;let a=Math.max(...i);if(a===0)continue;let o=G2(n),s=Math.min(o.width||0,o.height||0);s>0&&a>=s/2-.5||i.every(e=>Y2.has(e))||t.push(W2(this,n,`border-radius: ${r}`))}return t}},a4={id:`row-radius-token`,part:`§40`,severity:`error`,description:`§40: list rows consume --row-radius (8px) or 0 — no bespoke per-surface row radius.`,check(e){let t=[];for(let n of e.els){let r=G2(n).height;if(r<26||r>50||!n4(n))continue;let i=n.parentElement;if(!i||Array.from(i.children).filter(e=>{let t=G2(e);return Math.abs(t.height-r)<2}).length<3)continue;let a=K2(e.css(n).borderRadius);if(a.some(e=>Number.isNaN(e)))continue;let o=Math.max(0,...a);o===0||o===8||t.push(W2(this,n,`row radius ${e.css(n).borderRadius} (want 0 or var(--row-radius)=8px)`))}return t}},o4={id:`active-fill-token`,part:`§11`,severity:`error`,description:`§11: active/selected fill must be var(--selected) (theme-safe) — never bg-secondary (#F0F0F1 / #232325).`,check(e){let t=[],n=null;try{let t=e.root.documentElement??document.documentElement;t&&(n=q2(e.css(t).getPropertyValue(`--selected`).trim()))}catch{}let r=[X2.light,X2.dark].filter(Boolean);if(n&&r.includes(n)){let t=e.root.documentElement??document.documentElement;return[W2({id:this.id,part:this.part,severity:`warn`,description:`§11: active-fill-token unverifiable — --selected resolves EQUAL to bg-secondary this theme; cannot distinguish the token from the anti-pattern.`},t??e.els[0],`--selected == bg-secondary (${n})`)]}for(let n of e.els){if(!(n.matches(`[role="tab"], [role="button"], [aria-selected], [role="option"]`)||n.closest(`[role="tablist"], [role="menu"], [data-rail], nav`)))continue;let i=q2(e.css(n).backgroundColor);i&&r.includes(i)&&t.push(W2(this,n,`background bg-secondary (${e.css(n).backgroundColor}) — use var(--selected)`))}return t}},s4={id:`inline-style-state`,part:`§40`,severity:`error`,description:`§40: the shared row-action class owns ALL visual states; inline styles carry LAYOUT ONLY — an inline background/color kills the class state.`,check(e){let t=[];for(let n of e.els){let e=typeof n.className==`string`?n.className:``;if(!/pb-row-action|row-action/.test(e))continue;let r=n.getAttribute(`style`)??``;/(^|;|\s)(background|color)\s*:/.test(r)&&t.push(W2(this,n,`inline style="${r}" on a row-action element`))}return t}},c4={id:`icon-button-no-hover`,part:`§40`,severity:`warn`,description:`§40/Part 10: every interactive control changes color on hover — icon-only button with no :hover rule and no .pb-row-action.`,check(e){let t=[],n=$2(e.root.styleSheets?e.root:document);for(let r of e.els){if(!t4(r))continue;let i=typeof r.className==`string`?r.className:``;/pb-row-action|row-action/.test(i)||r.hasAttribute(`data-framer-appear-id`)||Array.from(r.attributes).some(e=>e.name.startsWith(`data-framer`))||e.css(r).willChange.includes(`transform`)||e4(r,n)||t.push(W2(this,r,`icon-only ${r.tagName.toLowerCase()} with no hover rule / .pb-row-action`))}return t}},l4={id:`icon-button-no-tooltip`,part:`§7`,severity:`warn`,description:`§7: every icon-only button gets the shared Tooltip — needs aria-describedby / data-slot="tooltip-trigger" (menu-row title exempt).`,check(e){let t=[];for(let n of e.els)t4(n)&&(n.hasAttribute(`aria-describedby`)||n.getAttribute(`data-slot`)===`tooltip-trigger`||n.closest(`[data-slot="tooltip-trigger"]`)||n.closest(`[role="menuitem"]`)&&n.hasAttribute(`title`)||t.push(W2(this,n,`icon-only ${n.tagName.toLowerCase()} with no tooltip trigger / aria-describedby`)));return t}},u4={id:`unvirtualized-scroller`,part:`§0`,severity:`error`,description:`§0: every dynamic-length scrolling list virtualizes — a tall scroller with 60+ direct children is unvirtualized.`,check(e){let t=[];for(let n of e.els){let r=e.css(n);if(![r.overflowY,r.overflowX,r.overflow].some(e=>e===`auto`||e===`scroll`)||n.scrollHeight<=2*n.clientHeight)continue;let i=n.children.length;i<=60||t.push(W2(this,n,`${i} direct children, scrollHeight ${n.scrollHeight} > 2×clientHeight ${n.clientHeight}`))}return t}},d4={id:`scroll-no-fade`,part:`§1`,severity:`warn`,description:`§1: a horizontal scroller needs a truthful scroll fade — no scroll-fade-x/scroll-fade-x-js class and no mask-image.`,check(e){let t=[];for(let n of e.els){if(n.scrollWidth<=n.clientWidth+8)continue;let r=typeof n.className==`string`?n.className:``;/scroll-fade-x(-js)?/.test(r)||J2(n,e.css(n))||t.push(W2(this,n,`horizontal scroller (scrollWidth ${n.scrollWidth} > clientWidth ${n.clientWidth}) with no fade`))}return t}},f4={id:`phantom-fade`,part:`§1`,severity:`warn`,description:`§1: a fade must NEVER paint when nothing is hidden — mask-image present but scrollWidth ≤ clientWidth (the fade lies).`,check(e){let t=[];for(let n of e.els)J2(n,e.css(n))&&(n.scrollWidth>n.clientWidth||t.push(W2(this,n,`mask-image fade but scrollWidth ${n.scrollWidth} ≤ clientWidth ${n.clientWidth}`)));return t}},p4={id:`scrollbar-flush`,part:`Part 5`,severity:`warn`,description:`Part 5: scrollables carry asymmetric right padding so content clears the scrollbar — padding-right < 8px while overflowing vertically with content reaching the right edge.`,check(e){let t=[];for(let n of e.els){let r=e.css(n);if(![r.overflowY,r.overflow].some(e=>e===`auto`||e===`scroll`)||n.scrollHeight<=n.clientHeight)continue;let i=parseFloat(r.paddingRight)||0;if(i>=8)continue;let a=G2(n);Array.from(n.children).some(e=>{let t=G2(e);return a.right-t.right<=4})&&t.push(W2(this,n,`padding-right ${i}px (<8px) with content flush to the scrollbar`))}return t}},m4=/^("?(Geist|Geist Mono)"?|ui-monospace|ui-sans-serif|-apple-system|system-ui)/i,h4={id:`font-family`,part:`Part 5`,severity:`warn`,description:`Part 5: Geist / Geist Mono ONLY — computed font-family does not start with the Geist / ui-monospace fallback chain.`,check(e){let t=[];for(let n of e.els){if(!O4(n))continue;let r=e.css(n).fontFamily;r&&(m4.test(r.trim())||t.push(W2(this,n,`font-family: ${r}`)))}return t}},g4={id:`uppercase-label`,part:`§25`,severity:`error`,description:`§25: group/section labels are sentence-case — never uppercase (text-transform: uppercase on small <13px text).`,check(e){let t=[];for(let n of e.els){if(!O4(n))continue;let r=e.css(n);r.textTransform===`uppercase`&&((parseFloat(r.fontSize)||0)>=13||t.push(W2(this,n,`text-transform: uppercase at ${r.fontSize}`)))}return t}},_4={id:`hardcoded-status-hex`,part:`Part 5`,severity:`warn`,description:`Part 5: raw hex only for the status trio (#50E3C2/#EE0000/#F5A623) — a saturated non-token color on small UI text.`,check(e){let t=[];for(let n of e.els){if(!O4(n))continue;let r=(n.getAttribute(`style`)??``).match(/#[0-9a-fA-F]{6}/g)??[],i=e.css(n);if((parseFloat(i.fontSize)||0)>=16)continue;for(let e of r)Z2.has(e.toLowerCase())||t.push(W2(this,n,`hardcoded ${e} (not the brand status trio)`));let a=q2(i.color);if(a&&r.length===0){let[e,r,o]=a.split(`,`).map(e=>parseFloat(e));Number.isFinite(e)&&k4(e,r,o)&&t.push(W2(this,n,`saturated non-token color ${i.color} on small UI text`))}}return t}},v4={id:`editor-shadow`,part:`Part 5`,severity:`error`,description:`Part 5: editor surfaces are FLAT — box-shadow that is not a 0.5px hairline ring / approved popover/card/input token shadow.`,check(e){let t=[];if(!(e.root.querySelector?.(`[data-editor-surface]`)||typeof location<`u`&&/\/editor\//.test(location.pathname)))return t;for(let n of e.els){if(!n.closest(`[data-editor-surface]`)&&!/\/editor\//.test(typeof location<`u`?location.pathname:``))continue;let r=e.css(n).boxShadow;!r||r===`none`||A4(r)||t.push(W2(this,n,`box-shadow: ${r}`))}return t}},y4={id:`focusring-clip`,part:`§24`,severity:`warn`,description:`§24/Part 5: focus rings never clip — a focusable element whose nearest overflow-hidden ancestor sits <4px from its border box.`,check(e){let t=[];for(let n of e.els){if(!n.matches(Q2)&&n.getAttribute(`tabindex`)==null)continue;let r=n.parentElement;for(;r;){let t=e.css(r);if([t.overflow,t.overflowX,t.overflowY].some(e=>e===`hidden`))break;r=r.parentElement}if(!r)continue;let i=G2(n),a=G2(r);if(i.width===0&&i.height===0)continue;let o=Math.min(i.left-a.left,a.right-i.right,i.top-a.top,a.bottom-i.bottom);o>=4||t.push(W2(this,n,`overflow-hidden ancestor ${o.toFixed(1)}px from border box — focus ring will clip`))}return t}},b4={id:`fixed-width-in-pane`,part:`§24`,severity:`warn`,description:`§24: no fixed field widths in resizable panes — w-[Npx] (N ≥ 200) inside a [data-panel] / flex-basis sibling clips at the pane edge.`,check(e){let t=[];for(let n of e.els){if(!n.closest(`[data-panel]`))continue;let e=typeof n.className==`string`?n.className:``,r=n.getAttribute(`style`)??``,i=e.match(/w-\[(\d+)px\]/)??r.match(/(?:^|;|\s)width\s*:\s*(\d+)px/);if(!i)continue;let a=parseFloat(i[1]);a<200||t.push(W2(this,n,`fixed width ${a}px inside a resizable pane — use w-full max-w-[${a}px]`))}return t}},x4={id:`menu-icons-all-or-none`,part:`§21`,severity:`error`,description:`§21: within one menu ALL items carry a leading icon or NONE do — a mixed set reads broken.`,check(e){let t=[],n=Array.from(e.root.querySelectorAll(`[role="menu"]`));for(let e of n){let n=Array.from(e.querySelectorAll(`[role="menuitem"]`));if(n.length<2)continue;let r=n.filter(e=>e.querySelector(`svg`)).length;r===0||r===n.length||t.push(W2(this,e,`${r}/${n.length} menu items have a leading icon`))}return t}},S4={id:`menu-icon-mixed-weight`,part:`§21`,severity:`warn`,description:`§21: menu icons are ONE stroke family — some filled (fill=currentColor) and some stroke-based, or rendered sizes differing >2px, mixes two design systems.`,check(e){let t=[],n=Array.from(e.root.querySelectorAll(`[role="menu"]`));for(let e of n){let n=Array.from(e.querySelectorAll(`[role="menuitem"] svg`));if(n.length<2)continue;let r=0,i=0,a=[];for(let e of n){Array.from(e.querySelectorAll(`path, circle, rect, polygon`)).some(e=>{let t=(e.getAttribute(`fill`)??``).toLowerCase();return t===`currentcolor`||t&&t!==`none`})?r++:i++;let t=G2(e);a.push(Math.max(t.width,t.height))}let o=Math.max(...a),s=Math.min(...a),c=r>0&&i>0,l=o>0&&o-s>2;!c&&!l||t.push(W2(this,e,c?`${r} filled + ${i} stroke-based icons in one menu`:`icon sizes differ by ${(o-s).toFixed(1)}px`))}return t}},C4={id:`chrome-selectable`,part:`Part 5`,severity:`warn`,description:`Part 5: chrome (rails/pills/tabs) is select-none — a rail/pill/tab without user-select:none.`,check(e){let t=[];for(let n of e.els){if(!(n.matches(`[role="tab"]`)||n.closest(`[role="tablist"], [data-rail], nav`)||/\bpill\b/.test(typeof n.className==`string`?n.className:``)))continue;let r=e.css(n).userSelect||e.css(n).webkitUserSelect;r!==`none`&&t.push(W2(this,n,`chrome element without user-select:none (user-select: ${r||`auto`})`))}return t}},w4={id:`cursor-mismatch`,part:`Part 5`,severity:`warn`,description:`Part 5: cursor semantics — buttons get pointer, static text gets default. A button with cursor:text, or static text with cursor:pointer and no interactive ancestor.`,check(e){let t=[];for(let n of e.els){let r=e.css(n).cursor;if(n.matches(`button, [role="button"]`)&&r===`text`){t.push(W2(this,n,`interactive control with cursor:text`));continue}if(r===`pointer`&&n.matches(`p, h1, h2, h3, h4, h5, h6, span, label`)){if(n.closest(`button, a, [role="button"], [onclick]`)||n.matches(`button, a, [role="button"]`))continue;t.push(W2(this,n,`static ${n.tagName.toLowerCase()} with cursor:pointer and no interactive ancestor`))}}return t}};function T4(e){let t=e.transitionProperty.split(`,`).map(e=>e.trim()).filter(Boolean);return t.length===1&&t[0]===`none`?[]:t}var E4=[i4,a4,o4,s4,c4,l4,u4,d4,f4,p4,h4,g4,_4,v4,y4,b4,x4,S4,C4,w4,{id:`transition-all`,part:`§42`,severity:`error`,description:`§42: never transition-all — scope to what changes (transition-colors / transition-[transform,...]).`,check(e){let t=[];for(let n of e.els){if(!n.matches(Q2))continue;let r=T4(e.css(n));r.includes(`all`)&&(v2(n,r)||t.push(W2(this,n,`transition-property: all`)))}return t}},{id:`transition-paint-prop`,part:`§42`,severity:`error`,description:`§42: never transition layout/paint props (box-shadow, width, height, top/left, margin, padding) — they reflow/repaint every frame; focus rings SNAP, shadows animate a pseudo-element opacity.`,check(e){let t=[];for(let n of e.els){let r=T4(e.css(n)),i=p2(r).filter(e=>e!==`all (includes layout/paint props)`);i.length!==0&&(v2(n,r)||t.push(W2(this,n,`transitions layout/paint props: ${i.join(`, `)}`)))}return t}},{id:`transition-too-slow`,part:`§42`,severity:`error`,description:`§42: interaction feedback is ≤100ms (duration-75 for small controls) — the 100ms theme default is fine; an explicit duration-150+ crept in.`,check(e){let t=[];for(let n of e.els){if(!n.matches(Q2))continue;let r=e.css(n),i=T4(r);if(i.length===0||v2(n,i))continue;let a=d2(r.transitionDuration),o=Math.max(0,...a);o<=100||t.push(W2(this,n,`transition-duration ${o}ms (>100ms) on [${i.join(`, `)}]`))}return t}},{id:`ease-in-feedback`,part:`§42`,severity:`warn`,description:`§42: house curve is cubic-bezier(.2,0,.1,1) ease-out — never ease-in / ease-in-out (or an ease-in-shaped bezier) on feedback.`,check(e){let t=[];for(let n of e.els){if(!n.matches(Q2))continue;let r=e.css(n),i=T4(r);if(i.length===0||v2(n,i))continue;let a=m2(r.transitionTimingFunction);a&&t.push(W2(this,n,`transition-timing-function: ${a}`))}return t}},{id:`press-instant-rule-missing`,part:`§42`,severity:`error`,description:"§42: the global press-instant rule (globals.css) must exist — `button:active{transition-duration:0s}` (or transition:none). Absent = press feedback animates instead of snapping.",check(e){let t=e.root.styleSheets?e.root:document,n=!1,r=e=>{for(let t of Array.from(e)){if(n)return;let e=t;if(e.cssRules&&e.cssRules.length){r(e.cssRules);continue}let i=t,a=i.selectorText;if(a&&/:active\b/.test(a)&&/(^|[\s,])(button|a|\[role=("|')?(button|tab|menuitem|option)("|')?\])/i.test(a))try{let e=i.style.transitionDuration,t=i.style.transition;if(e===`0s`||/(^|\s)none(\s|$)/.test(t)||/\b0s\b/.test(t)){n=!0;return}}catch{}}};try{for(let e of Array.from(t.styleSheets)){if(n)break;try{r(e.cssRules)}catch{}}}catch{}if(n)return[];let i=e.root.documentElement??e.els[0]??document.documentElement;return[W2(this,i,"no `button:active { transition-duration: 0s }` rule on this page")]}}],D4=[];function O4(e){for(let t of Array.from(e.childNodes))if(t.nodeType===3&&(t.textContent??``).trim().length>0)return!0;return!1}function k4(e,t,n){let r=Math.max(e,t,n);return r<40?!1:r-Math.min(e,t,n)>60}function A4(e){return!!(/\b0px\s+0px\s+0px\s+0?\.?5px\b/.test(e)||/\b0px?\s+0px?\s+0px?\s+0?\.?5px\b/.test(e))}function j4(e=typeof document<`u`?document:{}){let t=new Map,n=e=>{let n=t.get(e);return n||(n=getComputedStyle(e),t.set(e,n)),n};r4=n;let r=Array.from(e.querySelectorAll(`*`)),i=[];for(let e of r){if(e.closest(`#linear-grab-root`))continue;let t=G2(e);t.width===0&&t.height===0&&e.children.length===0||i.push(e)}let a={root:e,els:i,css:n},o=[];for(let e of[...E4,...D4])try{o.push(...e.check(a))}catch{}return r4=e=>getComputedStyle(e),o}function M4(e){let t=new Map;for(let n of e){let e=t.get(n.ruleId);e?e.push(n):t.set(n.ruleId,[n])}let n=Array.from(t.entries()).sort((e,t)=>{let n=e[1][0].severity===`error`?0:1,r=t[1][0].severity===`error`?0:1;return n===r?t[1].length===e[1].length?e[0].localeCompare(t[0]):t[1].length-e[1].length:n-r}),r=e.length,i=e.filter(e=>e.severity===`error`).length,a=[`# Slop scan — ${i} error${i===1?``:`s`}, ${r-i} warn (${r} total)`,``];for(let[e,t]of n){let n=t[0];a.push(`## ${e} — ${n.severity} ×${t.length} [${n.part}]`),a.push(n.description);for(let e of t)a.push(`- \`${e.selector}\` — ${e.evidence}`);a.push(``)}return a.join(`
273
+ `)}function W2(e,t,n){return{ruleId:e.id,part:e.part,severity:e.severity,description:e.description,selector:y2(t),evidence:n,el:typeof WeakRef<`u`?new WeakRef(t):void 0}}function G2(e){return e.getBoundingClientRect()}function K2(e){return e.split(`/`)[0].trim().split(/\s+/).filter(Boolean).map(e=>e.endsWith(`px`)?parseFloat(e):NaN)}function q2(e){let t=e.match(/rgba?\(([^)]+)\)/);return t?t[1].split(`,`).map(e=>e.trim()).join(`,`):null}function J2(e,t){let n=t,r=n.maskImage||n.webkitMaskImage||``;if(r&&r!==`none`)return r;let i=(e.getAttribute(`style`)??``).match(/(?:^|;|\s)(?:-webkit-)?mask-image\s*:\s*([^;]+)/i);return i&&i[1].trim()&&i[1].trim()!==`none`?i[1].trim():``}var Y2=new Set([0,8,12,16]),X2={light:q2(`rgb(240, 240, 241)`),dark:q2(`rgb(35, 35, 37)`)},Z2=new Set([`#50e3c2`,`#ee0000`,`#f5a623`]),Q2=`button, a, [role="button"], [role="tab"], [role="menuitem"], [role="option"], input, textarea, select`;function $2(e){let t=[],n=e=>{for(let r of Array.from(e)){let e=r;if(e.cssRules&&e.cssRules.length){n(e.cssRules);continue}let i=r.selectorText;i&&i.includes(`:hover`)&&t.push(i.replace(/:hover/g,``))}};for(let t of Array.from(e.styleSheets))try{n(t.cssRules)}catch{}return t}function e4(e,t){for(let n of t)if(n.trim())try{if(e.matches(n))return!0}catch{}return!1}function t4(e){return!e.matches(`button, [role="button"]`)||e.querySelectorAll(`svg`).length===0?!1:(e.textContent??``).trim().length===0}function n4(e){let t=e.parentElement;for(;t;){try{let e=r4(t).overflowY;if(e===`auto`||e===`scroll`)return t}catch{}t=t.parentElement}return null}var r4=e=>getComputedStyle(e),i4={id:`radius-vocabulary`,part:`§40`,severity:`error`,description:`§40: radius vocabulary is {8, 12, 16, full} — never rounded-md/[4px]/[6px]/[10px]. Off-vocabulary radius.`,check(e){let t=[];for(let n of e.els){let r=e.css(n).borderRadius;if(!r||r===`0px`)continue;let i=K2(r);if(i.some(e=>Number.isNaN(e)))continue;let a=Math.max(...i);if(a===0)continue;let o=G2(n),s=Math.min(o.width||0,o.height||0);s>0&&a>=s/2-.5||i.every(e=>Y2.has(e))||t.push(W2(this,n,`border-radius: ${r}`))}return t}},a4={id:`row-radius-token`,part:`§40`,severity:`error`,description:`§40: list rows consume --row-radius (8px) or 0 — no bespoke per-surface row radius.`,check(e){let t=[];for(let n of e.els){let r=G2(n).height;if(r<26||r>50||!n4(n))continue;let i=n.parentElement;if(!i||Array.from(i.children).filter(e=>{let t=G2(e);return Math.abs(t.height-r)<2}).length<3)continue;let a=K2(e.css(n).borderRadius);if(a.some(e=>Number.isNaN(e)))continue;let o=Math.max(0,...a);o===0||o===8||t.push(W2(this,n,`row radius ${e.css(n).borderRadius} (want 0 or var(--row-radius)=8px)`))}return t}},o4={id:`active-fill-token`,part:`§11`,severity:`error`,description:`§11: active/selected fill must be var(--selected) (theme-safe) — never bg-secondary (#F0F0F1 / #232325).`,check(e){let t=[],n=null;try{let t=e.root.documentElement??document.documentElement;t&&(n=q2(e.css(t).getPropertyValue(`--selected`).trim()))}catch{}let r=[X2.light,X2.dark].filter(Boolean);if(n&&r.includes(n)){let t=e.root.documentElement??document.documentElement;return[W2({id:this.id,part:this.part,severity:`warn`,description:`§11: active-fill-token unverifiable — --selected resolves EQUAL to bg-secondary this theme; cannot distinguish the token from the anti-pattern.`},t??e.els[0],`--selected == bg-secondary (${n})`)]}for(let n of e.els){if(!(n.matches(`[role="tab"], [role="button"], [aria-selected], [role="option"]`)||n.closest(`[role="tablist"], [role="menu"], [data-rail], nav`)))continue;let i=q2(e.css(n).backgroundColor);i&&r.includes(i)&&t.push(W2(this,n,`background bg-secondary (${e.css(n).backgroundColor}) — use var(--selected)`))}return t}},s4={id:`inline-style-state`,part:`§40`,severity:`error`,description:`§40: the shared row-action class owns ALL visual states; inline styles carry LAYOUT ONLY — an inline background/color kills the class state.`,check(e){let t=[];for(let n of e.els){let e=typeof n.className==`string`?n.className:``;if(!/pb-row-action|row-action/.test(e))continue;let r=n.getAttribute(`style`)??``;/(^|;|\s)(background|color)\s*:/.test(r)&&t.push(W2(this,n,`inline style="${r}" on a row-action element`))}return t}},c4={id:`icon-button-no-hover`,part:`§40`,severity:`warn`,description:`§40/Part 10: every interactive control changes color on hover — icon-only button with no :hover rule and no .pb-row-action.`,check(e){let t=[],n=$2(e.root.styleSheets?e.root:document);for(let r of e.els){if(!t4(r))continue;let i=typeof r.className==`string`?r.className:``;/pb-row-action|row-action/.test(i)||r.hasAttribute(`data-framer-appear-id`)||Array.from(r.attributes).some(e=>e.name.startsWith(`data-framer`))||e.css(r).willChange.includes(`transform`)||e4(r,n)||t.push(W2(this,r,`icon-only ${r.tagName.toLowerCase()} with no hover rule / .pb-row-action`))}return t}},l4={id:`icon-button-no-tooltip`,part:`§7`,severity:`warn`,description:`§7: every icon-only button gets the shared Tooltip — needs aria-describedby / data-slot="tooltip-trigger" (menu-row title exempt).`,check(e){let t=[];for(let n of e.els)t4(n)&&(n.hasAttribute(`aria-describedby`)||n.getAttribute(`data-slot`)===`tooltip-trigger`||n.closest(`[data-slot="tooltip-trigger"]`)||n.closest(`[role="menuitem"]`)&&n.hasAttribute(`title`)||t.push(W2(this,n,`icon-only ${n.tagName.toLowerCase()} with no tooltip trigger / aria-describedby`)));return t}},u4={id:`unvirtualized-scroller`,part:`§0`,severity:`error`,description:`§0: every dynamic-length scrolling list virtualizes — a tall scroller with 60+ direct children is unvirtualized.`,check(e){let t=[];for(let n of e.els){let r=e.css(n);if(![r.overflowY,r.overflowX,r.overflow].some(e=>e===`auto`||e===`scroll`)||n.scrollHeight<=2*n.clientHeight)continue;let i=n.children.length;i<=60||t.push(W2(this,n,`${i} direct children, scrollHeight ${n.scrollHeight} > 2×clientHeight ${n.clientHeight}`))}return t}},d4={id:`scroll-no-fade`,part:`§1`,severity:`warn`,description:`§1: a horizontal scroller needs a truthful scroll fade — no scroll-fade-x/scroll-fade-x-js class and no mask-image.`,check(e){let t=[];for(let n of e.els){if(n.scrollWidth<=n.clientWidth+8)continue;let r=typeof n.className==`string`?n.className:``;/scroll-fade-x(-js)?/.test(r)||J2(n,e.css(n))||t.push(W2(this,n,`horizontal scroller (scrollWidth ${n.scrollWidth} > clientWidth ${n.clientWidth}) with no fade`))}return t}},f4={id:`phantom-fade`,part:`§1`,severity:`warn`,description:`§1: a fade must NEVER paint when nothing is hidden — mask-image present but scrollWidth ≤ clientWidth (the fade lies).`,check(e){let t=[];for(let n of e.els)J2(n,e.css(n))&&(n.scrollWidth>n.clientWidth||t.push(W2(this,n,`mask-image fade but scrollWidth ${n.scrollWidth} ≤ clientWidth ${n.clientWidth}`)));return t}},p4={id:`scrollbar-flush`,part:`Part 5`,severity:`warn`,description:`Part 5: scrollables carry asymmetric right padding so content clears the scrollbar — padding-right < 8px while overflowing vertically with content reaching the right edge.`,check(e){let t=[];for(let n of e.els){let r=e.css(n);if(![r.overflowY,r.overflow].some(e=>e===`auto`||e===`scroll`)||n.scrollHeight<=n.clientHeight)continue;let i=parseFloat(r.paddingRight)||0;if(i>=8)continue;let a=G2(n);Array.from(n.children).some(e=>{let t=G2(e);return a.right-t.right<=4})&&t.push(W2(this,n,`padding-right ${i}px (<8px) with content flush to the scrollbar`))}return t}},m4=/^("?(Geist|Geist Mono)"?|ui-monospace|ui-sans-serif|-apple-system|system-ui)/i,h4={id:`font-family`,part:`Part 5`,severity:`warn`,description:`Part 5: Geist / Geist Mono ONLY — computed font-family does not start with the Geist / ui-monospace fallback chain.`,check(e){let t=[];for(let n of e.els){if(!O4(n))continue;let r=e.css(n).fontFamily;r&&(m4.test(r.trim())||t.push(W2(this,n,`font-family: ${r}`)))}return t}},g4={id:`uppercase-label`,part:`§25`,severity:`error`,description:`§25: group/section labels are sentence-case — never uppercase (text-transform: uppercase on small <13px text).`,check(e){let t=[];for(let n of e.els){if(!O4(n))continue;let r=e.css(n);r.textTransform===`uppercase`&&((parseFloat(r.fontSize)||0)>=13||t.push(W2(this,n,`text-transform: uppercase at ${r.fontSize}`)))}return t}},_4={id:`hardcoded-status-hex`,part:`Part 5`,severity:`warn`,description:`Part 5: raw hex only for the status trio (#50E3C2/#EE0000/#F5A623) — a saturated non-token color on small UI text.`,check(e){let t=[];for(let n of e.els){if(!O4(n))continue;let r=(n.getAttribute(`style`)??``).match(/#[0-9a-fA-F]{6}/g)??[],i=e.css(n);if((parseFloat(i.fontSize)||0)>=16)continue;for(let e of r)Z2.has(e.toLowerCase())||t.push(W2(this,n,`hardcoded ${e} (not the brand status trio)`));let a=q2(i.color);if(a&&r.length===0){let[e,r,o]=a.split(`,`).map(e=>parseFloat(e));Number.isFinite(e)&&k4(e,r,o)&&t.push(W2(this,n,`saturated non-token color ${i.color} on small UI text`))}}return t}},v4={id:`editor-shadow`,part:`Part 5`,severity:`error`,description:`Part 5: editor surfaces are FLAT — box-shadow that is not a 0.5px hairline ring / approved popover/card/input token shadow.`,check(e){let t=[];if(!(e.root.querySelector?.(`[data-editor-surface]`)||typeof location<`u`&&/\/editor\//.test(location.pathname)))return t;for(let n of e.els){if(!n.closest(`[data-editor-surface]`)&&!/\/editor\//.test(typeof location<`u`?location.pathname:``))continue;let r=e.css(n).boxShadow;!r||r===`none`||A4(r)||t.push(W2(this,n,`box-shadow: ${r}`))}return t}},y4={id:`focusring-clip`,part:`§24`,severity:`warn`,description:`§24/Part 5: focus rings never clip — a focusable element whose nearest overflow-hidden ancestor sits <4px from its border box.`,check(e){let t=[];for(let n of e.els){if(!n.matches(Q2)&&n.getAttribute(`tabindex`)==null)continue;let r=n.parentElement;for(;r;){let t=e.css(r);if([t.overflow,t.overflowX,t.overflowY].some(e=>e===`hidden`))break;r=r.parentElement}if(!r)continue;let i=G2(n),a=G2(r);if(i.width===0&&i.height===0)continue;let o=Math.min(i.left-a.left,a.right-i.right,i.top-a.top,a.bottom-i.bottom);o>=4||t.push(W2(this,n,`overflow-hidden ancestor ${o.toFixed(1)}px from border box — focus ring will clip`))}return t}},b4={id:`fixed-width-in-pane`,part:`§24`,severity:`warn`,description:`§24: no fixed field widths in resizable panes — w-[Npx] (N ≥ 200) inside a [data-panel] / flex-basis sibling clips at the pane edge.`,check(e){let t=[];for(let n of e.els){if(!n.closest(`[data-panel]`))continue;let e=typeof n.className==`string`?n.className:``,r=n.getAttribute(`style`)??``,i=e.match(/w-\[(\d+)px\]/)??r.match(/(?:^|;|\s)width\s*:\s*(\d+)px/);if(!i)continue;let a=parseFloat(i[1]);a<200||t.push(W2(this,n,`fixed width ${a}px inside a resizable pane — use w-full max-w-[${a}px]`))}return t}},x4={id:`menu-icons-all-or-none`,part:`§21`,severity:`error`,description:`§21: within one menu ALL items carry a leading icon or NONE do — a mixed set reads broken.`,check(e){let t=[],n=Array.from(e.root.querySelectorAll(`[role="menu"]`));for(let e of n){let n=Array.from(e.querySelectorAll(`[role="menuitem"]`));if(n.length<2)continue;let r=n.filter(e=>e.querySelector(`svg`)).length;r===0||r===n.length||t.push(W2(this,e,`${r}/${n.length} menu items have a leading icon`))}return t}},S4={id:`menu-icon-mixed-weight`,part:`§21`,severity:`warn`,description:`§21: menu icons are ONE stroke family — some filled (fill=currentColor) and some stroke-based, or rendered sizes differing >2px, mixes two design systems.`,check(e){let t=[],n=Array.from(e.root.querySelectorAll(`[role="menu"]`));for(let e of n){let n=Array.from(e.querySelectorAll(`[role="menuitem"] svg`));if(n.length<2)continue;let r=0,i=0,a=[];for(let e of n){Array.from(e.querySelectorAll(`path, circle, rect, polygon`)).some(e=>{let t=(e.getAttribute(`fill`)??``).toLowerCase();return t===`currentcolor`||t&&t!==`none`})?r++:i++;let t=G2(e);a.push(Math.max(t.width,t.height))}let o=Math.max(...a),s=Math.min(...a),c=r>0&&i>0,l=o>0&&o-s>2;!c&&!l||t.push(W2(this,e,c?`${r} filled + ${i} stroke-based icons in one menu`:`icon sizes differ by ${(o-s).toFixed(1)}px`))}return t}},C4={id:`chrome-selectable`,part:`Part 5`,severity:`warn`,description:`Part 5: chrome (rails/pills/tabs) is select-none — a rail/pill/tab without user-select:none.`,check(e){let t=[];for(let n of e.els){if(!(n.matches(`[role="tab"]`)||n.closest(`[role="tablist"], [data-rail], nav`)||/\bpill\b/.test(typeof n.className==`string`?n.className:``)))continue;let r=e.css(n).userSelect||e.css(n).webkitUserSelect;r!==`none`&&t.push(W2(this,n,`chrome element without user-select:none (user-select: ${r||`auto`})`))}return t}},w4={id:`cursor-mismatch`,part:`Part 5`,severity:`warn`,description:`Part 5: cursor semantics — buttons get pointer, static text gets default. A button with cursor:text, or static text with cursor:pointer and no interactive ancestor.`,check(e){let t=[];for(let n of e.els){let r=e.css(n).cursor;if(n.matches(`button, [role="button"]`)&&r===`text`){t.push(W2(this,n,`interactive control with cursor:text`));continue}if(r===`pointer`&&n.matches(`p, h1, h2, h3, h4, h5, h6, span, label`)){if(n.closest(`button, a, [role="button"], [onclick]`)||n.matches(`button, a, [role="button"]`))continue;t.push(W2(this,n,`static ${n.tagName.toLowerCase()} with cursor:pointer and no interactive ancestor`))}}return t}};function T4(e){let t=e.transitionProperty.split(`,`).map(e=>e.trim()).filter(Boolean);return t.length===1&&t[0]===`none`?[]:t}var E4=[i4,a4,o4,s4,c4,l4,u4,d4,f4,p4,h4,g4,_4,v4,y4,b4,x4,S4,C4,w4,{id:`transition-all`,part:`§42`,severity:`error`,description:`§42: never transition-all — scope to what changes (transition-colors / transition-[transform,...]).`,check(e){let t=[];for(let n of e.els){if(!n.matches(Q2))continue;let r=T4(e.css(n));r.includes(`all`)&&(v2(n,r)||t.push(W2(this,n,`transition-property: all`)))}return t}},{id:`transition-paint-prop`,part:`§42`,severity:`error`,description:`§42: never transition layout/paint props (box-shadow, width, height, top/left, margin, padding) — they reflow/repaint every frame; focus rings SNAP, shadows animate a pseudo-element opacity.`,check(e){let t=[];for(let n of e.els){let r=T4(e.css(n)),i=p2(r).filter(e=>e!==`all (includes layout/paint props)`);i.length!==0&&(v2(n,r)||t.push(W2(this,n,`transitions layout/paint props: ${i.join(`, `)}`)))}return t}},{id:`transition-too-slow`,part:`§42`,severity:`error`,description:`§42: interaction feedback is ≤100ms (duration-75 for small controls) — the 100ms theme default is fine; an explicit duration-150+ crept in.`,check(e){let t=[];for(let n of e.els){if(!n.matches(Q2))continue;let r=e.css(n),i=T4(r);if(i.length===0||v2(n,i))continue;let a=d2(r.transitionDuration),o=Math.max(0,...a);o<=100||t.push(W2(this,n,`transition-duration ${o}ms (>100ms) on [${i.join(`, `)}]`))}return t}},{id:`ease-in-feedback`,part:`§42`,severity:`warn`,description:`§42: house curve is cubic-bezier(.2,0,.1,1) ease-out — never ease-in / ease-in-out (or an ease-in-shaped bezier) on feedback.`,check(e){let t=[];for(let n of e.els){if(!n.matches(Q2))continue;let r=e.css(n),i=T4(r);if(i.length===0||v2(n,i))continue;let a=m2(r.transitionTimingFunction);a&&t.push(W2(this,n,`transition-timing-function: ${a}`))}return t}},{id:`press-instant-rule-missing`,part:`§42`,severity:`error`,description:"§42: the global press-instant rule (globals.css) must exist — `button:active{transition-duration:0s}` (or transition:none). Absent = press feedback animates instead of snapping.",check(e){let t=e.root.styleSheets?e.root:document,n=!1,r=e=>{for(let t of Array.from(e)){if(n)return;let e=t;if(e.cssRules&&e.cssRules.length){r(e.cssRules);continue}let i=t,a=i.selectorText;if(a&&/:active\b/.test(a)&&/(^|[\s,])(button|a|\[role=("|')?(button|tab|menuitem|option)("|')?\])/i.test(a))try{let e=i.style.transitionDuration,t=i.style.transition;if(e===`0s`||/(^|\s)none(\s|$)/.test(t)||/\b0s\b/.test(t)){n=!0;return}}catch{}}};try{for(let e of Array.from(t.styleSheets)){if(n)break;try{r(e.cssRules)}catch{}}}catch{}if(n)return[];let i=e.root.documentElement??e.els[0]??document.documentElement;return[W2(this,i,"no `button:active { transition-duration: 0s }` rule on this page")]}}],D4=[];function O4(e){for(let t of Array.from(e.childNodes))if(t.nodeType===3&&(t.textContent??``).trim().length>0)return!0;return!1}function k4(e,t,n){let r=Math.max(e,t,n);return r<40?!1:r-Math.min(e,t,n)>60}function A4(e){return!!(/\b0px\s+0px\s+0px\s+0?\.?5px\b/.test(e)||/\b0px?\s+0px?\s+0px?\s+0?\.?5px\b/.test(e))}function j4(e=typeof document<`u`?document:{}){let t=new Map,n=e=>{let n=t.get(e);return n||(n=getComputedStyle(e),t.set(e,n)),n};r4=n;let r=Array.from(e.querySelectorAll(`*`)),i=[],a=e.documentElement??null,o=e.body??null;for(let e of r){if(e===a||e===o||e.closest(`#linear-grab-root, #claude-agent-glow-border, [id^="react-scan"]`))continue;let t=G2(e);t.width===0&&t.height===0&&e.children.length===0||i.push(e)}let s={root:e,els:i,css:n},c=[];for(let e of[...E4,...D4])try{c.push(...e.check(s))}catch{}return r4=e=>getComputedStyle(e),c}function M4(e){let t=new Map;for(let n of e){let e=t.get(n.ruleId);e?e.push(n):t.set(n.ruleId,[n])}let n=Array.from(t.entries()).sort((e,t)=>{let n=e[1][0].severity===`error`?0:1,r=t[1][0].severity===`error`?0:1;return n===r?t[1].length===e[1].length?e[0].localeCompare(t[0]):t[1].length-e[1].length:n-r}),r=e.length,i=e.filter(e=>e.severity===`error`).length,a=[`# Slop scan — ${i} error${i===1?``:`s`}, ${r-i} warn (${r} total)`,``];for(let[e,t]of n){let n=t[0];a.push(`## ${e} — ${n.severity} ×${t.length} [${n.part}]`),a.push(n.description);for(let e of t)a.push(`- \`${e.selector}\` — ${e.evidence}`);a.push(``)}return a.join(`
274
274
  `)}function N4(e){return[`Below is a design-system SLOP report from my running app — each finding is a`,"live-DOM violation of the Linear/Protocolbase primitives contract (`Linear","-primitives.md`). Every rule cites its §id. Goal: zero errors. Rules:",``,`1. Fix at the TOKEN / VARIANT level, not the call sites. Most of these come`,` from shared row/button/menu variants — change --row-radius, --selected,`,` .pb-row-action, the Button/Input variant, or the globals.css §42 rules —`,` not one component at a time.`,`2. Cite the primitives §id in your fix (each finding carries it). Radius →`,` §40 vocabulary {8,12,16,full}; active fill → §11 var(--selected); menus →`,` §21 all-or-none icons + one stroke family; uppercase labels → §25;`,` editor flatness → Part 5; scrollers → §0–1 virtualize + truthful fade.`,`3. §42 group (transition-all / paint-prop / too-slow / ease-in / press-`,` instant): scope transitions to what changes, drop box-shadow/width/height`,` from transition lists, keep feedback ≤100ms (duration-75 small controls),`,` use ease-out (cubic-bezier(.2,0,.1,1)), and keep the global`," `button:active{transition-duration:0s}` press-instant rule.",`4. Do NOT touch EXEMPT intentional motion — popover/menu/dialog entry-exit,`,` svg icon micro-motion, and panel width/height transitions are §42-blessed.`,` The scan already excludes them; do not "fix" them.`,`5. 'unverifiable' warnings mean the token collapsed to the anti-pattern this`,` theme — verify the intent in BOTH themes, do not just silence it.`,`6. PROOF REQUIRED: after the change I will re-run the scan — it must show 0`,` errors. State what you changed per token/variant; do not claim done blind.`,``,`---`,``,M4(e)].join(`
275
275
  `)}var P4=N(`<span class="inline-block min-w-[7ch] text-center">Extract`),F4=N(`<div class="relative flex flex-col gap-2 pl-4"><div class="bg-border absolute top-1 bottom-1 left-[5px] w-px"aria-hidden>`),I4=N(`<span class="text-success text-[12px] leading-none">✓`),L4=N(`<svg width=12 height=12 viewBox="0 0 16 16"fill=none stroke=currentColor stroke-width=1.5 stroke-linecap=round aria-hidden><path d="M4 4l8 8M12 4l-8 8">`),R4=N(`<div class="flex items-center justify-end gap-1 px-1">`),z4=N(`<span class="relative inline-flex items-center justify-center"><svg width=12 height=12 viewBox="0 0 16 16"fill=currentColor aria-hidden><rect x=3.5 y=3 width=3.2 height=10 rx=1></rect><rect x=9.3 y=3 width=3.2 height=10 rx=1></rect></svg><span class="bg-success absolute -top-0.5 -right-0.5 size-1.5 animate-pulse rounded-full"aria-hidden>`),B4=N(`<span class="inline-block min-w-[5ch] text-center">Audit`),V4=N(`<div class="flex max-h-72 flex-col gap-1.5 overflow-y-auto pr-2">`),H4=N(`<span class="text-[12px] leading-none">✓`),U4=N(`<div class="flex items-center gap-1 px-1">`),W4=N(`<span class="inline-block min-w-[4ch] text-center">Scan`),G4=N(`<div class="flex items-center gap-1 px-1"><button class="border-border cursor-pointer rounded-full border px-2 py-0.5 text-[10px] font-medium tabular-nums"> errors</button><button class="border-border cursor-pointer rounded-full border px-2 py-0.5 text-[10px] font-medium tabular-nums"> warns`),K4=N(`<div class="flex h-full flex-col gap-3 overflow-y-auto pt-3 pb-4 pr-3 pl-2"><div class="flex items-center justify-between gap-2 px-1"><div class=min-w-0><p class="text-text text-[12.5px] font-semibold">Design genomes</p><p class="text-text-faint text-[10.5px] leading-snug">Point at a component → get its structure, tokens and interaction states as a spec you can hand to an agent.</p></div></div><div class="border-border mt-1 flex flex-col gap-2 border-t pt-3"><div class="flex items-center justify-between gap-2 px-1"><div class=min-w-0><p class="text-text text-[12.5px] font-semibold">CSS slowdowns</p><p class="text-text-faint text-[10.5px] leading-snug">Transitions/delays over the 100ms house cap (§42) on interactive elements — feedback that animates instead of being instant. Live hits appear as you use the app; Audit sweeps the whole page.</p></div><div class="flex shrink-0 items-center gap-1"></div></div></div><div class="border-border mt-1 flex flex-col gap-2 border-t pt-3"><div class="flex items-center justify-between gap-2 px-1"><div class=min-w-0><p class="text-text text-[12.5px] font-semibold">Slop scan</p><p class="text-text-faint text-[10.5px] leading-snug">Design-contract drift — Linear-primitives rules checked against the live DOM. Open menus/popovers first to include them.`),q4=N(`<p class="text-text mt-1 text-[11px] font-semibold">Interaction states`),J4=N(`<svg width=12 height=12 viewBox="0 0 16 16"aria-hidden><rect x=3.5 y=3.5 width=9 height=9 rx=1.5 fill=currentColor>`),Y4=N(`<span class="text-warn text-[10.5px] font-medium tabular-nums">s · <!> states`),X4=N(`<div class="border-border flex flex-col gap-2 border-t px-2.5 py-2"><p class="text-text-faint truncate text-[10px]"></p><div class="flex max-h-64 flex-col gap-2 overflow-y-auto pr-2"></div><div class="flex items-center gap-1">`),Z4=N(`<div class=relative><span class="border-border bg-surface absolute top-3 -left-[13.5px] size-[9px] rounded-full border"aria-hidden></span><div class="bg-surface border-border flex flex-col rounded-lg border"><button class="flex w-full cursor-pointer items-center gap-2 px-2.5 py-2 text-left"><span class="text-accent min-w-0 truncate font-mono text-[12px]"></span><span class="text-text-faint ml-auto shrink-0 text-[10.5px] tabular-nums"> roles<!> · </span><svg width=10 height=10 viewBox="0 0 16 16"fill=none stroke=currentColor stroke-width=1.5 class="text-text-faint shrink-0 transition-transform"aria-hidden><path d="m4 6 4 4 4-4">`),Q4=N(`<span class=text-text-faint> ×`),$4=N(`<div class="flex flex-col gap-0.5"><p class="text-text text-[11px] font-medium">`),e3=N(`<span class=text-accent> ← var(<!>)`),t3=N(`<p class="text-text-dim font-mono text-[10px] leading-snug break-all">: `),n3=N(`<div class="flex flex-col gap-0.5"><p class="text-warn text-[10.5px] font-medium"> — `),r3=N(`<p class="text-text-dim font-mono text-[10px] leading-snug break-all">: <!> → `),i3=N(`<svg width=13 height=13 viewBox="0 0 16 16"fill=none stroke=currentColor stroke-width=1.5 stroke-linecap=round aria-hidden><circle cx=8 cy=8 r=6></circle><circle cx=8 cy=8 r=2 fill=currentColor stroke=none>`),a3=N(`<svg width=13 height=13 viewBox="0 0 16 16"fill=none stroke=currentColor stroke-width=1.5 stroke-linejoin=round aria-hidden><rect x=5.5 y=5.5 width=9 height=9 rx=1.5></rect><path d="M10.5 5.5v-2a2 2 0 0 0-2-2h-5a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h2">`),o3=N(`<svg width=12 height=12 viewBox="0 0 16 16"fill=currentColor aria-hidden><path d="M4.5 2.5v11l9-5.5z">`),s3=N(`<p class="text-text-faint px-1 text-[10.5px]">Nothing caught yet — interact with the app, or run Audit.`),c3=N(`<span class="text-text-dim font-mono text-[10px] break-all">`),l3=N(`<span class="text-danger text-[10px] leading-snug">⚠ layout/paint transition: `),u3=N(`<span class="text-danger text-[10px] leading-snug">⚠ ease-in easing: `),d3=N(`<div class="bg-surface border-border flex flex-col gap-0.5 rounded-lg border p-2"><div class="flex items-center gap-2"><span class="text-warn shrink-0 text-[10px] font-semibold tracking-wide uppercase"></span><span class="text-accent min-w-0 truncate font-mono text-[11.5px]"></span><span class="text-text ml-auto shrink-0 text-[11px] font-medium tabular-nums">ms</span></div><span class="text-text-dim text-[10px] leading-snug"></span><span class="text-text-faint text-[10px] leading-snug">`),f3=N(`<svg width=13 height=13 viewBox="0 0 16 16"fill=none stroke=currentColor stroke-width=1.5 stroke-linecap=round stroke-linejoin=round aria-hidden><path d="M8 1.5 9.6 6l4.4 1.6L9.6 9.2 8 13.7 6.4 9.2 2 7.6 6.4 6Z"></path><path d="M13 11.5l.6 1.7 1.7.6-1.7.6-.6 1.7-.6-1.7-1.7-.6 1.7-.6Z">`),p3=N(`<p class="text-text-faint px-1 text-[10.5px]">No scan yet — click Scan to audit this page against the contract.`),m3=N(`<div class="border-border flex flex-col gap-1 border-t px-2 py-1.5"><p class="text-text-dim text-[10px] leading-snug">`),h3=N(`<div class="bg-surface border-border flex flex-col rounded-lg border"><button class="flex w-full cursor-pointer items-center gap-2 px-2 py-1.5 text-left"><span class="size-1.5 shrink-0 rounded-full"aria-hidden></span><span class="text-accent min-w-0 truncate font-mono text-[11.5px]"></span><span class="text-text-faint ml-auto shrink-0 text-[10.5px] tabular-nums">×</span><svg width=10 height=10 viewBox="0 0 16 16"fill=none stroke=currentColor stroke-width=1.5 class="text-text-faint shrink-0 transition-transform"aria-hidden><path d="m4 6 4 4 4-4">`),g3=N(`<button class="hover:bg-surface-2 flex w-full cursor-pointer flex-col gap-0.5 rounded px-1 py-0.5 text-left"title="Reveal on the page"><span class="text-text min-w-0 truncate font-mono text-[10.5px]"></span><span class="text-text-faint font-mono text-[10px] leading-snug break-all">`),_3=`linear-grab:genomes:v1`,v3=30;function y3(){try{return JSON.parse(localStorage.getItem(_3)??`[]`)}catch{return[]}}function b3(e){try{localStorage.setItem(_3,JSON.stringify(e.slice(0,v3)))}catch{}}var x3=new Map,S3=[];function C3(e){let t=e.el?.deref();if(!(t instanceof HTMLElement)||!t.isConnected)return!1;t.scrollIntoView({block:`center`,behavior:`smooth`});let n=t.style.outline,r=t.style.outlineOffset;return t.style.outline=`2px solid #e5a13a`,t.style.outlineOffset=`2px`,setTimeout(()=>{t.style.outline=n,t.style.outlineOffset=r},1400),!0}function w3(e){if(e.length===0)return;let t=Date.now(),n=e.slice(0,200).map(({el:e,...n})=>({kind:`slop-scan`,page:location.pathname,at:t,...n}));JO().then(e=>fetch(`${e}/scan/events`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({events:n}),keepalive:!0})).catch(()=>{})}function T3(){let[e,t]=T(y3()),[n,r]=T(null),[i,a]=T(null),[o,s]=T(null),[c,l]=T(null),[u,d]=T(null),[f,p]=T({active:!1,msLeft:0,total:0,byTrigger:{}});ae(i2(p));let[m,h]=T([]);ae(j2(h));let[g,_]=T(F2());ae(L2(_));let[v,y]=T(!1),[b,x]=T(!1),S=async()=>{y(!0);try{await V2()}finally{y(!1)}},C=async()=>{try{await navigator.clipboard.writeText(U2(m())),x(!0),setTimeout(()=>x(!1),1600)}catch{l(`Clipboard was blocked — click Copy again.`)}},[w,E]=T(!1),O=async()=>{try{await navigator.clipboard.writeText(H2(m())),E(!0),setTimeout(()=>E(!1),1600)}catch{l(`Clipboard was blocked — click Copy again.`)}},[k,ee]=T(S3),[te,A]=T(!1),[ne,re]=T(null),[ie,oe]=T(!0),[se,ce]=T(!0),[le,ue]=T(!1),[de,fe]=T(!1),pe=()=>{l(null),A(!0);try{let e=j4(document);S3=e,ee(e),re(null),w3(e)}catch(e){l(e instanceof Error?e.message:String(e))}finally{A(!1)}},me=()=>{S3=[],ee([]),re(null)},he=()=>k().filter(e=>e.severity===`error`?ie():se()),ge=()=>{let e=new Map;for(let t of he()){let n=e.get(t.ruleId);n?n.push(t):e.set(t.ruleId,[t])}return[...e.values()].sort((e,t)=>Number(t[0].severity===`error`)-Number(e[0].severity===`error`)||t.length-e.length)},_e=()=>k().filter(e=>e.severity===`error`).length,ve=()=>k().length-_e(),ye=async()=>{try{await navigator.clipboard.writeText(M4(he())),ue(!0),setTimeout(()=>ue(!1),1600)}catch{l(`Clipboard was blocked — click Copy again.`)}},be=async()=>{try{await navigator.clipboard.writeText(N4(he())),fe(!0),setTimeout(()=>fe(!1),1600)}catch{l(`Clipboard was blocked — click Copy again.`)}},xe=e=>{t(e),b3(e)},Se=async()=>{l(null),a(`extract`),window.dispatchEvent(new CustomEvent(BZ));try{let t=await u2();if(!t)return;let n=await e2(t),i=Date.now();x3.set(i,new WeakRef(t)),xe([{id:i,genome:n},...e()]),r(i)}catch(e){l(e instanceof Error?e.message:String(e))}finally{a(null),oj(`design`),window.dispatchEvent(new CustomEvent(VZ))}},Ce=async t=>{l(null),a(t.id),window.dispatchEvent(new CustomEvent(BZ));try{let n=x3.get(t.id)?.deref()??null;if(!n||!n.isConnected){if(n=await u2(),!n)return;x3.set(t.id,new WeakRef(n))}let r=await c2(n,8e3,e=>s(Math.ceil(e/1e3)));r.length===0&&l(`No state changes captured — hover/open the component while the 8s window runs.`),xe(e().map(e=>e.id===t.id?{...e,genome:{...e.genome,states:[...e.genome.states,...r]}}:e))}catch(e){l(e instanceof Error?e.message:String(e))}finally{a(null),s(null),oj(`design`),window.dispatchEvent(new CustomEvent(VZ))}},we=async e=>{try{await navigator.clipboard.writeText(l2(e.genome)),d(e.id),setTimeout(()=>d(null),1600)}catch{l(`Clipboard was blocked — click Copy again.`)}},Te=t=>{x3.delete(t),xe(e().filter(e=>e.id!==t))},[Ee,De]=T(!1),Oe=async()=>{try{let t=e().map(e=>l2(e.genome)).join(`
276
276
 
@@ -46838,21 +46838,21 @@ function j4(e = typeof document < "u" ? document : {}) {
46838
46838
  return n || (n = getComputedStyle(e), t.set(e, n)), n;
46839
46839
  };
46840
46840
  r4 = n;
46841
- let r = Array.from(e.querySelectorAll("*")), i = [];
46841
+ let r = Array.from(e.querySelectorAll("*")), i = [], a = e.documentElement ?? null, o = e.body ?? null;
46842
46842
  for (let e of r) {
46843
- if (e.closest("#linear-grab-root")) continue;
46843
+ if (e === a || e === o || e.closest("#linear-grab-root, #claude-agent-glow-border, [id^=\"react-scan\"]")) continue;
46844
46844
  let t = G2(e);
46845
46845
  t.width === 0 && t.height === 0 && e.children.length === 0 || i.push(e);
46846
46846
  }
46847
- let a = {
46847
+ let s = {
46848
46848
  root: e,
46849
46849
  els: i,
46850
46850
  css: n
46851
- }, o = [];
46851
+ }, c = [];
46852
46852
  for (let e of [...E4, ...D4]) try {
46853
- o.push(...e.check(a));
46853
+ c.push(...e.check(s));
46854
46854
  } catch {}
46855
- return r4 = (e) => getComputedStyle(e), o;
46855
+ return r4 = (e) => getComputedStyle(e), c;
46856
46856
  }
46857
46857
  function M4(e) {
46858
46858
  let t = /* @__PURE__ */ new Map();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linear-grab",
3
- "version": "0.24.0",
3
+ "version": "0.24.1",
4
4
  "type": "module",
5
5
  "description": "Point at any React element in your dev app, draft a Linear issue with AI, delegate it to the Cursor agent, and track it live — in any browser.",
6
6
  "exports": {