review-lens-react 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1,3 @@
1
- (function(x,r){typeof exports=="object"&&typeof module<"u"?r(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],r):(x=typeof globalThis<"u"?globalThis:x||self,r(x.ReviewLensReact={},x.jsxRuntime,x.React))})(this,(function(x,r,l){"use strict";const Ke=["https://www.googleapis.com/auth/spreadsheets","https://www.googleapis.com/auth/userinfo.email"].join(" "),Je="https://www.googleapis.com/oauth2/v3/userinfo";function de(e){const t=e.feedbackSheetName??"Feedback",n=e.messagesSheetName??"Messages",s=e.usersSheetName??"Users";let i,d;async function f(){return i??(i=tt(e.googleClientId)),i}async function b(c,w,h){const v=await f(),k=await fetch(`https://sheets.googleapis.com/v4/spreadsheets/${c}${w}`,{...h,headers:{Authorization:`Bearer ${v}`,"Content-Type":"application/json",...h==null?void 0:h.headers}});if(!k.ok)throw new Error(`Google Sheets request failed with ${k.status}`);return k.json()}async function u(c,w){return(await b(c,`/values/${encodeURIComponent(w)}`)).values??[]}return{async getCurrentUser(){if(!d){const c=await f(),w=await fetch(Je,{headers:{Authorization:`Bearer ${c}`}});if(!w.ok)throw new Error(`Google userinfo request failed with ${w.status}`);d=(await w.json()).email}if(!d)throw new Error("Google account did not return an email address");return{email:d}},async getPermissions(c){const[{email:w},h]=await Promise.all([this.getCurrentUser(),u(e.usersSpreadsheetId,s)]),v=ne(h),k=w.toLowerCase(),E=v.find(A=>{var p;return((p=A.email)==null?void 0:p.toLowerCase())===k&&A.active!=="false"&&(!A.projectKey||A.projectKey===c)});return Ye((E==null?void 0:E.role)??"designer")},async listFeedback(c){return ne(await u(e.contentSpreadsheetId,t)).map(ue).filter(h=>h!==null).filter(h=>h.projectKey===c.projectKey&&h.contentId===c.contentId&&h.normalizedPath===c.normalizedPath).sort((h,v)=>v.createdAt.localeCompare(h.createdAt))},async createFeedback(c){const w=new Date().toISOString(),h={...c,id:crypto.randomUUID(),attachments:[],createdAt:w,updatedAt:w};return await b(e.contentSpreadsheetId,`/values/${encodeURIComponent(t)}:append?valueInputOption=RAW`,{method:"POST",body:JSON.stringify({values:[he(h)]})}),h},async updateFeedback(c,w){const h=await u(e.contentSpreadsheetId,t),v=h[0]??ce,k=v.indexOf("id");if(k===-1)throw new Error(`Sheet ${t} is missing an id column`);const E=h.findIndex((m,C)=>C>0&&m[k]===c);if(E<1)throw new Error(`Feedback ${c} was not found`);const A=new Date().toISOString(),p=ue(pe(v,h[E]));if(!p)throw new Error(`Feedback ${c} could not be parsed before updating`);const B={...p,...w,updatedAt:A},y=he(B);return await b(e.contentSpreadsheetId,`/values/${encodeURIComponent(t)}!A${E+1}:${et(ce.length)}${E+1}?valueInputOption=RAW`,{method:"PUT",body:JSON.stringify({values:[y]})}),B},async listMessages(c){return ne(await u(e.contentSpreadsheetId,n)).map(Ge).filter(h=>h!==null).filter(h=>h.feedbackId===c).sort((h,v)=>h.createdAt.localeCompare(v.createdAt))},async createMessage(c){const w={...c,id:crypto.randomUUID(),createdAt:new Date().toISOString()};return await b(e.contentSpreadsheetId,`/values/${encodeURIComponent(n)}:append?valueInputOption=RAW`,{method:"POST",body:JSON.stringify({values:[Ve(w)]})}),w}}}const ce=["id","projectKey","contentId","normalizedPath","originalUrl","selector","selectorStrategy","elementFingerprintJson","createdCssSnapshotJson","comment","status","severity","category","assigneeEmail","viewportWidth","viewportHeight","viewportPreset","screenshotUrl","screenshotThumbnailUrl","attachmentJson","authorEmail","createdAt","updatedAt","fixedCssSnapshotJson","fixedAt","fixedBy","resolvedAt","resolvedBy"],qe=["id","feedbackId","body","authorEmail","createdAt"];function he(e){return[e.id,e.projectKey,e.contentId,e.normalizedPath,e.originalUrl,e.selector,e.selectorStrategy,JSON.stringify(e.elementFingerprint),JSON.stringify(e.createdCssSnapshot),e.comment,e.status,e.severity,e.category,e.assigneeEmail??"",String(e.viewportWidth),String(e.viewportHeight),e.viewportPreset,e.screenshotUrl??"",e.screenshotThumbnailUrl??"",JSON.stringify(e.attachments),e.authorEmail,e.createdAt,e.updatedAt,e.fixedCssSnapshot?JSON.stringify(e.fixedCssSnapshot):"",e.fixedAt??"",e.fixedBy??"",e.resolvedAt??"",e.resolvedBy??""]}function Ve(e){return qe.map(t=>e[t])}function ne(e){const[t,...n]=e;return t?n.map(s=>pe(t,s)):[]}function pe(e,t){return Object.fromEntries(e.map((n,s)=>[n,t[s]??""]))}function ue(e){return e.id?{id:e.id,projectKey:e.projectKey,contentId:e.contentId,normalizedPath:e.normalizedPath,originalUrl:e.originalUrl,selector:e.selector,selectorStrategy:e.selectorStrategy==="stable-attribute"?"stable-attribute":"css-path",elementFingerprint:re(e.elementFingerprintJson,{tagName:"",width:0,height:0}),createdCssSnapshot:ge(e.createdCssSnapshotJson),fixedCssSnapshot:e.fixedCssSnapshotJson?ge(e.fixedCssSnapshotJson):void 0,comment:e.comment,status:Xe(e.status),severity:Qe(e.severity),category:Ze(e.category),assigneeEmail:e.assigneeEmail||void 0,viewportWidth:Number(e.viewportWidth)||0,viewportHeight:Number(e.viewportHeight)||0,viewportPreset:Re(e.viewportPreset),screenshotUrl:e.screenshotUrl||void 0,screenshotThumbnailUrl:e.screenshotThumbnailUrl||void 0,attachments:re(e.attachmentJson,[]),authorEmail:e.authorEmail,createdAt:e.createdAt,updatedAt:e.updatedAt,fixedAt:e.fixedAt||void 0,fixedBy:e.fixedBy||void 0,resolvedAt:e.resolvedAt||void 0,resolvedBy:e.resolvedBy||void 0}:null}function Ge(e){return!e.id||!e.feedbackId?null:{id:e.id,feedbackId:e.feedbackId,body:e.body,authorEmail:e.authorEmail,createdAt:e.createdAt}}function re(e,t){try{return e?JSON.parse(e):t}catch{return t}}function ge(e){const t=re(e,{});return{margin:t.margin??"",marginTop:t.marginTop??"",marginRight:t.marginRight??"",marginBottom:t.marginBottom??"",marginLeft:t.marginLeft??"",padding:t.padding??"",paddingTop:t.paddingTop??"",paddingRight:t.paddingRight??"",paddingBottom:t.paddingBottom??"",paddingLeft:t.paddingLeft??"",border:t.border??"",borderTopWidth:t.borderTopWidth??"",borderRightWidth:t.borderRightWidth??"",borderBottomWidth:t.borderBottomWidth??"",borderLeftWidth:t.borderLeftWidth??"",fontFamily:t.fontFamily??"",fontSize:t.fontSize??"",lineHeight:t.lineHeight??"",color:t.color??"",backgroundColor:t.backgroundColor??"",borderRadius:t.borderRadius??"",width:t.width??0,height:t.height??0}}function Ye(e){return e==="admin"?["create","read","reply","update","assign"]:e==="developer"?["read","reply","update","assign"]:["create","read","reply"]}function Xe(e){return e==="in_progress"||e==="needs_clarification"||e==="fixed"||e==="wontfix"||e==="resolved"?e:"open"}function Qe(e){return e==="low"||e==="high"?e:"medium"}function Ze(e){return e==="visual"||e==="copy"||e==="accessibility"||e==="responsive"?e:"bug"}function Re(e){return e==="mobile"||e==="tablet"||e==="desktop"?e:"custom"}function et(e){let t=e,n="";for(;t>0;){const s=(t-1)%26;n=String.fromCharCode(65+s)+n,t=Math.floor((t-s)/26)}return n}async function tt(e){return await nt(),new Promise((t,n)=>{var i;const s=(i=window.google)==null?void 0:i.accounts.oauth2.initTokenClient({client_id:e,scope:Ke,callback:d=>{if(d.error||!d.access_token){n(new Error(d.error??"Google OAuth did not return an access token"));return}t(d.access_token)}});s==null||s.requestAccessToken({prompt:""})})}function nt(){var e;return(e=window.google)!=null&&e.accounts.oauth2?Promise.resolve():new Promise((t,n)=>{const s=document.querySelector('script[src="https://accounts.google.com/gsi/client"]');if(s){s.addEventListener("load",()=>t(),{once:!0}),s.addEventListener("error",()=>n(new Error("Google Identity failed to load")),{once:!0});return}const i=document.createElement("script");i.src="https://accounts.google.com/gsi/client",i.async=!0,i.defer=!0,i.onload=()=>t(),i.onerror=()=>n(new Error("Google Identity failed to load")),document.head.append(i)})}function fe(e){return new URL(e,window.location.href).pathname.replace(/\/+$/,"")||"/"}const be=l.createContext(null);function rt({config:e,children:t}){const n=l.useMemo(()=>e.adapter?e.adapter:de({googleClientId:se(e.googleClientId,"googleClientId"),contentSpreadsheetId:se(e.contentSpreadsheetId,"contentSpreadsheetId"),usersSpreadsheetId:se(e.usersSpreadsheetId,"usersSpreadsheetId"),feedbackSheetName:e.sheetName??"Feedback"}),[e.adapter,e.contentSpreadsheetId,e.googleClientId,e.sheetName,e.usersSpreadsheetId]),s=e.currentUrl??window.location.href,i=(e.normalizeUrl??fe)(s),[d,f]=l.useState(),[b,u]=l.useState([]),[c,w]=l.useState([]),h=l.useCallback(async()=>{const y=await n.listFeedback({projectKey:e.projectKey,contentId:e.contentId,normalizedPath:i});w(y)},[n,e.contentId,e.projectKey,i]);l.useEffect(()=>{let y=!0;async function m(){const[C,$]=await Promise.all([n.getCurrentUser(),n.getPermissions(e.projectKey)]);y&&(f(C),u($),await h())}return m(),()=>{y=!1}},[n,e.projectKey,h]);const v=l.useCallback(async y=>{const m=await n.createFeedback(y);return w(C=>[m,...C]),m},[n]),k=l.useCallback(async(y,m)=>{const C=await n.updateFeedback(y,m);return w($=>$.map(z=>z.id===y?C:z)),C},[n]),E=l.useCallback(y=>n.listMessages(y),[n]),A=l.useCallback(y=>n.createMessage(y),[n]),p=l.useCallback(async(y,m)=>{const C=e.uploadAttachment??n.uploadAttachment;if(!C)throw new Error("Review Lens attachment upload is not configured");return C(y,m)},[n,e]),B=l.useMemo(()=>({config:e,adapter:n,currentUser:d,permissions:b,feedback:c,normalizedPath:i,refreshFeedback:h,createFeedback:v,updateFeedback:k,listMessages:E,createMessage:A,uploadAttachment:p}),[n,e,v,d,c,i,b,h,k,E,A,p]);return r.jsx(be.Provider,{value:B,children:t})}function ve(){const e=l.useContext(be);if(!e)throw new Error("useReviewLens must be used inside ReviewLensProvider");return e}function se(e,t){if(!e)throw new Error(`review-lens-react requires config.${t} when no adapter is provided`);return e}const st=["data-review-id","data-testid","data-test-id","aria-label","name"];function W(e){const t=e.getBoundingClientRect(),n=at(e);return{selector:n.selector,selectorStrategy:n.strategy,fingerprint:ot(e,t),cssSnapshot:lt(e,t),rect:t}}function at(e){for(const t of st){const n=e.getAttribute(t);if(n)return{selector:`[${t}="${we(n)}"]`,strategy:"stable-attribute"}}return e.id?{selector:`#${we(e.id)}`,strategy:"stable-attribute"}:{selector:it(e),strategy:"css-path"}}function it(e){const t=[];let n=e;for(;n&&n.nodeType===Node.ELEMENT_NODE&&n!==document.body;){const s=n.parentElement,i=n.tagName.toLowerCase();if(!s){t.unshift(i);break}const d=n.tagName,f=Array.from(s.children).filter(u=>u.tagName===d),b=f.indexOf(n)+1;t.unshift(f.length>1?`${i}:nth-of-type(${b})`:i),n=s}return t.join(" > ")}function ot(e,t){var n;return{tagName:e.tagName.toLowerCase(),id:e.id||void 0,className:e.getAttribute("class")||void 0,textSnippet:((n=e.textContent)==null?void 0:n.trim().slice(0,80))||void 0,ariaLabel:e.getAttribute("aria-label")||void 0,width:Math.round(t.width),height:Math.round(t.height)}}function lt(e,t){const n=window.getComputedStyle(e);return{margin:ae(n.marginTop,n.marginRight,n.marginBottom,n.marginLeft),marginTop:n.marginTop,marginRight:n.marginRight,marginBottom:n.marginBottom,marginLeft:n.marginLeft,padding:ae(n.paddingTop,n.paddingRight,n.paddingBottom,n.paddingLeft),paddingTop:n.paddingTop,paddingRight:n.paddingRight,paddingBottom:n.paddingBottom,paddingLeft:n.paddingLeft,border:ae(n.borderTopWidth,n.borderRightWidth,n.borderBottomWidth,n.borderLeftWidth),borderTopWidth:n.borderTopWidth,borderRightWidth:n.borderRightWidth,borderBottomWidth:n.borderBottomWidth,borderLeftWidth:n.borderLeftWidth,fontFamily:n.fontFamily,fontSize:n.fontSize,lineHeight:n.lineHeight,color:n.color,backgroundColor:n.backgroundColor,borderRadius:n.borderRadius,width:Math.round(t.width),height:Math.round(t.height)}}function ae(e,t,n,s){return e===t&&t===n&&n===s?e:`${e} ${t} ${n} ${s}`}function we(e){return typeof CSS<"u"&&typeof CSS.escape=="function"?CSS.escape(e):e.replace(/["\\]/g,"\\$&")}const dt=[{label:"Desktop",value:"desktop"},{label:"Tablet",value:"tablet"},{label:"Mobile",value:"mobile"}],me=["open","in_progress","needs_clarification","fixed","wontfix","resolved"],ye=["low","medium","high"],Se=["bug","visual","copy","accessibility","responsive"],Z={open:"Open",in_progress:"In progress",needs_clarification:"Needs clarification",fixed:"Fixed",wontfix:"Won't fix",resolved:"Resolved"},H={low:"Low",medium:"Medium",high:"High"},j={bug:"Bug",visual:"Visual",copy:"Copy",accessibility:"Accessibility",responsive:"Responsive"};function ct({open:e,onOpenChange:t,placement:n="top-right",showResolved:s=!1,syncSelectionToUrl:i=!1,responsivePresets:d=dt}){var Oe;const{adapter:f,config:b,currentUser:u,feedback:c,normalizedPath:w,permissions:h,createFeedback:v,updateFeedback:k,listMessages:E,createMessage:A,uploadAttachment:p}=ve(),[B,y]=l.useState(),[m,C]=l.useState(),[$,z]=l.useState(""),[xe,Mt]=l.useState("medium"),[Ae,It]=l.useState("visual"),[Le,Fe]=l.useState(""),[Me,Tt]=l.useState(((Oe=d[0])==null?void 0:Oe.value)??"desktop"),[S,J]=l.useState(),[M,P]=l.useState("review"),[Bt,Ie]=l.useState(!1),[q,Te]=l.useState("all"),[V,Be]=l.useState("all"),[G,$e]=l.useState("all"),[Y,Pe]=l.useState("all"),[X,_e]=l.useState("all"),[$t,Pt]=l.useState(!1),[_t,Ue]=l.useState({}),[ie,oe]=l.useState(""),ee=l.useRef(null),I=!!u,T=h.includes("create"),We=h.includes("reply"),De=h.includes("update"),Ut=h.includes("assign"),U=B??m,Wt=!!m,Dt=!!(b.captureScreenshot&&(b.uploadAttachment||f.uploadAttachment)),zt=l.useMemo(()=>{const a=c.map(g=>g.assigneeEmail).filter(g=>!!g);return u!=null&&u.email&&a.push(u.email),Array.from(new Set(a)).sort((g,o)=>g.localeCompare(o))},[u==null?void 0:u.email,c]),F=l.useMemo(()=>c.filter(a=>s||a.status!=="resolved").filter(a=>q==="all"||a.status===q).filter(a=>V==="all"||a.severity===V).filter(a=>G==="all"||a.category===G).filter(a=>Y==="all"||a.assigneeEmail===Y).filter(a=>X==="all"||a.viewportPreset===X),[Y,G,c,V,s,q,X]),Ht=[q,V,G,Y,X].filter(a=>a!=="all").length;l.useEffect(()=>{e||(y(void 0),C(void 0),z(""),oe(""),P("review"))},[e]),l.useEffect(()=>{I||(y(void 0),C(void 0))},[I]),l.useEffect(()=>{!m||M!=="review"||window.requestAnimationFrame(()=>{var a,g,o;(g=(a=ee.current)==null?void 0:a.scrollIntoView)==null||g.call(a,{block:"nearest"}),(o=ee.current)==null||o.focus()})},[m,M]),l.useEffect(()=>{if(!S)return;let a=!0;return E(S.id).then(g=>{a&&Ue(o=>({...o,[S.id]:g}))}),()=>{a=!1}},[E,S]),l.useEffect(()=>{if(!e||!i||S||c.length===0)return;const a=new URL(window.location.href).searchParams.get("reviewLensFeedback"),g=c.find(o=>o.id===a);g&&Q(g,{syncUrl:!1})},[c,e,S,i]),l.useEffect(()=>{if(!e)return;function a(o){var N;if(o.key==="Shift"&&Ie(!0),o.key==="Escape"){o.preventDefault(),t==null||t(!1);return}xt(o.target)||((o.key==="n"||o.key==="ArrowDown")&&(o.preventDefault(),ze(1)),(o.key==="p"||o.key==="ArrowUp")&&(o.preventDefault(),ze(-1)),o.key==="c"&&(o.preventDefault(),P("review"),(N=ee.current)==null||N.focus()),o.key==="f"&&S&&De&&(o.preventDefault(),je(S)))}function g(o){o.key==="Shift"&&Ie(!1)}return window.addEventListener("keydown",a),window.addEventListener("keyup",g),()=>{window.removeEventListener("keydown",a),window.removeEventListener("keyup",g)}});const le=l.useCallback(a=>{const g=a.target instanceof Element?a.target:null;if(g)return g.closest("[data-review-lens-ui]")?null:g;const o=document.elementFromPoint(a.clientX,a.clientY);return!o||o.closest("[data-review-lens-ui]")?null:o},[]);if(l.useEffect(()=>{if(!e||!I)return;function a(o){const N=le(o);y(N?W(N):void 0)}function g(o){const N=le(o);N&&(o.preventDefault(),o.stopPropagation(),C(W(N)),P("review"))}return window.addEventListener("mousemove",a,!0),window.addEventListener("click",g,!0),()=>{window.removeEventListener("mousemove",a,!0),window.removeEventListener("click",g,!0)}},[I,le,e]),!e)return null;function Q(a,g={syncUrl:!0}){var N;if(J(a),C(void 0),P("feedback"),i&&g.syncUrl!==!1){const te=new URL(window.location.href);te.searchParams.set("reviewLensFeedback",a.id),window.history.replaceState({},"",te)}const o=D(a.selector);if(!o){y(void 0);return}(N=o.scrollIntoView)==null||N.call(o,{behavior:"smooth",block:"center",inline:"center"}),window.requestAnimationFrame(()=>{y(W(o))})}function ze(a){if(F.length===0)return;const g=S?F.findIndex(N=>N.id===S.id):-1,o=g<0?a>0?0:F.length-1:(g+a+F.length)%F.length;Q(F[o])}async function He(){if(!m||!$.trim()||!u||!T)return;let a=await v({projectKey:b.projectKey,contentId:b.contentId,normalizedPath:w,originalUrl:b.currentUrl??window.location.href,selector:m.selector,selectorStrategy:m.selectorStrategy,elementFingerprint:m.fingerprint,createdCssSnapshot:m.cssSnapshot,comment:$.trim(),status:"open",severity:xe,category:Ae,assigneeEmail:Le.trim()||void 0,viewportWidth:window.innerWidth,viewportHeight:window.innerHeight,viewportPreset:Me,screenshotUrl:void 0,screenshotThumbnailUrl:void 0,authorEmail:u.email});if(b.captureScreenshot)try{const g=await b.captureScreenshot(m),o=await p(a.id,{type:"screenshot",data:g,createdBy:u.email});a=await k(a.id,{attachments:[o],screenshotUrl:o.url,screenshotThumbnailUrl:o.thumbnailUrl})}catch{}z(""),Fe(""),C(void 0),y(void 0),P("feedback"),J(a)}async function jt(a,g){const o=new Date().toISOString(),N=g==="resolved"?{status:g,resolvedAt:o,resolvedBy:u==null?void 0:u.email}:{status:g},te=await k(a.id,N);J(te)}async function je(a){const g=D(a.selector);if(!g||!u)return;const o=W(g),N=await k(a.id,{status:"fixed",fixedCssSnapshot:o.cssSnapshot,fixedAt:new Date().toISOString(),fixedBy:u.email});J(N)}async function Ot(a){if(!ie.trim()||!u||!We)return;const g=await A({feedbackId:a.id,body:ie.trim(),authorEmail:u.email});Ue(o=>({...o,[a.id]:[...o[a.id]??[],g]})),oe("")}return r.jsxs("div",{className:"review-lens-root","data-review-lens-ui":!0,children:[I&&U?r.jsx(ft,{target:U,locked:!!m}):null,I&&m&&B&&Bt?r.jsx(bt,{from:m,to:B}):null,I?r.jsxs(r.Fragment,{children:[r.jsx(vt,{feedback:F,selectedFeedback:S,onSelect:Q}),r.jsx(mt,{feedback:F,selectedFeedback:S,onSelect:Q})]}):null,r.jsxs("aside",{className:`review-lens-panel review-lens-panel--${n}`,"data-review-lens-ui":!0,children:[r.jsxs("header",{className:"review-lens-panel__header",children:[r.jsxs("div",{children:[r.jsx("p",{className:"review-lens-kicker",children:"Review Lens"}),r.jsx("h2",{children:M==="summary"?"Summary":M==="feedback"?"Feedback":m?"Element locked":"Inspecting"})]}),r.jsx("button",{type:"button",onClick:()=>t==null?void 0:t(!1),children:"Close"})]}),r.jsxs("div",{className:"review-lens-panel__body",children:[r.jsxs("div",{className:"review-lens-mode-switch",role:"tablist","aria-label":"Review Lens mode",children:[r.jsx("button",{type:"button",role:"tab","aria-selected":M==="review",onClick:()=>P("review"),children:"Review"}),r.jsxs("button",{type:"button",role:"tab","aria-selected":M==="feedback",onClick:()=>P("feedback"),children:["Feedback ",r.jsx("span",{children:F.length})]}),r.jsx("button",{type:"button",role:"tab","aria-selected":M==="summary",onClick:()=>P("summary"),children:"Summary"})]}),M==="review"?r.jsxs("div",{className:"review-lens-review-pane",role:"tabpanel",children:[r.jsxs("div",{className:"review-lens-inspection",children:[I?null:r.jsx("p",{children:"Authenticate with Google to inspect this page."}),I&&U?r.jsxs(r.Fragment,{children:[r.jsx(yt,{target:U}),r.jsx(ke,{title:"Accessibility",items:St(U)}),r.jsx(ke,{title:"Design tokens",items:kt(U.cssSnapshot,b.designTokens)})]}):null,I&&!U?r.jsx("p",{children:"Move over the app to inspect."}):null]}),Wt?r.jsx("div",{className:"review-lens-composer-panel",children:r.jsxs("form",{className:"review-lens-feedback-form",onSubmit:a=>{a.preventDefault(),He()},children:[r.jsx("label",{htmlFor:"review-lens-comment",children:"New feedback"}),r.jsx("textarea",{ref:ee,id:"review-lens-comment",value:$,disabled:!T,onChange:a=>z(a.target.value),onKeyDown:a=>{a.key==="Enter"&&a.metaKey&&(a.preventDefault(),He())},placeholder:T?"Describe the UX issue...":"You do not have permission to comment."}),r.jsxs("div",{className:"review-lens-form-grid",children:[r.jsxs("label",{children:["Severity",r.jsx("select",{value:xe,onChange:a=>Mt(a.target.value),disabled:!T,children:ye.map(a=>r.jsx("option",{value:a,children:H[a]},a))})]}),r.jsxs("label",{children:["Type",r.jsx("select",{value:Ae,onChange:a=>It(a.target.value),disabled:!T,children:Se.map(a=>r.jsx("option",{value:a,children:j[a]},a))})]}),r.jsxs("label",{children:["Assignee",r.jsx("input",{value:Le,onChange:a=>Fe(a.target.value),disabled:!T,placeholder:"optional@email.com"})]}),r.jsxs("label",{children:["Viewport",r.jsx("select",{value:Me,onChange:a=>Tt(a.target.value),disabled:!T,children:d.map(a=>r.jsx("option",{value:a.value,children:a.label},a.value))})]})]}),T?r.jsxs("p",{className:"review-lens-feedback-form__hint",children:["Press ",r.jsx("kbd",{children:"Command"})," + ",r.jsx("kbd",{children:"Enter"})," to submit.",Dt?" Screenshot capture runs after save.":""]}):null,r.jsx("div",{className:"review-lens-actions",children:r.jsx("button",{type:"submit",disabled:!$.trim()||!T,children:"Save feedback"})})]})}):null]}):null,M==="feedback"?r.jsxs("div",{className:"review-lens-comments",children:[r.jsx(ht,{open:$t,activeCount:Ht,statusFilter:q,severityFilter:V,categoryFilter:G,assigneeFilter:Y,viewportFilter:X,assignees:zt,responsivePresets:d,onStatusChange:Te,onSeverityChange:Be,onCategoryChange:$e,onAssigneeChange:Pe,onViewportChange:_e,onToggle:()=>Pt(a=>!a),onClear:()=>{Te("all"),Be("all"),$e("all"),Pe("all"),_e("all")}}),r.jsxs("div",{className:"review-lens-list-panel",children:[r.jsxs("div",{className:"review-lens-comments__header",children:[r.jsx("h3",{children:"All feedback"}),r.jsx("span",{children:F.length})]}),r.jsxs("div",{className:"review-lens-comments__list",children:[F.length===0?r.jsx("p",{children:"No feedback for this view."}):null,F.map(a=>r.jsx(pt,{item:a,selected:(S==null?void 0:S.id)===a.id,onSelect:Q},a.id))]})]}),S?r.jsxs("div",{className:"review-lens-selected-panel",children:[r.jsx("div",{className:"review-lens-selected-panel__label",children:"Selected feedback"}),r.jsx(ut,{item:S,messages:_t[S.id]??[],messageDraft:ie,canReply:We,canUpdate:De,canAssign:Ut,onMessageDraftChange:oe,onSubmitMessage:()=>void Ot(S),onStatusChange:a=>void jt(S,a),onAssigneeChange:a=>void k(S.id,{assigneeEmail:a.trim()||void 0}).then(J),onMarkFixed:()=>void je(S)},S.id)]}):r.jsxs("div",{className:"review-lens-selected-panel review-lens-selected-panel--empty",children:[r.jsx("div",{className:"review-lens-selected-panel__label",children:"Selected feedback"}),r.jsx("p",{children:"Select a feedback item above to review status, assignment, drift, and replies."})]})]}):null,M==="summary"?r.jsx(gt,{feedback:c}):null]})]})]})}function ht({open:e,activeCount:t,statusFilter:n,severityFilter:s,categoryFilter:i,assigneeFilter:d,viewportFilter:f,assignees:b,responsivePresets:u,onStatusChange:c,onSeverityChange:w,onCategoryChange:h,onAssigneeChange:v,onViewportChange:k,onToggle:E,onClear:A}){return r.jsxs("div",{className:"review-lens-filter-shell",children:[r.jsxs("div",{className:"review-lens-filter-bar",children:[r.jsxs("button",{type:"button","aria-expanded":e,onClick:E,children:["Filters",t>0?r.jsx("span",{children:t}):null]}),t>0?r.jsx("button",{type:"button",onClick:A,children:"Clear"}):null]}),e?r.jsxs("div",{className:"review-lens-filters",children:[r.jsxs("label",{children:["Status",r.jsxs("select",{"aria-label":"Filter status",value:n,onChange:p=>c(p.target.value),children:[r.jsx("option",{value:"all",children:"All statuses"}),me.map(p=>r.jsx("option",{value:p,children:Z[p]},p))]})]}),r.jsxs("label",{children:["Priority",r.jsxs("select",{"aria-label":"Filter severity",value:s,onChange:p=>w(p.target.value),children:[r.jsx("option",{value:"all",children:"All priorities"}),ye.map(p=>r.jsx("option",{value:p,children:H[p]},p))]})]}),r.jsxs("label",{children:["Type",r.jsxs("select",{"aria-label":"Filter type",value:i,onChange:p=>h(p.target.value),children:[r.jsx("option",{value:"all",children:"All types"}),Se.map(p=>r.jsx("option",{value:p,children:j[p]},p))]})]}),r.jsxs("label",{children:["Assignee",r.jsxs("select",{"aria-label":"Filter assignee",value:d,onChange:p=>v(p.target.value),children:[r.jsx("option",{value:"all",children:"All assignees"}),b.map(p=>r.jsx("option",{value:p,children:p},p))]})]}),r.jsxs("label",{children:["Viewport",r.jsxs("select",{"aria-label":"Filter viewport",value:f,onChange:p=>k(p.target.value),children:[r.jsx("option",{value:"all",children:"All viewports"}),u.map(p=>r.jsx("option",{value:p.value,children:p.label},p.value))]})]})]}):null]})}function pt({item:e,selected:t,onSelect:n}){const s=Ce(e);return r.jsxs("article",{tabIndex:0,className:["review-lens-comment",`review-lens-comment--${e.severity}`,t?"review-lens-comment--selected":""].filter(Boolean).join(" "),onClick:()=>n(e),onKeyDown:i=>{(i.key==="Enter"||i.key===" ")&&(i.preventDefault(),n(e))},children:[r.jsxs("div",{className:"review-lens-comment__header",children:[r.jsx("span",{children:Z[e.status]}),r.jsx("strong",{children:H[e.severity]})]}),r.jsxs("div",{className:"review-lens-comment__content",children:[r.jsx("p",{children:e.comment}),r.jsxs("span",{children:[e.authorEmail,e.assigneeEmail?` -> ${e.assigneeEmail}`:""]})]}),r.jsxs("div",{className:"review-lens-tags",children:[r.jsx("span",{children:j[e.category]}),r.jsx("span",{children:e.viewportPreset}),r.jsx("span",{children:s.label})]})]})}function ut({item:e,messages:t,messageDraft:n,canReply:s,canUpdate:i,canAssign:d,onMessageDraftChange:f,onSubmitMessage:b,onStatusChange:u,onAssigneeChange:c,onMarkFixed:w}){const h=Ce(e);return r.jsxs("section",{className:"review-lens-detail","aria-label":"Selected feedback detail",children:[r.jsxs("div",{className:"review-lens-detail__header",children:[r.jsxs("h3",{children:[j[e.category]," feedback"]}),r.jsx("strong",{children:H[e.severity]})]}),r.jsx("blockquote",{children:e.comment}),r.jsxs("dl",{className:"review-lens-detail-meta",children:[r.jsxs("div",{children:[r.jsx("dt",{children:"Target"}),r.jsx("dd",{children:h.label})]}),r.jsxs("div",{children:[r.jsx("dt",{children:"Viewport"}),r.jsx("dd",{children:e.viewportPreset})]}),e.screenshotUrl?r.jsxs("div",{children:[r.jsx("dt",{children:"Evidence"}),r.jsx("dd",{children:r.jsx("a",{href:e.screenshotUrl,target:"_blank",rel:"noreferrer",children:"Screenshot"})})]}):null]}),r.jsxs("div",{className:"review-lens-form-grid",children:[r.jsxs("label",{children:["Status",r.jsx("select",{value:e.status,disabled:!i,onChange:v=>u(v.target.value),children:me.map(v=>r.jsx("option",{value:v,children:Z[v]},v))})]}),r.jsxs("label",{children:["Assignee",r.jsx("input",{defaultValue:e.assigneeEmail??"",disabled:!d,onBlur:v=>c(v.target.value),placeholder:"optional@email.com"})]})]}),r.jsxs("div",{className:"review-lens-status-actions",children:[r.jsx("button",{type:"button",className:"review-lens-button-secondary",disabled:!i,onClick:w,children:"Mark fixed"}),r.jsx("button",{type:"button",className:"review-lens-button-primary",disabled:!i,onClick:()=>u("resolved"),children:"Resolve"})]}),r.jsxs("div",{className:"review-lens-thread",children:[r.jsxs("div",{className:"review-lens-thread__header",children:[r.jsx("h3",{children:"Thread"}),r.jsx("span",{children:t.length})]}),t.length===0?r.jsx("p",{children:"No replies yet."}):null,t.map(v=>r.jsxs("div",{className:"review-lens-thread__message",children:[r.jsx("p",{children:v.body}),r.jsx("span",{children:v.authorEmail})]},v.id)),r.jsx("textarea",{"aria-label":"Reply",value:n,disabled:!s,onChange:v=>f(v.target.value),placeholder:s?"Reply...":"You do not have permission to reply."}),r.jsx("div",{className:"review-lens-actions",children:r.jsx("button",{type:"button",disabled:!n.trim()||!s,onClick:b,children:"Reply"})})]})]})}function gt({feedback:e}){return r.jsxs("div",{className:"review-lens-summary",role:"tabpanel",children:[r.jsx(O,{title:"Status",values:K(e,t=>Z[t.status])}),r.jsx(O,{title:"Severity",values:K(e,t=>H[t.severity])}),r.jsx(O,{title:"Type",values:K(e,t=>j[t.category])}),r.jsx(O,{title:"Assignee",values:K(e,t=>t.assigneeEmail??"Unassigned")}),r.jsx(O,{title:"Viewport",values:K(e,t=>t.viewportPreset)})]})}function O({title:e,values:t}){return r.jsxs("section",{children:[r.jsx("h3",{children:e}),r.jsx("dl",{children:t.map(([n,s])=>r.jsxs("div",{children:[r.jsx("dt",{children:n}),r.jsx("dd",{children:s})]},n))})]})}function ft({target:e,locked:t}){const n=Ft(e),s=Nt(e.fingerprint);return r.jsxs("div",{className:t?"review-lens-highlight review-lens-highlight--locked":"review-lens-highlight",style:{top:n.margin.top,left:n.margin.left,width:n.margin.width,height:n.margin.height},children:[r.jsx("div",{className:"review-lens-highlight__border",style:{top:n.border.top-n.margin.top,left:n.border.left-n.margin.left,width:n.border.width,height:n.border.height}}),r.jsx("div",{className:"review-lens-highlight__padding",style:{top:n.padding.top-n.margin.top,left:n.padding.left-n.margin.left,width:n.padding.width,height:n.padding.height}}),r.jsx("div",{className:"review-lens-highlight__content",style:{top:n.content.top-n.margin.top,left:n.content.left-n.margin.left,width:n.content.width,height:n.content.height}}),r.jsxs("div",{className:"review-lens-highlight__label",children:[r.jsx("strong",{children:s}),r.jsxs("span",{children:[Math.round(e.rect.width)," x ",Math.round(e.rect.height)]})]})]})}function bt({from:e,to:t}){const n=Et(e.rect,t.rect);return n.length===0?null:r.jsx(r.Fragment,{children:n.map(s=>r.jsx("div",{className:`review-lens-distance review-lens-distance--${s.axis}`,style:{top:s.top,left:s.left,width:s.width,height:s.height},children:r.jsx("span",{children:s.label})},s.key))})}function vt({feedback:e,selectedFeedback:t,onSelect:n}){return r.jsx(r.Fragment,{children:e.map(s=>r.jsx(wt,{feedback:s,selected:(t==null?void 0:t.id)===s.id,onSelect:n},s.id))})}function wt({feedback:e,selected:t,onSelect:n}){const s=l.useRef(null);return l.useLayoutEffect(()=>{let i=0;const d=()=>{i=0;const b=s.current,u=D(e.selector),c=u==null?void 0:u.getBoundingClientRect();!b||!c||(b.style.top=`${c.top}px`,b.style.left=`${c.right}px`,b.hidden=c.bottom<0||c.top>window.innerHeight)},f=()=>{i||(i=window.requestAnimationFrame(d))};return d(),window.addEventListener("scroll",f,!0),window.addEventListener("resize",f),()=>{i&&window.cancelAnimationFrame(i),window.removeEventListener("scroll",f,!0),window.removeEventListener("resize",f)}},[e.selector]),r.jsx("button",{ref:s,type:"button",className:t?"review-lens-marker review-lens-marker--selected":"review-lens-marker",onClick:()=>n(e),"aria-label":`Open feedback from ${e.authorEmail}`})}function mt({feedback:e,selectedFeedback:t,onSelect:n}){const s=e.map(i=>{const d=D(i.selector),f=d==null?void 0:d.getBoundingClientRect(),b=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight,window.innerHeight);return!f||b<=0?null:{item:i,top:Math.min(100,Math.max(0,(f.top+window.scrollY)/b*100))}}).filter(i=>i!==null);return s.length===0?null:r.jsx("div",{className:"review-lens-minimap","data-review-lens-ui":!0,"aria-label":"Feedback map",children:s.map(i=>r.jsx("button",{type:"button",className:(t==null?void 0:t.id)===i.item.id?"review-lens-minimap__point review-lens-minimap__point--selected":"review-lens-minimap__point",style:{top:`${i.top}%`},onClick:()=>n(i.item),"aria-label":`Jump to feedback from ${i.item.authorEmail}`},i.item.id))})}function yt({target:e}){const t=[["Selector",e.selector],["Size",`${e.cssSnapshot.width} x ${e.cssSnapshot.height}`],["Margin",e.cssSnapshot.margin],["Padding",e.cssSnapshot.padding],["Border",e.cssSnapshot.border],["Radius",e.cssSnapshot.borderRadius],["Font",`${e.cssSnapshot.fontSize} / ${e.cssSnapshot.lineHeight}`],["Family",e.cssSnapshot.fontFamily],["Color",e.cssSnapshot.color],["Background",e.cssSnapshot.backgroundColor]];return r.jsx("dl",{className:"review-lens-metrics",children:t.map(([n,s])=>r.jsxs("div",{children:[r.jsx("dt",{children:n}),r.jsx("dd",{children:s})]},n))})}function ke({title:e,items:t}){return r.jsxs("section",{className:"review-lens-insights",children:[r.jsx("h3",{children:e}),t.length===0?r.jsx("p",{children:"No issues detected."}):null,t.length>0?r.jsx("ul",{children:t.map(n=>r.jsx("li",{children:n},n))}):null]})}function D(e){try{return document.querySelector(e)}catch{return null}}function Ce(e){const t=D(e.selector);if(!t)return{label:"Target missing",level:"warning"};const n=W(t);return n.fingerprint.tagName!==e.elementFingerprint.tagName?{label:"Element changed",level:"warning"}:Math.abs(n.fingerprint.width-e.elementFingerprint.width)>2||Math.abs(n.fingerprint.height-e.elementFingerprint.height)>2?{label:"Size changed",level:"warning"}:n.cssSnapshot.fontSize!==e.createdCssSnapshot.fontSize||n.cssSnapshot.color!==e.createdCssSnapshot.color||n.cssSnapshot.padding!==e.createdCssSnapshot.padding?{label:"Style changed",level:"warning"}:{label:"Target unchanged",level:"ok"}}function St(e){var u;const t=D(e.selector);if(!t)return["Selected element is no longer available."];const n=[],s=t.tagName.toLowerCase(),i=t.getAttribute("role"),d=["button","a","input","select","textarea"].includes(s)||i==="button"||i==="link",f=t.getAttribute("aria-label")||t.getAttribute("title")||((u=t.textContent)==null?void 0:u.trim());d&&!f&&n.push("Interactive element has no accessible name."),d&&(e.rect.width<44||e.rect.height<44)&&n.push("Tap target is smaller than 44 x 44."),s==="img"&&!t.getAttribute("alt")&&n.push("Image is missing alt text.");const b=/^h[1-6]$/.test(s)?Number(s.slice(1)):0;return b>1&&!document.querySelector(`h${b-1}`)&&n.push("Heading may skip the previous level."),At(e.cssSnapshot.color,e.cssSnapshot.backgroundColor)&&n.push("Text contrast may be low."),n}function kt(e,t={}){const n=[];return _("Padding",e.padding,t.spacing,n,{allowComposite:!0}),_("Margin",e.margin,t.spacing,n,{allowComposite:!0}),_("Font size",e.fontSize,t.fontSize,n),_("Line height",e.lineHeight,t.lineHeight,n),_("Text color",e.color,t.color,n),_("Background",e.backgroundColor,t.color,n),_("Radius",e.borderRadius,t.radius,n,{allowComposite:!0}),n}function _(e,t,n,s,i={}){!n||n.length===0||!t||Ct(t,n,i)||s.push(`${e} ${t} is outside configured tokens.`)}function Ct(e,t,n={}){if(t.includes(e))return!0;if(!n.allowComposite)return!1;const s=e.trim().split(/\s+/);return s.length>1&&s.every(i=>t.includes(i))}function Nt(e){const t=e.id?`#${e.id}`:"",n=e.className?`.${e.className.split(/\s+/).filter(Boolean).slice(0,2).join(".")}`:"",s=e.ariaLabel?`[aria-label="${e.ariaLabel}"]`:"";return`${e.tagName}${t}${n}${s}`||e.tagName}function Et(e,t){const n=[],s=(Math.max(e.left,t.left)+Math.min(e.right,t.right))/2,i=(Math.max(e.top,t.top)+Math.min(e.bottom,t.bottom))/2;if(e.right<=t.left||t.right<=e.left){const d=e.right<=t.left?e.right:t.right,f=e.right<=t.left?t.left:e.left;n.push({key:"horizontal",axis:"horizontal",top:Ne(i,0,window.innerHeight),left:d,width:Math.max(f-d,1),height:1,label:`${Math.round(f-d)}px`})}if(e.bottom<=t.top||t.bottom<=e.top){const d=e.bottom<=t.top?e.bottom:t.bottom,f=e.bottom<=t.top?t.top:e.top;n.push({key:"vertical",axis:"vertical",top:d,left:Ne(s,0,window.innerWidth),width:1,height:Math.max(f-d,1),label:`${Math.round(f-d)}px`})}return n}function Ne(e,t,n){return Math.min(Math.max(e,t),n)}function K(e,t){const n=new Map;for(const s of e){const i=t(s);n.set(i,(n.get(i)??0)+1)}return Array.from(n.entries()).sort((s,i)=>i[1]-s[1]||s[0].localeCompare(i[0]))}function xt(e){return e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement||e instanceof HTMLSelectElement||e instanceof HTMLElement&&e.isContentEditable}function At(e,t){const n=Ee(e),s=Ee(t);return!n||!s||s.alpha===0?!1:Lt(n,s)<4.5}function Ee(e){const t=e.match(/rgba?\(([^)]+)\)/);if(!t)return null;const[n,s,i,d="1"]=t[1].split(",").map(f=>f.trim());return{red:Number(n),green:Number(s),blue:Number(i),alpha:Number(d)}}function Lt(e,t){const n=Math.max(R(e),R(t)),s=Math.min(R(e),R(t));return(n+.05)/(s+.05)}function R(e){const t=[e.red,e.green,e.blue].map(n=>{const s=n/255;return s<=.03928?s/12.92:((s+.055)/1.055)**2.4});return t[0]*.2126+t[1]*.7152+t[2]*.0722}function Ft(e){const t={top:L(e.cssSnapshot.marginTop),right:L(e.cssSnapshot.marginRight),bottom:L(e.cssSnapshot.marginBottom),left:L(e.cssSnapshot.marginLeft)},n={top:L(e.cssSnapshot.borderTopWidth),right:L(e.cssSnapshot.borderRightWidth),bottom:L(e.cssSnapshot.borderBottomWidth),left:L(e.cssSnapshot.borderLeftWidth)},s={top:L(e.cssSnapshot.paddingTop),right:L(e.cssSnapshot.paddingRight),bottom:L(e.cssSnapshot.paddingBottom),left:L(e.cssSnapshot.paddingLeft)},i={top:e.rect.top,left:e.rect.left,width:Math.max(e.rect.width,0),height:Math.max(e.rect.height,0)},d={top:i.top-t.top,left:i.left-t.left,width:i.width+t.left+t.right,height:i.height+t.top+t.bottom},f={top:i.top+n.top,left:i.left+n.left,width:Math.max(i.width-n.left-n.right,0),height:Math.max(i.height-n.top-n.bottom,0)},b={top:f.top+s.top,left:f.left+s.left,width:Math.max(f.width-s.left-s.right,0),height:Math.max(f.height-s.top-s.bottom,0)};return{margin:d,border:i,padding:f,content:b}}function L(e){const t=Number.parseFloat(e);return Number.isFinite(t)?t:0}x.ReviewLensOverlay=ct,x.ReviewLensProvider=rt,x.buildElementTarget=W,x.createGoogleSheetsAdapter=de,x.normalizeReviewUrl=fe,x.useReviewLens=ve,Object.defineProperty(x,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(L,n){typeof exports=="object"&&typeof module<"u"?n(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],n):(L=typeof globalThis<"u"?globalThis:L||self,n(L.ReviewLensReact={},L.jsxRuntime,L.React))})(this,(function(L,n,h){"use strict";const ue=["https://www.googleapis.com/auth/spreadsheets","https://www.googleapis.com/auth/userinfo.email"],et="https://www.googleapis.com/auth/gmail.send",pe="https://www.googleapis.com/oauth2/v3/userinfo",tt="https://gmail.googleapis.com/gmail/v1/users/me/messages/send",rt="review-lens-google-token";function fe(e){const t=e.feedbackSheetName??"Feedback",r=e.messagesSheetName??"Messages",s=e.usersSheetName??"Users",a=e.enableEmailNotifications?[...ue,et].join(" "):ue.join(" "),l=pt(e.googleClientId,a);let u,m;async function g(){const f=ft(l);if(f)return f.accessToken;u??(u=ut(e.googleClientId,a).then(c=>(gt(l,c),c)));const v=await u;return Date.now()>=v.expiresAt?(u=void 0,g()):v.accessToken}async function b(f,v,c){const S=await g(),o=await fetch(`https://sheets.googleapis.com/v4/spreadsheets/${f}${v}`,{...c,headers:{Authorization:`Bearer ${S}`,"Content-Type":"application/json",...c==null?void 0:c.headers}});if(o.status===401){ye(l),u=void 0;const C=await g(),w=await fetch(`https://sheets.googleapis.com/v4/spreadsheets/${f}${v}`,{...c,headers:{Authorization:`Bearer ${C}`,"Content-Type":"application/json",...c==null?void 0:c.headers}});if(!w.ok)throw new Error(`Google Sheets request failed with ${w.status}`);return w.json()}if(!o.ok)throw new Error(`Google Sheets request failed with ${o.status}`);return o.json()}async function A(f,v){return(await b(f,`/values/${encodeURIComponent(v)}`)).values??[]}return{async getCurrentUser(){if(m||(m=await x()),!m)throw new Error("Google account did not return an email address");return{email:m}},async getPermissions(f){const[{email:v},c]=await Promise.all([this.getCurrentUser(),A(e.usersSpreadsheetId,s)]),S=ne(c),o=v.toLowerCase(),C=S.find(w=>{var y;return((y=w.email)==null?void 0:y.toLowerCase())===o&&w.active!=="false"&&(!w.projectKey||w.projectKey===f)});return at((C==null?void 0:C.role)??"designer")},async listFeedback(f){return ne(await A(e.contentSpreadsheetId,t)).map(ve).filter(c=>c!==null).filter(c=>c.projectKey===f.projectKey&&c.contentId===f.contentId&&c.normalizedPath===f.normalizedPath).sort((c,S)=>S.createdAt.localeCompare(c.createdAt))},async createFeedback(f){const v=new Date().toISOString(),c={...f,id:crypto.randomUUID(),attachments:[],createdAt:v,updatedAt:v};return await b(e.contentSpreadsheetId,`/values/${encodeURIComponent(t)}:append?valueInputOption=RAW`,{method:"POST",body:JSON.stringify({values:[we(c)]})}),c},async updateFeedback(f,v){const c=await A(e.contentSpreadsheetId,t),S=c[0]??ge,o=S.indexOf("id");if(o===-1)throw new Error(`Sheet ${t} is missing an id column`);const C=c.findIndex((B,U)=>U>0&&B[o]===f);if(C<1)throw new Error(`Feedback ${f} was not found`);const w=new Date().toISOString(),y=ve(me(S,c[C]));if(!y)throw new Error(`Feedback ${f} could not be parsed before updating`);const E={...y,...v,updatedAt:w},F=we(E);return await b(e.contentSpreadsheetId,`/values/${encodeURIComponent(t)}!A${C+1}:${ht(ge.length)}${C+1}?valueInputOption=RAW`,{method:"PUT",body:JSON.stringify({values:[F]})}),E},async listMessages(f){return ne(await A(e.contentSpreadsheetId,r)).map(it).filter(c=>c!==null).filter(c=>c.feedbackId===f).sort((c,S)=>c.createdAt.localeCompare(S.createdAt))},async createMessage(f){const v={...f,id:crypto.randomUUID(),createdAt:new Date().toISOString()};return await b(e.contentSpreadsheetId,`/values/${encodeURIComponent(r)}:append?valueInputOption=RAW`,{method:"POST",body:JSON.stringify({values:[st(v)]})}),v},async sendEmail(f){if(!e.enableEmailNotifications||f.to.length===0)return;const v=await g(),c=await this.getCurrentUser(),S=await fetch(tt,{method:"POST",headers:{Authorization:`Bearer ${v}`,"Content-Type":"application/json"},body:JSON.stringify({raw:mt({from:c.email,to:f.to,subject:f.subject,text:f.text})})});if(!S.ok)throw new Error(`Gmail send request failed with ${S.status}`)}};async function x(){const f=await fetch(pe,{headers:{Authorization:`Bearer ${await g()}`}});if(f.status===401){ye(l),u=void 0;const c=await fetch(pe,{headers:{Authorization:`Bearer ${await g()}`}});if(!c.ok)throw new Error(`Google userinfo request failed with ${c.status}`);return(await c.json()).email}if(!f.ok)throw new Error(`Google userinfo request failed with ${f.status}`);return(await f.json()).email}}const ge=["id","projectKey","contentId","normalizedPath","originalUrl","selector","selectorStrategy","elementFingerprintJson","createdCssSnapshotJson","comment","status","severity","category","assigneeEmail","viewportWidth","viewportHeight","viewportPreset","screenshotUrl","screenshotThumbnailUrl","attachmentJson","authorEmail","createdAt","updatedAt","fixedCssSnapshotJson","fixedAt","fixedBy","resolvedAt","resolvedBy"],nt=["id","feedbackId","body","authorEmail","createdAt"];function we(e){return[e.id,e.projectKey,e.contentId,e.normalizedPath,e.originalUrl,e.selector,e.selectorStrategy,JSON.stringify(e.elementFingerprint),JSON.stringify(e.createdCssSnapshot),e.comment,e.status,e.severity,e.category,e.assigneeEmail??"",String(e.viewportWidth),String(e.viewportHeight),e.viewportPreset,e.screenshotUrl??"",e.screenshotThumbnailUrl??"",JSON.stringify(e.attachments),e.authorEmail,e.createdAt,e.updatedAt,e.fixedCssSnapshot?JSON.stringify(e.fixedCssSnapshot):"",e.fixedAt??"",e.fixedBy??"",e.resolvedAt??"",e.resolvedBy??""]}function st(e){return nt.map(t=>e[t])}function ne(e){const[t,...r]=e;return t?r.map(s=>me(t,s)):[]}function me(e,t){return Object.fromEntries(e.map((r,s)=>[r,t[s]??""]))}function ve(e){return e.id?{id:e.id,projectKey:e.projectKey,contentId:e.contentId,normalizedPath:e.normalizedPath,originalUrl:e.originalUrl,selector:e.selector,selectorStrategy:e.selectorStrategy==="stable-attribute"?"stable-attribute":"css-path",elementFingerprint:se(e.elementFingerprintJson,{tagName:"",width:0,height:0}),createdCssSnapshot:be(e.createdCssSnapshotJson),fixedCssSnapshot:e.fixedCssSnapshotJson?be(e.fixedCssSnapshotJson):void 0,comment:e.comment,status:ot(e.status),severity:lt(e.severity),category:dt(e.category),assigneeEmail:e.assigneeEmail||void 0,viewportWidth:Number(e.viewportWidth)||0,viewportHeight:Number(e.viewportHeight)||0,viewportPreset:ct(e.viewportPreset),screenshotUrl:e.screenshotUrl||void 0,screenshotThumbnailUrl:e.screenshotThumbnailUrl||void 0,attachments:se(e.attachmentJson,[]),authorEmail:e.authorEmail,createdAt:e.createdAt,updatedAt:e.updatedAt,fixedAt:e.fixedAt||void 0,fixedBy:e.fixedBy||void 0,resolvedAt:e.resolvedAt||void 0,resolvedBy:e.resolvedBy||void 0}:null}function it(e){return!e.id||!e.feedbackId?null:{id:e.id,feedbackId:e.feedbackId,body:e.body,authorEmail:e.authorEmail,createdAt:e.createdAt}}function se(e,t){try{return e?JSON.parse(e):t}catch{return t}}function be(e){const t=se(e,{});return{margin:t.margin??"",marginTop:t.marginTop??"",marginRight:t.marginRight??"",marginBottom:t.marginBottom??"",marginLeft:t.marginLeft??"",padding:t.padding??"",paddingTop:t.paddingTop??"",paddingRight:t.paddingRight??"",paddingBottom:t.paddingBottom??"",paddingLeft:t.paddingLeft??"",border:t.border??"",borderTopWidth:t.borderTopWidth??"",borderRightWidth:t.borderRightWidth??"",borderBottomWidth:t.borderBottomWidth??"",borderLeftWidth:t.borderLeftWidth??"",fontFamily:t.fontFamily??"",fontSize:t.fontSize??"",lineHeight:t.lineHeight??"",color:t.color??"",backgroundColor:t.backgroundColor??"",borderRadius:t.borderRadius??"",width:t.width??0,height:t.height??0}}function at(e){return e==="admin"?["create","read","reply","update","assign"]:e==="developer"?["read","reply","update","assign"]:["create","read","reply"]}function ot(e){return e==="in_progress"||e==="needs_clarification"||e==="fixed"||e==="wontfix"||e==="resolved"?e:"open"}function lt(e){return e==="low"||e==="high"?e:"medium"}function dt(e){return e==="visual"||e==="copy"||e==="accessibility"||e==="responsive"?e:"bug"}function ct(e){return e==="mobile"||e==="tablet"||e==="desktop"?e:"custom"}function ht(e){let t=e,r="";for(;t>0;){const s=(t-1)%26;r=String.fromCharCode(65+s)+r,t=Math.floor((t-s)/26)}return r}async function ut(e,t){return await wt(),new Promise((r,s)=>{var l;const a=(l=window.google)==null?void 0:l.accounts.oauth2.initTokenClient({client_id:e,scope:t,callback:u=>{if(u.error||!u.access_token){s(new Error(u.error??"Google OAuth did not return an access token"));return}const m=u.expires_in??3600;r({accessToken:u.access_token,expiresAt:Date.now()+m*1e3})}});a==null||a.requestAccessToken({prompt:""})})}function pt(e,t){return`${rt}:${e}:${t}`}function ft(e){try{const t=window.localStorage.getItem(e);if(!t)return;const r=JSON.parse(t);if(typeof r.accessToken!="string"||typeof r.expiresAt!="number"||Date.now()>=r.expiresAt-6e4){window.localStorage.removeItem(e);return}return r}catch{return}}function gt(e,t){try{window.localStorage.setItem(e,JSON.stringify(t))}catch{}}function ye(e){try{window.localStorage.removeItem(e)}catch{}}function wt(){var e;return(e=window.google)!=null&&e.accounts.oauth2?Promise.resolve():new Promise((t,r)=>{const s=document.querySelector('script[src="https://accounts.google.com/gsi/client"]');if(s){s.addEventListener("load",()=>t(),{once:!0}),s.addEventListener("error",()=>r(new Error("Google Identity failed to load")),{once:!0});return}const a=document.createElement("script");a.src="https://accounts.google.com/gsi/client",a.async=!0,a.defer=!0,a.onload=()=>t(),a.onerror=()=>r(new Error("Google Identity failed to load")),document.head.append(a)})}function mt(e){const t=[`From: ${e.from}`,`To: ${e.to.join(", ")}`,`Subject: ${e.subject}`,"Content-Type: text/plain; charset=UTF-8","",e.text].join(`\r
2
+ `);return vt(t)}function vt(e){const t=new TextEncoder().encode(e);let r="";return t.forEach(s=>{r+=String.fromCharCode(s)}),btoa(r).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/g,"")}function Se(e){return new URL(e,window.location.href).pathname.replace(/\/+$/,"")||"/"}const ke=h.createContext(null);function bt({config:e,children:t}){const r=h.useMemo(()=>e.adapter?e.adapter:fe({googleClientId:ie(e.googleClientId,"googleClientId"),contentSpreadsheetId:ie(e.contentSpreadsheetId,"contentSpreadsheetId"),usersSpreadsheetId:ie(e.usersSpreadsheetId,"usersSpreadsheetId"),feedbackSheetName:e.sheetName??"Feedback",enableEmailNotifications:Ce(e)}),[e.adapter,e.contentSpreadsheetId,e.emailNotifications,e.googleClientId,e.sheetName,e.usersSpreadsheetId]),s=e.currentUrl??window.location.href,a=(e.normalizeUrl??Se)(s),[l,u]=h.useState(),[m,g]=h.useState([]),[b,A]=h.useState([]),x=h.useCallback(async()=>{const w=await r.listFeedback({projectKey:e.projectKey,contentId:e.contentId,normalizedPath:a});A(w)},[r,e.contentId,e.projectKey,a]);h.useEffect(()=>{let w=!0;async function y(){const[E,F]=await Promise.all([r.getCurrentUser(),r.getPermissions(e.projectKey)]);w&&(u(E),g(F),await x())}return y(),()=>{w=!1}},[r,e.projectKey,x]);const f=h.useCallback(async w=>{const y=await r.createFeedback(w);return A(E=>[y,...E]),ae(e,r,{actorEmail:(l==null?void 0:l.email)??w.authorEmail,item:y,kind:"created"}),y},[r,e,l==null?void 0:l.email]),v=h.useCallback(async(w,y)=>{const E=b.find(B=>B.id===w),F=await r.updateFeedback(w,y);return A(B=>B.map(U=>U.id===w?F:U)),ae(e,r,{actorEmail:l==null?void 0:l.email,item:F,kind:yt(y),previousItem:E}),F},[r,e,l==null?void 0:l.email,b]),c=h.useCallback(w=>r.listMessages(w),[r]),S=h.useCallback(async w=>{const y=await r.createMessage(w),E=b.find(F=>F.id===w.feedbackId);return E&&ae(e,r,{actorEmail:w.authorEmail,item:E,kind:"reply",replyBody:w.body}),y},[r,e,b]),o=h.useCallback(async(w,y)=>{const E=e.uploadAttachment??r.uploadAttachment;if(!E)throw new Error("Review Lens attachment upload is not configured");return E(w,y)},[r,e]),C=h.useMemo(()=>({config:e,adapter:r,currentUser:l,permissions:m,feedback:b,normalizedPath:a,refreshFeedback:x,createFeedback:f,updateFeedback:v,listMessages:c,createMessage:S,uploadAttachment:o}),[r,e,f,l,b,a,m,x,v,c,S,o]);return n.jsx(ke.Provider,{value:C,children:t})}function Ee(){const e=h.useContext(ke);if(!e)throw new Error("useReviewLens must be used inside ReviewLensProvider");return e}function ie(e,t){if(!e)throw new Error(`review-lens-react requires config.${t} when no adapter is provided`);return e}function Ce(e){return typeof e.emailNotifications=="object"?e.emailNotifications.enabled!==!1:!!e.emailNotifications}function yt(e){return e.status==="resolved"?"resolved":e.status==="fixed"||e.fixedAt||e.fixedBy?"fixed":e.status?"status":"assigneeEmail"in e?"assigned":"updated"}async function ae(e,t,r){if(!Ce(e)||!t.sendEmail)return;const s=St(r);if(s.length!==0)try{await t.sendEmail({to:s,subject:kt(e,r),text:Et(r)})}catch(a){console.warn("Review Lens email notification failed",a)}}function St(e){var r;const t=new Set;return t.add(e.item.authorEmail),(r=e.previousItem)!=null&&r.assigneeEmail&&t.add(e.previousItem.assigneeEmail),e.item.assigneeEmail&&t.add(e.item.assigneeEmail),[...t].filter(s=>{var a;return s?s.toLowerCase()!==((a=e.actorEmail)==null?void 0:a.toLowerCase()):!1})}function kt(e,t){return`${(typeof e.emailNotifications=="object"?e.emailNotifications:{}).subjectPrefix??"Review Lens"}: ${Ne(t)}`}function Et(e){const t=["[Review Lens]",Ne(e),Ct(e),"",`Review: ${e.item.comment}`,`Status: ${xe(e.item.status)}`,`Author: ${e.item.authorEmail}`,`Assignee: ${e.item.assigneeEmail??"Unassigned"}`,`Link: ${Nt(e.item)}`];return e.replyBody&&t.splice(2,0,`Reply: ${e.replyBody}`,""),t.join(`
3
+ `)}function Ct(e){return e.actorEmail?`Sent by Review Lens on behalf of ${e.actorEmail}.`:"Sent by Review Lens on behalf of the signed-in Google user."}function Ne(e){return e.kind==="created"?"New review feedback":e.kind==="assigned"?`Review assignment changed to ${e.item.assigneeEmail??"unassigned"}`:e.kind==="status"?`Review status changed to ${xe(e.item.status)}`:e.kind==="fixed"?"Review marked fixed":e.kind==="resolved"?"Review resolved":e.kind==="reply"?"New review reply":"Review updated"}function Nt(e){try{const t=new URL(e.originalUrl);return t.searchParams.set("reviewLensFeedback",e.id),t.toString()}catch{return e.originalUrl}}function xe(e){return e.replace(/_/g," ")}const xt=["data-review-id","data-testid","data-test-id","aria-label","name"];function D(e){const t=e.getBoundingClientRect(),r=Lt(e);return{selector:r.selector,selectorStrategy:r.strategy,fingerprint:Ft(e,t),cssSnapshot:It(e,t),rect:t}}function Lt(e){for(const t of xt){const r=e.getAttribute(t);if(r)return{selector:`[${t}="${Le(r)}"]`,strategy:"stable-attribute"}}return e.id?{selector:`#${Le(e.id)}`,strategy:"stable-attribute"}:{selector:At(e),strategy:"css-path"}}function At(e){const t=[];let r=e;for(;r&&r.nodeType===Node.ELEMENT_NODE&&r!==document.body;){const s=r.parentElement,a=r.tagName.toLowerCase();if(!s){t.unshift(a);break}const l=r.tagName,u=Array.from(s.children).filter(g=>g.tagName===l),m=u.indexOf(r)+1;t.unshift(u.length>1?`${a}:nth-of-type(${m})`:a),r=s}return t.join(" > ")}function Ft(e,t){var r;return{tagName:e.tagName.toLowerCase(),id:e.id||void 0,className:e.getAttribute("class")||void 0,textSnippet:((r=e.textContent)==null?void 0:r.trim().slice(0,80))||void 0,ariaLabel:e.getAttribute("aria-label")||void 0,width:Math.round(t.width),height:Math.round(t.height)}}function It(e,t){const r=window.getComputedStyle(e);return{margin:oe(r.marginTop,r.marginRight,r.marginBottom,r.marginLeft),marginTop:r.marginTop,marginRight:r.marginRight,marginBottom:r.marginBottom,marginLeft:r.marginLeft,padding:oe(r.paddingTop,r.paddingRight,r.paddingBottom,r.paddingLeft),paddingTop:r.paddingTop,paddingRight:r.paddingRight,paddingBottom:r.paddingBottom,paddingLeft:r.paddingLeft,border:oe(r.borderTopWidth,r.borderRightWidth,r.borderBottomWidth,r.borderLeftWidth),borderTopWidth:r.borderTopWidth,borderRightWidth:r.borderRightWidth,borderBottomWidth:r.borderBottomWidth,borderLeftWidth:r.borderLeftWidth,fontFamily:r.fontFamily,fontSize:r.fontSize,lineHeight:r.lineHeight,color:r.color,backgroundColor:r.backgroundColor,borderRadius:r.borderRadius,width:Math.round(t.width),height:Math.round(t.height)}}function oe(e,t,r,s){return e===t&&t===r&&r===s?e:`${e} ${t} ${r} ${s}`}function Le(e){return typeof CSS<"u"&&typeof CSS.escape=="function"?CSS.escape(e):e.replace(/["\\]/g,"\\$&")}function Ae({className:e,title:t="Review Lens logo"}){return n.jsxs("svg",{className:e,role:"img","aria-label":t,viewBox:"0 0 40 40",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:[n.jsx("rect",{x:"4",y:"6",width:"28",height:"28",rx:"8",fill:"#171717"}),n.jsx("path",{d:"M13 15.5C14.5 13.4 17.1 12 20 12C23.9 12 27.3 14.5 28.7 18C27.3 21.5 23.9 24 20 24C17.1 24 14.5 22.6 13 20.5",stroke:"#FFFFFF",strokeWidth:"2.2",strokeLinecap:"round",strokeLinejoin:"round"}),n.jsx("circle",{cx:"20",cy:"18",r:"3.4",fill:"#F97316"}),n.jsx("path",{d:"M28.5 26.5L34.5 32.5",stroke:"#2563EB",strokeWidth:"4",strokeLinecap:"round"}),n.jsx("circle",{cx:"31.8",cy:"29.8",r:"2.1",fill:"#FACC15",stroke:"#171717",strokeWidth:"1.4"})]})}const Tt=[{label:"Desktop",value:"desktop"},{label:"Tablet",value:"tablet"},{label:"Mobile",value:"mobile"}],Fe=["open","in_progress","needs_clarification","fixed","wontfix","resolved"],Ie=["low","medium","high"],Te=["bug","visual","copy","accessibility","responsive"],R={open:"Open",in_progress:"In progress",needs_clarification:"Needs clarification",fixed:"Fixed",wontfix:"Won't fix",resolved:"Resolved"},O={low:"Low",medium:"Medium",high:"High"},K={bug:"Bug",visual:"Visual",copy:"Copy",accessibility:"Accessibility",responsive:"Responsive"};function Mt({open:e,onOpenChange:t,placement:r="top-right",showResolved:s=!1,syncSelectionToUrl:a=!1,responsivePresets:l=Tt}){var Re;const{adapter:u,config:m,currentUser:g,feedback:b,normalizedPath:A,permissions:x,createFeedback:f,updateFeedback:v,listMessages:c,createMessage:S,uploadAttachment:o}=Ee(),[C,w]=h.useState(),[y,E]=h.useState(),[F,B]=h.useState(""),[U,Rt]=h.useState("medium"),[_e,er]=h.useState("visual"),[je,Ue]=h.useState(""),[We,tr]=h.useState(((Re=l[0])==null?void 0:Re.value)??"desktop"),[k,q]=h.useState(),[M,_]=h.useState("review"),[rr,De]=h.useState(!1),[V,ze]=h.useState("all"),[Y,He]=h.useState("all"),[X,Oe]=h.useState("all"),[Q,Ke]=h.useState("all"),[Z,Je]=h.useState("all"),[nr,sr]=h.useState(!1),[ir,Ge]=h.useState({}),[le,de]=h.useState(""),te=h.useRef(null),qe=h.useRef(e),ce=h.useRef(a?new URL(window.location.href).searchParams.get("reviewLensFeedback"):null),$=!!g,P=x.includes("create"),Ve=x.includes("reply"),Ye=x.includes("update"),ar=x.includes("assign"),W=C??y,or=!!y,lr=!!(m.captureScreenshot&&(m.uploadAttachment||u.uploadAttachment)),dr=h.useMemo(()=>{const i=b.map(p=>p.assigneeEmail).filter(p=>!!p);return g!=null&&g.email&&i.push(g.email),Array.from(new Set(i)).sort((p,d)=>p.localeCompare(d))},[g==null?void 0:g.email,b]),T=h.useMemo(()=>b.filter(i=>s||i.status!=="resolved").filter(i=>V==="all"||i.status===V).filter(i=>Y==="all"||i.severity===Y).filter(i=>X==="all"||i.category===X).filter(i=>Q==="all"||i.assigneeEmail===Q).filter(i=>Z==="all"||i.viewportPreset===Z),[Q,X,b,Y,s,V,Z]),cr=[V,Y,X,Q,Z].filter(i=>i!=="all").length;h.useEffect(()=>{if(e){qe.current=!0;return}if(w(void 0),E(void 0),B(""),de(""),_("review"),a&&qe.current){const i=new URL(window.location.href);i.searchParams.has("reviewLensFeedback")&&(i.searchParams.delete("reviewLensFeedback"),window.history.replaceState({},"",i))}},[e,a]),h.useEffect(()=>{$||(w(void 0),E(void 0))},[$]),h.useEffect(()=>{!y||M!=="review"||window.requestAnimationFrame(()=>{var i,p,d;(p=(i=te.current)==null?void 0:i.scrollIntoView)==null||p.call(i,{block:"nearest"}),(d=te.current)==null||d.focus()})},[y,M]),h.useEffect(()=>{if(!k)return;let i=!0;return c(k.id).then(p=>{i&&Ge(d=>({...d,[k.id]:p}))}),()=>{i=!1}},[c,k]),h.useEffect(()=>{if(!a||!ce.current||b.length===0)return;const i=b.find(p=>p.id===ce.current);i&&(ce.current=null,t==null||t(!0),H(i,{syncUrl:!1}))},[b,a]),h.useEffect(()=>{if(!e||!a||k||b.length===0)return;const i=new URL(window.location.href).searchParams.get("reviewLensFeedback"),p=b.find(d=>d.id===i);p&&H(p,{syncUrl:!1})},[b,e,k,a]),h.useEffect(()=>{if(!e)return;function i(d){var N;if(d.key==="Shift"&&De(!0),d.key==="Escape"){d.preventDefault(),t==null||t(!1);return}Yt(d.target)||((d.key==="n"||d.key==="ArrowDown")&&(d.preventDefault(),Xe(1)),(d.key==="p"||d.key==="ArrowUp")&&(d.preventDefault(),Xe(-1)),d.key==="c"&&(d.preventDefault(),_("review"),(N=te.current)==null||N.focus()),d.key==="f"&&k&&Ye&&(d.preventDefault(),Ze(k)))}function p(d){d.key==="Shift"&&De(!1)}return window.addEventListener("keydown",i),window.addEventListener("keyup",p),()=>{window.removeEventListener("keydown",i),window.removeEventListener("keyup",p)}});const he=h.useCallback(i=>{const p=i.target instanceof Element?i.target:null;if(p)return p.closest("[data-review-lens-ui]")?null:p;const d=document.elementFromPoint(i.clientX,i.clientY);return!d||d.closest("[data-review-lens-ui]")?null:d},[]);if(h.useEffect(()=>{if(!e||!$)return;function i(d){const N=he(d);w(N?D(N):void 0)}function p(d){const N=he(d);N&&(d.preventDefault(),d.stopPropagation(),E(D(N)),_("review"))}return window.addEventListener("mousemove",i,!0),window.addEventListener("click",p,!0),()=>{window.removeEventListener("mousemove",i,!0),window.removeEventListener("click",p,!0)}},[$,he,e]),!e)return null;function H(i,p={syncUrl:!0}){var N;if(q(i),E(void 0),_("feedback"),a&&p.syncUrl!==!1){const re=new URL(window.location.href);re.searchParams.set("reviewLensFeedback",i.id),window.history.replaceState({},"",re)}const d=z(i.selector);if(!d){w(void 0);return}(N=d.scrollIntoView)==null||N.call(d,{behavior:"smooth",block:"center",inline:"center"}),window.requestAnimationFrame(()=>{w(D(d))})}function Xe(i){if(T.length===0)return;const p=k?T.findIndex(N=>N.id===k.id):-1,d=p<0?i>0?0:T.length-1:(p+i+T.length)%T.length;H(T[d])}async function Qe(){if(!y||!F.trim()||!g||!P)return;let i=await f({projectKey:m.projectKey,contentId:m.contentId,normalizedPath:A,originalUrl:m.currentUrl??window.location.href,selector:y.selector,selectorStrategy:y.selectorStrategy,elementFingerprint:y.fingerprint,createdCssSnapshot:y.cssSnapshot,comment:F.trim(),status:"open",severity:U,category:_e,assigneeEmail:je.trim()||void 0,viewportWidth:window.innerWidth,viewportHeight:window.innerHeight,viewportPreset:We,screenshotUrl:void 0,screenshotThumbnailUrl:void 0,authorEmail:g.email});if(m.captureScreenshot)try{const p=await m.captureScreenshot(y),d=await o(i.id,{type:"screenshot",data:p,createdBy:g.email});i=await v(i.id,{attachments:[d],screenshotUrl:d.url,screenshotThumbnailUrl:d.thumbnailUrl})}catch{}if(B(""),Ue(""),E(void 0),w(void 0),_("feedback"),q(i),a){const p=new URL(window.location.href);p.searchParams.set("reviewLensFeedback",i.id),window.history.replaceState({},"",p)}}async function hr(i,p){const d=new Date().toISOString(),N=p==="resolved"?{status:p,resolvedAt:d,resolvedBy:g==null?void 0:g.email}:{status:p},re=await v(i.id,N);q(re)}async function Ze(i){const p=z(i.selector);if(!p||!g)return;const d=D(p),N=await v(i.id,{status:"fixed",fixedCssSnapshot:d.cssSnapshot,fixedAt:new Date().toISOString(),fixedBy:g.email});q(N)}async function ur(i){if(!le.trim()||!g||!Ve)return;const p=await S({feedbackId:i.id,body:le.trim(),authorEmail:g.email});Ge(d=>({...d,[i.id]:[...d[i.id]??[],p]})),de("")}return n.jsxs("div",{className:"review-lens-root","data-review-lens-ui":!0,children:[$&&W?n.jsx(Ut,{target:W,locked:!!y}):null,$&&y&&C&&rr?n.jsx(Wt,{from:y,to:C}):null,$?n.jsxs(n.Fragment,{children:[n.jsx(Dt,{feedback:T,selectedFeedback:k,onSelect:H}),n.jsx(Ht,{feedback:T,selectedFeedback:k,onSelect:H})]}):null,n.jsxs("aside",{className:`review-lens-panel review-lens-panel--${r}`,"data-review-lens-ui":!0,children:[n.jsxs("header",{className:"review-lens-panel__header",children:[n.jsxs("div",{className:"review-lens-brand",children:[n.jsx(Ae,{className:"review-lens-brand__mark"}),n.jsxs("div",{children:[n.jsx("p",{className:"review-lens-kicker",children:"Review Lens"}),n.jsx("h2",{children:M==="summary"?"Summary":M==="feedback"?"Feedback":y?"Element locked":"Inspecting"})]})]}),n.jsx("button",{type:"button",onClick:()=>t==null?void 0:t(!1),children:"Close"})]}),n.jsxs("div",{className:"review-lens-panel__body",children:[n.jsxs("div",{className:"review-lens-mode-switch",role:"tablist","aria-label":"Review Lens mode",children:[n.jsx("button",{type:"button",role:"tab","aria-selected":M==="review",onClick:()=>_("review"),children:"Review"}),n.jsxs("button",{type:"button",role:"tab","aria-selected":M==="feedback",onClick:()=>_("feedback"),children:["Feedback ",n.jsx("span",{children:T.length})]}),n.jsx("button",{type:"button",role:"tab","aria-selected":M==="summary",onClick:()=>_("summary"),children:"Summary"})]}),M==="review"?n.jsxs("div",{className:"review-lens-review-pane",role:"tabpanel",children:[n.jsxs("div",{className:"review-lens-inspection",children:[$?null:n.jsx("p",{children:"Authenticate with Google to inspect this page."}),$&&W?n.jsxs(n.Fragment,{children:[n.jsx(Ot,{target:W}),n.jsx(Me,{title:"Accessibility",items:Kt(W)}),n.jsx(Me,{title:"Design tokens",items:Jt(W.cssSnapshot,m.designTokens)})]}):null,$&&!W?n.jsx("p",{children:"Move over the app to inspect."}):null]}),or?n.jsx("div",{className:"review-lens-composer-panel",children:n.jsxs("form",{className:"review-lens-feedback-form",onSubmit:i=>{i.preventDefault(),Qe()},children:[n.jsx("label",{htmlFor:"review-lens-comment",children:"New feedback"}),n.jsx("textarea",{ref:te,id:"review-lens-comment",value:F,disabled:!P,onChange:i=>B(i.target.value),onKeyDown:i=>{i.key==="Enter"&&i.metaKey&&(i.preventDefault(),Qe())},placeholder:P?"Describe the UX issue...":"You do not have permission to comment."}),n.jsxs("div",{className:"review-lens-form-grid",children:[n.jsxs("label",{children:["Severity",n.jsx("select",{value:U,onChange:i=>Rt(i.target.value),disabled:!P,children:Ie.map(i=>n.jsx("option",{value:i,children:O[i]},i))})]}),n.jsxs("label",{children:["Type",n.jsx("select",{value:_e,onChange:i=>er(i.target.value),disabled:!P,children:Te.map(i=>n.jsx("option",{value:i,children:K[i]},i))})]}),n.jsxs("label",{children:["Assignee",n.jsx("input",{value:je,onChange:i=>Ue(i.target.value),disabled:!P,placeholder:"optional@email.com"})]}),n.jsxs("label",{children:["Viewport",n.jsx("select",{value:We,onChange:i=>tr(i.target.value),disabled:!P,children:l.map(i=>n.jsx("option",{value:i.value,children:i.label},i.value))})]})]}),P?n.jsxs("p",{className:"review-lens-feedback-form__hint",children:["Press ",n.jsx("kbd",{children:"Command"})," + ",n.jsx("kbd",{children:"Enter"})," to submit.",lr?" Screenshot capture runs after save.":""]}):null,n.jsx("div",{className:"review-lens-actions",children:n.jsx("button",{type:"submit",disabled:!F.trim()||!P,children:"Save feedback"})})]})}):null]}):null,M==="feedback"?n.jsxs("div",{className:"review-lens-comments",children:[n.jsx($t,{open:nr,activeCount:cr,statusFilter:V,severityFilter:Y,categoryFilter:X,assigneeFilter:Q,viewportFilter:Z,assignees:dr,responsivePresets:l,onStatusChange:ze,onSeverityChange:He,onCategoryChange:Oe,onAssigneeChange:Ke,onViewportChange:Je,onToggle:()=>sr(i=>!i),onClear:()=>{ze("all"),He("all"),Oe("all"),Ke("all"),Je("all")}}),n.jsxs("div",{className:"review-lens-list-panel",children:[n.jsxs("div",{className:"review-lens-comments__header",children:[n.jsx("h3",{children:"All feedback"}),n.jsx("span",{children:T.length})]}),n.jsxs("div",{className:"review-lens-comments__list",children:[T.length===0?n.jsx("p",{children:"No feedback for this view."}):null,T.map(i=>n.jsx(Bt,{item:i,selected:(k==null?void 0:k.id)===i.id,onSelect:H},i.id))]})]}),k?n.jsxs("div",{className:"review-lens-selected-panel",children:[n.jsx("div",{className:"review-lens-selected-panel__label",children:"Selected feedback"}),n.jsx(Pt,{item:k,messages:ir[k.id]??[],messageDraft:le,canReply:Ve,canUpdate:Ye,canAssign:ar,syncSelectionToUrl:a,onMessageDraftChange:de,onSubmitMessage:()=>void ur(k),onStatusChange:i=>void hr(k,i),onAssigneeChange:i=>void v(k.id,{assigneeEmail:i.trim()||void 0}).then(q),onMarkFixed:()=>void Ze(k)},k.id)]}):n.jsxs("div",{className:"review-lens-selected-panel review-lens-selected-panel--empty",children:[n.jsx("div",{className:"review-lens-selected-panel__label",children:"Selected feedback"}),n.jsx("p",{children:"Select a feedback item above to review status, assignment, drift, and replies."})]})]}):null,M==="summary"?n.jsx(jt,{feedback:b}):null]})]})]})}function $t({open:e,activeCount:t,statusFilter:r,severityFilter:s,categoryFilter:a,assigneeFilter:l,viewportFilter:u,assignees:m,responsivePresets:g,onStatusChange:b,onSeverityChange:A,onCategoryChange:x,onAssigneeChange:f,onViewportChange:v,onToggle:c,onClear:S}){return n.jsxs("div",{className:"review-lens-filter-shell",children:[n.jsxs("div",{className:"review-lens-filter-bar",children:[n.jsxs("button",{type:"button","aria-expanded":e,onClick:c,children:["Filters",t>0?n.jsx("span",{children:t}):null]}),t>0?n.jsx("button",{type:"button",onClick:S,children:"Clear"}):null]}),e?n.jsxs("div",{className:"review-lens-filters",children:[n.jsxs("label",{children:["Status",n.jsxs("select",{"aria-label":"Filter status",value:r,onChange:o=>b(o.target.value),children:[n.jsx("option",{value:"all",children:"All statuses"}),Fe.map(o=>n.jsx("option",{value:o,children:R[o]},o))]})]}),n.jsxs("label",{children:["Priority",n.jsxs("select",{"aria-label":"Filter severity",value:s,onChange:o=>A(o.target.value),children:[n.jsx("option",{value:"all",children:"All priorities"}),Ie.map(o=>n.jsx("option",{value:o,children:O[o]},o))]})]}),n.jsxs("label",{children:["Type",n.jsxs("select",{"aria-label":"Filter type",value:a,onChange:o=>x(o.target.value),children:[n.jsx("option",{value:"all",children:"All types"}),Te.map(o=>n.jsx("option",{value:o,children:K[o]},o))]})]}),n.jsxs("label",{children:["Assignee",n.jsxs("select",{"aria-label":"Filter assignee",value:l,onChange:o=>f(o.target.value),children:[n.jsx("option",{value:"all",children:"All assignees"}),m.map(o=>n.jsx("option",{value:o,children:o},o))]})]}),n.jsxs("label",{children:["Viewport",n.jsxs("select",{"aria-label":"Filter viewport",value:u,onChange:o=>v(o.target.value),children:[n.jsx("option",{value:"all",children:"All viewports"}),g.map(o=>n.jsx("option",{value:o.value,children:o.label},o.value))]})]})]}):null]})}function Bt({item:e,selected:t,onSelect:r}){const s=$e(e);return n.jsxs("article",{tabIndex:0,className:["review-lens-comment",`review-lens-comment--${e.severity}`,t?"review-lens-comment--selected":""].filter(Boolean).join(" "),onClick:()=>r(e),onKeyDown:a=>{(a.key==="Enter"||a.key===" ")&&(a.preventDefault(),r(e))},children:[n.jsxs("div",{className:"review-lens-comment__header",children:[n.jsx("span",{children:R[e.status]}),n.jsx("strong",{children:O[e.severity]})]}),n.jsxs("div",{className:"review-lens-comment__content",children:[n.jsx("p",{children:e.comment}),n.jsxs("span",{children:[e.authorEmail,e.assigneeEmail?` -> ${e.assigneeEmail}`:""]})]}),n.jsxs("div",{className:"review-lens-tags",children:[n.jsx("span",{children:K[e.category]}),n.jsx("span",{children:e.viewportPreset}),n.jsx("span",{children:s.label})]})]})}function Pt({item:e,messages:t,messageDraft:r,canReply:s,canUpdate:a,canAssign:l,syncSelectionToUrl:u,onMessageDraftChange:m,onSubmitMessage:g,onStatusChange:b,onAssigneeChange:A,onMarkFixed:x}){const[f,v]=h.useState(!1),c=$e(e);function S(){const o=new URL(window.location.href);o.searchParams.set("reviewLensFeedback",e.id),_t(o.toString()).then(C=>{C&&(v(!0),setTimeout(()=>v(!1),2e3))})}return n.jsxs("section",{className:"review-lens-detail","aria-label":"Selected feedback detail",children:[n.jsxs("div",{className:"review-lens-detail__header",children:[n.jsxs("h3",{children:[K[e.category]," feedback"]}),n.jsxs("div",{className:"review-lens-detail__header-actions",children:[u?n.jsx("button",{type:"button",className:"review-lens-copy-link",onClick:S,"aria-label":"Copy link to this feedback",children:f?"Copied!":"Copy link"}):null,n.jsx("strong",{children:O[e.severity]})]})]}),n.jsx("blockquote",{children:e.comment}),n.jsxs("dl",{className:"review-lens-detail-meta",children:[n.jsxs("div",{children:[n.jsx("dt",{children:"Target"}),n.jsx("dd",{children:c.label})]}),n.jsxs("div",{children:[n.jsx("dt",{children:"Viewport"}),n.jsx("dd",{children:e.viewportPreset})]}),e.screenshotUrl?n.jsxs("div",{children:[n.jsx("dt",{children:"Evidence"}),n.jsx("dd",{children:n.jsx("a",{href:e.screenshotUrl,target:"_blank",rel:"noreferrer",children:"Screenshot"})})]}):null]}),n.jsxs("div",{className:"review-lens-form-grid",children:[n.jsxs("label",{children:["Status",n.jsx("select",{value:e.status,disabled:!a,onChange:o=>b(o.target.value),children:Fe.map(o=>n.jsx("option",{value:o,children:R[o]},o))})]}),n.jsxs("label",{children:["Assignee",n.jsx("input",{defaultValue:e.assigneeEmail??"",disabled:!l,onBlur:o=>A(o.target.value),placeholder:"optional@email.com"})]})]}),n.jsxs("div",{className:"review-lens-status-actions",children:[n.jsx("button",{type:"button",className:"review-lens-button-secondary",disabled:!a,onClick:x,children:"Mark fixed"}),n.jsx("button",{type:"button",className:"review-lens-button-primary",disabled:!a,onClick:()=>b("resolved"),children:"Resolve"})]}),n.jsxs("div",{className:"review-lens-thread",children:[n.jsxs("div",{className:"review-lens-thread__header",children:[n.jsx("h3",{children:"Thread"}),n.jsx("span",{children:t.length})]}),t.length===0?n.jsx("p",{children:"No replies yet."}):null,t.map(o=>n.jsxs("div",{className:"review-lens-thread__message",children:[n.jsx("p",{children:o.body}),n.jsx("span",{children:o.authorEmail})]},o.id)),n.jsx("textarea",{"aria-label":"Reply",value:r,disabled:!s,onChange:o=>m(o.target.value),placeholder:s?"Reply...":"You do not have permission to reply."}),n.jsx("div",{className:"review-lens-actions",children:n.jsx("button",{type:"button",disabled:!r.trim()||!s,onClick:g,children:"Reply"})})]})]})}async function _t(e){var r;if((r=navigator.clipboard)!=null&&r.writeText)return await navigator.clipboard.writeText(e),!0;const t=document.createElement("textarea");t.value=e,t.setAttribute("readonly",""),t.style.position="fixed",t.style.opacity="0",document.body.append(t),t.select();try{return document.execCommand("copy")}finally{t.remove()}}function jt({feedback:e}){return n.jsxs("div",{className:"review-lens-summary",role:"tabpanel",children:[n.jsx(J,{title:"Status",values:G(e,t=>R[t.status])}),n.jsx(J,{title:"Severity",values:G(e,t=>O[t.severity])}),n.jsx(J,{title:"Type",values:G(e,t=>K[t.category])}),n.jsx(J,{title:"Assignee",values:G(e,t=>t.assigneeEmail??"Unassigned")}),n.jsx(J,{title:"Viewport",values:G(e,t=>t.viewportPreset)})]})}function J({title:e,values:t}){return n.jsxs("section",{children:[n.jsx("h3",{children:e}),n.jsx("dl",{children:t.map(([r,s])=>n.jsxs("div",{children:[n.jsx("dt",{children:r}),n.jsx("dd",{children:s})]},r))})]})}function Ut({target:e,locked:t}){const r=Zt(e),s=qt(e.fingerprint);return n.jsxs("div",{className:t?"review-lens-highlight review-lens-highlight--locked":"review-lens-highlight",style:{top:r.margin.top,left:r.margin.left,width:r.margin.width,height:r.margin.height},children:[n.jsx("div",{className:"review-lens-highlight__border",style:{top:r.border.top-r.margin.top,left:r.border.left-r.margin.left,width:r.border.width,height:r.border.height}}),n.jsx("div",{className:"review-lens-highlight__padding",style:{top:r.padding.top-r.margin.top,left:r.padding.left-r.margin.left,width:r.padding.width,height:r.padding.height}}),n.jsx("div",{className:"review-lens-highlight__content",style:{top:r.content.top-r.margin.top,left:r.content.left-r.margin.left,width:r.content.width,height:r.content.height}}),n.jsxs("div",{className:"review-lens-highlight__label",children:[n.jsx("strong",{children:s}),n.jsxs("span",{children:[Math.round(e.rect.width)," x ",Math.round(e.rect.height)]})]})]})}function Wt({from:e,to:t}){const r=Vt(e.rect,t.rect);return r.length===0?null:n.jsx(n.Fragment,{children:r.map(s=>n.jsx("div",{className:`review-lens-distance review-lens-distance--${s.axis}`,style:{top:s.top,left:s.left,width:s.width,height:s.height},children:n.jsx("span",{children:s.label})},s.key))})}function Dt({feedback:e,selectedFeedback:t,onSelect:r}){return n.jsx(n.Fragment,{children:e.map(s=>n.jsx(zt,{feedback:s,selected:(t==null?void 0:t.id)===s.id,onSelect:r},s.id))})}function zt({feedback:e,selected:t,onSelect:r}){const s=h.useRef(null);return h.useLayoutEffect(()=>{let a=0;const l=()=>{a=0;const m=s.current,g=z(e.selector),b=g==null?void 0:g.getBoundingClientRect();!m||!b||(m.style.top=`${b.top}px`,m.style.left=`${b.right}px`,m.hidden=b.bottom<0||b.top>window.innerHeight)},u=()=>{a||(a=window.requestAnimationFrame(l))};return l(),window.addEventListener("scroll",u,!0),window.addEventListener("resize",u),()=>{a&&window.cancelAnimationFrame(a),window.removeEventListener("scroll",u,!0),window.removeEventListener("resize",u)}},[e.selector]),n.jsx("button",{ref:s,type:"button",className:t?"review-lens-marker review-lens-marker--selected":"review-lens-marker",onClick:()=>r(e),"aria-label":`Open feedback from ${e.authorEmail}`})}function Ht({feedback:e,selectedFeedback:t,onSelect:r}){const s=e.map(a=>{const l=z(a.selector),u=l==null?void 0:l.getBoundingClientRect(),m=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight,window.innerHeight);return!u||m<=0?null:{item:a,top:Math.min(100,Math.max(0,(u.top+window.scrollY)/m*100))}}).filter(a=>a!==null);return s.length===0?null:n.jsx("div",{className:"review-lens-minimap","data-review-lens-ui":!0,"aria-label":"Feedback map",children:s.map(a=>n.jsx("button",{type:"button",className:(t==null?void 0:t.id)===a.item.id?"review-lens-minimap__point review-lens-minimap__point--selected":"review-lens-minimap__point",style:{top:`${a.top}%`},onClick:()=>r(a.item),"aria-label":`Jump to feedback from ${a.item.authorEmail}`},a.item.id))})}function Ot({target:e}){const t=[["Selector",e.selector],["Size",`${e.cssSnapshot.width} x ${e.cssSnapshot.height}`],["Margin",e.cssSnapshot.margin],["Padding",e.cssSnapshot.padding],["Border",e.cssSnapshot.border],["Radius",e.cssSnapshot.borderRadius],["Font",`${e.cssSnapshot.fontSize} / ${e.cssSnapshot.lineHeight}`],["Family",e.cssSnapshot.fontFamily],["Color",e.cssSnapshot.color],["Background",e.cssSnapshot.backgroundColor]];return n.jsx("dl",{className:"review-lens-metrics",children:t.map(([r,s])=>n.jsxs("div",{children:[n.jsx("dt",{children:r}),n.jsx("dd",{children:s})]},r))})}function Me({title:e,items:t}){return n.jsxs("section",{className:"review-lens-insights",children:[n.jsx("h3",{children:e}),t.length===0?n.jsx("p",{children:"No issues detected."}):null,t.length>0?n.jsx("ul",{children:t.map(r=>n.jsx("li",{children:r},r))}):null]})}function z(e){try{return document.querySelector(e)}catch{return null}}function $e(e){const t=z(e.selector);if(!t)return{label:"Target missing",level:"warning"};const r=D(t);return r.fingerprint.tagName!==e.elementFingerprint.tagName?{label:"Element changed",level:"warning"}:Math.abs(r.fingerprint.width-e.elementFingerprint.width)>2||Math.abs(r.fingerprint.height-e.elementFingerprint.height)>2?{label:"Size changed",level:"warning"}:r.cssSnapshot.fontSize!==e.createdCssSnapshot.fontSize||r.cssSnapshot.color!==e.createdCssSnapshot.color||r.cssSnapshot.padding!==e.createdCssSnapshot.padding?{label:"Style changed",level:"warning"}:{label:"Target unchanged",level:"ok"}}function Kt(e){var g;const t=z(e.selector);if(!t)return["Selected element is no longer available."];const r=[],s=t.tagName.toLowerCase(),a=t.getAttribute("role"),l=["button","a","input","select","textarea"].includes(s)||a==="button"||a==="link",u=t.getAttribute("aria-label")||t.getAttribute("title")||((g=t.textContent)==null?void 0:g.trim());l&&!u&&r.push("Interactive element has no accessible name."),l&&(e.rect.width<44||e.rect.height<44)&&r.push("Tap target is smaller than 44 x 44."),s==="img"&&!t.getAttribute("alt")&&r.push("Image is missing alt text.");const m=/^h[1-6]$/.test(s)?Number(s.slice(1)):0;return m>1&&!document.querySelector(`h${m-1}`)&&r.push("Heading may skip the previous level."),Xt(e.cssSnapshot.color,e.cssSnapshot.backgroundColor)&&r.push("Text contrast may be low."),r}function Jt(e,t={}){const r=[];return j("Padding",e.padding,t.spacing,r,{allowComposite:!0}),j("Margin",e.margin,t.spacing,r,{allowComposite:!0}),j("Font size",e.fontSize,t.fontSize,r),j("Line height",e.lineHeight,t.lineHeight,r),j("Text color",e.color,t.color,r),j("Background",e.backgroundColor,t.color,r),j("Radius",e.borderRadius,t.radius,r,{allowComposite:!0}),r}function j(e,t,r,s,a={}){!r||r.length===0||!t||Gt(t,r,a)||s.push(`${e} ${t} is outside configured tokens.`)}function Gt(e,t,r={}){if(t.includes(e))return!0;if(!r.allowComposite)return!1;const s=e.trim().split(/\s+/);return s.length>1&&s.every(a=>t.includes(a))}function qt(e){const t=e.id?`#${e.id}`:"",r=e.className?`.${e.className.split(/\s+/).filter(Boolean).slice(0,2).join(".")}`:"",s=e.ariaLabel?`[aria-label="${e.ariaLabel}"]`:"";return`${e.tagName}${t}${r}${s}`||e.tagName}function Vt(e,t){const r=[],s=(Math.max(e.left,t.left)+Math.min(e.right,t.right))/2,a=(Math.max(e.top,t.top)+Math.min(e.bottom,t.bottom))/2;if(e.right<=t.left||t.right<=e.left){const l=e.right<=t.left?e.right:t.right,u=e.right<=t.left?t.left:e.left;r.push({key:"horizontal",axis:"horizontal",top:Be(a,0,window.innerHeight),left:l,width:Math.max(u-l,1),height:1,label:`${Math.round(u-l)}px`})}if(e.bottom<=t.top||t.bottom<=e.top){const l=e.bottom<=t.top?e.bottom:t.bottom,u=e.bottom<=t.top?t.top:e.top;r.push({key:"vertical",axis:"vertical",top:l,left:Be(s,0,window.innerWidth),width:1,height:Math.max(u-l,1),label:`${Math.round(u-l)}px`})}return r}function Be(e,t,r){return Math.min(Math.max(e,t),r)}function G(e,t){const r=new Map;for(const s of e){const a=t(s);r.set(a,(r.get(a)??0)+1)}return Array.from(r.entries()).sort((s,a)=>a[1]-s[1]||s[0].localeCompare(a[0]))}function Yt(e){return e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement||e instanceof HTMLSelectElement||e instanceof HTMLElement&&e.isContentEditable}function Xt(e,t){const r=Pe(e),s=Pe(t);return!r||!s||s.alpha===0?!1:Qt(r,s)<4.5}function Pe(e){const t=e.match(/rgba?\(([^)]+)\)/);if(!t)return null;const[r,s,a,l="1"]=t[1].split(",").map(u=>u.trim());return{red:Number(r),green:Number(s),blue:Number(a),alpha:Number(l)}}function Qt(e,t){const r=Math.max(ee(e),ee(t)),s=Math.min(ee(e),ee(t));return(r+.05)/(s+.05)}function ee(e){const t=[e.red,e.green,e.blue].map(r=>{const s=r/255;return s<=.03928?s/12.92:((s+.055)/1.055)**2.4});return t[0]*.2126+t[1]*.7152+t[2]*.0722}function Zt(e){const t={top:I(e.cssSnapshot.marginTop),right:I(e.cssSnapshot.marginRight),bottom:I(e.cssSnapshot.marginBottom),left:I(e.cssSnapshot.marginLeft)},r={top:I(e.cssSnapshot.borderTopWidth),right:I(e.cssSnapshot.borderRightWidth),bottom:I(e.cssSnapshot.borderBottomWidth),left:I(e.cssSnapshot.borderLeftWidth)},s={top:I(e.cssSnapshot.paddingTop),right:I(e.cssSnapshot.paddingRight),bottom:I(e.cssSnapshot.paddingBottom),left:I(e.cssSnapshot.paddingLeft)},a={top:e.rect.top,left:e.rect.left,width:Math.max(e.rect.width,0),height:Math.max(e.rect.height,0)},l={top:a.top-t.top,left:a.left-t.left,width:a.width+t.left+t.right,height:a.height+t.top+t.bottom},u={top:a.top+r.top,left:a.left+r.left,width:Math.max(a.width-r.left-r.right,0),height:Math.max(a.height-r.top-r.bottom,0)},m={top:u.top+s.top,left:u.left+s.left,width:Math.max(u.width-s.left-s.right,0),height:Math.max(u.height-s.top-s.bottom,0)};return{margin:l,border:a,padding:u,content:m}}function I(e){const t=Number.parseFloat(e);return Number.isFinite(t)?t:0}L.ReviewLensLogo=Ae,L.ReviewLensOverlay=Mt,L.ReviewLensProvider=bt,L.buildElementTarget=D,L.createGoogleSheetsAdapter=fe,L.normalizeReviewUrl=Se,L.useReviewLens=Ee,Object.defineProperty(L,Symbol.toStringTag,{value:"Module"})}));
@@ -7,6 +7,7 @@ type GoogleSheetsAdapterConfig = {
7
7
  messagesSheetName?: string;
8
8
  usersSheetName?: string;
9
9
  projectsSheetName?: string;
10
+ enableEmailNotifications?: boolean;
10
11
  };
11
12
  type TokenClient = {
12
13
  requestAccessToken(options?: {
@@ -23,6 +24,7 @@ declare global {
23
24
  scope: string;
24
25
  callback: (response: {
25
26
  access_token?: string;
27
+ expires_in?: number;
26
28
  error?: string;
27
29
  }) => void;
28
30
  }): TokenClient;
package/dist/styles.css CHANGED
@@ -1 +1 @@
1
- .review-lens-root{color:#171717;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;top:0;right:0;bottom:0;left:0;pointer-events:none;position:fixed;z-index:2147483647}.review-lens-highlight{background:#f9731633;box-shadow:0 0 0 9999px #0f172a14;box-sizing:border-box;pointer-events:none;position:fixed}.review-lens-highlight--locked{box-shadow:0 0 0 9999px #0f172a1f}.review-lens-highlight__border,.review-lens-highlight__padding,.review-lens-highlight__content{box-sizing:border-box;position:absolute}.review-lens-highlight__border{border:2px solid #facc15;background:#facc1538}.review-lens-highlight__padding{border:2px solid #22c55e;background:#22c55e38}.review-lens-highlight__content{border:2px solid #2563eb;background:#2563eb29}.review-lens-highlight--locked .review-lens-highlight__content{border-color:#f97316}.review-lens-highlight__label{background:#171717;border-radius:4px;color:#fff;display:grid;gap:3px;font-size:11px;left:0;line-height:1.1;max-width:240px;padding:6px 7px;position:absolute;top:-38px;white-space:nowrap}.review-lens-highlight__label strong{font-size:11px;font-weight:800;overflow:hidden;text-overflow:ellipsis}.review-lens-highlight__label span{color:#d4d4d4;font-size:10px;font-weight:700}.review-lens-distance{background:#2563eb;pointer-events:none;position:fixed;z-index:1}.review-lens-distance:before,.review-lens-distance:after{background:#2563eb;content:"";position:absolute}.review-lens-distance--horizontal:before,.review-lens-distance--horizontal:after{height:9px;top:-4px;width:1px}.review-lens-distance--horizontal:before{left:0}.review-lens-distance--horizontal:after{right:0}.review-lens-distance--vertical:before,.review-lens-distance--vertical:after{height:1px;left:-4px;width:9px}.review-lens-distance--vertical:before{top:0}.review-lens-distance--vertical:after{bottom:0}.review-lens-distance span{background:#2563eb;border-radius:999px;color:#fff;font-size:10px;font-weight:800;left:50%;line-height:1;padding:4px 6px;position:absolute;top:50%;transform:translate(-50%,-50%);white-space:nowrap}.review-lens-panel{background:#fafafa;border:1px solid #d4d4d4;border-radius:8px;box-shadow:0 24px 70px #0f172a38;box-sizing:border-box;display:flex;flex-direction:column;height:min(680px,calc(100vh - 32px));min-height:min(520px,calc(100vh - 32px));overflow:hidden;padding:16px;pointer-events:auto;position:fixed;width:min(380px,calc(100vw - 32px))}.review-lens-panel--top-left{left:16px;top:16px}.review-lens-panel--top-right{right:16px;top:16px}.review-lens-panel--bottom-left{bottom:16px;left:16px}.review-lens-panel--bottom-right{bottom:16px;right:16px}.review-lens-panel__header{flex:0 0 auto;align-items:flex-start;display:flex;gap:16px;justify-content:space-between;margin-bottom:12px}.review-lens-panel__body{display:flex;flex:1 1 auto;flex-direction:column;gap:16px;min-height:0;overflow:hidden}.review-lens-panel h2,.review-lens-panel h3,.review-lens-panel p{margin:0}.review-lens-panel h2{font-size:18px;line-height:1.25}.review-lens-panel h3{font-size:14px;margin-top:18px}.review-lens-kicker{color:#525252;font-size:11px;font-weight:700;letter-spacing:0;text-transform:uppercase}.review-lens-panel button{background:#171717;border:1px solid #171717;border-radius:6px;color:#fff;cursor:pointer;font:inherit;font-size:13px;min-height:32px;padding:6px 10px;transition:transform .14s cubic-bezier(.23,1,.32,1)}.review-lens-panel button:active{transform:scale(.97)}.review-lens-panel button:disabled{cursor:not-allowed;opacity:.45}.review-lens-mode-switch{background:#eee;border:1px solid #d4d4d4;border-radius:7px;display:grid;flex:0 0 auto;gap:2px;grid-template-columns:repeat(3,minmax(0,1fr));padding:2px}.review-lens-mode-switch button{background:transparent;border:0;color:#525252;min-height:30px}.review-lens-mode-switch button[aria-selected=true]{background:#fff;box-shadow:0 1px 4px #0f172a1f;color:#171717}.review-lens-mode-switch span{color:#737373;font-size:11px}.review-lens-metrics{border:1px solid #e5e5e5;border-radius:8px;display:grid;gap:0;margin:0;overflow:hidden}.review-lens-metrics div{display:grid;grid-template-columns:96px minmax(0,1fr)}.review-lens-metrics dt,.review-lens-metrics dd{border-bottom:1px solid #e5e5e5;font-size:11px;margin:0;min-width:0;padding:6px 8px}.review-lens-metrics dt{background:#f5f5f5;color:#525252;font-weight:700}.review-lens-metrics dd{overflow-wrap:anywhere}.review-lens-inspection{display:grid;flex:0 0 auto;gap:16px}.review-lens-review-pane{display:flex;flex:1 1 auto;flex-direction:column;gap:16px;min-height:0;overflow:auto;padding-right:4px}.review-lens-feedback-form{display:grid;flex:1 1 auto;gap:8px;grid-template-rows:auto minmax(96px,auto) auto auto auto;margin-top:0;min-height:0}.review-lens-composer-panel{background:linear-gradient(#fafafa,#fafafa00) top / 100% 12px no-repeat,#fff;border:1px solid #d4d4d4;border-radius:8px;box-shadow:inset 0 8px 12px #0f172a0a;display:block;flex:0 0 auto;max-height:min(360px,44vh);min-height:0;overflow:auto;padding:10px;position:relative}.review-lens-composer-panel:before{background:#d4d4d4;border-radius:999px;content:"";display:block;height:3px;margin:0 auto 8px;width:44px}.review-lens-feedback-form label,.review-lens-form-grid label{display:grid;gap:4px;font-size:13px;font-weight:700}.review-lens-feedback-form textarea,.review-lens-detail textarea,.review-lens-panel input,.review-lens-panel select{border:1px solid #d4d4d4;border-radius:8px;box-sizing:border-box;font:inherit;padding:10px;width:100%}.review-lens-feedback-form textarea,.review-lens-detail textarea{flex:1 1 auto;min-height:96px;resize:vertical}.review-lens-panel input,.review-lens-panel select{background:#fff;color:#171717;font-size:12px;min-height:34px;padding:6px 8px}.review-lens-form-grid{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr))}.review-lens-feedback-form__hint{color:#737373;font-size:12px;line-height:1.4}.review-lens-feedback-form__hint kbd{background:#f5f5f5;border:1px solid #d4d4d4;border-radius:4px;color:#404040;font-family:inherit;font-size:11px;padding:1px 4px}.review-lens-actions{display:flex;gap:8px;justify-content:flex-end}.review-lens-comments{display:grid;flex:1 1 auto;gap:10px;grid-template-rows:auto minmax(108px,1.35fr) minmax(200px,.9fr);min-height:0;overflow:hidden;padding-right:4px}.review-lens-filter-shell{display:grid;gap:8px}.review-lens-filter-bar{align-items:center;display:flex;gap:8px;justify-content:space-between}.review-lens-filter-bar button{align-items:center;background:#fff;border-color:#d4d4d4;color:#404040;display:inline-flex;gap:6px;min-height:30px;padding:5px 9px}.review-lens-filter-bar button:first-child{font-weight:700}.review-lens-filter-bar button[aria-expanded=true]{background:#171717;border-color:#171717;color:#fff}.review-lens-filter-bar span{align-items:center;background:#f97316;border-radius:999px;color:#fff;display:inline-flex;font-size:10px;height:17px;justify-content:center;min-width:17px;padding:0 5px}.review-lens-filters{background:#f5f5f5;border:1px solid #e5e5e5;border-radius:8px;display:grid;flex:0 0 auto;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));padding:8px}.review-lens-filters label{color:#525252;display:grid;font-size:10px;font-weight:800;gap:4px;letter-spacing:0;text-transform:uppercase}.review-lens-filters label:last-child{grid-column:1 / -1}.review-lens-list-panel{background:#fff;border:1px solid #d4d4d4;border-radius:10px;display:grid;gap:8px;grid-template-rows:auto minmax(0,1fr);min-height:0;overflow:hidden;padding:10px}.review-lens-comments__header{align-items:center;color:#171717;display:flex;gap:8px;justify-content:space-between}.review-lens-comments__header h3{color:#525252;font-size:11px;font-weight:800;letter-spacing:0;margin:0;text-transform:uppercase}.review-lens-comments__header span{align-items:center;background:#e5e5e5;border-radius:999px;color:#404040;display:inline-flex;font-size:11px;justify-content:center;min-width:22px;padding:2px 7px}.review-lens-comments__list{align-content:start;display:grid;gap:8px;min-height:0;overflow:auto;padding-right:2px}.review-lens-selected-panel{background:#171717;border-radius:10px;box-shadow:0 -10px 28px #0f172a2e;display:grid;gap:0;grid-template-rows:auto minmax(0,1fr);min-height:0;overflow:hidden;padding:4px}.review-lens-selected-panel__label{color:#d4d4d4;font-size:10px;font-weight:800;letter-spacing:0;padding:7px 8px 8px;text-transform:uppercase}.review-lens-selected-panel--empty{color:#d4d4d4;font-size:12px;line-height:1.4;padding:4px 12px 12px}.review-lens-selected-panel--empty p{color:#f5f5f5}.review-lens-detail{background:linear-gradient(180deg,#fff,#f8fafc);border:0;border-radius:8px;box-shadow:none;box-sizing:border-box;display:grid;gap:10px;min-height:0;overflow:auto;padding:12px}.review-lens-detail__header{align-items:start;display:flex;gap:12px;justify-content:space-between}.review-lens-detail__header h3{color:#171717;font-size:13px;line-height:1.2;margin:0;text-transform:capitalize}.review-lens-detail__header strong,.review-lens-comment__header strong{background:#fee2e2;border:1px solid #fecaca;border-radius:999px;color:#991b1b;flex:0 0 auto;font-size:10px;line-height:1;padding:5px 7px;text-transform:uppercase}.review-lens-detail blockquote{background:#f8fafc;border:1px solid #dbeafe;border-left:3px solid #2563eb;border-radius:6px;color:#171717;font-size:15px;font-weight:700;line-height:1.35;margin:0;padding:9px 10px}.review-lens-detail-meta{background:#fff;border:1px solid #e5e5e5;border-radius:6px;display:grid;gap:0;margin:0;overflow:hidden}.review-lens-detail-meta div{display:grid;gap:2px;grid-template-columns:72px minmax(0,1fr);padding:6px 8px}.review-lens-detail-meta div+div{border-top:1px solid #eeeeee}.review-lens-detail-meta dt,.review-lens-detail-meta dd{font-size:11px;line-height:1.25;margin:0;min-width:0}.review-lens-detail-meta dt{color:#737373;font-weight:800;text-transform:uppercase}.review-lens-detail-meta dd{color:#262626;overflow-wrap:anywhere}.review-lens-detail a{color:#2563eb;font-weight:700}.review-lens-comment{background:#fff;border:1px solid #e5e5e5;border-radius:8px;box-shadow:0 1px 2px #0f172a0a;cursor:pointer;display:grid;gap:10px;padding:12px;position:relative;text-align:left}.review-lens-comment:before{background:#737373;border-radius:999px;bottom:12px;content:"";left:8px;position:absolute;top:12px;width:3px}.review-lens-comment--high:before{background:#dc2626}.review-lens-comment--medium:before{background:#f97316}.review-lens-comment--low:before{background:#16a34a}.review-lens-comment:focus-visible{outline:2px solid #2563eb;outline-offset:2px}.review-lens-comment--selected{background:#eff6ff;border-color:#2563eb;box-shadow:0 8px 18px #2563eb1f}.review-lens-comment__header{align-items:center;display:flex;gap:8px;justify-content:space-between;padding-left:8px}.review-lens-comment__header span{color:#525252;font-size:10px;font-weight:800;letter-spacing:0;text-transform:uppercase}.review-lens-comment__content{display:grid;gap:6px;padding-left:8px}.review-lens-comment__content p{color:#171717;font-size:14px;line-height:1.35}.review-lens-comment__content span{color:#525252;font-size:11px;line-height:1}.review-lens-tags{display:flex;flex-wrap:wrap;gap:5px;padding-left:8px}.review-lens-tags span{background:#f5f5f5;border:1px solid #e5e5e5;border-radius:999px;color:#404040;font-size:10px;font-weight:700;line-height:1;padding:4px 6px}.review-lens-thread{border-top:1px solid #e5e5e5;display:grid;gap:6px;padding-top:10px}.review-lens-thread__header{align-items:center;display:flex;justify-content:space-between}.review-lens-thread h3{margin:0}.review-lens-thread__header span{color:#737373;font-size:11px;font-weight:800}.review-lens-detail .review-lens-thread textarea{min-height:48px}.review-lens-status-actions{align-items:center;display:grid;gap:8px;grid-template-columns:minmax(0,1fr) auto}.review-lens-status-actions button{min-height:30px}.review-lens-status-actions .review-lens-button-secondary{background:#fff;border-color:#d4d4d4;color:#404040}.review-lens-status-actions .review-lens-button-primary{background:#171717;border-color:#171717;color:#fff}.review-lens-thread__message{background:#f5f5f5;border-radius:6px;display:grid;gap:4px;padding:8px}.review-lens-thread__message p{font-size:12px;line-height:1.35}.review-lens-thread__message span{color:#737373;font-size:10px}.review-lens-comment__actions{display:flex;justify-content:flex-end}.review-lens-comment__actions button{background:transparent;border-color:#d4d4d4;color:#404040;min-height:28px;padding:4px 10px}.review-lens-comment__actions button:hover{background:#171717;border-color:#171717;color:#fff}.review-lens-marker{background:#f97316;border:2px solid #ffffff;border-radius:999px;box-shadow:0 8px 20px #0f172a3d;cursor:pointer;height:18px;pointer-events:auto;position:fixed;transform:translate(-50%,-50%);width:18px}.review-lens-marker--selected{background:#2563eb}.review-lens-minimap{background:#1717171f;border-radius:999px;bottom:24px;pointer-events:auto;position:fixed;right:8px;top:24px;width:10px}.review-lens-minimap__point{background:#f97316;border:2px solid #ffffff;border-radius:999px;box-shadow:0 2px 8px #0f172a38;height:14px;left:50%;min-height:0;padding:0;position:absolute;transform:translate(-50%,-50%);width:14px}.review-lens-minimap__point--selected{background:#2563eb}.review-lens-insights,.review-lens-summary{border:1px solid #e5e5e5;border-radius:8px;display:grid;gap:8px;padding:10px}.review-lens-insights h3,.review-lens-summary h3{margin:0}.review-lens-insights p,.review-lens-insights li,.review-lens-summary dt,.review-lens-summary dd{color:#404040;font-size:12px;line-height:1.35}.review-lens-insights ul{display:grid;gap:6px;margin:0;padding-left:18px}.review-lens-summary{align-content:start;flex:1 1 auto;min-height:0;overflow:auto;padding-right:4px}.review-lens-summary section{border-bottom:1px solid #e5e5e5;display:grid;gap:8px;padding-bottom:10px}.review-lens-summary section:last-child{border-bottom:0;padding-bottom:0}.review-lens-summary dl,.review-lens-summary div{display:grid;gap:6px;margin:0}.review-lens-summary div{grid-template-columns:minmax(0,1fr) auto}.review-lens-summary dd{font-weight:700;margin:0}
1
+ .review-lens-root{color:#171717;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;top:0;right:0;bottom:0;left:0;pointer-events:none;position:fixed;z-index:2147483647}.review-lens-highlight{background:#f9731633;box-shadow:0 0 0 9999px #0f172a14;box-sizing:border-box;pointer-events:none;position:fixed}.review-lens-highlight--locked{box-shadow:0 0 0 9999px #0f172a1f}.review-lens-highlight__border,.review-lens-highlight__padding,.review-lens-highlight__content{box-sizing:border-box;position:absolute}.review-lens-highlight__border{border:2px solid #facc15;background:#facc1538}.review-lens-highlight__padding{border:2px solid #22c55e;background:#22c55e38}.review-lens-highlight__content{border:2px solid #2563eb;background:#2563eb29}.review-lens-highlight--locked .review-lens-highlight__content{border-color:#f97316}.review-lens-highlight__label{background:#171717;border-radius:4px;color:#fff;display:grid;gap:3px;font-size:11px;left:0;line-height:1.1;max-width:240px;padding:6px 7px;position:absolute;top:-38px;white-space:nowrap}.review-lens-highlight__label strong{font-size:11px;font-weight:800;overflow:hidden;text-overflow:ellipsis}.review-lens-highlight__label span{color:#d4d4d4;font-size:10px;font-weight:700}.review-lens-distance{background:#2563eb;pointer-events:none;position:fixed;z-index:1}.review-lens-distance:before,.review-lens-distance:after{background:#2563eb;content:"";position:absolute}.review-lens-distance--horizontal:before,.review-lens-distance--horizontal:after{height:9px;top:-4px;width:1px}.review-lens-distance--horizontal:before{left:0}.review-lens-distance--horizontal:after{right:0}.review-lens-distance--vertical:before,.review-lens-distance--vertical:after{height:1px;left:-4px;width:9px}.review-lens-distance--vertical:before{top:0}.review-lens-distance--vertical:after{bottom:0}.review-lens-distance span{background:#2563eb;border-radius:999px;color:#fff;font-size:10px;font-weight:800;left:50%;line-height:1;padding:4px 6px;position:absolute;top:50%;transform:translate(-50%,-50%);white-space:nowrap}.review-lens-panel{background:#fafafa;border:1px solid #d4d4d4;border-radius:8px;box-shadow:0 24px 70px #0f172a38;box-sizing:border-box;display:flex;flex-direction:column;height:min(680px,calc(100vh - 32px));min-height:min(520px,calc(100vh - 32px));overflow:hidden;padding:16px;pointer-events:auto;position:fixed;width:min(380px,calc(100vw - 32px))}.review-lens-panel--top-left{left:16px;top:16px}.review-lens-panel--top-right{right:16px;top:16px}.review-lens-panel--bottom-left{bottom:16px;left:16px}.review-lens-panel--bottom-right{bottom:16px;right:16px}.review-lens-panel__header{flex:0 0 auto;align-items:flex-start;display:flex;gap:16px;justify-content:space-between;margin-bottom:12px}.review-lens-panel__body{display:flex;flex:1 1 auto;flex-direction:column;gap:16px;min-height:0;overflow:hidden}.review-lens-brand{align-items:center;display:flex;gap:10px;min-width:0}.review-lens-brand__mark{flex:0 0 auto;height:36px;width:36px}.review-lens-panel h2,.review-lens-panel h3,.review-lens-panel p{margin:0}.review-lens-panel h2{font-size:18px;line-height:1.25}.review-lens-panel h3{font-size:14px;margin-top:18px}.review-lens-kicker{color:#525252;font-size:11px;font-weight:700;letter-spacing:0;text-transform:uppercase}.review-lens-panel button{background:#171717;border:1px solid #171717;border-radius:6px;color:#fff;cursor:pointer;font:inherit;font-size:13px;min-height:32px;padding:6px 10px;transition:transform .14s cubic-bezier(.23,1,.32,1)}.review-lens-panel button:active{transform:scale(.97)}.review-lens-panel button:disabled{cursor:not-allowed;opacity:.45}.review-lens-mode-switch{background:#eee;border:1px solid #d4d4d4;border-radius:7px;display:grid;flex:0 0 auto;gap:2px;grid-template-columns:repeat(3,minmax(0,1fr));padding:2px}.review-lens-mode-switch button{background:transparent;border:0;color:#525252;min-height:30px}.review-lens-mode-switch button[aria-selected=true]{background:#fff;box-shadow:0 1px 4px #0f172a1f;color:#171717}.review-lens-mode-switch span{color:#737373;font-size:11px}.review-lens-metrics{border:1px solid #e5e5e5;border-radius:8px;display:grid;gap:0;margin:0;overflow:hidden}.review-lens-metrics div{display:grid;grid-template-columns:96px minmax(0,1fr)}.review-lens-metrics dt,.review-lens-metrics dd{border-bottom:1px solid #e5e5e5;font-size:11px;margin:0;min-width:0;padding:6px 8px}.review-lens-metrics dt{background:#f5f5f5;color:#525252;font-weight:700}.review-lens-metrics dd{overflow-wrap:anywhere}.review-lens-inspection{display:grid;flex:0 0 auto;gap:16px}.review-lens-review-pane{display:flex;flex:1 1 auto;flex-direction:column;gap:16px;min-height:0;overflow:auto;padding-right:4px}.review-lens-feedback-form{display:grid;flex:1 1 auto;gap:8px;grid-template-rows:auto minmax(96px,auto) auto auto auto;margin-top:0;min-height:0}.review-lens-composer-panel{background:linear-gradient(#fafafa,#fafafa00) top / 100% 12px no-repeat,#fff;border:1px solid #d4d4d4;border-radius:8px;box-shadow:inset 0 8px 12px #0f172a0a;display:block;flex:0 0 auto;max-height:min(360px,44vh);min-height:0;overflow:auto;padding:10px;position:relative}.review-lens-composer-panel:before{background:#d4d4d4;border-radius:999px;content:"";display:block;height:3px;margin:0 auto 8px;width:44px}.review-lens-feedback-form label,.review-lens-form-grid label{display:grid;gap:4px;font-size:13px;font-weight:700}.review-lens-feedback-form textarea,.review-lens-detail textarea,.review-lens-panel input,.review-lens-panel select{border:1px solid #d4d4d4;border-radius:8px;box-sizing:border-box;font:inherit;padding:10px;width:100%}.review-lens-feedback-form textarea,.review-lens-detail textarea{flex:1 1 auto;min-height:96px;resize:vertical}.review-lens-panel input,.review-lens-panel select{background:#fff;color:#171717;font-size:12px;min-height:34px;padding:6px 8px}.review-lens-form-grid{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr))}.review-lens-feedback-form__hint{color:#737373;font-size:12px;line-height:1.4}.review-lens-feedback-form__hint kbd{background:#f5f5f5;border:1px solid #d4d4d4;border-radius:4px;color:#404040;font-family:inherit;font-size:11px;padding:1px 4px}.review-lens-actions{display:flex;gap:8px;justify-content:flex-end}.review-lens-comments{display:grid;flex:1 1 auto;gap:10px;grid-template-rows:auto minmax(108px,1.35fr) minmax(200px,.9fr);min-height:0;overflow:hidden;padding-right:4px}.review-lens-filter-shell{display:grid;gap:8px}.review-lens-filter-bar{align-items:center;display:flex;gap:8px;justify-content:space-between}.review-lens-filter-bar button{align-items:center;background:#fff;border-color:#d4d4d4;color:#404040;display:inline-flex;gap:6px;min-height:30px;padding:5px 9px}.review-lens-filter-bar button:first-child{font-weight:700}.review-lens-filter-bar button[aria-expanded=true]{background:#171717;border-color:#171717;color:#fff}.review-lens-filter-bar span{align-items:center;background:#f97316;border-radius:999px;color:#fff;display:inline-flex;font-size:10px;height:17px;justify-content:center;min-width:17px;padding:0 5px}.review-lens-filters{background:#f5f5f5;border:1px solid #e5e5e5;border-radius:8px;display:grid;flex:0 0 auto;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));padding:8px}.review-lens-filters label{color:#525252;display:grid;font-size:10px;font-weight:800;gap:4px;letter-spacing:0;text-transform:uppercase}.review-lens-filters label:last-child{grid-column:1 / -1}.review-lens-list-panel{background:#fff;border:1px solid #d4d4d4;border-radius:10px;display:grid;gap:8px;grid-template-rows:auto minmax(0,1fr);min-height:0;overflow:hidden;padding:10px}.review-lens-comments__header{align-items:center;color:#171717;display:flex;gap:8px;justify-content:space-between}.review-lens-comments__header h3{color:#525252;font-size:11px;font-weight:800;letter-spacing:0;margin:0;text-transform:uppercase}.review-lens-comments__header span{align-items:center;background:#e5e5e5;border-radius:999px;color:#404040;display:inline-flex;font-size:11px;justify-content:center;min-width:22px;padding:2px 7px}.review-lens-comments__list{align-content:start;display:grid;gap:8px;min-height:0;overflow:auto;padding-right:2px}.review-lens-selected-panel{background:#171717;border-radius:10px;box-shadow:0 -10px 28px #0f172a2e;display:grid;gap:0;grid-template-rows:auto minmax(0,1fr);min-height:0;overflow:hidden;padding:4px}.review-lens-selected-panel__label{color:#d4d4d4;font-size:10px;font-weight:800;letter-spacing:0;padding:7px 8px 8px;text-transform:uppercase}.review-lens-selected-panel--empty{color:#d4d4d4;font-size:12px;line-height:1.4;padding:4px 12px 12px}.review-lens-selected-panel--empty p{color:#f5f5f5}.review-lens-detail{background:linear-gradient(180deg,#fff,#f8fafc);border:0;border-radius:8px;box-shadow:none;box-sizing:border-box;display:grid;gap:10px;min-height:0;overflow:auto;padding:12px}.review-lens-detail__header{align-items:start;display:flex;gap:12px;justify-content:space-between}.review-lens-detail__header-actions{align-items:center;display:flex;flex:0 0 auto;gap:8px}.review-lens-copy-link{background:none;border:1px solid #e2e8f0;border-radius:4px;color:#2563eb;cursor:pointer;font-size:11px;line-height:1;padding:4px 8px}.review-lens-copy-link:hover{background:#eff6ff}.review-lens-detail__header h3{color:#171717;font-size:13px;line-height:1.2;margin:0;text-transform:capitalize}.review-lens-detail__header strong,.review-lens-comment__header strong{background:#fee2e2;border:1px solid #fecaca;border-radius:999px;color:#991b1b;flex:0 0 auto;font-size:10px;line-height:1;padding:5px 7px;text-transform:uppercase}.review-lens-detail blockquote{background:#f8fafc;border:1px solid #dbeafe;border-left:3px solid #2563eb;border-radius:6px;color:#171717;font-size:15px;font-weight:700;line-height:1.35;margin:0;padding:9px 10px}.review-lens-detail-meta{background:#fff;border:1px solid #e5e5e5;border-radius:6px;display:grid;gap:0;margin:0;overflow:hidden}.review-lens-detail-meta div{display:grid;gap:2px;grid-template-columns:72px minmax(0,1fr);padding:6px 8px}.review-lens-detail-meta div+div{border-top:1px solid #eeeeee}.review-lens-detail-meta dt,.review-lens-detail-meta dd{font-size:11px;line-height:1.25;margin:0;min-width:0}.review-lens-detail-meta dt{color:#737373;font-weight:800;text-transform:uppercase}.review-lens-detail-meta dd{color:#262626;overflow-wrap:anywhere}.review-lens-detail a{color:#2563eb;font-weight:700}.review-lens-comment{background:#fff;border:1px solid #e5e5e5;border-radius:8px;box-shadow:0 1px 2px #0f172a0a;cursor:pointer;display:grid;gap:10px;padding:12px;position:relative;text-align:left}.review-lens-comment:before{background:#737373;border-radius:999px;bottom:12px;content:"";left:8px;position:absolute;top:12px;width:3px}.review-lens-comment--high:before{background:#dc2626}.review-lens-comment--medium:before{background:#f97316}.review-lens-comment--low:before{background:#16a34a}.review-lens-comment:focus-visible{outline:2px solid #2563eb;outline-offset:2px}.review-lens-comment--selected{background:#eff6ff;border-color:#2563eb;box-shadow:0 8px 18px #2563eb1f}.review-lens-comment__header{align-items:center;display:flex;gap:8px;justify-content:space-between;padding-left:8px}.review-lens-comment__header span{color:#525252;font-size:10px;font-weight:800;letter-spacing:0;text-transform:uppercase}.review-lens-comment__content{display:grid;gap:6px;padding-left:8px}.review-lens-comment__content p{color:#171717;font-size:14px;line-height:1.35}.review-lens-comment__content span{color:#525252;font-size:11px;line-height:1}.review-lens-tags{display:flex;flex-wrap:wrap;gap:5px;padding-left:8px}.review-lens-tags span{background:#f5f5f5;border:1px solid #e5e5e5;border-radius:999px;color:#404040;font-size:10px;font-weight:700;line-height:1;padding:4px 6px}.review-lens-thread{border-top:1px solid #e5e5e5;display:grid;gap:6px;padding-top:10px}.review-lens-thread__header{align-items:center;display:flex;justify-content:space-between}.review-lens-thread h3{margin:0}.review-lens-thread__header span{color:#737373;font-size:11px;font-weight:800}.review-lens-detail .review-lens-thread textarea{min-height:48px}.review-lens-status-actions{align-items:center;display:grid;gap:8px;grid-template-columns:minmax(0,1fr) auto}.review-lens-status-actions button{min-height:30px}.review-lens-status-actions .review-lens-button-secondary{background:#fff;border-color:#d4d4d4;color:#404040}.review-lens-status-actions .review-lens-button-primary{background:#171717;border-color:#171717;color:#fff}.review-lens-thread__message{background:#f5f5f5;border-radius:6px;display:grid;gap:4px;padding:8px}.review-lens-thread__message p{font-size:12px;line-height:1.35}.review-lens-thread__message span{color:#737373;font-size:10px}.review-lens-comment__actions{display:flex;justify-content:flex-end}.review-lens-comment__actions button{background:transparent;border-color:#d4d4d4;color:#404040;min-height:28px;padding:4px 10px}.review-lens-comment__actions button:hover{background:#171717;border-color:#171717;color:#fff}.review-lens-marker{background:#f97316;border:2px solid #ffffff;border-radius:999px;box-shadow:0 8px 20px #0f172a3d;cursor:pointer;height:18px;pointer-events:auto;position:fixed;transform:translate(-50%,-50%);width:18px}.review-lens-marker--selected{background:#2563eb}.review-lens-minimap{background:#1717171f;border-radius:999px;bottom:24px;pointer-events:auto;position:fixed;right:8px;top:24px;width:10px}.review-lens-minimap__point{background:#f97316;border:2px solid #ffffff;border-radius:999px;box-shadow:0 2px 8px #0f172a38;height:14px;left:50%;min-height:0;padding:0;position:absolute;transform:translate(-50%,-50%);width:14px}.review-lens-minimap__point--selected{background:#2563eb}.review-lens-insights,.review-lens-summary{border:1px solid #e5e5e5;border-radius:8px;display:grid;gap:8px;padding:10px}.review-lens-insights h3,.review-lens-summary h3{margin:0}.review-lens-insights p,.review-lens-insights li,.review-lens-summary dt,.review-lens-summary dd{color:#404040;font-size:12px;line-height:1.35}.review-lens-insights ul{display:grid;gap:6px;margin:0;padding-left:18px}.review-lens-summary{align-content:start;flex:1 1 auto;min-height:0;overflow:auto;padding-right:4px}.review-lens-summary section{border-bottom:1px solid #e5e5e5;display:grid;gap:8px;padding-bottom:10px}.review-lens-summary section:last-child{border-bottom:0;padding-bottom:0}.review-lens-summary dl,.review-lens-summary div{display:grid;gap:6px;margin:0}.review-lens-summary div{grid-template-columns:minmax(0,1fr) auto}.review-lens-summary dd{font-weight:700;margin:0}
package/dist/types.d.ts CHANGED
@@ -100,6 +100,15 @@ export type CreateAttachmentInput = {
100
100
  data: Blob | string;
101
101
  createdBy: string;
102
102
  };
103
+ export type ReviewLensEmailNotificationOptions = {
104
+ enabled?: boolean;
105
+ subjectPrefix?: string;
106
+ };
107
+ export type ReviewLensSendEmailInput = {
108
+ to: string[];
109
+ subject: string;
110
+ text: string;
111
+ };
103
112
  export type ReviewLensDesignTokens = {
104
113
  spacing?: string[];
105
114
  fontSize?: string[];
@@ -121,6 +130,7 @@ export type ReviewLensAdapter = {
121
130
  updateFeedback(id: string, patch: UpdateFeedbackInput): Promise<ReviewLensFeedback>;
122
131
  listMessages(feedbackId: string): Promise<ReviewLensThreadMessage[]>;
123
132
  createMessage(input: CreateMessageInput): Promise<ReviewLensThreadMessage>;
133
+ sendEmail?(input: ReviewLensSendEmailInput): Promise<void>;
124
134
  uploadAttachment?(feedbackId: string, input: CreateAttachmentInput): Promise<ReviewLensAttachment>;
125
135
  };
126
136
  export type ReviewLensConfig = {
@@ -134,6 +144,7 @@ export type ReviewLensConfig = {
134
144
  normalizeUrl?: (url: string) => string;
135
145
  designTokens?: ReviewLensDesignTokens;
136
146
  captureScreenshot?: (target: ReviewLensTarget) => Promise<Blob | string>;
147
+ emailNotifications?: boolean | ReviewLensEmailNotificationOptions;
137
148
  uploadAttachment?: (feedbackId: string, input: CreateAttachmentInput) => Promise<ReviewLensAttachment>;
138
149
  adapter?: ReviewLensAdapter;
139
150
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "review-lens-react",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "React overlay for UX review feedback backed by Google Sheets.",
5
5
  "repository": {
6
6
  "type": "git",