review-lens-react 0.2.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -7
- package/dist/review-lens-react.js +384 -364
- package/dist/review-lens-react.umd.cjs +1 -1
- package/dist/sheets/google-sheets-adapter.d.ts +2 -1
- package/dist/types.d.ts +2 -1
- package/package.json +5 -1
|
@@ -1 +1 @@
|
|
|
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 le(e){const t=e.feedbackSheetName??"Feedback",n=e.messagesSheetName??"Messages",s=e.usersSheetName??"Users";let a,d;async function f(){return a??(a=tt(e.googleClientId)),a}async function b(c,w){const u=await f(),v=await fetch(`https://sheets.googleapis.com/v4/spreadsheets/${e.spreadsheetId}${c}`,{...w,headers:{Authorization:`Bearer ${u}`,"Content-Type":"application/json",...w==null?void 0:w.headers}});if(!v.ok)throw new Error(`Google Sheets request failed with ${v.status}`);return v.json()}async function p(c){return(await b(`/values/${encodeURIComponent(c)}`)).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},u]=await Promise.all([this.getCurrentUser(),p(s)]),v=ne(u),E=w.toLowerCase(),N=v.find(A=>{var h;return((h=A.email)==null?void 0:h.toLowerCase())===E&&A.active!=="false"&&(!A.projectKey||A.projectKey===c)});return Ye((N==null?void 0:N.role)??"designer")},async listFeedback(c){return ne(await p(t)).map(pe).filter(u=>u!==null).filter(u=>u.projectKey===c.projectKey&&u.contentId===c.contentId&&u.normalizedPath===c.normalizedPath).sort((u,v)=>v.createdAt.localeCompare(u.createdAt))},async createFeedback(c){const w=new Date().toISOString(),u={...c,id:crypto.randomUUID(),attachments:[],createdAt:w,updatedAt:w};return await b(`/values/${encodeURIComponent(t)}:append?valueInputOption=RAW`,{method:"POST",body:JSON.stringify({values:[ce(u)]})}),u},async updateFeedback(c,w){const u=await p(t),v=u[0]??de,E=v.indexOf("id");if(E===-1)throw new Error(`Sheet ${t} is missing an id column`);const N=u.findIndex((m,k)=>k>0&&m[E]===c);if(N<1)throw new Error(`Feedback ${c} was not found`);const A=new Date().toISOString(),h=pe(he(v,u[N]));if(!h)throw new Error(`Feedback ${c} could not be parsed before updating`);const B={...h,...w,updatedAt:A},y=ce(B);return await b(`/values/${encodeURIComponent(t)}!A${N+1}:${et(de.length)}${N+1}?valueInputOption=RAW`,{method:"PUT",body:JSON.stringify({values:[y]})}),B},async listMessages(c){return ne(await p(n)).map(Ge).filter(u=>u!==null).filter(u=>u.feedbackId===c).sort((u,v)=>u.createdAt.localeCompare(v.createdAt))},async createMessage(c){const w={...c,id:crypto.randomUUID(),createdAt:new Date().toISOString()};return await b(`/values/${encodeURIComponent(n)}:append?valueInputOption=RAW`,{method:"POST",body:JSON.stringify({values:[Ve(w)]})}),w}}}const de=["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 ce(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=>he(t,s)):[]}function he(e,t){return Object.fromEntries(e.map((n,s)=>[n,t[s]??""]))}function pe(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:ue(e.createdCssSnapshotJson),fixedCssSnapshot:e.fixedCssSnapshotJson?ue(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 ue(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 a;const s=(a=window.google)==null?void 0:a.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 a=document.createElement("script");a.src="https://accounts.google.com/gsi/client",a.async=!0,a.defer=!0,a.onload=()=>t(),a.onerror=()=>n(new Error("Google Identity failed to load")),document.head.append(a)})}function ge(e){return new URL(e,window.location.href).pathname.replace(/\/+$/,"")||"/"}const fe=l.createContext(null);function rt({config:e,children:t}){const n=l.useMemo(()=>e.adapter?e.adapter:le({googleClientId:ve(e.googleClientId,"googleClientId"),spreadsheetId:ve(e.spreadsheetId,"spreadsheetId"),feedbackSheetName:e.sheetName??"Feedback"}),[e.adapter,e.googleClientId,e.sheetName,e.spreadsheetId]),s=e.currentUrl??window.location.href,a=(e.normalizeUrl??ge)(s),[d,f]=l.useState(),[b,p]=l.useState([]),[c,w]=l.useState([]),u=l.useCallback(async()=>{const y=await n.listFeedback({projectKey:e.projectKey,contentId:e.contentId,normalizedPath:a});w(y)},[n,e.contentId,e.projectKey,a]);l.useEffect(()=>{let y=!0;async function m(){const[k,$]=await Promise.all([n.getCurrentUser(),n.getPermissions(e.projectKey)]);y&&(f(k),p($),await u())}return m(),()=>{y=!1}},[n,e.projectKey,u]);const v=l.useCallback(async y=>{const m=await n.createFeedback(y);return w(k=>[m,...k]),m},[n]),E=l.useCallback(async(y,m)=>{const k=await n.updateFeedback(y,m);return w($=>$.map(z=>z.id===y?k:z)),k},[n]),N=l.useCallback(y=>n.listMessages(y),[n]),A=l.useCallback(y=>n.createMessage(y),[n]),h=l.useCallback(async(y,m)=>{const k=e.uploadAttachment??n.uploadAttachment;if(!k)throw new Error("Review Lens attachment upload is not configured");return k(y,m)},[n,e]),B=l.useMemo(()=>({config:e,adapter:n,currentUser:d,permissions:b,feedback:c,normalizedPath:a,refreshFeedback:u,createFeedback:v,updateFeedback:E,listMessages:N,createMessage:A,uploadAttachment:h}),[n,e,v,d,c,a,b,u,E,N,A,h]);return r.jsx(fe.Provider,{value:B,children:t})}function be(){const e=l.useContext(fe);if(!e)throw new Error("useReviewLens must be used inside ReviewLensProvider");return e}function ve(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=it(e);return{selector:n.selector,selectorStrategy:n.strategy,fingerprint:ot(e,t),cssSnapshot:lt(e,t),rect:t}}function it(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:at(e),strategy:"css-path"}}function at(e){const t=[];let n=e;for(;n&&n.nodeType===Node.ELEMENT_NODE&&n!==document.body;){const s=n.parentElement,a=n.tagName.toLowerCase();if(!s){t.unshift(a);break}const d=n.tagName,f=Array.from(s.children).filter(p=>p.tagName===d),b=f.indexOf(n)+1;t.unshift(f.length>1?`${a}:nth-of-type(${b})`:a),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:se(n.marginTop,n.marginRight,n.marginBottom,n.marginLeft),marginTop:n.marginTop,marginRight:n.marginRight,marginBottom:n.marginBottom,marginLeft:n.marginLeft,padding:se(n.paddingTop,n.paddingRight,n.paddingBottom,n.paddingLeft),paddingTop:n.paddingTop,paddingRight:n.paddingRight,paddingBottom:n.paddingBottom,paddingLeft:n.paddingLeft,border:se(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 se(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:a=!1,responsivePresets:d=dt}){var Oe;const{adapter:f,config:b,currentUser:p,feedback:c,normalizedPath:w,permissions:u,createFeedback:v,updateFeedback:E,listMessages:N,createMessage:A,uploadAttachment:h}=be(),[B,y]=l.useState(),[m,k]=l.useState(),[$,z]=l.useState(""),[xe,Mt]=l.useState("medium"),[Ae,Tt]=l.useState("visual"),[Le,Fe]=l.useState(""),[Me,It]=l.useState(((Oe=d[0])==null?void 0:Oe.value)??"desktop"),[S,J]=l.useState(),[M,P]=l.useState("review"),[Bt,Te]=l.useState(!1),[q,Ie]=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,ae]=l.useState(""),ee=l.useRef(null),T=!!p,I=u.includes("create"),We=u.includes("reply"),De=u.includes("update"),Ut=u.includes("assign"),U=B??m,Wt=!!m,Dt=!!(b.captureScreenshot&&(b.uploadAttachment||f.uploadAttachment)),zt=l.useMemo(()=>{const i=c.map(g=>g.assigneeEmail).filter(g=>!!g);return p!=null&&p.email&&i.push(p.email),Array.from(new Set(i)).sort((g,o)=>g.localeCompare(o))},[p==null?void 0:p.email,c]),F=l.useMemo(()=>c.filter(i=>s||i.status!=="resolved").filter(i=>q==="all"||i.status===q).filter(i=>V==="all"||i.severity===V).filter(i=>G==="all"||i.category===G).filter(i=>Y==="all"||i.assigneeEmail===Y).filter(i=>X==="all"||i.viewportPreset===X),[Y,G,c,V,s,q,X]),Ht=[q,V,G,Y,X].filter(i=>i!=="all").length;l.useEffect(()=>{e||(y(void 0),k(void 0),z(""),ae(""),P("review"))},[e]),l.useEffect(()=>{T||(y(void 0),k(void 0))},[T]),l.useEffect(()=>{!m||M!=="review"||window.requestAnimationFrame(()=>{var i,g,o;(g=(i=ee.current)==null?void 0:i.scrollIntoView)==null||g.call(i,{block:"nearest"}),(o=ee.current)==null||o.focus()})},[m,M]),l.useEffect(()=>{if(!S)return;let i=!0;return N(S.id).then(g=>{i&&Ue(o=>({...o,[S.id]:g}))}),()=>{i=!1}},[N,S]),l.useEffect(()=>{if(!e||!a||S||c.length===0)return;const i=new URL(window.location.href).searchParams.get("reviewLensFeedback"),g=c.find(o=>o.id===i);g&&Q(g,{syncUrl:!1})},[c,e,S,a]),l.useEffect(()=>{if(!e)return;function i(o){var C;if(o.key==="Shift"&&Te(!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"),(C=ee.current)==null||C.focus()),o.key==="f"&&S&&De&&(o.preventDefault(),je(S)))}function g(o){o.key==="Shift"&&Te(!1)}return window.addEventListener("keydown",i),window.addEventListener("keyup",g),()=>{window.removeEventListener("keydown",i),window.removeEventListener("keyup",g)}});const oe=l.useCallback(i=>{const g=i.target instanceof Element?i.target:null;if(g)return g.closest("[data-review-lens-ui]")?null:g;const o=document.elementFromPoint(i.clientX,i.clientY);return!o||o.closest("[data-review-lens-ui]")?null:o},[]);if(l.useEffect(()=>{if(!e||!T)return;function i(o){const C=oe(o);y(C?W(C):void 0)}function g(o){const C=oe(o);C&&(o.preventDefault(),o.stopPropagation(),k(W(C)),P("review"))}return window.addEventListener("mousemove",i,!0),window.addEventListener("click",g,!0),()=>{window.removeEventListener("mousemove",i,!0),window.removeEventListener("click",g,!0)}},[T,oe,e]),!e)return null;function Q(i,g={syncUrl:!0}){var C;if(J(i),k(void 0),P("feedback"),a&&g.syncUrl!==!1){const te=new URL(window.location.href);te.searchParams.set("reviewLensFeedback",i.id),window.history.replaceState({},"",te)}const o=D(i.selector);if(!o){y(void 0);return}(C=o.scrollIntoView)==null||C.call(o,{behavior:"smooth",block:"center",inline:"center"}),window.requestAnimationFrame(()=>{y(W(o))})}function ze(i){if(F.length===0)return;const g=S?F.findIndex(C=>C.id===S.id):-1,o=g<0?i>0?0:F.length-1:(g+i+F.length)%F.length;Q(F[o])}async function He(){if(!m||!$.trim()||!p||!I)return;let i=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:p.email});if(b.captureScreenshot)try{const g=await b.captureScreenshot(m),o=await h(i.id,{type:"screenshot",data:g,createdBy:p.email});i=await E(i.id,{attachments:[o],screenshotUrl:o.url,screenshotThumbnailUrl:o.thumbnailUrl})}catch{}z(""),Fe(""),k(void 0),y(void 0),P("feedback"),J(i)}async function jt(i,g){const o=new Date().toISOString(),C=g==="resolved"?{status:g,resolvedAt:o,resolvedBy:p==null?void 0:p.email}:{status:g},te=await E(i.id,C);J(te)}async function je(i){const g=D(i.selector);if(!g||!p)return;const o=W(g),C=await E(i.id,{status:"fixed",fixedCssSnapshot:o.cssSnapshot,fixedAt:new Date().toISOString(),fixedBy:p.email});J(C)}async function Ot(i){if(!ie.trim()||!p||!We)return;const g=await A({feedbackId:i.id,body:ie.trim(),authorEmail:p.email});Ue(o=>({...o,[i.id]:[...o[i.id]??[],g]})),ae("")}return r.jsxs("div",{className:"review-lens-root","data-review-lens-ui":!0,children:[T&&U?r.jsx(ft,{target:U,locked:!!m}):null,T&&m&&B&&Bt?r.jsx(bt,{from:m,to:B}):null,T?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:[T?null:r.jsx("p",{children:"Authenticate with Google to inspect this page."}),T&&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,T&&!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:i=>{i.preventDefault(),He()},children:[r.jsx("label",{htmlFor:"review-lens-comment",children:"New feedback"}),r.jsx("textarea",{ref:ee,id:"review-lens-comment",value:$,disabled:!I,onChange:i=>z(i.target.value),onKeyDown:i=>{i.key==="Enter"&&i.metaKey&&(i.preventDefault(),He())},placeholder:I?"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:i=>Mt(i.target.value),disabled:!I,children:ye.map(i=>r.jsx("option",{value:i,children:H[i]},i))})]}),r.jsxs("label",{children:["Type",r.jsx("select",{value:Ae,onChange:i=>Tt(i.target.value),disabled:!I,children:Se.map(i=>r.jsx("option",{value:i,children:j[i]},i))})]}),r.jsxs("label",{children:["Assignee",r.jsx("input",{value:Le,onChange:i=>Fe(i.target.value),disabled:!I,placeholder:"optional@email.com"})]}),r.jsxs("label",{children:["Viewport",r.jsx("select",{value:Me,onChange:i=>It(i.target.value),disabled:!I,children:d.map(i=>r.jsx("option",{value:i.value,children:i.label},i.value))})]})]}),I?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()||!I,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:Ie,onSeverityChange:Be,onCategoryChange:$e,onAssigneeChange:Pe,onViewportChange:_e,onToggle:()=>Pt(i=>!i),onClear:()=>{Ie("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(i=>r.jsx(pt,{item:i,selected:(S==null?void 0:S.id)===i.id,onSelect:Q},i.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:ae,onSubmitMessage:()=>void Ot(S),onStatusChange:i=>void jt(S,i),onAssigneeChange:i=>void E(S.id,{assigneeEmail:i.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:a,assigneeFilter:d,viewportFilter:f,assignees:b,responsivePresets:p,onStatusChange:c,onSeverityChange:w,onCategoryChange:u,onAssigneeChange:v,onViewportChange:E,onToggle:N,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:N,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:h=>c(h.target.value),children:[r.jsx("option",{value:"all",children:"All statuses"}),me.map(h=>r.jsx("option",{value:h,children:Z[h]},h))]})]}),r.jsxs("label",{children:["Priority",r.jsxs("select",{"aria-label":"Filter severity",value:s,onChange:h=>w(h.target.value),children:[r.jsx("option",{value:"all",children:"All priorities"}),ye.map(h=>r.jsx("option",{value:h,children:H[h]},h))]})]}),r.jsxs("label",{children:["Type",r.jsxs("select",{"aria-label":"Filter type",value:a,onChange:h=>u(h.target.value),children:[r.jsx("option",{value:"all",children:"All types"}),Se.map(h=>r.jsx("option",{value:h,children:j[h]},h))]})]}),r.jsxs("label",{children:["Assignee",r.jsxs("select",{"aria-label":"Filter assignee",value:d,onChange:h=>v(h.target.value),children:[r.jsx("option",{value:"all",children:"All assignees"}),b.map(h=>r.jsx("option",{value:h,children:h},h))]})]}),r.jsxs("label",{children:["Viewport",r.jsxs("select",{"aria-label":"Filter viewport",value:f,onChange:h=>E(h.target.value),children:[r.jsx("option",{value:"all",children:"All viewports"}),p.map(h=>r.jsx("option",{value:h.value,children:h.label},h.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:a=>{(a.key==="Enter"||a.key===" ")&&(a.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:a,canAssign:d,onMessageDraftChange:f,onSubmitMessage:b,onStatusChange:p,onAssigneeChange:c,onMarkFixed:w}){const u=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:u.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:!a,onChange:v=>p(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:!a,onClick:w,children:"Mark fixed"}),r.jsx("button",{type:"button",className:"review-lens-button-primary",disabled:!a,onClick:()=>p("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 a=0;const d=()=>{a=0;const b=s.current,p=D(e.selector),c=p==null?void 0:p.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=()=>{a||(a=window.requestAnimationFrame(d))};return d(),window.addEventListener("scroll",f,!0),window.addEventListener("resize",f),()=>{a&&window.cancelAnimationFrame(a),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(a=>{const d=D(a.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:a,top:Math.min(100,Math.max(0,(f.top+window.scrollY)/b*100))}}).filter(a=>a!==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(a=>r.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:()=>n(a.item),"aria-label":`Jump to feedback from ${a.item.authorEmail}`},a.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 p;const t=D(e.selector);if(!t)return["Selected element is no longer available."];const n=[],s=t.tagName.toLowerCase(),a=t.getAttribute("role"),d=["button","a","input","select","textarea"].includes(s)||a==="button"||a==="link",f=t.getAttribute("aria-label")||t.getAttribute("title")||((p=t.textContent)==null?void 0:p.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,a={}){!n||n.length===0||!t||Ct(t,n,a)||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(a=>t.includes(a))}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,a=(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(a,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 a=t(s);n.set(a,(n.get(a)??0)+1)}return Array.from(n.entries()).sort((s,a)=>a[1]-s[1]||s[0].localeCompare(a[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,a,d="1"]=t[1].split(",").map(f=>f.trim());return{red:Number(n),green:Number(s),blue:Number(a),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)},a={top:e.rect.top,left:e.rect.left,width:Math.max(e.rect.width,0),height:Math.max(e.rect.height,0)},d={top:a.top-t.top,left:a.left-t.left,width:a.width+t.left+t.right,height:a.height+t.top+t.bottom},f={top:a.top+n.top,left:a.left+n.left,width:Math.max(a.width-n.left-n.right,0),height:Math.max(a.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:a,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=le,x.normalizeReviewUrl=ge,x.useReviewLens=be,Object.defineProperty(x,Symbol.toStringTag,{value:"Module"})}));
|
|
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,7 +1,8 @@
|
|
|
1
1
|
import type { ReviewLensAdapter } from "../types";
|
|
2
2
|
type GoogleSheetsAdapterConfig = {
|
|
3
3
|
googleClientId: string;
|
|
4
|
-
|
|
4
|
+
contentSpreadsheetId: string;
|
|
5
|
+
usersSpreadsheetId: string;
|
|
5
6
|
feedbackSheetName?: string;
|
|
6
7
|
messagesSheetName?: string;
|
|
7
8
|
usersSheetName?: string;
|
package/dist/types.d.ts
CHANGED
|
@@ -125,7 +125,8 @@ export type ReviewLensAdapter = {
|
|
|
125
125
|
};
|
|
126
126
|
export type ReviewLensConfig = {
|
|
127
127
|
googleClientId?: string;
|
|
128
|
-
|
|
128
|
+
contentSpreadsheetId?: string;
|
|
129
|
+
usersSpreadsheetId?: string;
|
|
129
130
|
sheetName?: string;
|
|
130
131
|
projectKey: string;
|
|
131
132
|
contentId: string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "review-lens-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "React overlay for UX review feedback backed by Google Sheets.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/ichichich3011/review-lens-react"
|
|
8
|
+
},
|
|
5
9
|
"type": "module",
|
|
6
10
|
"main": "./dist/review-lens-react.umd.cjs",
|
|
7
11
|
"module": "./dist/review-lens-react.js",
|