commerce-kit 0.36.8 → 0.36.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/browser.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{useEffect as $,useRef as j,useState as E}from"react";import{createRoot as q}from"react-dom/client";import{Fragment as
|
|
2
|
-
`)},_=e=>{let t=e;for(;t;){if(t instanceof HTMLElement&&t.dataset.ynsFeedbackUi==="true")return!0;t=t.parentElement}return!1},Z=new Set(["HTML","HEAD","SCRIPT","STYLE","NOSCRIPT"]),ee=1e4,te=e=>{let t=new Date(e),n=t.getTime()-Date.now(),o=t.toLocaleString(void 0,{weekday:"short",month:"short",day:"numeric",hour:"numeric",minute:"2-digit"});if(n<=0)return`Any moment now (estimated by ${o})`;let i=Math.ceil(n/6e4);if(i<60)return`~${i} minutes (by ${o})`;let a=Math.round(n/36e5);if(a<24)return`~${a} ${a===1?"hour":"hours"} (by ${o})`;let l=Math.round(n/864e5);return`~${l} ${l===1?"day":"days"} (by ${o})`};function ne(){let[e,t]=E(null),[n,o]=E(!0),[i,a]=E(!1),[l,s]=E(null),[d,b]=E(null),[g,h]=E(!1),[P,y]=E(!1),c=j(null);$(()=>{if(c.current=G(),!c.current){o(!1);return}let r=!1,u=new AbortController,S=async()=>{try{let L=await fetch(`${c.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,{credentials:"include",signal:u.signal});if(r)return;if(!L.ok){t(null);return}let k=await L.json();if(r)return;t(k),k.sessionStatus==="done"&&window.location.reload()}catch{r||t(null)}finally{r||o(!1)}};S();let w=window.setInterval(S,ee);return()=>{r=!0,u.abort(),window.clearInterval(w)}},[]),$(()=>{!e||e.canComment||(a(!1),s(null),h(!1),b(null))},[e]),$(()=>{if(i)return document.body.style.cursor="crosshair",()=>{document.body.style.cursor=""}},[i]),$(()=>{if(!i)return;let r=document.createElement("div");r.dataset.ynsFeedbackUi="true",r.style.cssText=["position: fixed","pointer-events: none","z-index: 2147483644","border: 2px dashed #10b981","background: rgba(16, 185, 129, 0.08)","border-radius: 3px","display: none","transition: top 0.05s, left 0.05s, width 0.05s, height 0.05s"].join(";");let u=document.createElement("div");u.dataset.ynsFeedbackUi="true",u.textContent="Click to comment",u.style.cssText=["position: fixed","pointer-events: none","z-index: 2147483645","background: #059669","color: #fff","font-size: 11px","font-family: ui-sans-serif, system-ui, sans-serif","padding: 3px 8px","border-radius: 4px","white-space: nowrap","display: none","box-shadow: 0 2px 6px rgba(0,0,0,0.15)"].join(";"),document.documentElement.appendChild(r),document.documentElement.appendChild(u);let S=null,w=k=>{let R=document.elementFromPoint(k.clientX,k.clientY);if(!R||Z.has(R.tagName)||_(R)){r.style.display="none",u.style.display="none",S=null;return}S=R,requestAnimationFrame(()=>{if(S!==R)return;let I=R.getBoundingClientRect();r.style.top=`${I.top}px`,r.style.left=`${I.left}px`,r.style.width=`${I.width}px`,r.style.height=`${I.height}px`,r.style.display="block",u.style.left=`${k.clientX+14}px`,u.style.top=`${k.clientY+14}px`,u.style.display="block"})},L=()=>{r.style.display="none",u.style.display="none",S=null};return document.addEventListener("mousemove",w,!0),document.addEventListener("mouseleave",L),()=>{document.removeEventListener("mousemove",w,!0),document.removeEventListener("mouseleave",L),r.remove(),u.remove()}},[i]),$(()=>{if(!i)return;let r=u=>{let S=u.target;if(!(S instanceof Element)||_(S))return;u.preventDefault(),u.stopPropagation();let w=S.getBoundingClientRect(),L=w.width>0?(u.clientX-w.left)/w.width:.5,k=w.height>0?(u.clientY-w.top)/w.height:.5;s({cssSelector:K(S),pagePath:window.location.pathname,surroundingHtml:Q(S),rect:{top:w.top+window.scrollY,left:w.left+window.scrollX,width:w.width,height:w.height},clickX:u.clientX+window.scrollX,clickY:u.clientY+window.scrollY,offsetXRatio:Math.min(1,Math.max(0,L)),offsetYRatio:Math.min(1,Math.max(0,k))}),a(!1)};return document.addEventListener("click",r,{capture:!0}),()=>document.removeEventListener("click",r,{capture:!0})},[i]);let f=async()=>{if(!c.current)return;let r=await fetch(`${c.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,{credentials:"include"});if(!r.ok)return;let u=await r.json();t(u)},m=async r=>{!c.current||!e||!l||!(await fetch(`${c.current}/api/feedback-comments`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({feedbackSessionId:e.feedbackSessionId,content:r,pagePath:l.pagePath,cssSelector:l.cssSelector,surroundingHtml:l.surroundingHtml,offsetXRatio:l.offsetXRatio,offsetYRatio:l.offsetYRatio})})).ok||(s(null),a(!0),await f())},v=async(r,u)=>{!c.current||!(await fetch(`${c.current}/api/feedback-comments/${r}`,{method:"PATCH",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({content:u})})).ok||(b(null),await f())},C=async r=>{!c.current||!(await fetch(`${c.current}/api/feedback-comments/${r}`,{method:"DELETE",credentials:"include"})).ok||await f()},V=async()=>{if(!(!c.current||!e)&&window.confirm("Finalize this feedback session? You won't be able to add more comments.")){y(!0);try{if(!(await fetch(`${c.current}/api/feedback-sessions/${e.feedbackSessionId}/finalize`,{method:"POST",credentials:"include"})).ok)return;t(u=>u&&{...u,canComment:!1})}finally{y(!1)}}},W=r=>{if(r.pagePath!==window.location.pathname){window.location.assign(r.pagePath);return}let u=null;try{u=document.querySelector(r.cssSelector)}catch{u=null}u&&(u.scrollIntoView({behavior:"smooth",block:"center"}),b(r.id))};if(n||!e)return null;if(!e.canComment)return e.sessionStatus!=="processing"&&e.sessionStatus!=="in_review"?null:p("div",{"data-yns-feedback-ui":"true",children:p(oe,{progress:e.progress,eta:e.eta,status:e.sessionStatus})});let z=e.comments.filter(r=>r.pagePath===window.location.pathname&&r.status!=="done");return x("div",{"data-yns-feedback-ui":"true",children:[z.map((r,u)=>p(ie,{pin:r,number:u+1,editing:d===r.id,onStartEdit:()=>b(r.id),onCancelEdit:()=>b(null),onSave:S=>v(r.id,S),onRemove:()=>C(r.id)},r.id)),l&&p(se,{pending:l,onCancel:()=>{s(null),a(!0)},onSave:r=>m(r)}),g&&p(re,{comments:e.comments,currentPath:window.location.pathname,onClose:()=>h(!1),onSelect:W}),x("div",{style:ae,children:[p("button",{type:"button",onClick:()=>a(r=>!r),style:i?de:Y,children:i?"Cancel":"Add comment"}),p("button",{type:"button",onClick:()=>h(r=>!r),style:ce,children:g?"Hide list":`List (${e.comments.length})`}),p("button",{type:"button",onClick:V,disabled:P,style:ue,children:P?"Finalizing\u2026":"Finalize"}),p("span",{style:pe,children:i?"Click any element to comment":`${z.length} on this page`})]})]})}function oe({progress:e,eta:t,status:n}){let[,o]=E(0);$(()=>{let s=window.setInterval(()=>o(d=>d+1),6e4);return()=>window.clearInterval(s)},[]);let i=te(t);return x("div",{style:$e,children:[x("div",{style:Le,children:[p("span",{style:Re,children:"Submitted"}),p("strong",{style:{fontSize:13},children:n==="in_review"?"Feedback under review":"Applying feedback"})]}),x("div",{style:Ie,children:[p("span",{children:"Review progress"}),p("span",{style:{opacity:.7},children:e.label})]}),p("div",{style:Te,children:p("div",{style:{...Ae,width:`${e.fillPct}%`}})}),x("p",{style:Me,children:["Estimated delivery: ",p("span",{style:{fontWeight:600},children:i})]})]})}function ie({pin:e,number:t,editing:n,onStartEdit:o,onCancelEdit:i,onSave:a,onRemove:l}){let s=le(e.cssSelector,e.offsetXRatio,e.offsetYRatio);return s?x("div",{style:{position:"absolute",top:s.top,left:s.left,zIndex:2147483600,pointerEvents:"auto"},children:[p("button",{type:"button",onClick:o,style:D,title:e.content,children:t}),n&&p(X,{initial:e.content,onCancel:i,onSave:a,onRemove:l})]}):null}function se({pending:e,onCancel:t,onSave:n}){return x(Ne,{children:[p("div",{style:{position:"absolute",top:e.rect.top+e.rect.height*e.offsetYRatio-12,left:e.rect.left+e.rect.width*e.offsetXRatio-12,zIndex:2147483600,pointerEvents:"none"},children:p("div",{style:D,children:"\u2022"})}),p("div",{style:{position:"absolute",top:e.clickY+10,left:e.clickX+10,zIndex:2147483647},children:p(X,{initial:"",onCancel:t,onSave:n})})]})}function re({comments:e,currentPath:t,onClose:n,onSelect:o}){let i=new Map;for(let l of e){let s=i.get(l.pagePath)??[];s.push(l),i.set(l.pagePath,s)}let a=Array.from(i.keys()).sort((l,s)=>l===t?-1:s===t?1:l.localeCompare(s));return x("div",{style:he,children:[x("div",{style:ve,children:[x("strong",{style:{fontSize:14},children:["Comments (",e.length,")"]}),p("button",{type:"button",onClick:n,style:U,children:"Close"})]}),p("div",{style:Se,children:e.length===0?p("div",{style:we,children:"No comments yet. Click \u201CAdd comment\u201D to leave one."}):a.map(l=>x("div",{style:{marginBottom:12},children:[p("div",{style:xe,children:l===t?`${l} \xB7 current`:l}),(i.get(l)??[]).map((s,d)=>x("button",{type:"button",onClick:()=>o(s),style:Ee,disabled:s.status==="done"&&!1,children:[p("span",{style:Ce,children:d+1}),p("span",{style:ke,children:s.content.length>140?`${s.content.slice(0,140)}\u2026`:s.content}),s.status==="done"&&p("span",{style:Pe,children:"done"})]},s.id))]},l))})]})}function X({initial:e,onCancel:t,onSave:n,onRemove:o}){let[i,a]=E(e),[l,s]=E(!1),d=j(null);return $(()=>{let g=d.current;if(!g)return;g.focus();let h=g.value.length;g.setSelectionRange(h,h)},[]),x("form",{onSubmit:async g=>{g.preventDefault();let h=i.trim();if(h){s(!0);try{await n(h)}finally{s(!1)}}},style:me,children:[p("textarea",{ref:d,value:i,onChange:g=>a(g.target.value),placeholder:"Leave a comment\u2026",style:fe,rows:3}),x("div",{style:ge,children:[o&&p("button",{type:"button",onClick:()=>o(),style:be,disabled:l,children:"Delete"}),p("div",{style:{flex:1}}),p("button",{type:"button",onClick:t,style:U,disabled:l,children:"Cancel"}),p("button",{type:"submit",style:ye,disabled:l||!i.trim(),children:l?"Saving\u2026":"Save"})]})]})}function le(e,t,n){let[o,i]=E(null);return $(()=>{let a=()=>{let s=null;try{s=document.querySelector(e)}catch{s=null}if(!s){i(null);return}let d=s.getBoundingClientRect();i({top:d.top+window.scrollY+d.height*n-12,left:d.left+window.scrollX+d.width*t-12})};a();let l=new ResizeObserver(a);try{let s=document.querySelector(e);s&&l.observe(s)}catch{}return window.addEventListener("scroll",a,!0),window.addEventListener("resize",a),()=>{l.disconnect(),window.removeEventListener("scroll",a,!0),window.removeEventListener("resize",a)}},[e,t,n]),o}var ae={position:"fixed",bottom:16,left:"50%",transform:"translateX(-50%)",zIndex:2147483646,display:"flex",alignItems:"center",gap:8,padding:"8px 12px",background:"rgba(17, 17, 17, 0.92)",color:"white",borderRadius:999,boxShadow:"0 8px 24px rgba(0,0,0,0.25)",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',fontSize:14,pointerEvents:"auto"},Y={border:"none",background:"white",color:"#111",padding:"6px 12px",borderRadius:999,cursor:"pointer",fontWeight:600,fontSize:13},de={...Y,background:"#ef4444",color:"white"},ce={border:"1px solid rgba(255,255,255,0.4)",background:"transparent",color:"white",padding:"6px 12px",borderRadius:999,cursor:"pointer",fontWeight:500,fontSize:13},ue={border:"none",background:"#10b981",color:"white",padding:"6px 12px",borderRadius:999,cursor:"pointer",fontWeight:600,fontSize:13},pe={opacity:.8,fontSize:12},D={width:24,height:24,borderRadius:999,background:"#10b981",color:"white",border:"2px solid white",cursor:"pointer",fontSize:12,fontWeight:700,boxShadow:"0 2px 6px rgba(0,0,0,0.3)",display:"inline-flex",alignItems:"center",justifyContent:"center",padding:0},me={background:"white",border:"1px solid #e5e7eb",borderRadius:8,padding:12,width:280,boxShadow:"0 12px 32px rgba(0,0,0,0.18)",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',display:"flex",flexDirection:"column",gap:8,color:"#111"},fe={width:"100%",border:"1px solid #d1d5db",borderRadius:6,padding:8,fontSize:14,resize:"vertical",fontFamily:"inherit",color:"#111",background:"white",boxSizing:"border-box"},ge={display:"flex",alignItems:"center",gap:6},N={border:"1px solid transparent",borderRadius:6,padding:"6px 10px",fontSize:13,cursor:"pointer",fontWeight:500},ye={...N,background:"#111",color:"white"},U={...N,background:"transparent",color:"#374151",borderColor:"#d1d5db"},be={...N,background:"transparent",color:"#b91c1c",borderColor:"#fecaca"},he={position:"fixed",top:0,right:0,bottom:0,width:360,maxWidth:"92vw",background:"white",borderLeft:"1px solid #e5e7eb",boxShadow:"-12px 0 32px rgba(0,0,0,0.12)",zIndex:2147483646,display:"flex",flexDirection:"column",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',color:"#111"},ve={display:"flex",alignItems:"center",justifyContent:"space-between",padding:"12px 16px",borderBottom:"1px solid #e5e7eb"},Se={flex:1,overflow:"auto",padding:"12px 12px 24px"},we={color:"#6b7280",fontSize:13,padding:"24px 8px",textAlign:"center"},xe={fontSize:11,textTransform:"uppercase",letterSpacing:.5,color:"#6b7280",padding:"4px 4px 6px",wordBreak:"break-all"},Ee={display:"flex",alignItems:"flex-start",gap:8,width:"100%",textAlign:"left",background:"white",border:"1px solid #e5e7eb",borderRadius:6,padding:"8px 10px",cursor:"pointer",fontSize:13,marginBottom:6,color:"#111"},Ce={flexShrink:0,width:22,height:22,borderRadius:999,background:"#10b981",color:"white",fontWeight:700,fontSize:11,display:"inline-flex",alignItems:"center",justifyContent:"center"},ke={flex:1,whiteSpace:"pre-wrap",wordBreak:"break-word"},Pe={flexShrink:0,background:"#d1fae5",color:"#065f46",fontSize:11,fontWeight:600,padding:"2px 6px",borderRadius:4,alignSelf:"center"},$e={position:"fixed",bottom:16,left:"50%",transform:"translateX(-50%)",zIndex:2147483646,display:"flex",flexDirection:"column",gap:8,padding:"12px 16px",width:"min(420px, calc(100vw - 32px))",background:"white",border:"1px solid #e5e7eb",borderRadius:12,boxShadow:"0 12px 32px rgba(0,0,0,0.18)",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',color:"#111",pointerEvents:"auto"},Le={display:"flex",alignItems:"center",gap:8},Re={background:"#d1fae5",color:"#065f46",fontSize:11,fontWeight:700,padding:"2px 8px",borderRadius:999,textTransform:"uppercase",letterSpacing:.5},Ie={display:"flex",justifyContent:"space-between",fontSize:12},Te={width:"100%",height:6,background:"#f3f4f6",borderRadius:999,overflow:"hidden"},Ae={height:"100%",background:"#10b981",borderRadius:999,transition:"width 500ms"},Me={margin:0,fontSize:12,color:"#6b7280"};function B(){if(console.log("[YNS Feedback Toolbar] mountFeedbackToolbar() called",{isWindow:typeof window<"u",vercelEnv:process.env.NEXT_PUBLIC_VERCEL_ENV,alreadyMounted:typeof document<"u"&&!!document.getElementById(A)}),typeof window>"u")return null;if(process.env.NEXT_PUBLIC_VERCEL_ENV!=="preview")return console.log("[YNS Feedback Toolbar] gate failed \u2014 NEXT_PUBLIC_VERCEL_ENV is",process.env.NEXT_PUBLIC_VERCEL_ENV),null;if(document.getElementById(A))return null;let e=document.createElement("div");e.id=A,e.dataset.ynsFeedbackUi="true",document.body.appendChild(e);let t=q(e);return t.render(p(ne,{})),{unmount:()=>{t.unmount(),e.remove()}}}typeof window<"u"&&console.log("[YNS Feedback Toolbar] module evaluated",{vercelEnv:process.env.NEXT_PUBLIC_VERCEL_ENV,readyState:document.readyState,willAutoMount:process.env.NEXT_PUBLIC_VERCEL_ENV==="preview"});typeof window<"u"&&process.env.NEXT_PUBLIC_VERCEL_ENV==="preview"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{B()}):B());function H(){typeof window>"u"||(console.log("[YNS Sandbox Inspectors] module evaluated"),_e(),Be())}H();var ze=new Set(["HEAD","SCRIPT","STYLE","NOSCRIPT","HTML"]);function Fe(e){if(e.id)return`${e.tagName.toLowerCase()}#${CSS.escape(e.id)}`;let t=[],n=e;for(;n&&n!==document.body&&n!==document.documentElement;){let o=n.tagName.toLowerCase();if(n.id){t.unshift(`${o}#${CSS.escape(n.id)}`);break}let i=Array.from(n.classList).filter(s=>!s.startsWith("data-yns-"));i.length>0&&(o+=`.${i.map(s=>CSS.escape(s)).join(".")}`);let a=n.parentElement,l=n.tagName;if(a&&Array.from(a.children).filter(d=>d.tagName===l&&(i.length===0||i.every(b=>d.classList.contains(b)))).length>1){let d=Array.from(a.children).indexOf(n)+1;o+=`:nth-child(${d})`}t.unshift(o),n=n.parentElement}return t.join(" > ")}function O(e,t){let n=e.getBoundingClientRect(),o=window.getComputedStyle(e),i=(e.textContent??"").trim();return{tag:e.tagName.toLowerCase(),id:e.id||void 0,classes:Array.from(e.classList).filter(a=>!a.startsWith("data-yns-")),textContent:i.length>t?`${i.slice(0,t)}\u2026`:i,cssSelector:Fe(e),boundingRect:{top:n.top,left:n.left,width:n.width,height:n.height},computedStyles:{color:o.color,backgroundColor:o.backgroundColor,fontSize:o.fontSize,fontFamily:o.fontFamily,padding:o.padding,margin:o.margin}}}function T(e,t){if(!e||!e.tagName||ze.has(e.tagName))return!0;for(let n of t)if(e.hasAttribute?.(n))return!0;return!1}function _e(){let e=!1,t=null,n=null,o=document.createElement("div");o.setAttribute("data-yns-design-overlay","hover"),o.style.cssText=["position: fixed","pointer-events: none","z-index: 2147483646","border: 2px solid #3b82f6","background: rgba(59, 130, 246, 0.08)","border-radius: 3px","display: none","transition: top 0.05s, left 0.05s, width 0.05s, height 0.05s"].join(";");let i=document.createElement("div");i.setAttribute("data-yns-design-overlay","label"),i.style.cssText=["position: fixed","pointer-events: none","z-index: 2147483647","background: #3b82f6","color: #fff","font-size: 11px","font-family: ui-monospace, monospace","padding: 2px 6px","border-radius: 3px","white-space: nowrap","display: none"].join(";"),document.documentElement.appendChild(o),document.documentElement.appendChild(i);let a=document.createElement("style");a.setAttribute("data-yns-design-overlay","style"),a.textContent="[data-yns-selected] { outline: 2px solid #3b82f6 !important; outline-offset: 1px; }",document.head.appendChild(a);function l(y){let c=y.getBoundingClientRect();o.style.top=`${c.top}px`,o.style.left=`${c.left}px`,o.style.width=`${c.width}px`,o.style.height=`${c.height}px`,o.style.display="block";let f=Array.from(y.classList).filter(v=>!v.startsWith("data-yns-")),m=y.tagName.toLowerCase()+(f.length?`.${f[0]}`:"");i.textContent=m,i.style.left=`${c.left}px`,i.style.top=`${Math.max(0,c.top-22)}px`,i.style.display="block"}let s=y=>{let c=document.elementFromPoint(y.clientX,y.clientY);if(T(c,["data-yns-design-overlay"])){o.style.display="none",i.style.display="none",t=null;return}t=c,requestAnimationFrame(()=>{t===c&&e&&c&&l(c)})},d=()=>{o.style.display="none",i.style.display="none",t=null},b=y=>{if(!e||!t)return;y.preventDefault(),y.stopPropagation(),y.stopImmediatePropagation();let c=t;if(T(c,["data-yns-design-overlay"]))return;let f=O(c,120),m=f.cssSelector;n&&n.el.removeAttribute("data-yns-selected"),n&&n.cssSelector===m?(n=null,window.parent.postMessage({type:"element-deselected",data:f},"*")):(n={el:c,cssSelector:m},c.setAttribute("data-yns-selected",""),window.parent.postMessage({type:"element-selected",data:f},"*"))},g=y=>{y.key==="Escape"&&e&&(P(),window.parent.postMessage({type:"design-mode-cleared"},"*"))};function h(){e=!0,document.addEventListener("mousemove",s,!0),document.addEventListener("mouseleave",d),document.addEventListener("click",b,!0),document.addEventListener("keydown",g,!0)}function P(){e=!1,document.removeEventListener("mousemove",s,!0),document.removeEventListener("mouseleave",d),document.removeEventListener("click",b,!0),document.removeEventListener("keydown",g,!0),o.style.display="none",i.style.display="none",t=null,n&&(n.el.removeAttribute("data-yns-selected"),n=null)}window.addEventListener("message",y=>{let c=y.data;!c||typeof c!="object"||(c.type==="design-mode-toggle"&&(c.enabled?h():(P(),window.parent.postMessage({type:"design-mode-cleared"},"*"))),c.type==="design-mode-deselect"&&n&&(n.el.removeAttribute("data-yns-selected"),n=null))})}function Be(){let e=!1,t=null,n=[],o=document.createElement("div");o.setAttribute("data-yns-comment-overlay","hover"),o.style.cssText=["position: fixed","pointer-events: none","z-index: 2147483644","border: 2px dashed #10b981","background: rgba(16, 185, 129, 0.06)","border-radius: 3px","display: none","transition: top 0.05s, left 0.05s, width 0.05s, height 0.05s"].join(";");let i=document.createElement("div");i.setAttribute("data-yns-comment-overlay","cursor-label"),i.textContent="Click to comment",i.style.cssText=["position: fixed","pointer-events: none","z-index: 2147483645","background: #059669","color: #fff","font-size: 11px","font-family: ui-sans-serif, system-ui, sans-serif","padding: 3px 8px","border-radius: 4px","white-space: nowrap","display: none","box-shadow: 0 2px 6px rgba(0,0,0,0.15)"].join(";");let a=document.createElement("div");a.setAttribute("data-yns-comment-overlay","pins"),a.style.cssText="position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 2147483643;",document.documentElement.appendChild(o),document.documentElement.appendChild(i),document.documentElement.appendChild(a);function l(){a.innerHTML="";for(let f of n){let m=null;try{m=document.querySelector(f.selector)}catch{m=null}if(!m)continue;let v=m.getBoundingClientRect(),C=document.createElement("div");C.style.cssText=["position: fixed",`top: ${v.top-12}px`,`left: ${v.left+v.width/2-12}px`,"width: 24px","height: 24px","border-radius: 50%","background: #059669","color: #fff","display: flex","align-items: center","justify-content: center","font-size: 12px","font-weight: 600","font-family: ui-sans-serif, system-ui, sans-serif","box-shadow: 0 2px 8px rgba(0,0,0,0.2)","pointer-events: none"].join(";"),C.textContent=String(f.number),a.appendChild(C)}}let s=f=>{let m=document.elementFromPoint(f.clientX,f.clientY);if(T(m,["data-yns-comment-overlay","data-yns-design-overlay"])){o.style.display="none",i.style.display="none",t=null;return}t=m,requestAnimationFrame(()=>{if(t===m&&e&&m){let v=m.getBoundingClientRect();o.style.top=`${v.top}px`,o.style.left=`${v.left}px`,o.style.width=`${v.width}px`,o.style.height=`${v.height}px`,o.style.display="block",i.style.left=`${f.clientX+14}px`,i.style.top=`${f.clientY+14}px`,i.style.display="block"}})},d=()=>{o.style.display="none",i.style.display="none",t=null},b=f=>{if(!e||!t)return;f.preventDefault(),f.stopPropagation(),f.stopImmediatePropagation();let m=t;if(T(m,["data-yns-comment-overlay","data-yns-design-overlay"]))return;let v=O(m,200),C=m.getBoundingClientRect();window.parent.postMessage({type:"comment-click",data:{element:v,clickPosition:{x:f.clientX,y:f.clientY},elementRect:{top:C.top,left:C.left,width:C.width,height:C.height},pagePath:window.location.pathname}},"*")},g=f=>{f.key==="Escape"&&e&&(P(),window.parent.postMessage({type:"comment-mode-cleared"},"*"))};function h(){e=!0,document.body.style.cursor="crosshair",document.addEventListener("mousemove",s,!0),document.addEventListener("mouseleave",d),document.addEventListener("click",b,!0),document.addEventListener("keydown",g,!0)}function P(){e=!1,document.body.style.cursor="",document.removeEventListener("mousemove",s,!0),document.removeEventListener("mouseleave",d),document.removeEventListener("click",b,!0),document.removeEventListener("keydown",g,!0),o.style.display="none",i.style.display="none",t=null}let y=!1,c=()=>{y||(y=!0,requestAnimationFrame(()=>{y=!1,l()}))};window.addEventListener("scroll",c,{passive:!0}),window.addEventListener("resize",c,{passive:!0}),window.addEventListener("message",f=>{let m=f.data;!m||typeof m!="object"||(m.type==="comment-mode-toggle"&&(m.enabled?h():P()),m.type==="comment-pins-update"&&(n=m.pins??[],l()),m.type==="comment-pin-remove"&&(n=n.filter(v=>v.id!==m.pinId),l()))})}export{B as mountFeedbackToolbar,H as startSandboxInspectors};
|
|
1
|
+
import{useEffect as $,useRef as j,useState as E}from"react";import{createRoot as q}from"react-dom/client";import{Fragment as Fe,jsx as p,jsxs as x}from"react/jsx-runtime";var A="yns-feedback-toolbar-root",G=()=>{if(typeof window>"u")return null;let e=(process.env.NEXT_PUBLIC_YNS_API_BASE??"").trim();if(e)return e.replace(/\/$/,"");let t=window.location.hostname,n=window.location.protocol;return t.endsWith(".yns.store")?`${n}//yns.store`:t.endsWith(".yns.cx")?`${n}//yns.cx`:window.location.origin},K=e=>{if(e.id)return`#${CSS.escape(e.id)}`;let t=[],n=e;for(;n&&n.nodeType===Node.ELEMENT_NODE&&t.length<6;){let o=n.tagName.toLowerCase(),i=n.getAttribute("class");if(i){let s=i.split(/\s+/).filter(Boolean).slice(0,2).map(d=>`.${CSS.escape(d)}`).join("");o+=s}let a=n.parentElement,l=n.tagName;if(a){let s=Array.from(a.children).filter(d=>d.tagName===l);if(s.length>1){let d=s.indexOf(n)+1;o+=`:nth-of-type(${d})`}}t.unshift(o),n=a}return t.join(" > ")},J=["id","class","data-testid","aria-label","role","name","href","src"],M=e=>{let t=[];for(let n of J){let o=e.getAttribute(n);if(!o)continue;let i=o.length>80?`${o.slice(0,80)}\u2026`:o;t.push(` ${n}="${i.replace(/"/g,""")}"`)}return t.join("")},F=e=>{let t=(e.textContent??"").replace(/\s+/g," ").trim();return t?t.length>100?`${t.slice(0,100)}\u2026`:t:""},Q=e=>{let t=[],n=e;for(;n&&n!==document.documentElement&&t.length<5;){let d=n.parentElement;if(!d)break;t.unshift(d),n=d}let o=[],i=0;for(let d of t){let b=" ".repeat(i);o.push(`${b}<${d.tagName.toLowerCase()}${M(d)}>`),i++}let a=" ".repeat(i),l=e.tagName.toLowerCase(),s=F(e);if(o.push(`${a}<${l}${M(e)}>${s?`${s}</${l}>`:""} \u2190 TARGET`),!s){let d=" ".repeat(i+1),b=Array.from(e.children).slice(0,6);for(let g of b){let h=F(g);o.push(`${d}<${g.tagName.toLowerCase()}${M(g)}>${h?`${h}</${g.tagName.toLowerCase()}>`:""}`)}e.children.length>6&&o.push(`${d}\u2026 (${e.children.length-6} more children)`),o.push(`${a}</${l}>`)}for(let d=t.length-1;d>=0;d--){let b=" ".repeat(d),g=t[d];g&&o.push(`${b}</${g.tagName.toLowerCase()}>`)}return o.join(`
|
|
2
|
+
`)},_=e=>{let t=e;for(;t;){if(t instanceof HTMLElement&&t.dataset.ynsFeedbackUi==="true")return!0;t=t.parentElement}return!1},Z=new Set(["HTML","HEAD","SCRIPT","STYLE","NOSCRIPT"]),ee=1e4,te=e=>{let t=new Date(e),n=t.getTime()-Date.now(),o=t.toLocaleString(void 0,{weekday:"short",month:"short",day:"numeric",hour:"numeric",minute:"2-digit"});if(n<=0)return`Any moment now (estimated by ${o})`;let i=Math.ceil(n/6e4);if(i<60)return`~${i} minutes (by ${o})`;let a=Math.round(n/36e5);if(a<24)return`~${a} ${a===1?"hour":"hours"} (by ${o})`;let l=Math.round(n/864e5);return`~${l} ${l===1?"day":"days"} (by ${o})`};function ne(){let[e,t]=E(null),[n,o]=E(!0),[i,a]=E(!1),[l,s]=E(null),[d,b]=E(null),[g,h]=E(!1),[P,y]=E(!1),c=j(null);$(()=>{if(c.current=G(),!c.current){o(!1);return}let r=!1,u=new AbortController,S=async()=>{try{let L=await fetch(`${c.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,{credentials:"include",signal:u.signal});if(r)return;if(!L.ok){t(null);return}let k=await L.json();if(r)return;t(k),k.sessionStatus==="done"&&window.location.reload()}catch{r||t(null)}finally{r||o(!1)}};S();let w=window.setInterval(S,ee);return()=>{r=!0,u.abort(),window.clearInterval(w)}},[]),$(()=>{!e||e.canComment||(a(!1),s(null),h(!1),b(null))},[e]),$(()=>{if(i)return document.body.style.cursor="crosshair",()=>{document.body.style.cursor=""}},[i]),$(()=>{if(!i)return;let r=document.createElement("div");r.dataset.ynsFeedbackUi="true",r.style.cssText=["position: fixed","pointer-events: none","z-index: 2147483644","border: 2px dashed #10b981","background: rgba(16, 185, 129, 0.08)","border-radius: 3px","display: none","transition: top 0.05s, left 0.05s, width 0.05s, height 0.05s"].join(";");let u=document.createElement("div");u.dataset.ynsFeedbackUi="true",u.textContent="Click to comment",u.style.cssText=["position: fixed","pointer-events: none","z-index: 2147483645","background: #059669","color: #fff","font-size: 11px","font-family: ui-sans-serif, system-ui, sans-serif","padding: 3px 8px","border-radius: 4px","white-space: nowrap","display: none","box-shadow: 0 2px 6px rgba(0,0,0,0.15)"].join(";"),document.documentElement.appendChild(r),document.documentElement.appendChild(u);let S=null,w=k=>{let R=document.elementFromPoint(k.clientX,k.clientY);if(!R||Z.has(R.tagName)||_(R)){r.style.display="none",u.style.display="none",S=null;return}S=R,requestAnimationFrame(()=>{if(S!==R)return;let I=R.getBoundingClientRect();r.style.top=`${I.top}px`,r.style.left=`${I.left}px`,r.style.width=`${I.width}px`,r.style.height=`${I.height}px`,r.style.display="block",u.style.left=`${k.clientX+14}px`,u.style.top=`${k.clientY+14}px`,u.style.display="block"})},L=()=>{r.style.display="none",u.style.display="none",S=null};return document.addEventListener("mousemove",w,!0),document.addEventListener("mouseleave",L),()=>{document.removeEventListener("mousemove",w,!0),document.removeEventListener("mouseleave",L),r.remove(),u.remove()}},[i]),$(()=>{if(!i)return;let r=u=>{let S=u.target;if(!(S instanceof Element)||_(S))return;u.preventDefault(),u.stopPropagation();let w=S.getBoundingClientRect(),L=w.width>0?(u.clientX-w.left)/w.width:.5,k=w.height>0?(u.clientY-w.top)/w.height:.5;s({cssSelector:K(S),pagePath:window.location.pathname,surroundingHtml:Q(S),rect:{top:w.top+window.scrollY,left:w.left+window.scrollX,width:w.width,height:w.height},clickX:u.clientX+window.scrollX,clickY:u.clientY+window.scrollY,offsetXRatio:Math.min(1,Math.max(0,L)),offsetYRatio:Math.min(1,Math.max(0,k))}),a(!1)};return document.addEventListener("click",r,{capture:!0}),()=>document.removeEventListener("click",r,{capture:!0})},[i]);let f=async()=>{if(!c.current)return;let r=await fetch(`${c.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,{credentials:"include"});if(!r.ok)return;let u=await r.json();t(u)},m=async r=>{!c.current||!e||!l||!(await fetch(`${c.current}/api/feedback-comments`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({feedbackSessionId:e.feedbackSessionId,content:r,pagePath:l.pagePath,cssSelector:l.cssSelector,surroundingHtml:l.surroundingHtml,offsetXRatio:l.offsetXRatio,offsetYRatio:l.offsetYRatio})})).ok||(s(null),a(!0),await f())},v=async(r,u)=>{!c.current||!(await fetch(`${c.current}/api/feedback-comments/${r}`,{method:"PATCH",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({content:u})})).ok||(b(null),await f())},C=async r=>{!c.current||!(await fetch(`${c.current}/api/feedback-comments/${r}`,{method:"DELETE",credentials:"include"})).ok||await f()},V=async()=>{if(!(!c.current||!e)&&window.confirm("Finalize this feedback session? You won't be able to add more comments.")){y(!0);try{if(!(await fetch(`${c.current}/api/feedback-sessions/${e.feedbackSessionId}/finalize`,{method:"POST",credentials:"include"})).ok)return;t(u=>u&&{...u,canComment:!1})}finally{y(!1)}}},W=r=>{if(r.pagePath!==window.location.pathname){window.location.assign(r.pagePath);return}let u=null;try{u=document.querySelector(r.cssSelector)}catch{u=null}u&&(u.scrollIntoView({behavior:"smooth",block:"center"}),b(r.id))};if(n||!e)return null;if(!e.canComment)return e.sessionStatus!=="processing"&&e.sessionStatus!=="in_review"?null:p("div",{"data-yns-feedback-ui":"true",children:p(oe,{progress:e.progress,eta:e.eta,status:e.sessionStatus})});let z=e.comments.filter(r=>r.pagePath===window.location.pathname&&r.status!=="done");return x("div",{"data-yns-feedback-ui":"true",children:[z.map((r,u)=>p(re,{pin:r,number:u+1,editing:d===r.id,onStartEdit:()=>b(r.id),onCancelEdit:()=>b(null),onSave:S=>v(r.id,S),onRemove:()=>C(r.id)},r.id)),l&&p(le,{pending:l,onCancel:()=>{s(null),a(!0)},onSave:r=>m(r)}),g&&p(ae,{comments:e.comments,currentPath:window.location.pathname,onClose:()=>h(!1),onSelect:W}),x("div",{style:ce,children:[p("button",{type:"button",onClick:()=>a(r=>!r),style:i?ue:Y,children:i?"Cancel":"Add comment"}),p("button",{type:"button",onClick:()=>h(r=>!r),style:pe,children:g?"Hide list":`List (${e.comments.length})`}),p("button",{type:"button",onClick:V,disabled:P,style:me,children:P?"Finalizing\u2026":"Finalize"}),p("span",{style:fe,children:i?"Click any element to comment":`${z.length} on this page`})]})]})}function oe({progress:e,eta:t,status:n}){let[,o]=E(0);$(()=>{let s=window.setInterval(()=>o(d=>d+1),6e4);return()=>window.clearInterval(s)},[]);let i=te(t);return x("div",{style:Re,children:[p("style",{children:ie}),x("div",{style:Ie,children:[p(se,{}),p("span",{style:Te,children:"Submitted"}),p("strong",{style:{fontSize:13},children:n==="in_review"?"Feedback under review":"Applying feedback"})]}),x("div",{style:Ae,children:[p("span",{children:"Review progress"}),p("span",{style:{opacity:.7},children:e.label})]}),p("div",{style:Me,children:p("div",{style:{...Ne,width:`${e.fillPct}%`}})}),x("p",{style:ze,children:["Estimated delivery: ",p("span",{style:{fontWeight:600},children:i})]})]})}var ie="@keyframes yns-feedback-spin { to { transform: rotate(360deg); } }";function se({size:e=14}){return p("span",{"aria-hidden":!0,style:{display:"inline-block",width:e,height:e,border:"2px solid rgba(16, 185, 129, 0.25)",borderTopColor:"#10b981",borderRadius:999,animation:"yns-feedback-spin 0.9s linear infinite"}})}function re({pin:e,number:t,editing:n,onStartEdit:o,onCancelEdit:i,onSave:a,onRemove:l}){let s=de(e.cssSelector,e.offsetXRatio,e.offsetYRatio);return s?x("div",{style:{position:"absolute",top:s.top,left:s.left,zIndex:2147483600,pointerEvents:"auto"},children:[p("button",{type:"button",onClick:o,style:D,title:e.content,children:t}),n&&p(X,{initial:e.content,onCancel:i,onSave:a,onRemove:l})]}):null}function le({pending:e,onCancel:t,onSave:n}){return x(Fe,{children:[p("div",{style:{position:"absolute",top:e.rect.top+e.rect.height*e.offsetYRatio-12,left:e.rect.left+e.rect.width*e.offsetXRatio-12,zIndex:2147483600,pointerEvents:"none"},children:p("div",{style:D,children:"\u2022"})}),p("div",{style:{position:"absolute",top:e.clickY+10,left:e.clickX+10,zIndex:2147483647},children:p(X,{initial:"",onCancel:t,onSave:n})})]})}function ae({comments:e,currentPath:t,onClose:n,onSelect:o}){let i=new Map;for(let l of e){let s=i.get(l.pagePath)??[];s.push(l),i.set(l.pagePath,s)}let a=Array.from(i.keys()).sort((l,s)=>l===t?-1:s===t?1:l.localeCompare(s));return x("div",{style:Se,children:[x("div",{style:we,children:[x("strong",{style:{fontSize:14},children:["Comments (",e.length,")"]}),p("button",{type:"button",onClick:n,style:U,children:"Close"})]}),p("div",{style:xe,children:e.length===0?p("div",{style:Ee,children:"No comments yet. Click \u201CAdd comment\u201D to leave one."}):a.map(l=>x("div",{style:{marginBottom:12},children:[p("div",{style:Ce,children:l===t?`${l} \xB7 current`:l}),(i.get(l)??[]).map((s,d)=>x("button",{type:"button",onClick:()=>o(s),style:ke,disabled:s.status==="done"&&!1,children:[p("span",{style:Pe,children:d+1}),p("span",{style:$e,children:s.content.length>140?`${s.content.slice(0,140)}\u2026`:s.content}),s.status==="done"&&p("span",{style:Le,children:"done"})]},s.id))]},l))})]})}function X({initial:e,onCancel:t,onSave:n,onRemove:o}){let[i,a]=E(e),[l,s]=E(!1),d=j(null);return $(()=>{let g=d.current;if(!g)return;g.focus();let h=g.value.length;g.setSelectionRange(h,h)},[]),x("form",{onSubmit:async g=>{g.preventDefault();let h=i.trim();if(h){s(!0);try{await n(h)}finally{s(!1)}}},style:ge,children:[p("textarea",{ref:d,value:i,onChange:g=>a(g.target.value),placeholder:"Leave a comment\u2026",style:ye,rows:3}),x("div",{style:be,children:[o&&p("button",{type:"button",onClick:()=>o(),style:ve,disabled:l,children:"Delete"}),p("div",{style:{flex:1}}),p("button",{type:"button",onClick:t,style:U,disabled:l,children:"Cancel"}),p("button",{type:"submit",style:he,disabled:l||!i.trim(),children:l?"Saving\u2026":"Save"})]})]})}function de(e,t,n){let[o,i]=E(null);return $(()=>{let a=()=>{let s=null;try{s=document.querySelector(e)}catch{s=null}if(!s){i(null);return}let d=s.getBoundingClientRect();i({top:d.top+window.scrollY+d.height*n-12,left:d.left+window.scrollX+d.width*t-12})};a();let l=new ResizeObserver(a);try{let s=document.querySelector(e);s&&l.observe(s)}catch{}return window.addEventListener("scroll",a,!0),window.addEventListener("resize",a),()=>{l.disconnect(),window.removeEventListener("scroll",a,!0),window.removeEventListener("resize",a)}},[e,t,n]),o}var ce={position:"fixed",bottom:16,left:"50%",transform:"translateX(-50%)",zIndex:2147483646,display:"flex",alignItems:"center",gap:8,padding:"8px 12px",background:"rgba(17, 17, 17, 0.92)",color:"white",borderRadius:999,boxShadow:"0 8px 24px rgba(0,0,0,0.25)",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',fontSize:14,pointerEvents:"auto"},Y={border:"none",background:"white",color:"#111",padding:"6px 12px",borderRadius:999,cursor:"pointer",fontWeight:600,fontSize:13},ue={...Y,background:"#ef4444",color:"white"},pe={border:"1px solid rgba(255,255,255,0.4)",background:"transparent",color:"white",padding:"6px 12px",borderRadius:999,cursor:"pointer",fontWeight:500,fontSize:13},me={border:"none",background:"#10b981",color:"white",padding:"6px 12px",borderRadius:999,cursor:"pointer",fontWeight:600,fontSize:13},fe={opacity:.8,fontSize:12},D={width:24,height:24,borderRadius:999,background:"#10b981",color:"white",border:"2px solid white",cursor:"pointer",fontSize:12,fontWeight:700,boxShadow:"0 2px 6px rgba(0,0,0,0.3)",display:"inline-flex",alignItems:"center",justifyContent:"center",padding:0},ge={background:"white",border:"1px solid #e5e7eb",borderRadius:8,padding:12,width:280,boxShadow:"0 12px 32px rgba(0,0,0,0.18)",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',display:"flex",flexDirection:"column",gap:8,color:"#111"},ye={width:"100%",border:"1px solid #d1d5db",borderRadius:6,padding:8,fontSize:14,resize:"vertical",fontFamily:"inherit",color:"#111",background:"white",boxSizing:"border-box"},be={display:"flex",alignItems:"center",gap:6},N={border:"1px solid transparent",borderRadius:6,padding:"6px 10px",fontSize:13,cursor:"pointer",fontWeight:500},he={...N,background:"#111",color:"white"},U={...N,background:"transparent",color:"#374151",borderColor:"#d1d5db"},ve={...N,background:"transparent",color:"#b91c1c",borderColor:"#fecaca"},Se={position:"fixed",top:0,right:0,bottom:0,width:360,maxWidth:"92vw",background:"white",borderLeft:"1px solid #e5e7eb",boxShadow:"-12px 0 32px rgba(0,0,0,0.12)",zIndex:2147483646,display:"flex",flexDirection:"column",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',color:"#111"},we={display:"flex",alignItems:"center",justifyContent:"space-between",padding:"12px 16px",borderBottom:"1px solid #e5e7eb"},xe={flex:1,overflow:"auto",padding:"12px 12px 24px"},Ee={color:"#6b7280",fontSize:13,padding:"24px 8px",textAlign:"center"},Ce={fontSize:11,textTransform:"uppercase",letterSpacing:.5,color:"#6b7280",padding:"4px 4px 6px",wordBreak:"break-all"},ke={display:"flex",alignItems:"flex-start",gap:8,width:"100%",textAlign:"left",background:"white",border:"1px solid #e5e7eb",borderRadius:6,padding:"8px 10px",cursor:"pointer",fontSize:13,marginBottom:6,color:"#111"},Pe={flexShrink:0,width:22,height:22,borderRadius:999,background:"#10b981",color:"white",fontWeight:700,fontSize:11,display:"inline-flex",alignItems:"center",justifyContent:"center"},$e={flex:1,whiteSpace:"pre-wrap",wordBreak:"break-word"},Le={flexShrink:0,background:"#d1fae5",color:"#065f46",fontSize:11,fontWeight:600,padding:"2px 6px",borderRadius:4,alignSelf:"center"},Re={position:"fixed",bottom:16,left:"50%",transform:"translateX(-50%)",zIndex:2147483646,display:"flex",flexDirection:"column",gap:8,padding:"12px 16px",width:"min(420px, calc(100vw - 32px))",background:"white",border:"1px solid #e5e7eb",borderRadius:12,boxShadow:"0 12px 32px rgba(0,0,0,0.18)",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',color:"#111",pointerEvents:"auto"},Ie={display:"flex",alignItems:"center",gap:8},Te={background:"#d1fae5",color:"#065f46",fontSize:11,fontWeight:700,padding:"2px 8px",borderRadius:999,textTransform:"uppercase",letterSpacing:.5},Ae={display:"flex",justifyContent:"space-between",fontSize:12},Me={width:"100%",height:6,background:"#f3f4f6",borderRadius:999,overflow:"hidden"},Ne={height:"100%",background:"#10b981",borderRadius:999,transition:"width 500ms"},ze={margin:0,fontSize:12,color:"#6b7280"};function B(){if(console.log("[YNS Feedback Toolbar] mountFeedbackToolbar() called",{isWindow:typeof window<"u",vercelEnv:process.env.NEXT_PUBLIC_VERCEL_ENV,alreadyMounted:typeof document<"u"&&!!document.getElementById(A)}),typeof window>"u")return null;if(process.env.NEXT_PUBLIC_VERCEL_ENV!=="preview")return console.log("[YNS Feedback Toolbar] gate failed \u2014 NEXT_PUBLIC_VERCEL_ENV is",process.env.NEXT_PUBLIC_VERCEL_ENV),null;if(document.getElementById(A))return null;let e=document.createElement("div");e.id=A,e.dataset.ynsFeedbackUi="true",document.body.appendChild(e);let t=q(e);return t.render(p(ne,{})),{unmount:()=>{t.unmount(),e.remove()}}}typeof window<"u"&&console.log("[YNS Feedback Toolbar] module evaluated",{vercelEnv:process.env.NEXT_PUBLIC_VERCEL_ENV,readyState:document.readyState,willAutoMount:process.env.NEXT_PUBLIC_VERCEL_ENV==="preview"});typeof window<"u"&&process.env.NEXT_PUBLIC_VERCEL_ENV==="preview"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{B()}):B());function H(){typeof window>"u"||(console.log("[YNS Sandbox Inspectors] module evaluated"),je(),Xe())}H();var _e=new Set(["HEAD","SCRIPT","STYLE","NOSCRIPT","HTML"]);function Be(e){if(e.id)return`${e.tagName.toLowerCase()}#${CSS.escape(e.id)}`;let t=[],n=e;for(;n&&n!==document.body&&n!==document.documentElement;){let o=n.tagName.toLowerCase();if(n.id){t.unshift(`${o}#${CSS.escape(n.id)}`);break}let i=Array.from(n.classList).filter(s=>!s.startsWith("data-yns-"));i.length>0&&(o+=`.${i.map(s=>CSS.escape(s)).join(".")}`);let a=n.parentElement,l=n.tagName;if(a&&Array.from(a.children).filter(d=>d.tagName===l&&(i.length===0||i.every(b=>d.classList.contains(b)))).length>1){let d=Array.from(a.children).indexOf(n)+1;o+=`:nth-child(${d})`}t.unshift(o),n=n.parentElement}return t.join(" > ")}function O(e,t){let n=e.getBoundingClientRect(),o=window.getComputedStyle(e),i=(e.textContent??"").trim();return{tag:e.tagName.toLowerCase(),id:e.id||void 0,classes:Array.from(e.classList).filter(a=>!a.startsWith("data-yns-")),textContent:i.length>t?`${i.slice(0,t)}\u2026`:i,cssSelector:Be(e),boundingRect:{top:n.top,left:n.left,width:n.width,height:n.height},computedStyles:{color:o.color,backgroundColor:o.backgroundColor,fontSize:o.fontSize,fontFamily:o.fontFamily,padding:o.padding,margin:o.margin}}}function T(e,t){if(!e||!e.tagName||_e.has(e.tagName))return!0;for(let n of t)if(e.hasAttribute?.(n))return!0;return!1}function je(){let e=!1,t=null,n=null,o=document.createElement("div");o.setAttribute("data-yns-design-overlay","hover"),o.style.cssText=["position: fixed","pointer-events: none","z-index: 2147483646","border: 2px solid #3b82f6","background: rgba(59, 130, 246, 0.08)","border-radius: 3px","display: none","transition: top 0.05s, left 0.05s, width 0.05s, height 0.05s"].join(";");let i=document.createElement("div");i.setAttribute("data-yns-design-overlay","label"),i.style.cssText=["position: fixed","pointer-events: none","z-index: 2147483647","background: #3b82f6","color: #fff","font-size: 11px","font-family: ui-monospace, monospace","padding: 2px 6px","border-radius: 3px","white-space: nowrap","display: none"].join(";"),document.documentElement.appendChild(o),document.documentElement.appendChild(i);let a=document.createElement("style");a.setAttribute("data-yns-design-overlay","style"),a.textContent="[data-yns-selected] { outline: 2px solid #3b82f6 !important; outline-offset: 1px; }",document.head.appendChild(a);function l(y){let c=y.getBoundingClientRect();o.style.top=`${c.top}px`,o.style.left=`${c.left}px`,o.style.width=`${c.width}px`,o.style.height=`${c.height}px`,o.style.display="block";let f=Array.from(y.classList).filter(v=>!v.startsWith("data-yns-")),m=y.tagName.toLowerCase()+(f.length?`.${f[0]}`:"");i.textContent=m,i.style.left=`${c.left}px`,i.style.top=`${Math.max(0,c.top-22)}px`,i.style.display="block"}let s=y=>{let c=document.elementFromPoint(y.clientX,y.clientY);if(T(c,["data-yns-design-overlay"])){o.style.display="none",i.style.display="none",t=null;return}t=c,requestAnimationFrame(()=>{t===c&&e&&c&&l(c)})},d=()=>{o.style.display="none",i.style.display="none",t=null},b=y=>{if(!e||!t)return;y.preventDefault(),y.stopPropagation(),y.stopImmediatePropagation();let c=t;if(T(c,["data-yns-design-overlay"]))return;let f=O(c,120),m=f.cssSelector;n&&n.el.removeAttribute("data-yns-selected"),n&&n.cssSelector===m?(n=null,window.parent.postMessage({type:"element-deselected",data:f},"*")):(n={el:c,cssSelector:m},c.setAttribute("data-yns-selected",""),window.parent.postMessage({type:"element-selected",data:f},"*"))},g=y=>{y.key==="Escape"&&e&&(P(),window.parent.postMessage({type:"design-mode-cleared"},"*"))};function h(){e=!0,document.addEventListener("mousemove",s,!0),document.addEventListener("mouseleave",d),document.addEventListener("click",b,!0),document.addEventListener("keydown",g,!0)}function P(){e=!1,document.removeEventListener("mousemove",s,!0),document.removeEventListener("mouseleave",d),document.removeEventListener("click",b,!0),document.removeEventListener("keydown",g,!0),o.style.display="none",i.style.display="none",t=null,n&&(n.el.removeAttribute("data-yns-selected"),n=null)}window.addEventListener("message",y=>{let c=y.data;!c||typeof c!="object"||(c.type==="design-mode-toggle"&&(c.enabled?h():(P(),window.parent.postMessage({type:"design-mode-cleared"},"*"))),c.type==="design-mode-deselect"&&n&&(n.el.removeAttribute("data-yns-selected"),n=null))})}function Xe(){let e=!1,t=null,n=[],o=document.createElement("div");o.setAttribute("data-yns-comment-overlay","hover"),o.style.cssText=["position: fixed","pointer-events: none","z-index: 2147483644","border: 2px dashed #10b981","background: rgba(16, 185, 129, 0.06)","border-radius: 3px","display: none","transition: top 0.05s, left 0.05s, width 0.05s, height 0.05s"].join(";");let i=document.createElement("div");i.setAttribute("data-yns-comment-overlay","cursor-label"),i.textContent="Click to comment",i.style.cssText=["position: fixed","pointer-events: none","z-index: 2147483645","background: #059669","color: #fff","font-size: 11px","font-family: ui-sans-serif, system-ui, sans-serif","padding: 3px 8px","border-radius: 4px","white-space: nowrap","display: none","box-shadow: 0 2px 6px rgba(0,0,0,0.15)"].join(";");let a=document.createElement("div");a.setAttribute("data-yns-comment-overlay","pins"),a.style.cssText="position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 2147483643;",document.documentElement.appendChild(o),document.documentElement.appendChild(i),document.documentElement.appendChild(a);function l(){a.innerHTML="";for(let f of n){let m=null;try{m=document.querySelector(f.selector)}catch{m=null}if(!m)continue;let v=m.getBoundingClientRect(),C=document.createElement("div");C.style.cssText=["position: fixed",`top: ${v.top-12}px`,`left: ${v.left+v.width/2-12}px`,"width: 24px","height: 24px","border-radius: 50%","background: #059669","color: #fff","display: flex","align-items: center","justify-content: center","font-size: 12px","font-weight: 600","font-family: ui-sans-serif, system-ui, sans-serif","box-shadow: 0 2px 8px rgba(0,0,0,0.2)","pointer-events: none"].join(";"),C.textContent=String(f.number),a.appendChild(C)}}let s=f=>{let m=document.elementFromPoint(f.clientX,f.clientY);if(T(m,["data-yns-comment-overlay","data-yns-design-overlay"])){o.style.display="none",i.style.display="none",t=null;return}t=m,requestAnimationFrame(()=>{if(t===m&&e&&m){let v=m.getBoundingClientRect();o.style.top=`${v.top}px`,o.style.left=`${v.left}px`,o.style.width=`${v.width}px`,o.style.height=`${v.height}px`,o.style.display="block",i.style.left=`${f.clientX+14}px`,i.style.top=`${f.clientY+14}px`,i.style.display="block"}})},d=()=>{o.style.display="none",i.style.display="none",t=null},b=f=>{if(!e||!t)return;f.preventDefault(),f.stopPropagation(),f.stopImmediatePropagation();let m=t;if(T(m,["data-yns-comment-overlay","data-yns-design-overlay"]))return;let v=O(m,200),C=m.getBoundingClientRect();window.parent.postMessage({type:"comment-click",data:{element:v,clickPosition:{x:f.clientX,y:f.clientY},elementRect:{top:C.top,left:C.left,width:C.width,height:C.height},pagePath:window.location.pathname}},"*")},g=f=>{f.key==="Escape"&&e&&(P(),window.parent.postMessage({type:"comment-mode-cleared"},"*"))};function h(){e=!0,document.body.style.cursor="crosshair",document.addEventListener("mousemove",s,!0),document.addEventListener("mouseleave",d),document.addEventListener("click",b,!0),document.addEventListener("keydown",g,!0)}function P(){e=!1,document.body.style.cursor="",document.removeEventListener("mousemove",s,!0),document.removeEventListener("mouseleave",d),document.removeEventListener("click",b,!0),document.removeEventListener("keydown",g,!0),o.style.display="none",i.style.display="none",t=null}let y=!1,c=()=>{y||(y=!0,requestAnimationFrame(()=>{y=!1,l()}))};window.addEventListener("scroll",c,{passive:!0}),window.addEventListener("resize",c,{passive:!0}),window.addEventListener("message",f=>{let m=f.data;!m||typeof m!="object"||(m.type==="comment-mode-toggle"&&(m.enabled?h():P()),m.type==="comment-pins-update"&&(n=m.pins??[],l()),m.type==="comment-pin-remove"&&(n=n.filter(v=>v.id!==m.pinId),l()))})}export{B as mountFeedbackToolbar,H as startSandboxInspectors};
|
|
3
3
|
//# sourceMappingURL=browser.js.map
|
package/dist/browser.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/feedback-toolbar.tsx","../src/sandbox-inspectors.ts"],"sourcesContent":["/**\n * Feedback session toolbar — side-effect entry.\n *\n * Importing `commerce-kit/feedback-toolbar` (or `commerce-kit/browser` for the\n * combined entry) mounts a floating toolbar onto the page that lets reviewers\n * leave click-anchored comments. Comments persist via YNS API endpoints,\n * authenticated with the `better-auth` session cookie sent through\n * `credentials: \"include\"`.\n *\n * Gated on `process.env.NEXT_PUBLIC_VERCEL_ENV === \"preview\"` — only Vercel\n * preview deploys get the toolbar. Sandbox dev (env undefined) and production\n * (env === \"production\") skip it.\n */\n\nimport { type CSSProperties, type FormEvent, useEffect, useRef, useState } from \"react\";\nimport { createRoot } from \"react-dom/client\";\n\nconst MOUNT_NODE_ID = \"yns-feedback-toolbar-root\";\n\ninterface FeedbackComment {\n\tid: string;\n\tpagePath: string;\n\tcssSelector: string;\n\tcontent: string;\n\tstatus: \"todo\" | \"done\";\n\toffsetXRatio: number;\n\toffsetYRatio: number;\n}\n\ntype SessionStatus = \"created\" | \"in_progress\" | \"processing\" | \"in_review\" | \"done\";\n\ninterface ReviewProgress {\n\tfillPct: number;\n\tlabel: string;\n}\n\ninterface ActiveSession {\n\tfeedbackSessionId: string;\n\tcomments: FeedbackComment[];\n\tcanComment: boolean;\n\tsessionStatus: SessionStatus;\n\tcommentTotal: number;\n\tcommentDone: number;\n\teta: string;\n\tprogress: ReviewProgress;\n}\n\ninterface PendingPin {\n\tcssSelector: string;\n\tpagePath: string;\n\tsurroundingHtml: string;\n\trect: { top: number; left: number; width: number; height: number };\n\tclickX: number;\n\tclickY: number;\n\toffsetXRatio: number;\n\toffsetYRatio: number;\n}\n\nconst buildApiBase = (): string | null => {\n\tif (typeof window === \"undefined\") return null;\n\tconst override = (process.env.NEXT_PUBLIC_YNS_API_BASE ?? \"\").trim();\n\tif (override) return override.replace(/\\/$/, \"\");\n\n\t// Toolbar runs on a per-store preview deploy (mystore-preview.yns.{store|cx})\n\t// served by the merchant's Vercel project — that host has no YNS API. Aim\n\t// at the apex `yns.store` / `yns.cx`, which yns-app serves and where the\n\t// API lives. Cookies scoped to `.yns.store` / `.yns.cx` travel along.\n\tconst host = window.location.hostname;\n\tconst protocol = window.location.protocol;\n\tif (host.endsWith(\".yns.store\")) return `${protocol}//yns.store`;\n\tif (host.endsWith(\".yns.cx\")) return `${protocol}//yns.cx`;\n\treturn window.location.origin;\n};\n\nconst computeCssSelector = (el: Element): string => {\n\tif (el.id) return `#${CSS.escape(el.id)}`;\n\tconst path: string[] = [];\n\tlet node: Element | null = el;\n\twhile (node && node.nodeType === Node.ELEMENT_NODE && path.length < 6) {\n\t\tlet part = node.tagName.toLowerCase();\n\t\tconst className = node.getAttribute(\"class\");\n\t\tif (className) {\n\t\t\tconst classes = className\n\t\t\t\t.split(/\\s+/)\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.slice(0, 2)\n\t\t\t\t.map((c) => `.${CSS.escape(c)}`)\n\t\t\t\t.join(\"\");\n\t\t\tpart += classes;\n\t\t}\n\t\tconst parent: Element | null = node.parentElement;\n\t\tconst tag = node.tagName;\n\t\tif (parent) {\n\t\t\tconst siblings: Element[] = Array.from(parent.children).filter((c) => c.tagName === tag);\n\t\t\tif (siblings.length > 1) {\n\t\t\t\tconst idx = siblings.indexOf(node) + 1;\n\t\t\t\tpart += `:nth-of-type(${idx})`;\n\t\t\t}\n\t\t}\n\t\tpath.unshift(part);\n\t\tnode = parent;\n\t}\n\treturn path.join(\" > \");\n};\n\nconst ATTRS_OF_INTEREST = [\"id\", \"class\", \"data-testid\", \"aria-label\", \"role\", \"name\", \"href\", \"src\"];\n\nconst formatAttrs = (el: Element): string => {\n\tconst parts: string[] = [];\n\tfor (const attr of ATTRS_OF_INTEREST) {\n\t\tconst v = el.getAttribute(attr);\n\t\tif (!v) continue;\n\t\tconst trimmed = v.length > 80 ? `${v.slice(0, 80)}…` : v;\n\t\tparts.push(` ${attr}=\"${trimmed.replace(/\"/g, \""\")}\"`);\n\t}\n\treturn parts.join(\"\");\n};\n\nconst formatTextContent = (el: Element): string => {\n\tconst text = (el.textContent ?? \"\").replace(/\\s+/g, \" \").trim();\n\tif (!text) return \"\";\n\treturn text.length > 100 ? `${text.slice(0, 100)}…` : text;\n};\n\nconst buildSurroundingHtml = (target: Element): string => {\n\tconst ancestors: Element[] = [];\n\tlet cur: Element | null = target;\n\twhile (cur && cur !== document.documentElement && ancestors.length < 5) {\n\t\tconst parent: Element | null = cur.parentElement;\n\t\tif (!parent) break;\n\t\tancestors.unshift(parent);\n\t\tcur = parent;\n\t}\n\n\tconst lines: string[] = [];\n\tlet depth = 0;\n\tfor (const ancestor of ancestors) {\n\t\tconst indent = \" \".repeat(depth);\n\t\tlines.push(`${indent}<${ancestor.tagName.toLowerCase()}${formatAttrs(ancestor)}>`);\n\t\tdepth++;\n\t}\n\n\tconst targetIndent = \" \".repeat(depth);\n\tconst targetTag = target.tagName.toLowerCase();\n\tconst targetText = formatTextContent(target);\n\tlines.push(\n\t\t`${targetIndent}<${targetTag}${formatAttrs(target)}>${\n\t\t\ttargetText ? `${targetText}</${targetTag}>` : \"\"\n\t\t} ← TARGET`,\n\t);\n\n\tif (!targetText) {\n\t\tconst childIndent = \" \".repeat(depth + 1);\n\t\tconst children = Array.from(target.children).slice(0, 6);\n\t\tfor (const child of children) {\n\t\t\tconst childText = formatTextContent(child);\n\t\t\tlines.push(\n\t\t\t\t`${childIndent}<${child.tagName.toLowerCase()}${formatAttrs(child)}>${\n\t\t\t\t\tchildText ? `${childText}</${child.tagName.toLowerCase()}>` : \"\"\n\t\t\t\t}`,\n\t\t\t);\n\t\t}\n\t\tif (target.children.length > 6) {\n\t\t\tlines.push(`${childIndent}… (${target.children.length - 6} more children)`);\n\t\t}\n\t\tlines.push(`${targetIndent}</${targetTag}>`);\n\t}\n\n\tfor (let i = ancestors.length - 1; i >= 0; i--) {\n\t\tconst indent = \" \".repeat(i);\n\t\tconst ancestor = ancestors[i];\n\t\tif (!ancestor) continue;\n\t\tlines.push(`${indent}</${ancestor.tagName.toLowerCase()}>`);\n\t}\n\n\treturn lines.join(\"\\n\");\n};\n\nconst isInsideToolbar = (el: Element | null): boolean => {\n\tlet node = el;\n\twhile (node) {\n\t\tif (node instanceof HTMLElement && node.dataset.ynsFeedbackUi === \"true\") return true;\n\t\tnode = node.parentElement;\n\t}\n\treturn false;\n};\n\nconst IGNORED_TAGS = new Set([\"HTML\", \"HEAD\", \"SCRIPT\", \"STYLE\", \"NOSCRIPT\"]);\n\nconst POLL_INTERVAL_MS = 10_000;\n\n// Format a server-computed ETA as remaining + absolute label. The deadline\n// math itself lives in yns-app's `lib/feedback-comments-api.ts` so the toolbar\n// and YNS lock screen stay in sync.\nconst formatEta = (etaIso: string): string => {\n\tconst eta = new Date(etaIso);\n\tconst remainingMs = eta.getTime() - Date.now();\n\tconst dateLabel = eta.toLocaleString(undefined, {\n\t\tweekday: \"short\",\n\t\tmonth: \"short\",\n\t\tday: \"numeric\",\n\t\thour: \"numeric\",\n\t\tminute: \"2-digit\",\n\t});\n\n\tif (remainingMs <= 0) return `Any moment now (estimated by ${dateLabel})`;\n\tconst remainingMin = Math.ceil(remainingMs / 60_000);\n\tif (remainingMin < 60) return `~${remainingMin} minutes (by ${dateLabel})`;\n\tconst remainingHours = Math.round(remainingMs / 3_600_000);\n\tif (remainingHours < 24) {\n\t\treturn `~${remainingHours} ${remainingHours === 1 ? \"hour\" : \"hours\"} (by ${dateLabel})`;\n\t}\n\tconst remainingDays = Math.round(remainingMs / 86_400_000);\n\treturn `~${remainingDays} ${remainingDays === 1 ? \"day\" : \"days\"} (by ${dateLabel})`;\n};\n\nfunction FeedbackToolbar() {\n\tconst [session, setSession] = useState<ActiveSession | null>(null);\n\tconst [loading, setLoading] = useState(true);\n\tconst [pinMode, setPinMode] = useState(false);\n\tconst [pending, setPending] = useState<PendingPin | null>(null);\n\tconst [editingId, setEditingId] = useState<string | null>(null);\n\tconst [sidebarOpen, setSidebarOpen] = useState(false);\n\tconst [finalizing, setFinalizing] = useState(false);\n\tconst apiBase = useRef<string | null>(null);\n\n\tuseEffect(() => {\n\t\tapiBase.current = buildApiBase();\n\t\tif (!apiBase.current) {\n\t\t\tsetLoading(false);\n\t\t\treturn;\n\t\t}\n\n\t\tlet cancelled = false;\n\t\tconst controller = new AbortController();\n\n\t\tconst fetchOnce = async () => {\n\t\t\ttry {\n\t\t\t\tconst res = await fetch(\n\t\t\t\t\t`${apiBase.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,\n\t\t\t\t\t{ credentials: \"include\", signal: controller.signal },\n\t\t\t\t);\n\t\t\t\tif (cancelled) return;\n\t\t\t\tif (!res.ok) {\n\t\t\t\t\tsetSession(null);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst data = (await res.json()) as ActiveSession;\n\t\t\t\tif (cancelled) return;\n\t\t\t\tsetSession(data);\n\t\t\t\t// Hard-refresh once the AI run lands and the preview is up to date.\n\t\t\t\t// Out of scope to diff anything smarter — the new deploy replaces\n\t\t\t\t// the page entirely so a full reload is appropriate here.\n\t\t\t\tif (data.sessionStatus === \"done\") {\n\t\t\t\t\twindow.location.reload();\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tif (!cancelled) setSession(null);\n\t\t\t} finally {\n\t\t\t\tif (!cancelled) setLoading(false);\n\t\t\t}\n\t\t};\n\n\t\tvoid fetchOnce();\n\t\tconst interval = window.setInterval(fetchOnce, POLL_INTERVAL_MS);\n\n\t\treturn () => {\n\t\t\tcancelled = true;\n\t\t\tcontroller.abort();\n\t\t\twindow.clearInterval(interval);\n\t\t};\n\t}, []);\n\n\t// When the session stops accepting comments (finalized → in_review, or\n\t// closed), tear down any in-flight UI so the hover overlay/popover/sidebar\n\t// don't linger after the toolbar's main render returns null.\n\tuseEffect(() => {\n\t\tif (!session || session.canComment) return;\n\t\tsetPinMode(false);\n\t\tsetPending(null);\n\t\tsetSidebarOpen(false);\n\t\tsetEditingId(null);\n\t}, [session]);\n\n\tuseEffect(() => {\n\t\tif (!pinMode) return;\n\t\tdocument.body.style.cursor = \"crosshair\";\n\t\treturn () => {\n\t\t\tdocument.body.style.cursor = \"\";\n\t\t};\n\t}, [pinMode]);\n\n\tuseEffect(() => {\n\t\tif (!pinMode) return;\n\n\t\tconst overlay = document.createElement(\"div\");\n\t\toverlay.dataset.ynsFeedbackUi = \"true\";\n\t\toverlay.style.cssText = [\n\t\t\t\"position: fixed\",\n\t\t\t\"pointer-events: none\",\n\t\t\t\"z-index: 2147483644\",\n\t\t\t\"border: 2px dashed #10b981\",\n\t\t\t\"background: rgba(16, 185, 129, 0.08)\",\n\t\t\t\"border-radius: 3px\",\n\t\t\t\"display: none\",\n\t\t\t\"transition: top 0.05s, left 0.05s, width 0.05s, height 0.05s\",\n\t\t].join(\";\");\n\n\t\tconst label = document.createElement(\"div\");\n\t\tlabel.dataset.ynsFeedbackUi = \"true\";\n\t\tlabel.textContent = \"Click to comment\";\n\t\tlabel.style.cssText = [\n\t\t\t\"position: fixed\",\n\t\t\t\"pointer-events: none\",\n\t\t\t\"z-index: 2147483645\",\n\t\t\t\"background: #059669\",\n\t\t\t\"color: #fff\",\n\t\t\t\"font-size: 11px\",\n\t\t\t\"font-family: ui-sans-serif, system-ui, sans-serif\",\n\t\t\t\"padding: 3px 8px\",\n\t\t\t\"border-radius: 4px\",\n\t\t\t\"white-space: nowrap\",\n\t\t\t\"display: none\",\n\t\t\t\"box-shadow: 0 2px 6px rgba(0,0,0,0.15)\",\n\t\t].join(\";\");\n\n\t\tdocument.documentElement.appendChild(overlay);\n\t\tdocument.documentElement.appendChild(label);\n\n\t\tlet hovered: Element | null = null;\n\t\tconst handleMove = (e: MouseEvent) => {\n\t\t\tconst el = document.elementFromPoint(e.clientX, e.clientY);\n\t\t\tif (!el || IGNORED_TAGS.has(el.tagName) || isInsideToolbar(el)) {\n\t\t\t\toverlay.style.display = \"none\";\n\t\t\t\tlabel.style.display = \"none\";\n\t\t\t\thovered = null;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\thovered = el;\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\tif (hovered !== el) return;\n\t\t\t\tconst rect = el.getBoundingClientRect();\n\t\t\t\toverlay.style.top = `${rect.top}px`;\n\t\t\t\toverlay.style.left = `${rect.left}px`;\n\t\t\t\toverlay.style.width = `${rect.width}px`;\n\t\t\t\toverlay.style.height = `${rect.height}px`;\n\t\t\t\toverlay.style.display = \"block\";\n\t\t\t\tlabel.style.left = `${e.clientX + 14}px`;\n\t\t\t\tlabel.style.top = `${e.clientY + 14}px`;\n\t\t\t\tlabel.style.display = \"block\";\n\t\t\t});\n\t\t};\n\n\t\tconst handleLeave = () => {\n\t\t\toverlay.style.display = \"none\";\n\t\t\tlabel.style.display = \"none\";\n\t\t\thovered = null;\n\t\t};\n\n\t\tdocument.addEventListener(\"mousemove\", handleMove, true);\n\t\tdocument.addEventListener(\"mouseleave\", handleLeave);\n\n\t\treturn () => {\n\t\t\tdocument.removeEventListener(\"mousemove\", handleMove, true);\n\t\t\tdocument.removeEventListener(\"mouseleave\", handleLeave);\n\t\t\toverlay.remove();\n\t\t\tlabel.remove();\n\t\t};\n\t}, [pinMode]);\n\n\tuseEffect(() => {\n\t\tif (!pinMode) return;\n\t\tconst handleClick = (event: MouseEvent) => {\n\t\t\tconst target = event.target;\n\t\t\tif (!(target instanceof Element)) return;\n\t\t\tif (isInsideToolbar(target)) return;\n\n\t\t\tevent.preventDefault();\n\t\t\tevent.stopPropagation();\n\n\t\t\tconst rect = target.getBoundingClientRect();\n\t\t\tconst offsetXRatio = rect.width > 0 ? (event.clientX - rect.left) / rect.width : 0.5;\n\t\t\tconst offsetYRatio = rect.height > 0 ? (event.clientY - rect.top) / rect.height : 0.5;\n\t\t\tsetPending({\n\t\t\t\tcssSelector: computeCssSelector(target),\n\t\t\t\tpagePath: window.location.pathname,\n\t\t\t\tsurroundingHtml: buildSurroundingHtml(target),\n\t\t\t\trect: {\n\t\t\t\t\ttop: rect.top + window.scrollY,\n\t\t\t\t\tleft: rect.left + window.scrollX,\n\t\t\t\t\twidth: rect.width,\n\t\t\t\t\theight: rect.height,\n\t\t\t\t},\n\t\t\t\tclickX: event.clientX + window.scrollX,\n\t\t\t\tclickY: event.clientY + window.scrollY,\n\t\t\t\toffsetXRatio: Math.min(1, Math.max(0, offsetXRatio)),\n\t\t\t\toffsetYRatio: Math.min(1, Math.max(0, offsetYRatio)),\n\t\t\t});\n\t\t\tsetPinMode(false);\n\t\t};\n\t\tdocument.addEventListener(\"click\", handleClick, { capture: true });\n\t\treturn () => document.removeEventListener(\"click\", handleClick, { capture: true });\n\t}, [pinMode]);\n\n\tconst refreshComments = async () => {\n\t\tif (!apiBase.current) return;\n\t\tconst res = await fetch(\n\t\t\t`${apiBase.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,\n\t\t\t{ credentials: \"include\" },\n\t\t);\n\t\tif (!res.ok) return;\n\t\tconst data = (await res.json()) as ActiveSession;\n\t\tsetSession(data);\n\t};\n\n\tconst submitNewComment = async (content: string) => {\n\t\tif (!apiBase.current || !session || !pending) return;\n\t\tconst res = await fetch(`${apiBase.current}/api/feedback-comments`, {\n\t\t\tmethod: \"POST\",\n\t\t\tcredentials: \"include\",\n\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\tbody: JSON.stringify({\n\t\t\t\tfeedbackSessionId: session.feedbackSessionId,\n\t\t\t\tcontent,\n\t\t\t\tpagePath: pending.pagePath,\n\t\t\t\tcssSelector: pending.cssSelector,\n\t\t\t\tsurroundingHtml: pending.surroundingHtml,\n\t\t\t\toffsetXRatio: pending.offsetXRatio,\n\t\t\t\toffsetYRatio: pending.offsetYRatio,\n\t\t\t}),\n\t\t});\n\t\tif (!res.ok) return;\n\t\tsetPending(null);\n\t\tsetPinMode(true);\n\t\tawait refreshComments();\n\t};\n\n\tconst updateComment = async (id: string, content: string) => {\n\t\tif (!apiBase.current) return;\n\t\tconst res = await fetch(`${apiBase.current}/api/feedback-comments/${id}`, {\n\t\t\tmethod: \"PATCH\",\n\t\t\tcredentials: \"include\",\n\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\tbody: JSON.stringify({ content }),\n\t\t});\n\t\tif (!res.ok) return;\n\t\tsetEditingId(null);\n\t\tawait refreshComments();\n\t};\n\n\tconst removeComment = async (id: string) => {\n\t\tif (!apiBase.current) return;\n\t\tconst res = await fetch(`${apiBase.current}/api/feedback-comments/${id}`, {\n\t\t\tmethod: \"DELETE\",\n\t\t\tcredentials: \"include\",\n\t\t});\n\t\tif (!res.ok) return;\n\t\tawait refreshComments();\n\t};\n\n\tconst finalizeSession = async () => {\n\t\tif (!apiBase.current || !session) return;\n\t\tif (!window.confirm(\"Finalize this feedback session? You won't be able to add more comments.\")) return;\n\t\tsetFinalizing(true);\n\t\ttry {\n\t\t\tconst res = await fetch(\n\t\t\t\t`${apiBase.current}/api/feedback-sessions/${session.feedbackSessionId}/finalize`,\n\t\t\t\t{ method: \"POST\", credentials: \"include\" },\n\t\t\t);\n\t\t\tif (!res.ok) return;\n\t\t\tsetSession((prev) => (prev ? { ...prev, canComment: false } : prev));\n\t\t} finally {\n\t\t\tsetFinalizing(false);\n\t\t}\n\t};\n\n\tconst scrollToComment = (comment: FeedbackComment) => {\n\t\tif (comment.pagePath !== window.location.pathname) {\n\t\t\twindow.location.assign(comment.pagePath);\n\t\t\treturn;\n\t\t}\n\t\tlet el: Element | null = null;\n\t\ttry {\n\t\t\tel = document.querySelector(comment.cssSelector);\n\t\t} catch {\n\t\t\tel = null;\n\t\t}\n\t\tif (!el) return;\n\t\tel.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n\t\tsetEditingId(comment.id);\n\t};\n\n\tif (loading || !session) return null;\n\n\t// Session has been finalized for AI review. Show a small status panel with\n\t// the same progress + ETA shown in YNS, but no commenting controls. Polling\n\t// continues in the background; on `done` we hard-reload to pick up the\n\t// fresh deploy.\n\tif (!session.canComment) {\n\t\tif (session.sessionStatus !== \"processing\" && session.sessionStatus !== \"in_review\") return null;\n\t\treturn (\n\t\t\t<div data-yns-feedback-ui=\"true\">\n\t\t\t\t<SubmittedPanel progress={session.progress} eta={session.eta} status={session.sessionStatus} />\n\t\t\t</div>\n\t\t);\n\t}\n\n\tconst visiblePins = session.comments.filter(\n\t\t(c) => c.pagePath === window.location.pathname && c.status !== \"done\",\n\t);\n\n\treturn (\n\t\t<div data-yns-feedback-ui=\"true\">\n\t\t\t{visiblePins.map((pin, idx) => (\n\t\t\t\t<PinOverlay\n\t\t\t\t\tkey={pin.id}\n\t\t\t\t\tpin={pin}\n\t\t\t\t\tnumber={idx + 1}\n\t\t\t\t\tediting={editingId === pin.id}\n\t\t\t\t\tonStartEdit={() => setEditingId(pin.id)}\n\t\t\t\t\tonCancelEdit={() => setEditingId(null)}\n\t\t\t\t\tonSave={(content) => updateComment(pin.id, content)}\n\t\t\t\t\tonRemove={() => removeComment(pin.id)}\n\t\t\t\t/>\n\t\t\t))}\n\n\t\t\t{pending && (\n\t\t\t\t<PendingCommentPopover\n\t\t\t\t\tpending={pending}\n\t\t\t\t\tonCancel={() => {\n\t\t\t\t\t\tsetPending(null);\n\t\t\t\t\t\tsetPinMode(true);\n\t\t\t\t\t}}\n\t\t\t\t\tonSave={(content) => submitNewComment(content)}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t{sidebarOpen && (\n\t\t\t\t<CommentsSidebar\n\t\t\t\t\tcomments={session.comments}\n\t\t\t\t\tcurrentPath={window.location.pathname}\n\t\t\t\t\tonClose={() => setSidebarOpen(false)}\n\t\t\t\t\tonSelect={scrollToComment}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t<div style={toolbarStyle}>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tonClick={() => setPinMode((v) => !v)}\n\t\t\t\t\tstyle={pinMode ? toolbarButtonActiveStyle : toolbarButtonStyle}\n\t\t\t\t>\n\t\t\t\t\t{pinMode ? \"Cancel\" : \"Add comment\"}\n\t\t\t\t</button>\n\t\t\t\t<button type=\"button\" onClick={() => setSidebarOpen((v) => !v)} style={toolbarButtonGhostStyle}>\n\t\t\t\t\t{sidebarOpen ? \"Hide list\" : `List (${session.comments.length})`}\n\t\t\t\t</button>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tonClick={finalizeSession}\n\t\t\t\t\tdisabled={finalizing}\n\t\t\t\t\tstyle={toolbarButtonFinalizeStyle}\n\t\t\t\t>\n\t\t\t\t\t{finalizing ? \"Finalizing…\" : \"Finalize\"}\n\t\t\t\t</button>\n\t\t\t\t<span style={toolbarHintStyle}>\n\t\t\t\t\t{pinMode ? \"Click any element to comment\" : `${visiblePins.length} on this page`}\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction SubmittedPanel({\n\tprogress,\n\teta: etaIso,\n\tstatus,\n}: {\n\tprogress: ReviewProgress;\n\teta: string;\n\tstatus: SessionStatus;\n}) {\n\t// Re-render every minute so the relative ETA label stays fresh between\n\t// the 10s data polls (which only re-render when the API payload changes).\n\tconst [, setTick] = useState(0);\n\tuseEffect(() => {\n\t\tconst id = window.setInterval(() => setTick((t) => t + 1), 60_000);\n\t\treturn () => window.clearInterval(id);\n\t}, []);\n\n\tconst eta = formatEta(etaIso);\n\tconst isInReview = status === \"in_review\";\n\tconst headline = isInReview ? \"Feedback under review\" : \"Applying feedback\";\n\n\treturn (\n\t\t<div style={submittedPanelStyle}>\n\t\t\t<div style={submittedHeaderStyle}>\n\t\t\t\t<span style={submittedBadgeStyle}>Submitted</span>\n\t\t\t\t<strong style={{ fontSize: 13 }}>{headline}</strong>\n\t\t\t</div>\n\t\t\t<div style={submittedProgressLabelStyle}>\n\t\t\t\t<span>Review progress</span>\n\t\t\t\t<span style={{ opacity: 0.7 }}>{progress.label}</span>\n\t\t\t</div>\n\t\t\t<div style={submittedProgressTrackStyle}>\n\t\t\t\t<div style={{ ...submittedProgressFillStyle, width: `${progress.fillPct}%` }} />\n\t\t\t</div>\n\t\t\t<p style={submittedEtaStyle}>\n\t\t\t\tEstimated delivery: <span style={{ fontWeight: 600 }}>{eta}</span>\n\t\t\t</p>\n\t\t</div>\n\t);\n}\n\nfunction PinOverlay({\n\tpin,\n\tnumber,\n\tediting,\n\tonStartEdit,\n\tonCancelEdit,\n\tonSave,\n\tonRemove,\n}: {\n\tpin: FeedbackComment;\n\tnumber: number;\n\tediting: boolean;\n\tonStartEdit: () => void;\n\tonCancelEdit: () => void;\n\tonSave: (content: string) => Promise<void>;\n\tonRemove: () => Promise<void>;\n}) {\n\tconst target = useTargetPosition(pin.cssSelector, pin.offsetXRatio, pin.offsetYRatio);\n\tif (!target) return null;\n\n\treturn (\n\t\t<div\n\t\t\tstyle={{\n\t\t\t\tposition: \"absolute\",\n\t\t\t\ttop: target.top,\n\t\t\t\tleft: target.left,\n\t\t\t\tzIndex: 2147483600,\n\t\t\t\tpointerEvents: \"auto\",\n\t\t\t}}\n\t\t>\n\t\t\t<button type=\"button\" onClick={onStartEdit} style={pinDotStyle} title={pin.content}>\n\t\t\t\t{number}\n\t\t\t</button>\n\t\t\t{editing && (\n\t\t\t\t<EditPopover initial={pin.content} onCancel={onCancelEdit} onSave={onSave} onRemove={onRemove} />\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nfunction PendingCommentPopover({\n\tpending,\n\tonCancel,\n\tonSave,\n}: {\n\tpending: PendingPin;\n\tonCancel: () => void;\n\tonSave: (content: string) => Promise<void>;\n}) {\n\treturn (\n\t\t<>\n\t\t\t<div\n\t\t\t\tstyle={{\n\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\ttop: pending.rect.top + pending.rect.height * pending.offsetYRatio - 12,\n\t\t\t\t\tleft: pending.rect.left + pending.rect.width * pending.offsetXRatio - 12,\n\t\t\t\t\tzIndex: 2147483600,\n\t\t\t\t\tpointerEvents: \"none\",\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<div style={pinDotStyle}>•</div>\n\t\t\t</div>\n\t\t\t<div\n\t\t\t\tstyle={{\n\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\ttop: pending.clickY + 10,\n\t\t\t\t\tleft: pending.clickX + 10,\n\t\t\t\t\tzIndex: 2147483647,\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<EditPopover initial=\"\" onCancel={onCancel} onSave={onSave} />\n\t\t\t</div>\n\t\t</>\n\t);\n}\n\nfunction CommentsSidebar({\n\tcomments,\n\tcurrentPath,\n\tonClose,\n\tonSelect,\n}: {\n\tcomments: FeedbackComment[];\n\tcurrentPath: string;\n\tonClose: () => void;\n\tonSelect: (comment: FeedbackComment) => void;\n}) {\n\tconst groups = new Map<string, FeedbackComment[]>();\n\tfor (const c of comments) {\n\t\tconst list = groups.get(c.pagePath) ?? [];\n\t\tlist.push(c);\n\t\tgroups.set(c.pagePath, list);\n\t}\n\tconst paths = Array.from(groups.keys()).sort((a, b) => {\n\t\tif (a === currentPath) return -1;\n\t\tif (b === currentPath) return 1;\n\t\treturn a.localeCompare(b);\n\t});\n\n\treturn (\n\t\t<div style={sidebarStyle}>\n\t\t\t<div style={sidebarHeaderStyle}>\n\t\t\t\t<strong style={{ fontSize: 14 }}>Comments ({comments.length})</strong>\n\t\t\t\t<button type=\"button\" onClick={onClose} style={ghostButtonStyle}>\n\t\t\t\t\tClose\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t\t<div style={sidebarScrollStyle}>\n\t\t\t\t{comments.length === 0 ? (\n\t\t\t\t\t<div style={sidebarEmptyStyle}>No comments yet. Click “Add comment” to leave one.</div>\n\t\t\t\t) : (\n\t\t\t\t\tpaths.map((path) => (\n\t\t\t\t\t\t<div key={path} style={{ marginBottom: 12 }}>\n\t\t\t\t\t\t\t<div style={sidebarPathStyle}>{path === currentPath ? `${path} · current` : path}</div>\n\t\t\t\t\t\t\t{(groups.get(path) ?? []).map((c, idx) => (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\tkey={c.id}\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={() => onSelect(c)}\n\t\t\t\t\t\t\t\t\tstyle={sidebarItemStyle}\n\t\t\t\t\t\t\t\t\tdisabled={c.status === \"done\" && false}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<span style={sidebarItemIndexStyle}>{idx + 1}</span>\n\t\t\t\t\t\t\t\t\t<span style={sidebarItemTextStyle}>\n\t\t\t\t\t\t\t\t\t\t{c.content.length > 140 ? `${c.content.slice(0, 140)}…` : c.content}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t{c.status === \"done\" && <span style={sidebarItemDoneStyle}>done</span>}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction EditPopover({\n\tinitial,\n\tonCancel,\n\tonSave,\n\tonRemove,\n}: {\n\tinitial: string;\n\tonCancel: () => void;\n\tonSave: (content: string) => Promise<void>;\n\tonRemove?: () => Promise<void>;\n}) {\n\tconst [value, setValue] = useState(initial);\n\tconst [saving, setSaving] = useState(false);\n\tconst textareaRef = useRef<HTMLTextAreaElement | null>(null);\n\n\tuseEffect(() => {\n\t\tconst el = textareaRef.current;\n\t\tif (!el) return;\n\t\tel.focus();\n\t\tconst len = el.value.length;\n\t\tel.setSelectionRange(len, len);\n\t}, []);\n\n\tconst handleSubmit = async (e: FormEvent) => {\n\t\te.preventDefault();\n\t\tconst content = value.trim();\n\t\tif (!content) return;\n\t\tsetSaving(true);\n\t\ttry {\n\t\t\tawait onSave(content);\n\t\t} finally {\n\t\t\tsetSaving(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t<form onSubmit={handleSubmit} style={popoverStyle}>\n\t\t\t<textarea\n\t\t\t\tref={textareaRef}\n\t\t\t\tvalue={value}\n\t\t\t\tonChange={(e) => setValue(e.target.value)}\n\t\t\t\tplaceholder=\"Leave a comment…\"\n\t\t\t\tstyle={textareaStyle}\n\t\t\t\trows={3}\n\t\t\t/>\n\t\t\t<div style={popoverActionsStyle}>\n\t\t\t\t{onRemove && (\n\t\t\t\t\t<button type=\"button\" onClick={() => onRemove()} style={dangerButtonStyle} disabled={saving}>\n\t\t\t\t\t\tDelete\n\t\t\t\t\t</button>\n\t\t\t\t)}\n\t\t\t\t<div style={{ flex: 1 }} />\n\t\t\t\t<button type=\"button\" onClick={onCancel} style={ghostButtonStyle} disabled={saving}>\n\t\t\t\t\tCancel\n\t\t\t\t</button>\n\t\t\t\t<button type=\"submit\" style={primaryButtonStyle} disabled={saving || !value.trim()}>\n\t\t\t\t\t{saving ? \"Saving…\" : \"Save\"}\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t</form>\n\t);\n}\n\nfunction useTargetPosition(selector: string, offsetXRatio: number, offsetYRatio: number) {\n\tconst [pos, setPos] = useState<{ top: number; left: number } | null>(null);\n\n\tuseEffect(() => {\n\t\tconst update = () => {\n\t\t\tlet el: Element | null = null;\n\t\t\ttry {\n\t\t\t\tel = document.querySelector(selector);\n\t\t\t} catch {\n\t\t\t\tel = null;\n\t\t\t}\n\t\t\tif (!el) {\n\t\t\t\tsetPos(null);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst r = el.getBoundingClientRect();\n\t\t\tsetPos({\n\t\t\t\ttop: r.top + window.scrollY + r.height * offsetYRatio - 12,\n\t\t\t\tleft: r.left + window.scrollX + r.width * offsetXRatio - 12,\n\t\t\t});\n\t\t};\n\n\t\tupdate();\n\t\tconst ro = new ResizeObserver(update);\n\t\ttry {\n\t\t\tconst el = document.querySelector(selector);\n\t\t\tif (el) ro.observe(el);\n\t\t} catch {\n\t\t\t// swallow invalid selector\n\t\t}\n\t\twindow.addEventListener(\"scroll\", update, true);\n\t\twindow.addEventListener(\"resize\", update);\n\t\treturn () => {\n\t\t\tro.disconnect();\n\t\t\twindow.removeEventListener(\"scroll\", update, true);\n\t\t\twindow.removeEventListener(\"resize\", update);\n\t\t};\n\t}, [selector, offsetXRatio, offsetYRatio]);\n\n\treturn pos;\n}\n\n// Inline styles — toolbar is injected into arbitrary stores, so we avoid relying\n// on any CSS framework being present.\nconst toolbarStyle: CSSProperties = {\n\tposition: \"fixed\",\n\tbottom: 16,\n\tleft: \"50%\",\n\ttransform: \"translateX(-50%)\",\n\tzIndex: 2147483646,\n\tdisplay: \"flex\",\n\talignItems: \"center\",\n\tgap: 8,\n\tpadding: \"8px 12px\",\n\tbackground: \"rgba(17, 17, 17, 0.92)\",\n\tcolor: \"white\",\n\tborderRadius: 999,\n\tboxShadow: \"0 8px 24px rgba(0,0,0,0.25)\",\n\tfontFamily:\n\t\t'-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\"',\n\tfontSize: 14,\n\tpointerEvents: \"auto\",\n};\n\nconst toolbarButtonStyle: CSSProperties = {\n\tborder: \"none\",\n\tbackground: \"white\",\n\tcolor: \"#111\",\n\tpadding: \"6px 12px\",\n\tborderRadius: 999,\n\tcursor: \"pointer\",\n\tfontWeight: 600,\n\tfontSize: 13,\n};\n\nconst toolbarButtonActiveStyle: CSSProperties = {\n\t...toolbarButtonStyle,\n\tbackground: \"#ef4444\",\n\tcolor: \"white\",\n};\n\nconst toolbarButtonGhostStyle: CSSProperties = {\n\tborder: \"1px solid rgba(255,255,255,0.4)\",\n\tbackground: \"transparent\",\n\tcolor: \"white\",\n\tpadding: \"6px 12px\",\n\tborderRadius: 999,\n\tcursor: \"pointer\",\n\tfontWeight: 500,\n\tfontSize: 13,\n};\n\nconst toolbarButtonFinalizeStyle: CSSProperties = {\n\tborder: \"none\",\n\tbackground: \"#10b981\",\n\tcolor: \"white\",\n\tpadding: \"6px 12px\",\n\tborderRadius: 999,\n\tcursor: \"pointer\",\n\tfontWeight: 600,\n\tfontSize: 13,\n};\n\nconst toolbarHintStyle: CSSProperties = {\n\topacity: 0.8,\n\tfontSize: 12,\n};\n\nconst pinDotStyle: CSSProperties = {\n\twidth: 24,\n\theight: 24,\n\tborderRadius: 999,\n\tbackground: \"#10b981\",\n\tcolor: \"white\",\n\tborder: \"2px solid white\",\n\tcursor: \"pointer\",\n\tfontSize: 12,\n\tfontWeight: 700,\n\tboxShadow: \"0 2px 6px rgba(0,0,0,0.3)\",\n\tdisplay: \"inline-flex\",\n\talignItems: \"center\",\n\tjustifyContent: \"center\",\n\tpadding: 0,\n};\n\nconst popoverStyle: CSSProperties = {\n\tbackground: \"white\",\n\tborder: \"1px solid #e5e7eb\",\n\tborderRadius: 8,\n\tpadding: 12,\n\twidth: 280,\n\tboxShadow: \"0 12px 32px rgba(0,0,0,0.18)\",\n\tfontFamily:\n\t\t'-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\"',\n\tdisplay: \"flex\",\n\tflexDirection: \"column\",\n\tgap: 8,\n\tcolor: \"#111\",\n};\n\nconst textareaStyle: CSSProperties = {\n\twidth: \"100%\",\n\tborder: \"1px solid #d1d5db\",\n\tborderRadius: 6,\n\tpadding: 8,\n\tfontSize: 14,\n\tresize: \"vertical\",\n\tfontFamily: \"inherit\",\n\tcolor: \"#111\",\n\tbackground: \"white\",\n\tboxSizing: \"border-box\",\n};\n\nconst popoverActionsStyle: CSSProperties = {\n\tdisplay: \"flex\",\n\talignItems: \"center\",\n\tgap: 6,\n};\n\nconst baseButton: CSSProperties = {\n\tborder: \"1px solid transparent\",\n\tborderRadius: 6,\n\tpadding: \"6px 10px\",\n\tfontSize: 13,\n\tcursor: \"pointer\",\n\tfontWeight: 500,\n};\n\nconst primaryButtonStyle: CSSProperties = {\n\t...baseButton,\n\tbackground: \"#111\",\n\tcolor: \"white\",\n};\n\nconst ghostButtonStyle: CSSProperties = {\n\t...baseButton,\n\tbackground: \"transparent\",\n\tcolor: \"#374151\",\n\tborderColor: \"#d1d5db\",\n};\n\nconst dangerButtonStyle: CSSProperties = {\n\t...baseButton,\n\tbackground: \"transparent\",\n\tcolor: \"#b91c1c\",\n\tborderColor: \"#fecaca\",\n};\n\nconst sidebarStyle: CSSProperties = {\n\tposition: \"fixed\",\n\ttop: 0,\n\tright: 0,\n\tbottom: 0,\n\twidth: 360,\n\tmaxWidth: \"92vw\",\n\tbackground: \"white\",\n\tborderLeft: \"1px solid #e5e7eb\",\n\tboxShadow: \"-12px 0 32px rgba(0,0,0,0.12)\",\n\tzIndex: 2147483646,\n\tdisplay: \"flex\",\n\tflexDirection: \"column\",\n\tfontFamily:\n\t\t'-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\"',\n\tcolor: \"#111\",\n};\n\nconst sidebarHeaderStyle: CSSProperties = {\n\tdisplay: \"flex\",\n\talignItems: \"center\",\n\tjustifyContent: \"space-between\",\n\tpadding: \"12px 16px\",\n\tborderBottom: \"1px solid #e5e7eb\",\n};\n\nconst sidebarScrollStyle: CSSProperties = {\n\tflex: 1,\n\toverflow: \"auto\",\n\tpadding: \"12px 12px 24px\",\n};\n\nconst sidebarEmptyStyle: CSSProperties = {\n\tcolor: \"#6b7280\",\n\tfontSize: 13,\n\tpadding: \"24px 8px\",\n\ttextAlign: \"center\",\n};\n\nconst sidebarPathStyle: CSSProperties = {\n\tfontSize: 11,\n\ttextTransform: \"uppercase\",\n\tletterSpacing: 0.5,\n\tcolor: \"#6b7280\",\n\tpadding: \"4px 4px 6px\",\n\twordBreak: \"break-all\",\n};\n\nconst sidebarItemStyle: CSSProperties = {\n\tdisplay: \"flex\",\n\talignItems: \"flex-start\",\n\tgap: 8,\n\twidth: \"100%\",\n\ttextAlign: \"left\",\n\tbackground: \"white\",\n\tborder: \"1px solid #e5e7eb\",\n\tborderRadius: 6,\n\tpadding: \"8px 10px\",\n\tcursor: \"pointer\",\n\tfontSize: 13,\n\tmarginBottom: 6,\n\tcolor: \"#111\",\n};\n\nconst sidebarItemIndexStyle: CSSProperties = {\n\tflexShrink: 0,\n\twidth: 22,\n\theight: 22,\n\tborderRadius: 999,\n\tbackground: \"#10b981\",\n\tcolor: \"white\",\n\tfontWeight: 700,\n\tfontSize: 11,\n\tdisplay: \"inline-flex\",\n\talignItems: \"center\",\n\tjustifyContent: \"center\",\n};\n\nconst sidebarItemTextStyle: CSSProperties = {\n\tflex: 1,\n\twhiteSpace: \"pre-wrap\",\n\twordBreak: \"break-word\",\n};\n\nconst sidebarItemDoneStyle: CSSProperties = {\n\tflexShrink: 0,\n\tbackground: \"#d1fae5\",\n\tcolor: \"#065f46\",\n\tfontSize: 11,\n\tfontWeight: 600,\n\tpadding: \"2px 6px\",\n\tborderRadius: 4,\n\talignSelf: \"center\",\n};\n\nconst submittedPanelStyle: CSSProperties = {\n\tposition: \"fixed\",\n\tbottom: 16,\n\tleft: \"50%\",\n\ttransform: \"translateX(-50%)\",\n\tzIndex: 2147483646,\n\tdisplay: \"flex\",\n\tflexDirection: \"column\",\n\tgap: 8,\n\tpadding: \"12px 16px\",\n\twidth: \"min(420px, calc(100vw - 32px))\",\n\tbackground: \"white\",\n\tborder: \"1px solid #e5e7eb\",\n\tborderRadius: 12,\n\tboxShadow: \"0 12px 32px rgba(0,0,0,0.18)\",\n\tfontFamily:\n\t\t'-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\"',\n\tcolor: \"#111\",\n\tpointerEvents: \"auto\",\n};\n\nconst submittedHeaderStyle: CSSProperties = {\n\tdisplay: \"flex\",\n\talignItems: \"center\",\n\tgap: 8,\n};\n\nconst submittedBadgeStyle: CSSProperties = {\n\tbackground: \"#d1fae5\",\n\tcolor: \"#065f46\",\n\tfontSize: 11,\n\tfontWeight: 700,\n\tpadding: \"2px 8px\",\n\tborderRadius: 999,\n\ttextTransform: \"uppercase\",\n\tletterSpacing: 0.5,\n};\n\nconst submittedProgressLabelStyle: CSSProperties = {\n\tdisplay: \"flex\",\n\tjustifyContent: \"space-between\",\n\tfontSize: 12,\n};\n\nconst submittedProgressTrackStyle: CSSProperties = {\n\twidth: \"100%\",\n\theight: 6,\n\tbackground: \"#f3f4f6\",\n\tborderRadius: 999,\n\toverflow: \"hidden\",\n};\n\nconst submittedProgressFillStyle: CSSProperties = {\n\theight: \"100%\",\n\tbackground: \"#10b981\",\n\tborderRadius: 999,\n\ttransition: \"width 500ms\",\n};\n\nconst submittedEtaStyle: CSSProperties = {\n\tmargin: 0,\n\tfontSize: 12,\n\tcolor: \"#6b7280\",\n};\n\nexport function mountFeedbackToolbar(): { unmount: () => void } | null {\n\tconsole.log(\"[YNS Feedback Toolbar] mountFeedbackToolbar() called\", {\n\t\tisWindow: typeof window !== \"undefined\",\n\t\tvercelEnv: process.env.NEXT_PUBLIC_VERCEL_ENV,\n\t\talreadyMounted: typeof document !== \"undefined\" && Boolean(document.getElementById(MOUNT_NODE_ID)),\n\t});\n\tif (typeof window === \"undefined\") return null;\n\tif (process.env.NEXT_PUBLIC_VERCEL_ENV !== \"preview\") {\n\t\tconsole.log(\n\t\t\t\"[YNS Feedback Toolbar] gate failed — NEXT_PUBLIC_VERCEL_ENV is\",\n\t\t\tprocess.env.NEXT_PUBLIC_VERCEL_ENV,\n\t\t);\n\t\treturn null;\n\t}\n\tif (document.getElementById(MOUNT_NODE_ID)) return null;\n\n\tconst container = document.createElement(\"div\");\n\tcontainer.id = MOUNT_NODE_ID;\n\tcontainer.dataset.ynsFeedbackUi = \"true\";\n\tdocument.body.appendChild(container);\n\n\tconst root = createRoot(container);\n\troot.render(<FeedbackToolbar />);\n\n\treturn {\n\t\tunmount: () => {\n\t\t\troot.unmount();\n\t\t\tcontainer.remove();\n\t\t},\n\t};\n}\n\n// Auto-mount on import. Wait for DOM ready.\nif (typeof window !== \"undefined\") {\n\tconsole.log(\"[YNS Feedback Toolbar] module evaluated\", {\n\t\tvercelEnv: process.env.NEXT_PUBLIC_VERCEL_ENV,\n\t\treadyState: document.readyState,\n\t\twillAutoMount: process.env.NEXT_PUBLIC_VERCEL_ENV === \"preview\",\n\t});\n}\nif (typeof window !== \"undefined\" && process.env.NEXT_PUBLIC_VERCEL_ENV === \"preview\") {\n\tif (document.readyState === \"loading\") {\n\t\tdocument.addEventListener(\"DOMContentLoaded\", () => {\n\t\t\tmountFeedbackToolbar();\n\t\t});\n\t} else {\n\t\tmountFeedbackToolbar();\n\t}\n}\n","/**\n * Sandbox Inspectors — side-effect entry.\n *\n * Auto-mounts the design-mode + comment-mode inspectors inside per-store\n * storefronts running under the AI Builder sandbox dev server. Communicates\n * with the parent /design iframe via postMessage.\n *\n * Always-on: inspectors are passive postMessage listeners and don't affect the\n * page until the AI Builder iframe sends a `*-mode-toggle` message. Gating on\n * NODE_ENV is wrong (always \"production\" on Vercel) and any build-time gate\n * gets folded out by the commerce-kit bundle, leaving an empty file.\n */\n\n/**\n * Exported so `browser.tsx` can `export { startSandboxInspectors }` — the\n * named binding anchors this module against esbuild tree-shaking when\n * commerce-kit is bundled. Without it, an `export * from \"./sandbox-inspectors\"`\n * re-exports an empty binding set and esbuild DCEs the whole file.\n */\nexport function startSandboxInspectors(): void {\n\tif (typeof window === \"undefined\") return;\n\tconsole.log(\"[YNS Sandbox Inspectors] module evaluated\");\n\tinitDesignModeInspector();\n\tinitCommentModeInspector();\n}\n\n// Auto-start on import.\nstartSandboxInspectors();\n\ninterface ElementInfo {\n\ttag: string;\n\tid: string | undefined;\n\tclasses: string[];\n\ttextContent: string;\n\tcssSelector: string;\n\tboundingRect: { top: number; left: number; width: number; height: number };\n\tcomputedStyles: Partial<{\n\t\tcolor: string;\n\t\tbackgroundColor: string;\n\t\tfontSize: string;\n\t\tfontFamily: string;\n\t\tpadding: string;\n\t\tmargin: string;\n\t}>;\n}\n\nconst IGNORED_TAGS = new Set([\"HEAD\", \"SCRIPT\", \"STYLE\", \"NOSCRIPT\", \"HTML\"]);\n\nfunction generateCssSelector(el: Element): string {\n\tif (el.id) return `${el.tagName.toLowerCase()}#${CSS.escape(el.id)}`;\n\n\tconst parts: string[] = [];\n\tlet current: Element | null = el;\n\twhile (current && current !== document.body && current !== document.documentElement) {\n\t\tlet selector = current.tagName.toLowerCase();\n\n\t\tif (current.id) {\n\t\t\tparts.unshift(`${selector}#${CSS.escape(current.id)}`);\n\t\t\tbreak;\n\t\t}\n\n\t\tconst classes = Array.from(current.classList).filter((c) => !c.startsWith(\"data-yns-\"));\n\t\tif (classes.length > 0) {\n\t\t\tselector += `.${classes.map((c) => CSS.escape(c)).join(\".\")}`;\n\t\t}\n\n\t\tconst parent: Element | null = current.parentElement;\n\t\tconst tag = current.tagName;\n\t\tif (parent) {\n\t\t\tconst siblings = Array.from(parent.children).filter(\n\t\t\t\t(s) => s.tagName === tag && (classes.length === 0 || classes.every((c) => s.classList.contains(c))),\n\t\t\t);\n\t\t\tif (siblings.length > 1) {\n\t\t\t\tconst index = Array.from(parent.children).indexOf(current) + 1;\n\t\t\t\tselector += `:nth-child(${index})`;\n\t\t\t}\n\t\t}\n\n\t\tparts.unshift(selector);\n\t\tcurrent = current.parentElement;\n\t}\n\treturn parts.join(\" > \");\n}\n\nfunction buildElementInfo(el: Element, textLimit: number): ElementInfo {\n\tconst rect = el.getBoundingClientRect();\n\tconst computed = window.getComputedStyle(el);\n\tconst text = (el.textContent ?? \"\").trim();\n\treturn {\n\t\ttag: el.tagName.toLowerCase(),\n\t\tid: el.id || undefined,\n\t\tclasses: Array.from(el.classList).filter((c) => !c.startsWith(\"data-yns-\")),\n\t\ttextContent: text.length > textLimit ? `${text.slice(0, textLimit)}…` : text,\n\t\tcssSelector: generateCssSelector(el),\n\t\tboundingRect: { top: rect.top, left: rect.left, width: rect.width, height: rect.height },\n\t\tcomputedStyles: {\n\t\t\tcolor: computed.color,\n\t\t\tbackgroundColor: computed.backgroundColor,\n\t\t\tfontSize: computed.fontSize,\n\t\t\tfontFamily: computed.fontFamily,\n\t\t\tpadding: computed.padding,\n\t\t\tmargin: computed.margin,\n\t\t},\n\t};\n}\n\nfunction shouldIgnore(el: Element | null, overlayAttrs: string[]): el is null {\n\tif (!el || !el.tagName) return true;\n\tif (IGNORED_TAGS.has(el.tagName)) return true;\n\tfor (const attr of overlayAttrs) {\n\t\tif (el.hasAttribute?.(attr)) return true;\n\t}\n\treturn false;\n}\n\nfunction initDesignModeInspector() {\n\tlet enabled = false;\n\tlet hoveredElement: Element | null = null;\n\tlet selectedElement: { el: Element; cssSelector: string } | null = null;\n\n\tconst overlay = document.createElement(\"div\");\n\toverlay.setAttribute(\"data-yns-design-overlay\", \"hover\");\n\toverlay.style.cssText = [\n\t\t\"position: fixed\",\n\t\t\"pointer-events: none\",\n\t\t\"z-index: 2147483646\",\n\t\t\"border: 2px solid #3b82f6\",\n\t\t\"background: rgba(59, 130, 246, 0.08)\",\n\t\t\"border-radius: 3px\",\n\t\t\"display: none\",\n\t\t\"transition: top 0.05s, left 0.05s, width 0.05s, height 0.05s\",\n\t].join(\";\");\n\n\tconst label = document.createElement(\"div\");\n\tlabel.setAttribute(\"data-yns-design-overlay\", \"label\");\n\tlabel.style.cssText = [\n\t\t\"position: fixed\",\n\t\t\"pointer-events: none\",\n\t\t\"z-index: 2147483647\",\n\t\t\"background: #3b82f6\",\n\t\t\"color: #fff\",\n\t\t\"font-size: 11px\",\n\t\t\"font-family: ui-monospace, monospace\",\n\t\t\"padding: 2px 6px\",\n\t\t\"border-radius: 3px\",\n\t\t\"white-space: nowrap\",\n\t\t\"display: none\",\n\t].join(\";\");\n\n\tdocument.documentElement.appendChild(overlay);\n\tdocument.documentElement.appendChild(label);\n\n\tconst style = document.createElement(\"style\");\n\tstyle.setAttribute(\"data-yns-design-overlay\", \"style\");\n\tstyle.textContent = \"[data-yns-selected] { outline: 2px solid #3b82f6 !important; outline-offset: 1px; }\";\n\tdocument.head.appendChild(style);\n\n\tfunction positionOverlay(el: Element) {\n\t\tconst rect = el.getBoundingClientRect();\n\t\toverlay.style.top = `${rect.top}px`;\n\t\toverlay.style.left = `${rect.left}px`;\n\t\toverlay.style.width = `${rect.width}px`;\n\t\toverlay.style.height = `${rect.height}px`;\n\t\toverlay.style.display = \"block\";\n\n\t\tconst classes = Array.from(el.classList).filter((c) => !c.startsWith(\"data-yns-\"));\n\t\tconst labelText = el.tagName.toLowerCase() + (classes.length ? `.${classes[0]}` : \"\");\n\t\tlabel.textContent = labelText;\n\t\tlabel.style.left = `${rect.left}px`;\n\t\tlabel.style.top = `${Math.max(0, rect.top - 22)}px`;\n\t\tlabel.style.display = \"block\";\n\t}\n\n\tconst handleMouseMove = (e: MouseEvent) => {\n\t\tconst el = document.elementFromPoint(e.clientX, e.clientY);\n\t\tif (shouldIgnore(el, [\"data-yns-design-overlay\"])) {\n\t\t\toverlay.style.display = \"none\";\n\t\t\tlabel.style.display = \"none\";\n\t\t\thoveredElement = null;\n\t\t\treturn;\n\t\t}\n\t\thoveredElement = el;\n\t\trequestAnimationFrame(() => {\n\t\t\tif (hoveredElement === el && enabled && el) {\n\t\t\t\tpositionOverlay(el);\n\t\t\t}\n\t\t});\n\t};\n\n\tconst handleMouseLeave = () => {\n\t\toverlay.style.display = \"none\";\n\t\tlabel.style.display = \"none\";\n\t\thoveredElement = null;\n\t};\n\n\tconst handleClick = (e: MouseEvent) => {\n\t\tif (!enabled || !hoveredElement) return;\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\t\te.stopImmediatePropagation();\n\n\t\tconst el = hoveredElement;\n\t\tif (shouldIgnore(el, [\"data-yns-design-overlay\"])) return;\n\n\t\tconst info = buildElementInfo(el, 120);\n\t\tconst selector = info.cssSelector;\n\n\t\tif (selectedElement) {\n\t\t\tselectedElement.el.removeAttribute(\"data-yns-selected\");\n\t\t}\n\n\t\tif (selectedElement && selectedElement.cssSelector === selector) {\n\t\t\tselectedElement = null;\n\t\t\twindow.parent.postMessage({ type: \"element-deselected\", data: info }, \"*\");\n\t\t} else {\n\t\t\tselectedElement = { el, cssSelector: selector };\n\t\t\tel.setAttribute(\"data-yns-selected\", \"\");\n\t\t\twindow.parent.postMessage({ type: \"element-selected\", data: info }, \"*\");\n\t\t}\n\t};\n\n\tconst handleKeyDown = (e: KeyboardEvent) => {\n\t\tif (e.key === \"Escape\" && enabled) {\n\t\t\tdisableDesignMode();\n\t\t\twindow.parent.postMessage({ type: \"design-mode-cleared\" }, \"*\");\n\t\t}\n\t};\n\n\tfunction enableDesignMode() {\n\t\tenabled = true;\n\t\tdocument.addEventListener(\"mousemove\", handleMouseMove, true);\n\t\tdocument.addEventListener(\"mouseleave\", handleMouseLeave);\n\t\tdocument.addEventListener(\"click\", handleClick, true);\n\t\tdocument.addEventListener(\"keydown\", handleKeyDown, true);\n\t}\n\n\tfunction disableDesignMode() {\n\t\tenabled = false;\n\t\tdocument.removeEventListener(\"mousemove\", handleMouseMove, true);\n\t\tdocument.removeEventListener(\"mouseleave\", handleMouseLeave);\n\t\tdocument.removeEventListener(\"click\", handleClick, true);\n\t\tdocument.removeEventListener(\"keydown\", handleKeyDown, true);\n\n\t\toverlay.style.display = \"none\";\n\t\tlabel.style.display = \"none\";\n\t\thoveredElement = null;\n\n\t\tif (selectedElement) {\n\t\t\tselectedElement.el.removeAttribute(\"data-yns-selected\");\n\t\t\tselectedElement = null;\n\t\t}\n\t}\n\n\twindow.addEventListener(\"message\", (event) => {\n\t\tconst msg = event.data;\n\t\tif (!msg || typeof msg !== \"object\") return;\n\n\t\tif (msg.type === \"design-mode-toggle\") {\n\t\t\tif (msg.enabled) {\n\t\t\t\tenableDesignMode();\n\t\t\t} else {\n\t\t\t\tdisableDesignMode();\n\t\t\t\twindow.parent.postMessage({ type: \"design-mode-cleared\" }, \"*\");\n\t\t\t}\n\t\t}\n\n\t\tif (msg.type === \"design-mode-deselect\") {\n\t\t\tif (selectedElement) {\n\t\t\t\tselectedElement.el.removeAttribute(\"data-yns-selected\");\n\t\t\t\tselectedElement = null;\n\t\t\t}\n\t\t}\n\t});\n}\n\nfunction initCommentModeInspector() {\n\tlet enabled = false;\n\tlet hoveredElement: Element | null = null;\n\tlet pins: Array<{ id: string; selector: string; number: number }> = [];\n\n\tconst overlay = document.createElement(\"div\");\n\toverlay.setAttribute(\"data-yns-comment-overlay\", \"hover\");\n\toverlay.style.cssText = [\n\t\t\"position: fixed\",\n\t\t\"pointer-events: none\",\n\t\t\"z-index: 2147483644\",\n\t\t\"border: 2px dashed #10b981\",\n\t\t\"background: rgba(16, 185, 129, 0.06)\",\n\t\t\"border-radius: 3px\",\n\t\t\"display: none\",\n\t\t\"transition: top 0.05s, left 0.05s, width 0.05s, height 0.05s\",\n\t].join(\";\");\n\n\tconst cursorLabel = document.createElement(\"div\");\n\tcursorLabel.setAttribute(\"data-yns-comment-overlay\", \"cursor-label\");\n\tcursorLabel.textContent = \"Click to comment\";\n\tcursorLabel.style.cssText = [\n\t\t\"position: fixed\",\n\t\t\"pointer-events: none\",\n\t\t\"z-index: 2147483645\",\n\t\t\"background: #059669\",\n\t\t\"color: #fff\",\n\t\t\"font-size: 11px\",\n\t\t\"font-family: ui-sans-serif, system-ui, sans-serif\",\n\t\t\"padding: 3px 8px\",\n\t\t\"border-radius: 4px\",\n\t\t\"white-space: nowrap\",\n\t\t\"display: none\",\n\t\t\"box-shadow: 0 2px 6px rgba(0,0,0,0.15)\",\n\t].join(\";\");\n\n\tconst pinContainer = document.createElement(\"div\");\n\tpinContainer.setAttribute(\"data-yns-comment-overlay\", \"pins\");\n\tpinContainer.style.cssText =\n\t\t\"position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 2147483643;\";\n\n\tdocument.documentElement.appendChild(overlay);\n\tdocument.documentElement.appendChild(cursorLabel);\n\tdocument.documentElement.appendChild(pinContainer);\n\n\tfunction renderPins() {\n\t\tpinContainer.innerHTML = \"\";\n\t\tfor (const pin of pins) {\n\t\t\tlet el: Element | null = null;\n\t\t\ttry {\n\t\t\t\tel = document.querySelector(pin.selector);\n\t\t\t} catch {\n\t\t\t\tel = null;\n\t\t\t}\n\t\t\tif (!el) continue;\n\n\t\t\tconst r = el.getBoundingClientRect();\n\t\t\tconst pinEl = document.createElement(\"div\");\n\t\t\tpinEl.style.cssText = [\n\t\t\t\t\"position: fixed\",\n\t\t\t\t`top: ${r.top - 12}px`,\n\t\t\t\t`left: ${r.left + r.width / 2 - 12}px`,\n\t\t\t\t\"width: 24px\",\n\t\t\t\t\"height: 24px\",\n\t\t\t\t\"border-radius: 50%\",\n\t\t\t\t\"background: #059669\",\n\t\t\t\t\"color: #fff\",\n\t\t\t\t\"display: flex\",\n\t\t\t\t\"align-items: center\",\n\t\t\t\t\"justify-content: center\",\n\t\t\t\t\"font-size: 12px\",\n\t\t\t\t\"font-weight: 600\",\n\t\t\t\t\"font-family: ui-sans-serif, system-ui, sans-serif\",\n\t\t\t\t\"box-shadow: 0 2px 8px rgba(0,0,0,0.2)\",\n\t\t\t\t\"pointer-events: none\",\n\t\t\t].join(\";\");\n\t\t\tpinEl.textContent = String(pin.number);\n\t\t\tpinContainer.appendChild(pinEl);\n\t\t}\n\t}\n\n\tconst handleMouseMove = (e: MouseEvent) => {\n\t\tconst el = document.elementFromPoint(e.clientX, e.clientY);\n\t\tif (shouldIgnore(el, [\"data-yns-comment-overlay\", \"data-yns-design-overlay\"])) {\n\t\t\toverlay.style.display = \"none\";\n\t\t\tcursorLabel.style.display = \"none\";\n\t\t\thoveredElement = null;\n\t\t\treturn;\n\t\t}\n\t\thoveredElement = el;\n\t\trequestAnimationFrame(() => {\n\t\t\tif (hoveredElement === el && enabled && el) {\n\t\t\t\tconst rect = el.getBoundingClientRect();\n\t\t\t\toverlay.style.top = `${rect.top}px`;\n\t\t\t\toverlay.style.left = `${rect.left}px`;\n\t\t\t\toverlay.style.width = `${rect.width}px`;\n\t\t\t\toverlay.style.height = `${rect.height}px`;\n\t\t\t\toverlay.style.display = \"block\";\n\n\t\t\t\tcursorLabel.style.left = `${e.clientX + 14}px`;\n\t\t\t\tcursorLabel.style.top = `${e.clientY + 14}px`;\n\t\t\t\tcursorLabel.style.display = \"block\";\n\t\t\t}\n\t\t});\n\t};\n\n\tconst handleMouseLeave = () => {\n\t\toverlay.style.display = \"none\";\n\t\tcursorLabel.style.display = \"none\";\n\t\thoveredElement = null;\n\t};\n\n\tconst handleClick = (e: MouseEvent) => {\n\t\tif (!enabled || !hoveredElement) return;\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\t\te.stopImmediatePropagation();\n\n\t\tconst el = hoveredElement;\n\t\tif (shouldIgnore(el, [\"data-yns-comment-overlay\", \"data-yns-design-overlay\"])) return;\n\n\t\tconst info = buildElementInfo(el, 200);\n\t\tconst rect = el.getBoundingClientRect();\n\n\t\twindow.parent.postMessage(\n\t\t\t{\n\t\t\t\ttype: \"comment-click\",\n\t\t\t\tdata: {\n\t\t\t\t\telement: info,\n\t\t\t\t\tclickPosition: { x: e.clientX, y: e.clientY },\n\t\t\t\t\telementRect: { top: rect.top, left: rect.left, width: rect.width, height: rect.height },\n\t\t\t\t\tpagePath: window.location.pathname,\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"*\",\n\t\t);\n\t};\n\n\tconst handleKeyDown = (e: KeyboardEvent) => {\n\t\tif (e.key === \"Escape\" && enabled) {\n\t\t\tdisableCommentMode();\n\t\t\twindow.parent.postMessage({ type: \"comment-mode-cleared\" }, \"*\");\n\t\t}\n\t};\n\n\tfunction enableCommentMode() {\n\t\tenabled = true;\n\t\tdocument.body.style.cursor = \"crosshair\";\n\t\tdocument.addEventListener(\"mousemove\", handleMouseMove, true);\n\t\tdocument.addEventListener(\"mouseleave\", handleMouseLeave);\n\t\tdocument.addEventListener(\"click\", handleClick, true);\n\t\tdocument.addEventListener(\"keydown\", handleKeyDown, true);\n\t}\n\n\tfunction disableCommentMode() {\n\t\tenabled = false;\n\t\tdocument.body.style.cursor = \"\";\n\t\tdocument.removeEventListener(\"mousemove\", handleMouseMove, true);\n\t\tdocument.removeEventListener(\"mouseleave\", handleMouseLeave);\n\t\tdocument.removeEventListener(\"click\", handleClick, true);\n\t\tdocument.removeEventListener(\"keydown\", handleKeyDown, true);\n\n\t\toverlay.style.display = \"none\";\n\t\tcursorLabel.style.display = \"none\";\n\t\thoveredElement = null;\n\t}\n\n\tlet rafPending = false;\n\tconst scheduleRender = () => {\n\t\tif (rafPending) return;\n\t\trafPending = true;\n\t\trequestAnimationFrame(() => {\n\t\t\trafPending = false;\n\t\t\trenderPins();\n\t\t});\n\t};\n\twindow.addEventListener(\"scroll\", scheduleRender, { passive: true });\n\twindow.addEventListener(\"resize\", scheduleRender, { passive: true });\n\n\twindow.addEventListener(\"message\", (event) => {\n\t\tconst msg = event.data;\n\t\tif (!msg || typeof msg !== \"object\") return;\n\n\t\tif (msg.type === \"comment-mode-toggle\") {\n\t\t\tif (msg.enabled) enableCommentMode();\n\t\t\telse disableCommentMode();\n\t\t}\n\n\t\tif (msg.type === \"comment-pins-update\") {\n\t\t\tpins = msg.pins ?? [];\n\t\t\trenderPins();\n\t\t}\n\n\t\tif (msg.type === \"comment-pin-remove\") {\n\t\t\tpins = pins.filter((p) => p.id !== msg.pinId);\n\t\t\trenderPins();\n\t\t}\n\t});\n}\n"],"mappings":"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","names":["useEffect","useRef","useState","createRoot","Fragment","jsx","jsxs","MOUNT_NODE_ID","buildApiBase","override","host","protocol","computeCssSelector","el","path","node","part","className","classes","c","parent","tag","siblings","idx","ATTRS_OF_INTEREST","formatAttrs","parts","attr","v","trimmed","formatTextContent","text","buildSurroundingHtml","target","ancestors","cur","lines","depth","ancestor","indent","targetIndent","targetTag","targetText","childIndent","children","child","childText","i","isInsideToolbar","IGNORED_TAGS","POLL_INTERVAL_MS","formatEta","etaIso","eta","remainingMs","dateLabel","remainingMin","remainingHours","remainingDays","FeedbackToolbar","session","setSession","loading","setLoading","pinMode","setPinMode","pending","setPending","editingId","setEditingId","sidebarOpen","setSidebarOpen","finalizing","setFinalizing","apiBase","cancelled","controller","fetchOnce","res","data","interval","overlay","label","hovered","handleMove","e","rect","handleLeave","handleClick","event","offsetXRatio","offsetYRatio","refreshComments","submitNewComment","content","updateComment","id","removeComment","finalizeSession","prev","scrollToComment","comment","SubmittedPanel","visiblePins","pin","PinOverlay","PendingCommentPopover","CommentsSidebar","toolbarStyle","toolbarButtonActiveStyle","toolbarButtonStyle","toolbarButtonGhostStyle","toolbarButtonFinalizeStyle","toolbarHintStyle","progress","status","setTick","t","submittedPanelStyle","submittedHeaderStyle","submittedBadgeStyle","submittedProgressLabelStyle","submittedProgressTrackStyle","submittedProgressFillStyle","submittedEtaStyle","number","editing","onStartEdit","onCancelEdit","onSave","onRemove","useTargetPosition","pinDotStyle","EditPopover","onCancel","comments","currentPath","onClose","onSelect","groups","list","paths","a","b","sidebarStyle","sidebarHeaderStyle","ghostButtonStyle","sidebarScrollStyle","sidebarEmptyStyle","sidebarPathStyle","sidebarItemStyle","sidebarItemIndexStyle","sidebarItemTextStyle","sidebarItemDoneStyle","initial","value","setValue","saving","setSaving","textareaRef","len","popoverStyle","textareaStyle","popoverActionsStyle","dangerButtonStyle","primaryButtonStyle","selector","pos","setPos","update","r","ro","baseButton","mountFeedbackToolbar","container","root","startSandboxInspectors","initDesignModeInspector","initCommentModeInspector","IGNORED_TAGS","generateCssSelector","el","parts","current","selector","classes","c","parent","tag","s","index","buildElementInfo","textLimit","rect","computed","text","shouldIgnore","overlayAttrs","attr","enabled","hoveredElement","selectedElement","overlay","label","style","positionOverlay","labelText","handleMouseMove","e","handleMouseLeave","handleClick","info","handleKeyDown","disableDesignMode","enableDesignMode","event","msg","pins","cursorLabel","pinContainer","renderPins","pin","r","pinEl","disableCommentMode","enableCommentMode","rafPending","scheduleRender","p"]}
|
|
1
|
+
{"version":3,"sources":["../src/feedback-toolbar.tsx","../src/sandbox-inspectors.ts"],"sourcesContent":["/**\n * Feedback session toolbar — side-effect entry.\n *\n * Importing `commerce-kit/feedback-toolbar` (or `commerce-kit/browser` for the\n * combined entry) mounts a floating toolbar onto the page that lets reviewers\n * leave click-anchored comments. Comments persist via YNS API endpoints,\n * authenticated with the `better-auth` session cookie sent through\n * `credentials: \"include\"`.\n *\n * Gated on `process.env.NEXT_PUBLIC_VERCEL_ENV === \"preview\"` — only Vercel\n * preview deploys get the toolbar. Sandbox dev (env undefined) and production\n * (env === \"production\") skip it.\n */\n\nimport { type CSSProperties, type FormEvent, useEffect, useRef, useState } from \"react\";\nimport { createRoot } from \"react-dom/client\";\n\nconst MOUNT_NODE_ID = \"yns-feedback-toolbar-root\";\n\ninterface FeedbackComment {\n\tid: string;\n\tpagePath: string;\n\tcssSelector: string;\n\tcontent: string;\n\tstatus: \"todo\" | \"done\";\n\toffsetXRatio: number;\n\toffsetYRatio: number;\n}\n\ntype SessionStatus = \"created\" | \"in_progress\" | \"processing\" | \"in_review\" | \"done\";\n\ninterface ReviewProgress {\n\tfillPct: number;\n\tlabel: string;\n}\n\ninterface ActiveSession {\n\tfeedbackSessionId: string;\n\tcomments: FeedbackComment[];\n\tcanComment: boolean;\n\tsessionStatus: SessionStatus;\n\tcommentTotal: number;\n\tcommentDone: number;\n\teta: string;\n\tprogress: ReviewProgress;\n}\n\ninterface PendingPin {\n\tcssSelector: string;\n\tpagePath: string;\n\tsurroundingHtml: string;\n\trect: { top: number; left: number; width: number; height: number };\n\tclickX: number;\n\tclickY: number;\n\toffsetXRatio: number;\n\toffsetYRatio: number;\n}\n\nconst buildApiBase = (): string | null => {\n\tif (typeof window === \"undefined\") return null;\n\tconst override = (process.env.NEXT_PUBLIC_YNS_API_BASE ?? \"\").trim();\n\tif (override) return override.replace(/\\/$/, \"\");\n\n\t// Toolbar runs on a per-store preview deploy (mystore-preview.yns.{store|cx})\n\t// served by the merchant's Vercel project — that host has no YNS API. Aim\n\t// at the apex `yns.store` / `yns.cx`, which yns-app serves and where the\n\t// API lives. Cookies scoped to `.yns.store` / `.yns.cx` travel along.\n\tconst host = window.location.hostname;\n\tconst protocol = window.location.protocol;\n\tif (host.endsWith(\".yns.store\")) return `${protocol}//yns.store`;\n\tif (host.endsWith(\".yns.cx\")) return `${protocol}//yns.cx`;\n\treturn window.location.origin;\n};\n\nconst computeCssSelector = (el: Element): string => {\n\tif (el.id) return `#${CSS.escape(el.id)}`;\n\tconst path: string[] = [];\n\tlet node: Element | null = el;\n\twhile (node && node.nodeType === Node.ELEMENT_NODE && path.length < 6) {\n\t\tlet part = node.tagName.toLowerCase();\n\t\tconst className = node.getAttribute(\"class\");\n\t\tif (className) {\n\t\t\tconst classes = className\n\t\t\t\t.split(/\\s+/)\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.slice(0, 2)\n\t\t\t\t.map((c) => `.${CSS.escape(c)}`)\n\t\t\t\t.join(\"\");\n\t\t\tpart += classes;\n\t\t}\n\t\tconst parent: Element | null = node.parentElement;\n\t\tconst tag = node.tagName;\n\t\tif (parent) {\n\t\t\tconst siblings: Element[] = Array.from(parent.children).filter((c) => c.tagName === tag);\n\t\t\tif (siblings.length > 1) {\n\t\t\t\tconst idx = siblings.indexOf(node) + 1;\n\t\t\t\tpart += `:nth-of-type(${idx})`;\n\t\t\t}\n\t\t}\n\t\tpath.unshift(part);\n\t\tnode = parent;\n\t}\n\treturn path.join(\" > \");\n};\n\nconst ATTRS_OF_INTEREST = [\"id\", \"class\", \"data-testid\", \"aria-label\", \"role\", \"name\", \"href\", \"src\"];\n\nconst formatAttrs = (el: Element): string => {\n\tconst parts: string[] = [];\n\tfor (const attr of ATTRS_OF_INTEREST) {\n\t\tconst v = el.getAttribute(attr);\n\t\tif (!v) continue;\n\t\tconst trimmed = v.length > 80 ? `${v.slice(0, 80)}…` : v;\n\t\tparts.push(` ${attr}=\"${trimmed.replace(/\"/g, \""\")}\"`);\n\t}\n\treturn parts.join(\"\");\n};\n\nconst formatTextContent = (el: Element): string => {\n\tconst text = (el.textContent ?? \"\").replace(/\\s+/g, \" \").trim();\n\tif (!text) return \"\";\n\treturn text.length > 100 ? `${text.slice(0, 100)}…` : text;\n};\n\nconst buildSurroundingHtml = (target: Element): string => {\n\tconst ancestors: Element[] = [];\n\tlet cur: Element | null = target;\n\twhile (cur && cur !== document.documentElement && ancestors.length < 5) {\n\t\tconst parent: Element | null = cur.parentElement;\n\t\tif (!parent) break;\n\t\tancestors.unshift(parent);\n\t\tcur = parent;\n\t}\n\n\tconst lines: string[] = [];\n\tlet depth = 0;\n\tfor (const ancestor of ancestors) {\n\t\tconst indent = \" \".repeat(depth);\n\t\tlines.push(`${indent}<${ancestor.tagName.toLowerCase()}${formatAttrs(ancestor)}>`);\n\t\tdepth++;\n\t}\n\n\tconst targetIndent = \" \".repeat(depth);\n\tconst targetTag = target.tagName.toLowerCase();\n\tconst targetText = formatTextContent(target);\n\tlines.push(\n\t\t`${targetIndent}<${targetTag}${formatAttrs(target)}>${\n\t\t\ttargetText ? `${targetText}</${targetTag}>` : \"\"\n\t\t} ← TARGET`,\n\t);\n\n\tif (!targetText) {\n\t\tconst childIndent = \" \".repeat(depth + 1);\n\t\tconst children = Array.from(target.children).slice(0, 6);\n\t\tfor (const child of children) {\n\t\t\tconst childText = formatTextContent(child);\n\t\t\tlines.push(\n\t\t\t\t`${childIndent}<${child.tagName.toLowerCase()}${formatAttrs(child)}>${\n\t\t\t\t\tchildText ? `${childText}</${child.tagName.toLowerCase()}>` : \"\"\n\t\t\t\t}`,\n\t\t\t);\n\t\t}\n\t\tif (target.children.length > 6) {\n\t\t\tlines.push(`${childIndent}… (${target.children.length - 6} more children)`);\n\t\t}\n\t\tlines.push(`${targetIndent}</${targetTag}>`);\n\t}\n\n\tfor (let i = ancestors.length - 1; i >= 0; i--) {\n\t\tconst indent = \" \".repeat(i);\n\t\tconst ancestor = ancestors[i];\n\t\tif (!ancestor) continue;\n\t\tlines.push(`${indent}</${ancestor.tagName.toLowerCase()}>`);\n\t}\n\n\treturn lines.join(\"\\n\");\n};\n\nconst isInsideToolbar = (el: Element | null): boolean => {\n\tlet node = el;\n\twhile (node) {\n\t\tif (node instanceof HTMLElement && node.dataset.ynsFeedbackUi === \"true\") return true;\n\t\tnode = node.parentElement;\n\t}\n\treturn false;\n};\n\nconst IGNORED_TAGS = new Set([\"HTML\", \"HEAD\", \"SCRIPT\", \"STYLE\", \"NOSCRIPT\"]);\n\nconst POLL_INTERVAL_MS = 10_000;\n\n// Format a server-computed ETA as remaining + absolute label. The deadline\n// math itself lives in yns-app's `lib/feedback-comments-api.ts` so the toolbar\n// and YNS lock screen stay in sync.\nconst formatEta = (etaIso: string): string => {\n\tconst eta = new Date(etaIso);\n\tconst remainingMs = eta.getTime() - Date.now();\n\tconst dateLabel = eta.toLocaleString(undefined, {\n\t\tweekday: \"short\",\n\t\tmonth: \"short\",\n\t\tday: \"numeric\",\n\t\thour: \"numeric\",\n\t\tminute: \"2-digit\",\n\t});\n\n\tif (remainingMs <= 0) return `Any moment now (estimated by ${dateLabel})`;\n\tconst remainingMin = Math.ceil(remainingMs / 60_000);\n\tif (remainingMin < 60) return `~${remainingMin} minutes (by ${dateLabel})`;\n\tconst remainingHours = Math.round(remainingMs / 3_600_000);\n\tif (remainingHours < 24) {\n\t\treturn `~${remainingHours} ${remainingHours === 1 ? \"hour\" : \"hours\"} (by ${dateLabel})`;\n\t}\n\tconst remainingDays = Math.round(remainingMs / 86_400_000);\n\treturn `~${remainingDays} ${remainingDays === 1 ? \"day\" : \"days\"} (by ${dateLabel})`;\n};\n\nfunction FeedbackToolbar() {\n\tconst [session, setSession] = useState<ActiveSession | null>(null);\n\tconst [loading, setLoading] = useState(true);\n\tconst [pinMode, setPinMode] = useState(false);\n\tconst [pending, setPending] = useState<PendingPin | null>(null);\n\tconst [editingId, setEditingId] = useState<string | null>(null);\n\tconst [sidebarOpen, setSidebarOpen] = useState(false);\n\tconst [finalizing, setFinalizing] = useState(false);\n\tconst apiBase = useRef<string | null>(null);\n\n\tuseEffect(() => {\n\t\tapiBase.current = buildApiBase();\n\t\tif (!apiBase.current) {\n\t\t\tsetLoading(false);\n\t\t\treturn;\n\t\t}\n\n\t\tlet cancelled = false;\n\t\tconst controller = new AbortController();\n\n\t\tconst fetchOnce = async () => {\n\t\t\ttry {\n\t\t\t\tconst res = await fetch(\n\t\t\t\t\t`${apiBase.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,\n\t\t\t\t\t{ credentials: \"include\", signal: controller.signal },\n\t\t\t\t);\n\t\t\t\tif (cancelled) return;\n\t\t\t\tif (!res.ok) {\n\t\t\t\t\tsetSession(null);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst data = (await res.json()) as ActiveSession;\n\t\t\t\tif (cancelled) return;\n\t\t\t\tsetSession(data);\n\t\t\t\t// Hard-refresh once the AI run lands and the preview is up to date.\n\t\t\t\t// Out of scope to diff anything smarter — the new deploy replaces\n\t\t\t\t// the page entirely so a full reload is appropriate here.\n\t\t\t\tif (data.sessionStatus === \"done\") {\n\t\t\t\t\twindow.location.reload();\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tif (!cancelled) setSession(null);\n\t\t\t} finally {\n\t\t\t\tif (!cancelled) setLoading(false);\n\t\t\t}\n\t\t};\n\n\t\tvoid fetchOnce();\n\t\tconst interval = window.setInterval(fetchOnce, POLL_INTERVAL_MS);\n\n\t\treturn () => {\n\t\t\tcancelled = true;\n\t\t\tcontroller.abort();\n\t\t\twindow.clearInterval(interval);\n\t\t};\n\t}, []);\n\n\t// When the session stops accepting comments (finalized → in_review, or\n\t// closed), tear down any in-flight UI so the hover overlay/popover/sidebar\n\t// don't linger after the toolbar's main render returns null.\n\tuseEffect(() => {\n\t\tif (!session || session.canComment) return;\n\t\tsetPinMode(false);\n\t\tsetPending(null);\n\t\tsetSidebarOpen(false);\n\t\tsetEditingId(null);\n\t}, [session]);\n\n\tuseEffect(() => {\n\t\tif (!pinMode) return;\n\t\tdocument.body.style.cursor = \"crosshair\";\n\t\treturn () => {\n\t\t\tdocument.body.style.cursor = \"\";\n\t\t};\n\t}, [pinMode]);\n\n\tuseEffect(() => {\n\t\tif (!pinMode) return;\n\n\t\tconst overlay = document.createElement(\"div\");\n\t\toverlay.dataset.ynsFeedbackUi = \"true\";\n\t\toverlay.style.cssText = [\n\t\t\t\"position: fixed\",\n\t\t\t\"pointer-events: none\",\n\t\t\t\"z-index: 2147483644\",\n\t\t\t\"border: 2px dashed #10b981\",\n\t\t\t\"background: rgba(16, 185, 129, 0.08)\",\n\t\t\t\"border-radius: 3px\",\n\t\t\t\"display: none\",\n\t\t\t\"transition: top 0.05s, left 0.05s, width 0.05s, height 0.05s\",\n\t\t].join(\";\");\n\n\t\tconst label = document.createElement(\"div\");\n\t\tlabel.dataset.ynsFeedbackUi = \"true\";\n\t\tlabel.textContent = \"Click to comment\";\n\t\tlabel.style.cssText = [\n\t\t\t\"position: fixed\",\n\t\t\t\"pointer-events: none\",\n\t\t\t\"z-index: 2147483645\",\n\t\t\t\"background: #059669\",\n\t\t\t\"color: #fff\",\n\t\t\t\"font-size: 11px\",\n\t\t\t\"font-family: ui-sans-serif, system-ui, sans-serif\",\n\t\t\t\"padding: 3px 8px\",\n\t\t\t\"border-radius: 4px\",\n\t\t\t\"white-space: nowrap\",\n\t\t\t\"display: none\",\n\t\t\t\"box-shadow: 0 2px 6px rgba(0,0,0,0.15)\",\n\t\t].join(\";\");\n\n\t\tdocument.documentElement.appendChild(overlay);\n\t\tdocument.documentElement.appendChild(label);\n\n\t\tlet hovered: Element | null = null;\n\t\tconst handleMove = (e: MouseEvent) => {\n\t\t\tconst el = document.elementFromPoint(e.clientX, e.clientY);\n\t\t\tif (!el || IGNORED_TAGS.has(el.tagName) || isInsideToolbar(el)) {\n\t\t\t\toverlay.style.display = \"none\";\n\t\t\t\tlabel.style.display = \"none\";\n\t\t\t\thovered = null;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\thovered = el;\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\tif (hovered !== el) return;\n\t\t\t\tconst rect = el.getBoundingClientRect();\n\t\t\t\toverlay.style.top = `${rect.top}px`;\n\t\t\t\toverlay.style.left = `${rect.left}px`;\n\t\t\t\toverlay.style.width = `${rect.width}px`;\n\t\t\t\toverlay.style.height = `${rect.height}px`;\n\t\t\t\toverlay.style.display = \"block\";\n\t\t\t\tlabel.style.left = `${e.clientX + 14}px`;\n\t\t\t\tlabel.style.top = `${e.clientY + 14}px`;\n\t\t\t\tlabel.style.display = \"block\";\n\t\t\t});\n\t\t};\n\n\t\tconst handleLeave = () => {\n\t\t\toverlay.style.display = \"none\";\n\t\t\tlabel.style.display = \"none\";\n\t\t\thovered = null;\n\t\t};\n\n\t\tdocument.addEventListener(\"mousemove\", handleMove, true);\n\t\tdocument.addEventListener(\"mouseleave\", handleLeave);\n\n\t\treturn () => {\n\t\t\tdocument.removeEventListener(\"mousemove\", handleMove, true);\n\t\t\tdocument.removeEventListener(\"mouseleave\", handleLeave);\n\t\t\toverlay.remove();\n\t\t\tlabel.remove();\n\t\t};\n\t}, [pinMode]);\n\n\tuseEffect(() => {\n\t\tif (!pinMode) return;\n\t\tconst handleClick = (event: MouseEvent) => {\n\t\t\tconst target = event.target;\n\t\t\tif (!(target instanceof Element)) return;\n\t\t\tif (isInsideToolbar(target)) return;\n\n\t\t\tevent.preventDefault();\n\t\t\tevent.stopPropagation();\n\n\t\t\tconst rect = target.getBoundingClientRect();\n\t\t\tconst offsetXRatio = rect.width > 0 ? (event.clientX - rect.left) / rect.width : 0.5;\n\t\t\tconst offsetYRatio = rect.height > 0 ? (event.clientY - rect.top) / rect.height : 0.5;\n\t\t\tsetPending({\n\t\t\t\tcssSelector: computeCssSelector(target),\n\t\t\t\tpagePath: window.location.pathname,\n\t\t\t\tsurroundingHtml: buildSurroundingHtml(target),\n\t\t\t\trect: {\n\t\t\t\t\ttop: rect.top + window.scrollY,\n\t\t\t\t\tleft: rect.left + window.scrollX,\n\t\t\t\t\twidth: rect.width,\n\t\t\t\t\theight: rect.height,\n\t\t\t\t},\n\t\t\t\tclickX: event.clientX + window.scrollX,\n\t\t\t\tclickY: event.clientY + window.scrollY,\n\t\t\t\toffsetXRatio: Math.min(1, Math.max(0, offsetXRatio)),\n\t\t\t\toffsetYRatio: Math.min(1, Math.max(0, offsetYRatio)),\n\t\t\t});\n\t\t\tsetPinMode(false);\n\t\t};\n\t\tdocument.addEventListener(\"click\", handleClick, { capture: true });\n\t\treturn () => document.removeEventListener(\"click\", handleClick, { capture: true });\n\t}, [pinMode]);\n\n\tconst refreshComments = async () => {\n\t\tif (!apiBase.current) return;\n\t\tconst res = await fetch(\n\t\t\t`${apiBase.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,\n\t\t\t{ credentials: \"include\" },\n\t\t);\n\t\tif (!res.ok) return;\n\t\tconst data = (await res.json()) as ActiveSession;\n\t\tsetSession(data);\n\t};\n\n\tconst submitNewComment = async (content: string) => {\n\t\tif (!apiBase.current || !session || !pending) return;\n\t\tconst res = await fetch(`${apiBase.current}/api/feedback-comments`, {\n\t\t\tmethod: \"POST\",\n\t\t\tcredentials: \"include\",\n\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\tbody: JSON.stringify({\n\t\t\t\tfeedbackSessionId: session.feedbackSessionId,\n\t\t\t\tcontent,\n\t\t\t\tpagePath: pending.pagePath,\n\t\t\t\tcssSelector: pending.cssSelector,\n\t\t\t\tsurroundingHtml: pending.surroundingHtml,\n\t\t\t\toffsetXRatio: pending.offsetXRatio,\n\t\t\t\toffsetYRatio: pending.offsetYRatio,\n\t\t\t}),\n\t\t});\n\t\tif (!res.ok) return;\n\t\tsetPending(null);\n\t\tsetPinMode(true);\n\t\tawait refreshComments();\n\t};\n\n\tconst updateComment = async (id: string, content: string) => {\n\t\tif (!apiBase.current) return;\n\t\tconst res = await fetch(`${apiBase.current}/api/feedback-comments/${id}`, {\n\t\t\tmethod: \"PATCH\",\n\t\t\tcredentials: \"include\",\n\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\tbody: JSON.stringify({ content }),\n\t\t});\n\t\tif (!res.ok) return;\n\t\tsetEditingId(null);\n\t\tawait refreshComments();\n\t};\n\n\tconst removeComment = async (id: string) => {\n\t\tif (!apiBase.current) return;\n\t\tconst res = await fetch(`${apiBase.current}/api/feedback-comments/${id}`, {\n\t\t\tmethod: \"DELETE\",\n\t\t\tcredentials: \"include\",\n\t\t});\n\t\tif (!res.ok) return;\n\t\tawait refreshComments();\n\t};\n\n\tconst finalizeSession = async () => {\n\t\tif (!apiBase.current || !session) return;\n\t\tif (!window.confirm(\"Finalize this feedback session? You won't be able to add more comments.\")) return;\n\t\tsetFinalizing(true);\n\t\ttry {\n\t\t\tconst res = await fetch(\n\t\t\t\t`${apiBase.current}/api/feedback-sessions/${session.feedbackSessionId}/finalize`,\n\t\t\t\t{ method: \"POST\", credentials: \"include\" },\n\t\t\t);\n\t\t\tif (!res.ok) return;\n\t\t\tsetSession((prev) => (prev ? { ...prev, canComment: false } : prev));\n\t\t} finally {\n\t\t\tsetFinalizing(false);\n\t\t}\n\t};\n\n\tconst scrollToComment = (comment: FeedbackComment) => {\n\t\tif (comment.pagePath !== window.location.pathname) {\n\t\t\twindow.location.assign(comment.pagePath);\n\t\t\treturn;\n\t\t}\n\t\tlet el: Element | null = null;\n\t\ttry {\n\t\t\tel = document.querySelector(comment.cssSelector);\n\t\t} catch {\n\t\t\tel = null;\n\t\t}\n\t\tif (!el) return;\n\t\tel.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n\t\tsetEditingId(comment.id);\n\t};\n\n\tif (loading || !session) return null;\n\n\t// Session has been finalized for AI review. Show a small status panel with\n\t// the same progress + ETA shown in YNS, but no commenting controls. Polling\n\t// continues in the background; on `done` we hard-reload to pick up the\n\t// fresh deploy.\n\tif (!session.canComment) {\n\t\tif (session.sessionStatus !== \"processing\" && session.sessionStatus !== \"in_review\") return null;\n\t\treturn (\n\t\t\t<div data-yns-feedback-ui=\"true\">\n\t\t\t\t<SubmittedPanel progress={session.progress} eta={session.eta} status={session.sessionStatus} />\n\t\t\t</div>\n\t\t);\n\t}\n\n\tconst visiblePins = session.comments.filter(\n\t\t(c) => c.pagePath === window.location.pathname && c.status !== \"done\",\n\t);\n\n\treturn (\n\t\t<div data-yns-feedback-ui=\"true\">\n\t\t\t{visiblePins.map((pin, idx) => (\n\t\t\t\t<PinOverlay\n\t\t\t\t\tkey={pin.id}\n\t\t\t\t\tpin={pin}\n\t\t\t\t\tnumber={idx + 1}\n\t\t\t\t\tediting={editingId === pin.id}\n\t\t\t\t\tonStartEdit={() => setEditingId(pin.id)}\n\t\t\t\t\tonCancelEdit={() => setEditingId(null)}\n\t\t\t\t\tonSave={(content) => updateComment(pin.id, content)}\n\t\t\t\t\tonRemove={() => removeComment(pin.id)}\n\t\t\t\t/>\n\t\t\t))}\n\n\t\t\t{pending && (\n\t\t\t\t<PendingCommentPopover\n\t\t\t\t\tpending={pending}\n\t\t\t\t\tonCancel={() => {\n\t\t\t\t\t\tsetPending(null);\n\t\t\t\t\t\tsetPinMode(true);\n\t\t\t\t\t}}\n\t\t\t\t\tonSave={(content) => submitNewComment(content)}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t{sidebarOpen && (\n\t\t\t\t<CommentsSidebar\n\t\t\t\t\tcomments={session.comments}\n\t\t\t\t\tcurrentPath={window.location.pathname}\n\t\t\t\t\tonClose={() => setSidebarOpen(false)}\n\t\t\t\t\tonSelect={scrollToComment}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t<div style={toolbarStyle}>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tonClick={() => setPinMode((v) => !v)}\n\t\t\t\t\tstyle={pinMode ? toolbarButtonActiveStyle : toolbarButtonStyle}\n\t\t\t\t>\n\t\t\t\t\t{pinMode ? \"Cancel\" : \"Add comment\"}\n\t\t\t\t</button>\n\t\t\t\t<button type=\"button\" onClick={() => setSidebarOpen((v) => !v)} style={toolbarButtonGhostStyle}>\n\t\t\t\t\t{sidebarOpen ? \"Hide list\" : `List (${session.comments.length})`}\n\t\t\t\t</button>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tonClick={finalizeSession}\n\t\t\t\t\tdisabled={finalizing}\n\t\t\t\t\tstyle={toolbarButtonFinalizeStyle}\n\t\t\t\t>\n\t\t\t\t\t{finalizing ? \"Finalizing…\" : \"Finalize\"}\n\t\t\t\t</button>\n\t\t\t\t<span style={toolbarHintStyle}>\n\t\t\t\t\t{pinMode ? \"Click any element to comment\" : `${visiblePins.length} on this page`}\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction SubmittedPanel({\n\tprogress,\n\teta: etaIso,\n\tstatus,\n}: {\n\tprogress: ReviewProgress;\n\teta: string;\n\tstatus: SessionStatus;\n}) {\n\t// Re-render every minute so the relative ETA label stays fresh between\n\t// the 10s data polls (which only re-render when the API payload changes).\n\tconst [, setTick] = useState(0);\n\tuseEffect(() => {\n\t\tconst id = window.setInterval(() => setTick((t) => t + 1), 60_000);\n\t\treturn () => window.clearInterval(id);\n\t}, []);\n\n\tconst eta = formatEta(etaIso);\n\tconst isInReview = status === \"in_review\";\n\tconst headline = isInReview ? \"Feedback under review\" : \"Applying feedback\";\n\n\treturn (\n\t\t<div style={submittedPanelStyle}>\n\t\t\t<style>{spinnerKeyframes}</style>\n\t\t\t<div style={submittedHeaderStyle}>\n\t\t\t\t<Spinner />\n\t\t\t\t<span style={submittedBadgeStyle}>Submitted</span>\n\t\t\t\t<strong style={{ fontSize: 13 }}>{headline}</strong>\n\t\t\t</div>\n\t\t\t<div style={submittedProgressLabelStyle}>\n\t\t\t\t<span>Review progress</span>\n\t\t\t\t<span style={{ opacity: 0.7 }}>{progress.label}</span>\n\t\t\t</div>\n\t\t\t<div style={submittedProgressTrackStyle}>\n\t\t\t\t<div style={{ ...submittedProgressFillStyle, width: `${progress.fillPct}%` }} />\n\t\t\t</div>\n\t\t\t<p style={submittedEtaStyle}>\n\t\t\t\tEstimated delivery: <span style={{ fontWeight: 600 }}>{eta}</span>\n\t\t\t</p>\n\t\t</div>\n\t);\n}\n\n// Inline keyframes — toolbar avoids relying on a global stylesheet.\nconst spinnerKeyframes = `@keyframes yns-feedback-spin { to { transform: rotate(360deg); } }`;\n\nfunction Spinner({ size = 14 }: { size?: number }) {\n\treturn (\n\t\t<span\n\t\t\taria-hidden\n\t\t\tstyle={{\n\t\t\t\tdisplay: \"inline-block\",\n\t\t\t\twidth: size,\n\t\t\t\theight: size,\n\t\t\t\tborder: \"2px solid rgba(16, 185, 129, 0.25)\",\n\t\t\t\tborderTopColor: \"#10b981\",\n\t\t\t\tborderRadius: 999,\n\t\t\t\tanimation: \"yns-feedback-spin 0.9s linear infinite\",\n\t\t\t}}\n\t\t/>\n\t);\n}\n\nfunction PinOverlay({\n\tpin,\n\tnumber,\n\tediting,\n\tonStartEdit,\n\tonCancelEdit,\n\tonSave,\n\tonRemove,\n}: {\n\tpin: FeedbackComment;\n\tnumber: number;\n\tediting: boolean;\n\tonStartEdit: () => void;\n\tonCancelEdit: () => void;\n\tonSave: (content: string) => Promise<void>;\n\tonRemove: () => Promise<void>;\n}) {\n\tconst target = useTargetPosition(pin.cssSelector, pin.offsetXRatio, pin.offsetYRatio);\n\tif (!target) return null;\n\n\treturn (\n\t\t<div\n\t\t\tstyle={{\n\t\t\t\tposition: \"absolute\",\n\t\t\t\ttop: target.top,\n\t\t\t\tleft: target.left,\n\t\t\t\tzIndex: 2147483600,\n\t\t\t\tpointerEvents: \"auto\",\n\t\t\t}}\n\t\t>\n\t\t\t<button type=\"button\" onClick={onStartEdit} style={pinDotStyle} title={pin.content}>\n\t\t\t\t{number}\n\t\t\t</button>\n\t\t\t{editing && (\n\t\t\t\t<EditPopover initial={pin.content} onCancel={onCancelEdit} onSave={onSave} onRemove={onRemove} />\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nfunction PendingCommentPopover({\n\tpending,\n\tonCancel,\n\tonSave,\n}: {\n\tpending: PendingPin;\n\tonCancel: () => void;\n\tonSave: (content: string) => Promise<void>;\n}) {\n\treturn (\n\t\t<>\n\t\t\t<div\n\t\t\t\tstyle={{\n\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\ttop: pending.rect.top + pending.rect.height * pending.offsetYRatio - 12,\n\t\t\t\t\tleft: pending.rect.left + pending.rect.width * pending.offsetXRatio - 12,\n\t\t\t\t\tzIndex: 2147483600,\n\t\t\t\t\tpointerEvents: \"none\",\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<div style={pinDotStyle}>•</div>\n\t\t\t</div>\n\t\t\t<div\n\t\t\t\tstyle={{\n\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\ttop: pending.clickY + 10,\n\t\t\t\t\tleft: pending.clickX + 10,\n\t\t\t\t\tzIndex: 2147483647,\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<EditPopover initial=\"\" onCancel={onCancel} onSave={onSave} />\n\t\t\t</div>\n\t\t</>\n\t);\n}\n\nfunction CommentsSidebar({\n\tcomments,\n\tcurrentPath,\n\tonClose,\n\tonSelect,\n}: {\n\tcomments: FeedbackComment[];\n\tcurrentPath: string;\n\tonClose: () => void;\n\tonSelect: (comment: FeedbackComment) => void;\n}) {\n\tconst groups = new Map<string, FeedbackComment[]>();\n\tfor (const c of comments) {\n\t\tconst list = groups.get(c.pagePath) ?? [];\n\t\tlist.push(c);\n\t\tgroups.set(c.pagePath, list);\n\t}\n\tconst paths = Array.from(groups.keys()).sort((a, b) => {\n\t\tif (a === currentPath) return -1;\n\t\tif (b === currentPath) return 1;\n\t\treturn a.localeCompare(b);\n\t});\n\n\treturn (\n\t\t<div style={sidebarStyle}>\n\t\t\t<div style={sidebarHeaderStyle}>\n\t\t\t\t<strong style={{ fontSize: 14 }}>Comments ({comments.length})</strong>\n\t\t\t\t<button type=\"button\" onClick={onClose} style={ghostButtonStyle}>\n\t\t\t\t\tClose\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t\t<div style={sidebarScrollStyle}>\n\t\t\t\t{comments.length === 0 ? (\n\t\t\t\t\t<div style={sidebarEmptyStyle}>No comments yet. Click “Add comment” to leave one.</div>\n\t\t\t\t) : (\n\t\t\t\t\tpaths.map((path) => (\n\t\t\t\t\t\t<div key={path} style={{ marginBottom: 12 }}>\n\t\t\t\t\t\t\t<div style={sidebarPathStyle}>{path === currentPath ? `${path} · current` : path}</div>\n\t\t\t\t\t\t\t{(groups.get(path) ?? []).map((c, idx) => (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\tkey={c.id}\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={() => onSelect(c)}\n\t\t\t\t\t\t\t\t\tstyle={sidebarItemStyle}\n\t\t\t\t\t\t\t\t\tdisabled={c.status === \"done\" && false}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<span style={sidebarItemIndexStyle}>{idx + 1}</span>\n\t\t\t\t\t\t\t\t\t<span style={sidebarItemTextStyle}>\n\t\t\t\t\t\t\t\t\t\t{c.content.length > 140 ? `${c.content.slice(0, 140)}…` : c.content}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t{c.status === \"done\" && <span style={sidebarItemDoneStyle}>done</span>}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction EditPopover({\n\tinitial,\n\tonCancel,\n\tonSave,\n\tonRemove,\n}: {\n\tinitial: string;\n\tonCancel: () => void;\n\tonSave: (content: string) => Promise<void>;\n\tonRemove?: () => Promise<void>;\n}) {\n\tconst [value, setValue] = useState(initial);\n\tconst [saving, setSaving] = useState(false);\n\tconst textareaRef = useRef<HTMLTextAreaElement | null>(null);\n\n\tuseEffect(() => {\n\t\tconst el = textareaRef.current;\n\t\tif (!el) return;\n\t\tel.focus();\n\t\tconst len = el.value.length;\n\t\tel.setSelectionRange(len, len);\n\t}, []);\n\n\tconst handleSubmit = async (e: FormEvent) => {\n\t\te.preventDefault();\n\t\tconst content = value.trim();\n\t\tif (!content) return;\n\t\tsetSaving(true);\n\t\ttry {\n\t\t\tawait onSave(content);\n\t\t} finally {\n\t\t\tsetSaving(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t<form onSubmit={handleSubmit} style={popoverStyle}>\n\t\t\t<textarea\n\t\t\t\tref={textareaRef}\n\t\t\t\tvalue={value}\n\t\t\t\tonChange={(e) => setValue(e.target.value)}\n\t\t\t\tplaceholder=\"Leave a comment…\"\n\t\t\t\tstyle={textareaStyle}\n\t\t\t\trows={3}\n\t\t\t/>\n\t\t\t<div style={popoverActionsStyle}>\n\t\t\t\t{onRemove && (\n\t\t\t\t\t<button type=\"button\" onClick={() => onRemove()} style={dangerButtonStyle} disabled={saving}>\n\t\t\t\t\t\tDelete\n\t\t\t\t\t</button>\n\t\t\t\t)}\n\t\t\t\t<div style={{ flex: 1 }} />\n\t\t\t\t<button type=\"button\" onClick={onCancel} style={ghostButtonStyle} disabled={saving}>\n\t\t\t\t\tCancel\n\t\t\t\t</button>\n\t\t\t\t<button type=\"submit\" style={primaryButtonStyle} disabled={saving || !value.trim()}>\n\t\t\t\t\t{saving ? \"Saving…\" : \"Save\"}\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t</form>\n\t);\n}\n\nfunction useTargetPosition(selector: string, offsetXRatio: number, offsetYRatio: number) {\n\tconst [pos, setPos] = useState<{ top: number; left: number } | null>(null);\n\n\tuseEffect(() => {\n\t\tconst update = () => {\n\t\t\tlet el: Element | null = null;\n\t\t\ttry {\n\t\t\t\tel = document.querySelector(selector);\n\t\t\t} catch {\n\t\t\t\tel = null;\n\t\t\t}\n\t\t\tif (!el) {\n\t\t\t\tsetPos(null);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst r = el.getBoundingClientRect();\n\t\t\tsetPos({\n\t\t\t\ttop: r.top + window.scrollY + r.height * offsetYRatio - 12,\n\t\t\t\tleft: r.left + window.scrollX + r.width * offsetXRatio - 12,\n\t\t\t});\n\t\t};\n\n\t\tupdate();\n\t\tconst ro = new ResizeObserver(update);\n\t\ttry {\n\t\t\tconst el = document.querySelector(selector);\n\t\t\tif (el) ro.observe(el);\n\t\t} catch {\n\t\t\t// swallow invalid selector\n\t\t}\n\t\twindow.addEventListener(\"scroll\", update, true);\n\t\twindow.addEventListener(\"resize\", update);\n\t\treturn () => {\n\t\t\tro.disconnect();\n\t\t\twindow.removeEventListener(\"scroll\", update, true);\n\t\t\twindow.removeEventListener(\"resize\", update);\n\t\t};\n\t}, [selector, offsetXRatio, offsetYRatio]);\n\n\treturn pos;\n}\n\n// Inline styles — toolbar is injected into arbitrary stores, so we avoid relying\n// on any CSS framework being present.\nconst toolbarStyle: CSSProperties = {\n\tposition: \"fixed\",\n\tbottom: 16,\n\tleft: \"50%\",\n\ttransform: \"translateX(-50%)\",\n\tzIndex: 2147483646,\n\tdisplay: \"flex\",\n\talignItems: \"center\",\n\tgap: 8,\n\tpadding: \"8px 12px\",\n\tbackground: \"rgba(17, 17, 17, 0.92)\",\n\tcolor: \"white\",\n\tborderRadius: 999,\n\tboxShadow: \"0 8px 24px rgba(0,0,0,0.25)\",\n\tfontFamily:\n\t\t'-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\"',\n\tfontSize: 14,\n\tpointerEvents: \"auto\",\n};\n\nconst toolbarButtonStyle: CSSProperties = {\n\tborder: \"none\",\n\tbackground: \"white\",\n\tcolor: \"#111\",\n\tpadding: \"6px 12px\",\n\tborderRadius: 999,\n\tcursor: \"pointer\",\n\tfontWeight: 600,\n\tfontSize: 13,\n};\n\nconst toolbarButtonActiveStyle: CSSProperties = {\n\t...toolbarButtonStyle,\n\tbackground: \"#ef4444\",\n\tcolor: \"white\",\n};\n\nconst toolbarButtonGhostStyle: CSSProperties = {\n\tborder: \"1px solid rgba(255,255,255,0.4)\",\n\tbackground: \"transparent\",\n\tcolor: \"white\",\n\tpadding: \"6px 12px\",\n\tborderRadius: 999,\n\tcursor: \"pointer\",\n\tfontWeight: 500,\n\tfontSize: 13,\n};\n\nconst toolbarButtonFinalizeStyle: CSSProperties = {\n\tborder: \"none\",\n\tbackground: \"#10b981\",\n\tcolor: \"white\",\n\tpadding: \"6px 12px\",\n\tborderRadius: 999,\n\tcursor: \"pointer\",\n\tfontWeight: 600,\n\tfontSize: 13,\n};\n\nconst toolbarHintStyle: CSSProperties = {\n\topacity: 0.8,\n\tfontSize: 12,\n};\n\nconst pinDotStyle: CSSProperties = {\n\twidth: 24,\n\theight: 24,\n\tborderRadius: 999,\n\tbackground: \"#10b981\",\n\tcolor: \"white\",\n\tborder: \"2px solid white\",\n\tcursor: \"pointer\",\n\tfontSize: 12,\n\tfontWeight: 700,\n\tboxShadow: \"0 2px 6px rgba(0,0,0,0.3)\",\n\tdisplay: \"inline-flex\",\n\talignItems: \"center\",\n\tjustifyContent: \"center\",\n\tpadding: 0,\n};\n\nconst popoverStyle: CSSProperties = {\n\tbackground: \"white\",\n\tborder: \"1px solid #e5e7eb\",\n\tborderRadius: 8,\n\tpadding: 12,\n\twidth: 280,\n\tboxShadow: \"0 12px 32px rgba(0,0,0,0.18)\",\n\tfontFamily:\n\t\t'-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\"',\n\tdisplay: \"flex\",\n\tflexDirection: \"column\",\n\tgap: 8,\n\tcolor: \"#111\",\n};\n\nconst textareaStyle: CSSProperties = {\n\twidth: \"100%\",\n\tborder: \"1px solid #d1d5db\",\n\tborderRadius: 6,\n\tpadding: 8,\n\tfontSize: 14,\n\tresize: \"vertical\",\n\tfontFamily: \"inherit\",\n\tcolor: \"#111\",\n\tbackground: \"white\",\n\tboxSizing: \"border-box\",\n};\n\nconst popoverActionsStyle: CSSProperties = {\n\tdisplay: \"flex\",\n\talignItems: \"center\",\n\tgap: 6,\n};\n\nconst baseButton: CSSProperties = {\n\tborder: \"1px solid transparent\",\n\tborderRadius: 6,\n\tpadding: \"6px 10px\",\n\tfontSize: 13,\n\tcursor: \"pointer\",\n\tfontWeight: 500,\n};\n\nconst primaryButtonStyle: CSSProperties = {\n\t...baseButton,\n\tbackground: \"#111\",\n\tcolor: \"white\",\n};\n\nconst ghostButtonStyle: CSSProperties = {\n\t...baseButton,\n\tbackground: \"transparent\",\n\tcolor: \"#374151\",\n\tborderColor: \"#d1d5db\",\n};\n\nconst dangerButtonStyle: CSSProperties = {\n\t...baseButton,\n\tbackground: \"transparent\",\n\tcolor: \"#b91c1c\",\n\tborderColor: \"#fecaca\",\n};\n\nconst sidebarStyle: CSSProperties = {\n\tposition: \"fixed\",\n\ttop: 0,\n\tright: 0,\n\tbottom: 0,\n\twidth: 360,\n\tmaxWidth: \"92vw\",\n\tbackground: \"white\",\n\tborderLeft: \"1px solid #e5e7eb\",\n\tboxShadow: \"-12px 0 32px rgba(0,0,0,0.12)\",\n\tzIndex: 2147483646,\n\tdisplay: \"flex\",\n\tflexDirection: \"column\",\n\tfontFamily:\n\t\t'-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\"',\n\tcolor: \"#111\",\n};\n\nconst sidebarHeaderStyle: CSSProperties = {\n\tdisplay: \"flex\",\n\talignItems: \"center\",\n\tjustifyContent: \"space-between\",\n\tpadding: \"12px 16px\",\n\tborderBottom: \"1px solid #e5e7eb\",\n};\n\nconst sidebarScrollStyle: CSSProperties = {\n\tflex: 1,\n\toverflow: \"auto\",\n\tpadding: \"12px 12px 24px\",\n};\n\nconst sidebarEmptyStyle: CSSProperties = {\n\tcolor: \"#6b7280\",\n\tfontSize: 13,\n\tpadding: \"24px 8px\",\n\ttextAlign: \"center\",\n};\n\nconst sidebarPathStyle: CSSProperties = {\n\tfontSize: 11,\n\ttextTransform: \"uppercase\",\n\tletterSpacing: 0.5,\n\tcolor: \"#6b7280\",\n\tpadding: \"4px 4px 6px\",\n\twordBreak: \"break-all\",\n};\n\nconst sidebarItemStyle: CSSProperties = {\n\tdisplay: \"flex\",\n\talignItems: \"flex-start\",\n\tgap: 8,\n\twidth: \"100%\",\n\ttextAlign: \"left\",\n\tbackground: \"white\",\n\tborder: \"1px solid #e5e7eb\",\n\tborderRadius: 6,\n\tpadding: \"8px 10px\",\n\tcursor: \"pointer\",\n\tfontSize: 13,\n\tmarginBottom: 6,\n\tcolor: \"#111\",\n};\n\nconst sidebarItemIndexStyle: CSSProperties = {\n\tflexShrink: 0,\n\twidth: 22,\n\theight: 22,\n\tborderRadius: 999,\n\tbackground: \"#10b981\",\n\tcolor: \"white\",\n\tfontWeight: 700,\n\tfontSize: 11,\n\tdisplay: \"inline-flex\",\n\talignItems: \"center\",\n\tjustifyContent: \"center\",\n};\n\nconst sidebarItemTextStyle: CSSProperties = {\n\tflex: 1,\n\twhiteSpace: \"pre-wrap\",\n\twordBreak: \"break-word\",\n};\n\nconst sidebarItemDoneStyle: CSSProperties = {\n\tflexShrink: 0,\n\tbackground: \"#d1fae5\",\n\tcolor: \"#065f46\",\n\tfontSize: 11,\n\tfontWeight: 600,\n\tpadding: \"2px 6px\",\n\tborderRadius: 4,\n\talignSelf: \"center\",\n};\n\nconst submittedPanelStyle: CSSProperties = {\n\tposition: \"fixed\",\n\tbottom: 16,\n\tleft: \"50%\",\n\ttransform: \"translateX(-50%)\",\n\tzIndex: 2147483646,\n\tdisplay: \"flex\",\n\tflexDirection: \"column\",\n\tgap: 8,\n\tpadding: \"12px 16px\",\n\twidth: \"min(420px, calc(100vw - 32px))\",\n\tbackground: \"white\",\n\tborder: \"1px solid #e5e7eb\",\n\tborderRadius: 12,\n\tboxShadow: \"0 12px 32px rgba(0,0,0,0.18)\",\n\tfontFamily:\n\t\t'-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\"',\n\tcolor: \"#111\",\n\tpointerEvents: \"auto\",\n};\n\nconst submittedHeaderStyle: CSSProperties = {\n\tdisplay: \"flex\",\n\talignItems: \"center\",\n\tgap: 8,\n};\n\nconst submittedBadgeStyle: CSSProperties = {\n\tbackground: \"#d1fae5\",\n\tcolor: \"#065f46\",\n\tfontSize: 11,\n\tfontWeight: 700,\n\tpadding: \"2px 8px\",\n\tborderRadius: 999,\n\ttextTransform: \"uppercase\",\n\tletterSpacing: 0.5,\n};\n\nconst submittedProgressLabelStyle: CSSProperties = {\n\tdisplay: \"flex\",\n\tjustifyContent: \"space-between\",\n\tfontSize: 12,\n};\n\nconst submittedProgressTrackStyle: CSSProperties = {\n\twidth: \"100%\",\n\theight: 6,\n\tbackground: \"#f3f4f6\",\n\tborderRadius: 999,\n\toverflow: \"hidden\",\n};\n\nconst submittedProgressFillStyle: CSSProperties = {\n\theight: \"100%\",\n\tbackground: \"#10b981\",\n\tborderRadius: 999,\n\ttransition: \"width 500ms\",\n};\n\nconst submittedEtaStyle: CSSProperties = {\n\tmargin: 0,\n\tfontSize: 12,\n\tcolor: \"#6b7280\",\n};\n\nexport function mountFeedbackToolbar(): { unmount: () => void } | null {\n\tconsole.log(\"[YNS Feedback Toolbar] mountFeedbackToolbar() called\", {\n\t\tisWindow: typeof window !== \"undefined\",\n\t\tvercelEnv: process.env.NEXT_PUBLIC_VERCEL_ENV,\n\t\talreadyMounted: typeof document !== \"undefined\" && Boolean(document.getElementById(MOUNT_NODE_ID)),\n\t});\n\tif (typeof window === \"undefined\") return null;\n\tif (process.env.NEXT_PUBLIC_VERCEL_ENV !== \"preview\") {\n\t\tconsole.log(\n\t\t\t\"[YNS Feedback Toolbar] gate failed — NEXT_PUBLIC_VERCEL_ENV is\",\n\t\t\tprocess.env.NEXT_PUBLIC_VERCEL_ENV,\n\t\t);\n\t\treturn null;\n\t}\n\tif (document.getElementById(MOUNT_NODE_ID)) return null;\n\n\tconst container = document.createElement(\"div\");\n\tcontainer.id = MOUNT_NODE_ID;\n\tcontainer.dataset.ynsFeedbackUi = \"true\";\n\tdocument.body.appendChild(container);\n\n\tconst root = createRoot(container);\n\troot.render(<FeedbackToolbar />);\n\n\treturn {\n\t\tunmount: () => {\n\t\t\troot.unmount();\n\t\t\tcontainer.remove();\n\t\t},\n\t};\n}\n\n// Auto-mount on import. Wait for DOM ready.\nif (typeof window !== \"undefined\") {\n\tconsole.log(\"[YNS Feedback Toolbar] module evaluated\", {\n\t\tvercelEnv: process.env.NEXT_PUBLIC_VERCEL_ENV,\n\t\treadyState: document.readyState,\n\t\twillAutoMount: process.env.NEXT_PUBLIC_VERCEL_ENV === \"preview\",\n\t});\n}\nif (typeof window !== \"undefined\" && process.env.NEXT_PUBLIC_VERCEL_ENV === \"preview\") {\n\tif (document.readyState === \"loading\") {\n\t\tdocument.addEventListener(\"DOMContentLoaded\", () => {\n\t\t\tmountFeedbackToolbar();\n\t\t});\n\t} else {\n\t\tmountFeedbackToolbar();\n\t}\n}\n","/**\n * Sandbox Inspectors — side-effect entry.\n *\n * Auto-mounts the design-mode + comment-mode inspectors inside per-store\n * storefronts running under the AI Builder sandbox dev server. Communicates\n * with the parent /design iframe via postMessage.\n *\n * Always-on: inspectors are passive postMessage listeners and don't affect the\n * page until the AI Builder iframe sends a `*-mode-toggle` message. Gating on\n * NODE_ENV is wrong (always \"production\" on Vercel) and any build-time gate\n * gets folded out by the commerce-kit bundle, leaving an empty file.\n */\n\n/**\n * Exported so `browser.tsx` can `export { startSandboxInspectors }` — the\n * named binding anchors this module against esbuild tree-shaking when\n * commerce-kit is bundled. Without it, an `export * from \"./sandbox-inspectors\"`\n * re-exports an empty binding set and esbuild DCEs the whole file.\n */\nexport function startSandboxInspectors(): void {\n\tif (typeof window === \"undefined\") return;\n\tconsole.log(\"[YNS Sandbox Inspectors] module evaluated\");\n\tinitDesignModeInspector();\n\tinitCommentModeInspector();\n}\n\n// Auto-start on import.\nstartSandboxInspectors();\n\ninterface ElementInfo {\n\ttag: string;\n\tid: string | undefined;\n\tclasses: string[];\n\ttextContent: string;\n\tcssSelector: string;\n\tboundingRect: { top: number; left: number; width: number; height: number };\n\tcomputedStyles: Partial<{\n\t\tcolor: string;\n\t\tbackgroundColor: string;\n\t\tfontSize: string;\n\t\tfontFamily: string;\n\t\tpadding: string;\n\t\tmargin: string;\n\t}>;\n}\n\nconst IGNORED_TAGS = new Set([\"HEAD\", \"SCRIPT\", \"STYLE\", \"NOSCRIPT\", \"HTML\"]);\n\nfunction generateCssSelector(el: Element): string {\n\tif (el.id) return `${el.tagName.toLowerCase()}#${CSS.escape(el.id)}`;\n\n\tconst parts: string[] = [];\n\tlet current: Element | null = el;\n\twhile (current && current !== document.body && current !== document.documentElement) {\n\t\tlet selector = current.tagName.toLowerCase();\n\n\t\tif (current.id) {\n\t\t\tparts.unshift(`${selector}#${CSS.escape(current.id)}`);\n\t\t\tbreak;\n\t\t}\n\n\t\tconst classes = Array.from(current.classList).filter((c) => !c.startsWith(\"data-yns-\"));\n\t\tif (classes.length > 0) {\n\t\t\tselector += `.${classes.map((c) => CSS.escape(c)).join(\".\")}`;\n\t\t}\n\n\t\tconst parent: Element | null = current.parentElement;\n\t\tconst tag = current.tagName;\n\t\tif (parent) {\n\t\t\tconst siblings = Array.from(parent.children).filter(\n\t\t\t\t(s) => s.tagName === tag && (classes.length === 0 || classes.every((c) => s.classList.contains(c))),\n\t\t\t);\n\t\t\tif (siblings.length > 1) {\n\t\t\t\tconst index = Array.from(parent.children).indexOf(current) + 1;\n\t\t\t\tselector += `:nth-child(${index})`;\n\t\t\t}\n\t\t}\n\n\t\tparts.unshift(selector);\n\t\tcurrent = current.parentElement;\n\t}\n\treturn parts.join(\" > \");\n}\n\nfunction buildElementInfo(el: Element, textLimit: number): ElementInfo {\n\tconst rect = el.getBoundingClientRect();\n\tconst computed = window.getComputedStyle(el);\n\tconst text = (el.textContent ?? \"\").trim();\n\treturn {\n\t\ttag: el.tagName.toLowerCase(),\n\t\tid: el.id || undefined,\n\t\tclasses: Array.from(el.classList).filter((c) => !c.startsWith(\"data-yns-\")),\n\t\ttextContent: text.length > textLimit ? `${text.slice(0, textLimit)}…` : text,\n\t\tcssSelector: generateCssSelector(el),\n\t\tboundingRect: { top: rect.top, left: rect.left, width: rect.width, height: rect.height },\n\t\tcomputedStyles: {\n\t\t\tcolor: computed.color,\n\t\t\tbackgroundColor: computed.backgroundColor,\n\t\t\tfontSize: computed.fontSize,\n\t\t\tfontFamily: computed.fontFamily,\n\t\t\tpadding: computed.padding,\n\t\t\tmargin: computed.margin,\n\t\t},\n\t};\n}\n\nfunction shouldIgnore(el: Element | null, overlayAttrs: string[]): el is null {\n\tif (!el || !el.tagName) return true;\n\tif (IGNORED_TAGS.has(el.tagName)) return true;\n\tfor (const attr of overlayAttrs) {\n\t\tif (el.hasAttribute?.(attr)) return true;\n\t}\n\treturn false;\n}\n\nfunction initDesignModeInspector() {\n\tlet enabled = false;\n\tlet hoveredElement: Element | null = null;\n\tlet selectedElement: { el: Element; cssSelector: string } | null = null;\n\n\tconst overlay = document.createElement(\"div\");\n\toverlay.setAttribute(\"data-yns-design-overlay\", \"hover\");\n\toverlay.style.cssText = [\n\t\t\"position: fixed\",\n\t\t\"pointer-events: none\",\n\t\t\"z-index: 2147483646\",\n\t\t\"border: 2px solid #3b82f6\",\n\t\t\"background: rgba(59, 130, 246, 0.08)\",\n\t\t\"border-radius: 3px\",\n\t\t\"display: none\",\n\t\t\"transition: top 0.05s, left 0.05s, width 0.05s, height 0.05s\",\n\t].join(\";\");\n\n\tconst label = document.createElement(\"div\");\n\tlabel.setAttribute(\"data-yns-design-overlay\", \"label\");\n\tlabel.style.cssText = [\n\t\t\"position: fixed\",\n\t\t\"pointer-events: none\",\n\t\t\"z-index: 2147483647\",\n\t\t\"background: #3b82f6\",\n\t\t\"color: #fff\",\n\t\t\"font-size: 11px\",\n\t\t\"font-family: ui-monospace, monospace\",\n\t\t\"padding: 2px 6px\",\n\t\t\"border-radius: 3px\",\n\t\t\"white-space: nowrap\",\n\t\t\"display: none\",\n\t].join(\";\");\n\n\tdocument.documentElement.appendChild(overlay);\n\tdocument.documentElement.appendChild(label);\n\n\tconst style = document.createElement(\"style\");\n\tstyle.setAttribute(\"data-yns-design-overlay\", \"style\");\n\tstyle.textContent = \"[data-yns-selected] { outline: 2px solid #3b82f6 !important; outline-offset: 1px; }\";\n\tdocument.head.appendChild(style);\n\n\tfunction positionOverlay(el: Element) {\n\t\tconst rect = el.getBoundingClientRect();\n\t\toverlay.style.top = `${rect.top}px`;\n\t\toverlay.style.left = `${rect.left}px`;\n\t\toverlay.style.width = `${rect.width}px`;\n\t\toverlay.style.height = `${rect.height}px`;\n\t\toverlay.style.display = \"block\";\n\n\t\tconst classes = Array.from(el.classList).filter((c) => !c.startsWith(\"data-yns-\"));\n\t\tconst labelText = el.tagName.toLowerCase() + (classes.length ? `.${classes[0]}` : \"\");\n\t\tlabel.textContent = labelText;\n\t\tlabel.style.left = `${rect.left}px`;\n\t\tlabel.style.top = `${Math.max(0, rect.top - 22)}px`;\n\t\tlabel.style.display = \"block\";\n\t}\n\n\tconst handleMouseMove = (e: MouseEvent) => {\n\t\tconst el = document.elementFromPoint(e.clientX, e.clientY);\n\t\tif (shouldIgnore(el, [\"data-yns-design-overlay\"])) {\n\t\t\toverlay.style.display = \"none\";\n\t\t\tlabel.style.display = \"none\";\n\t\t\thoveredElement = null;\n\t\t\treturn;\n\t\t}\n\t\thoveredElement = el;\n\t\trequestAnimationFrame(() => {\n\t\t\tif (hoveredElement === el && enabled && el) {\n\t\t\t\tpositionOverlay(el);\n\t\t\t}\n\t\t});\n\t};\n\n\tconst handleMouseLeave = () => {\n\t\toverlay.style.display = \"none\";\n\t\tlabel.style.display = \"none\";\n\t\thoveredElement = null;\n\t};\n\n\tconst handleClick = (e: MouseEvent) => {\n\t\tif (!enabled || !hoveredElement) return;\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\t\te.stopImmediatePropagation();\n\n\t\tconst el = hoveredElement;\n\t\tif (shouldIgnore(el, [\"data-yns-design-overlay\"])) return;\n\n\t\tconst info = buildElementInfo(el, 120);\n\t\tconst selector = info.cssSelector;\n\n\t\tif (selectedElement) {\n\t\t\tselectedElement.el.removeAttribute(\"data-yns-selected\");\n\t\t}\n\n\t\tif (selectedElement && selectedElement.cssSelector === selector) {\n\t\t\tselectedElement = null;\n\t\t\twindow.parent.postMessage({ type: \"element-deselected\", data: info }, \"*\");\n\t\t} else {\n\t\t\tselectedElement = { el, cssSelector: selector };\n\t\t\tel.setAttribute(\"data-yns-selected\", \"\");\n\t\t\twindow.parent.postMessage({ type: \"element-selected\", data: info }, \"*\");\n\t\t}\n\t};\n\n\tconst handleKeyDown = (e: KeyboardEvent) => {\n\t\tif (e.key === \"Escape\" && enabled) {\n\t\t\tdisableDesignMode();\n\t\t\twindow.parent.postMessage({ type: \"design-mode-cleared\" }, \"*\");\n\t\t}\n\t};\n\n\tfunction enableDesignMode() {\n\t\tenabled = true;\n\t\tdocument.addEventListener(\"mousemove\", handleMouseMove, true);\n\t\tdocument.addEventListener(\"mouseleave\", handleMouseLeave);\n\t\tdocument.addEventListener(\"click\", handleClick, true);\n\t\tdocument.addEventListener(\"keydown\", handleKeyDown, true);\n\t}\n\n\tfunction disableDesignMode() {\n\t\tenabled = false;\n\t\tdocument.removeEventListener(\"mousemove\", handleMouseMove, true);\n\t\tdocument.removeEventListener(\"mouseleave\", handleMouseLeave);\n\t\tdocument.removeEventListener(\"click\", handleClick, true);\n\t\tdocument.removeEventListener(\"keydown\", handleKeyDown, true);\n\n\t\toverlay.style.display = \"none\";\n\t\tlabel.style.display = \"none\";\n\t\thoveredElement = null;\n\n\t\tif (selectedElement) {\n\t\t\tselectedElement.el.removeAttribute(\"data-yns-selected\");\n\t\t\tselectedElement = null;\n\t\t}\n\t}\n\n\twindow.addEventListener(\"message\", (event) => {\n\t\tconst msg = event.data;\n\t\tif (!msg || typeof msg !== \"object\") return;\n\n\t\tif (msg.type === \"design-mode-toggle\") {\n\t\t\tif (msg.enabled) {\n\t\t\t\tenableDesignMode();\n\t\t\t} else {\n\t\t\t\tdisableDesignMode();\n\t\t\t\twindow.parent.postMessage({ type: \"design-mode-cleared\" }, \"*\");\n\t\t\t}\n\t\t}\n\n\t\tif (msg.type === \"design-mode-deselect\") {\n\t\t\tif (selectedElement) {\n\t\t\t\tselectedElement.el.removeAttribute(\"data-yns-selected\");\n\t\t\t\tselectedElement = null;\n\t\t\t}\n\t\t}\n\t});\n}\n\nfunction initCommentModeInspector() {\n\tlet enabled = false;\n\tlet hoveredElement: Element | null = null;\n\tlet pins: Array<{ id: string; selector: string; number: number }> = [];\n\n\tconst overlay = document.createElement(\"div\");\n\toverlay.setAttribute(\"data-yns-comment-overlay\", \"hover\");\n\toverlay.style.cssText = [\n\t\t\"position: fixed\",\n\t\t\"pointer-events: none\",\n\t\t\"z-index: 2147483644\",\n\t\t\"border: 2px dashed #10b981\",\n\t\t\"background: rgba(16, 185, 129, 0.06)\",\n\t\t\"border-radius: 3px\",\n\t\t\"display: none\",\n\t\t\"transition: top 0.05s, left 0.05s, width 0.05s, height 0.05s\",\n\t].join(\";\");\n\n\tconst cursorLabel = document.createElement(\"div\");\n\tcursorLabel.setAttribute(\"data-yns-comment-overlay\", \"cursor-label\");\n\tcursorLabel.textContent = \"Click to comment\";\n\tcursorLabel.style.cssText = [\n\t\t\"position: fixed\",\n\t\t\"pointer-events: none\",\n\t\t\"z-index: 2147483645\",\n\t\t\"background: #059669\",\n\t\t\"color: #fff\",\n\t\t\"font-size: 11px\",\n\t\t\"font-family: ui-sans-serif, system-ui, sans-serif\",\n\t\t\"padding: 3px 8px\",\n\t\t\"border-radius: 4px\",\n\t\t\"white-space: nowrap\",\n\t\t\"display: none\",\n\t\t\"box-shadow: 0 2px 6px rgba(0,0,0,0.15)\",\n\t].join(\";\");\n\n\tconst pinContainer = document.createElement(\"div\");\n\tpinContainer.setAttribute(\"data-yns-comment-overlay\", \"pins\");\n\tpinContainer.style.cssText =\n\t\t\"position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 2147483643;\";\n\n\tdocument.documentElement.appendChild(overlay);\n\tdocument.documentElement.appendChild(cursorLabel);\n\tdocument.documentElement.appendChild(pinContainer);\n\n\tfunction renderPins() {\n\t\tpinContainer.innerHTML = \"\";\n\t\tfor (const pin of pins) {\n\t\t\tlet el: Element | null = null;\n\t\t\ttry {\n\t\t\t\tel = document.querySelector(pin.selector);\n\t\t\t} catch {\n\t\t\t\tel = null;\n\t\t\t}\n\t\t\tif (!el) continue;\n\n\t\t\tconst r = el.getBoundingClientRect();\n\t\t\tconst pinEl = document.createElement(\"div\");\n\t\t\tpinEl.style.cssText = [\n\t\t\t\t\"position: fixed\",\n\t\t\t\t`top: ${r.top - 12}px`,\n\t\t\t\t`left: ${r.left + r.width / 2 - 12}px`,\n\t\t\t\t\"width: 24px\",\n\t\t\t\t\"height: 24px\",\n\t\t\t\t\"border-radius: 50%\",\n\t\t\t\t\"background: #059669\",\n\t\t\t\t\"color: #fff\",\n\t\t\t\t\"display: flex\",\n\t\t\t\t\"align-items: center\",\n\t\t\t\t\"justify-content: center\",\n\t\t\t\t\"font-size: 12px\",\n\t\t\t\t\"font-weight: 600\",\n\t\t\t\t\"font-family: ui-sans-serif, system-ui, sans-serif\",\n\t\t\t\t\"box-shadow: 0 2px 8px rgba(0,0,0,0.2)\",\n\t\t\t\t\"pointer-events: none\",\n\t\t\t].join(\";\");\n\t\t\tpinEl.textContent = String(pin.number);\n\t\t\tpinContainer.appendChild(pinEl);\n\t\t}\n\t}\n\n\tconst handleMouseMove = (e: MouseEvent) => {\n\t\tconst el = document.elementFromPoint(e.clientX, e.clientY);\n\t\tif (shouldIgnore(el, [\"data-yns-comment-overlay\", \"data-yns-design-overlay\"])) {\n\t\t\toverlay.style.display = \"none\";\n\t\t\tcursorLabel.style.display = \"none\";\n\t\t\thoveredElement = null;\n\t\t\treturn;\n\t\t}\n\t\thoveredElement = el;\n\t\trequestAnimationFrame(() => {\n\t\t\tif (hoveredElement === el && enabled && el) {\n\t\t\t\tconst rect = el.getBoundingClientRect();\n\t\t\t\toverlay.style.top = `${rect.top}px`;\n\t\t\t\toverlay.style.left = `${rect.left}px`;\n\t\t\t\toverlay.style.width = `${rect.width}px`;\n\t\t\t\toverlay.style.height = `${rect.height}px`;\n\t\t\t\toverlay.style.display = \"block\";\n\n\t\t\t\tcursorLabel.style.left = `${e.clientX + 14}px`;\n\t\t\t\tcursorLabel.style.top = `${e.clientY + 14}px`;\n\t\t\t\tcursorLabel.style.display = \"block\";\n\t\t\t}\n\t\t});\n\t};\n\n\tconst handleMouseLeave = () => {\n\t\toverlay.style.display = \"none\";\n\t\tcursorLabel.style.display = \"none\";\n\t\thoveredElement = null;\n\t};\n\n\tconst handleClick = (e: MouseEvent) => {\n\t\tif (!enabled || !hoveredElement) return;\n\t\te.preventDefault();\n\t\te.stopPropagation();\n\t\te.stopImmediatePropagation();\n\n\t\tconst el = hoveredElement;\n\t\tif (shouldIgnore(el, [\"data-yns-comment-overlay\", \"data-yns-design-overlay\"])) return;\n\n\t\tconst info = buildElementInfo(el, 200);\n\t\tconst rect = el.getBoundingClientRect();\n\n\t\twindow.parent.postMessage(\n\t\t\t{\n\t\t\t\ttype: \"comment-click\",\n\t\t\t\tdata: {\n\t\t\t\t\telement: info,\n\t\t\t\t\tclickPosition: { x: e.clientX, y: e.clientY },\n\t\t\t\t\telementRect: { top: rect.top, left: rect.left, width: rect.width, height: rect.height },\n\t\t\t\t\tpagePath: window.location.pathname,\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"*\",\n\t\t);\n\t};\n\n\tconst handleKeyDown = (e: KeyboardEvent) => {\n\t\tif (e.key === \"Escape\" && enabled) {\n\t\t\tdisableCommentMode();\n\t\t\twindow.parent.postMessage({ type: \"comment-mode-cleared\" }, \"*\");\n\t\t}\n\t};\n\n\tfunction enableCommentMode() {\n\t\tenabled = true;\n\t\tdocument.body.style.cursor = \"crosshair\";\n\t\tdocument.addEventListener(\"mousemove\", handleMouseMove, true);\n\t\tdocument.addEventListener(\"mouseleave\", handleMouseLeave);\n\t\tdocument.addEventListener(\"click\", handleClick, true);\n\t\tdocument.addEventListener(\"keydown\", handleKeyDown, true);\n\t}\n\n\tfunction disableCommentMode() {\n\t\tenabled = false;\n\t\tdocument.body.style.cursor = \"\";\n\t\tdocument.removeEventListener(\"mousemove\", handleMouseMove, true);\n\t\tdocument.removeEventListener(\"mouseleave\", handleMouseLeave);\n\t\tdocument.removeEventListener(\"click\", handleClick, true);\n\t\tdocument.removeEventListener(\"keydown\", handleKeyDown, true);\n\n\t\toverlay.style.display = \"none\";\n\t\tcursorLabel.style.display = \"none\";\n\t\thoveredElement = null;\n\t}\n\n\tlet rafPending = false;\n\tconst scheduleRender = () => {\n\t\tif (rafPending) return;\n\t\trafPending = true;\n\t\trequestAnimationFrame(() => {\n\t\t\trafPending = false;\n\t\t\trenderPins();\n\t\t});\n\t};\n\twindow.addEventListener(\"scroll\", scheduleRender, { passive: true });\n\twindow.addEventListener(\"resize\", scheduleRender, { passive: true });\n\n\twindow.addEventListener(\"message\", (event) => {\n\t\tconst msg = event.data;\n\t\tif (!msg || typeof msg !== \"object\") return;\n\n\t\tif (msg.type === \"comment-mode-toggle\") {\n\t\t\tif (msg.enabled) enableCommentMode();\n\t\t\telse disableCommentMode();\n\t\t}\n\n\t\tif (msg.type === \"comment-pins-update\") {\n\t\t\tpins = msg.pins ?? [];\n\t\t\trenderPins();\n\t\t}\n\n\t\tif (msg.type === \"comment-pin-remove\") {\n\t\t\tpins = pins.filter((p) => p.id !== msg.pinId);\n\t\t\trenderPins();\n\t\t}\n\t});\n}\n"],"mappings":"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","names":["useEffect","useRef","useState","createRoot","Fragment","jsx","jsxs","MOUNT_NODE_ID","buildApiBase","override","host","protocol","computeCssSelector","el","path","node","part","className","classes","c","parent","tag","siblings","idx","ATTRS_OF_INTEREST","formatAttrs","parts","attr","v","trimmed","formatTextContent","text","buildSurroundingHtml","target","ancestors","cur","lines","depth","ancestor","indent","targetIndent","targetTag","targetText","childIndent","children","child","childText","i","isInsideToolbar","IGNORED_TAGS","POLL_INTERVAL_MS","formatEta","etaIso","eta","remainingMs","dateLabel","remainingMin","remainingHours","remainingDays","FeedbackToolbar","session","setSession","loading","setLoading","pinMode","setPinMode","pending","setPending","editingId","setEditingId","sidebarOpen","setSidebarOpen","finalizing","setFinalizing","apiBase","cancelled","controller","fetchOnce","res","data","interval","overlay","label","hovered","handleMove","e","rect","handleLeave","handleClick","event","offsetXRatio","offsetYRatio","refreshComments","submitNewComment","content","updateComment","id","removeComment","finalizeSession","prev","scrollToComment","comment","SubmittedPanel","visiblePins","pin","PinOverlay","PendingCommentPopover","CommentsSidebar","toolbarStyle","toolbarButtonActiveStyle","toolbarButtonStyle","toolbarButtonGhostStyle","toolbarButtonFinalizeStyle","toolbarHintStyle","progress","status","setTick","t","submittedPanelStyle","spinnerKeyframes","submittedHeaderStyle","Spinner","submittedBadgeStyle","submittedProgressLabelStyle","submittedProgressTrackStyle","submittedProgressFillStyle","submittedEtaStyle","size","number","editing","onStartEdit","onCancelEdit","onSave","onRemove","useTargetPosition","pinDotStyle","EditPopover","onCancel","comments","currentPath","onClose","onSelect","groups","list","paths","a","b","sidebarStyle","sidebarHeaderStyle","ghostButtonStyle","sidebarScrollStyle","sidebarEmptyStyle","sidebarPathStyle","sidebarItemStyle","sidebarItemIndexStyle","sidebarItemTextStyle","sidebarItemDoneStyle","initial","value","setValue","saving","setSaving","textareaRef","len","popoverStyle","textareaStyle","popoverActionsStyle","dangerButtonStyle","primaryButtonStyle","selector","pos","setPos","update","r","ro","baseButton","mountFeedbackToolbar","container","root","startSandboxInspectors","initDesignModeInspector","initCommentModeInspector","IGNORED_TAGS","generateCssSelector","el","parts","current","selector","classes","c","parent","tag","s","index","buildElementInfo","textLimit","rect","computed","text","shouldIgnore","overlayAttrs","attr","enabled","hoveredElement","selectedElement","overlay","label","style","positionOverlay","labelText","handleMouseMove","e","handleMouseLeave","handleClick","info","handleKeyDown","disableDesignMode","enableDesignMode","event","msg","pins","cursorLabel","pinContainer","renderPins","pin","r","pinEl","disableCommentMode","enableCommentMode","rafPending","scheduleRender","p"]}
|
package/dist/feedback-toolbar.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{useEffect as v,useRef as A,useState as h}from"react";import{createRoot as O}from"react-dom/client";import{Fragment as
|
|
2
|
-
`)},N=e=>{let t=e;for(;t;){if(t instanceof HTMLElement&&t.dataset.ynsFeedbackUi==="true")return!0;t=t.parentElement}return!1},J=new Set(["HTML","HEAD","SCRIPT","STYLE","NOSCRIPT"]),K=1e4,Q=e=>{let t=new Date(e),r=t.getTime()-Date.now(),l=t.toLocaleString(void 0,{weekday:"short",month:"short",day:"numeric",hour:"numeric",minute:"2-digit"});if(r<=0)return`Any moment now (estimated by ${l})`;let s=Math.ceil(r/6e4);if(s<60)return`~${s} minutes (by ${l})`;let u=Math.round(r/36e5);if(u<24)return`~${u} ${u===1?"hour":"hours"} (by ${l})`;let i=Math.round(r/864e5);return`~${i} ${i===1?"day":"days"} (by ${l})`};function Z(){let[e,t]=h(null),[r,l]=h(!0),[s,u]=h(!1),[i,o]=h(null),[d,S]=h(null),[p,y]=h(!1),[I,T]=h(!1),g=A(null);v(()=>{if(g.current=V(),!g.current){l(!1);return}let n=!1,a=new AbortController,m=async()=>{try{let x=await fetch(`${g.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,{credentials:"include",signal:a.signal});if(n)return;if(!x.ok){t(null);return}let w=await x.json();if(n)return;t(w),w.sessionStatus==="done"&&window.location.reload()}catch{n||t(null)}finally{n||l(!1)}};m();let f=window.setInterval(m,K);return()=>{n=!0,a.abort(),window.clearInterval(f)}},[]),v(()=>{!e||e.canComment||(u(!1),o(null),y(!1),S(null))},[e]),v(()=>{if(s)return document.body.style.cursor="crosshair",()=>{document.body.style.cursor=""}},[s]),v(()=>{if(!s)return;let n=document.createElement("div");n.dataset.ynsFeedbackUi="true",n.style.cssText=["position: fixed","pointer-events: none","z-index: 2147483644","border: 2px dashed #10b981","background: rgba(16, 185, 129, 0.08)","border-radius: 3px","display: none","transition: top 0.05s, left 0.05s, width 0.05s, height 0.05s"].join(";");let a=document.createElement("div");a.dataset.ynsFeedbackUi="true",a.textContent="Click to comment",a.style.cssText=["position: fixed","pointer-events: none","z-index: 2147483645","background: #059669","color: #fff","font-size: 11px","font-family: ui-sans-serif, system-ui, sans-serif","padding: 3px 8px","border-radius: 4px","white-space: nowrap","display: none","box-shadow: 0 2px 6px rgba(0,0,0,0.15)"].join(";"),document.documentElement.appendChild(n),document.documentElement.appendChild(a);let m=null,f=w=>{let C=document.elementFromPoint(w.clientX,w.clientY);if(!C||J.has(C.tagName)||N(C)){n.style.display="none",a.style.display="none",m=null;return}m=C,requestAnimationFrame(()=>{if(m!==C)return;let E=C.getBoundingClientRect();n.style.top=`${E.top}px`,n.style.left=`${E.left}px`,n.style.width=`${E.width}px`,n.style.height=`${E.height}px`,n.style.display="block",a.style.left=`${w.clientX+14}px`,a.style.top=`${w.clientY+14}px`,a.style.display="block"})},x=()=>{n.style.display="none",a.style.display="none",m=null};return document.addEventListener("mousemove",f,!0),document.addEventListener("mouseleave",x),()=>{document.removeEventListener("mousemove",f,!0),document.removeEventListener("mouseleave",x),n.remove(),a.remove()}},[s]),v(()=>{if(!s)return;let n=a=>{let m=a.target;if(!(m instanceof Element)||N(m))return;a.preventDefault(),a.stopPropagation();let f=m.getBoundingClientRect(),x=f.width>0?(a.clientX-f.left)/f.width:.5,w=f.height>0?(a.clientY-f.top)/f.height:.5;o({cssSelector:W(m),pagePath:window.location.pathname,surroundingHtml:G(m),rect:{top:f.top+window.scrollY,left:f.left+window.scrollX,width:f.width,height:f.height},clickX:a.clientX+window.scrollX,clickY:a.clientY+window.scrollY,offsetXRatio:Math.min(1,Math.max(0,x)),offsetYRatio:Math.min(1,Math.max(0,w))}),u(!1)};return document.addEventListener("click",n,{capture:!0}),()=>document.removeEventListener("click",n,{capture:!0})},[s]);let k=async()=>{if(!g.current)return;let n=await fetch(`${g.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,{credentials:"include"});if(!n.ok)return;let a=await n.json();t(a)},U=async n=>{!g.current||!e||!i||!(await fetch(`${g.current}/api/feedback-comments`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({feedbackSessionId:e.feedbackSessionId,content:n,pagePath:i.pagePath,cssSelector:i.cssSelector,surroundingHtml:i.surroundingHtml,offsetXRatio:i.offsetXRatio,offsetYRatio:i.offsetYRatio})})).ok||(o(null),u(!0),await k())},Y=async(n,a)=>{!g.current||!(await fetch(`${g.current}/api/feedback-comments/${n}`,{method:"PATCH",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({content:a})})).ok||(S(null),await k())},j=async n=>{!g.current||!(await fetch(`${g.current}/api/feedback-comments/${n}`,{method:"DELETE",credentials:"include"})).ok||await k()},D=async()=>{if(!(!g.current||!e)&&window.confirm("Finalize this feedback session? You won't be able to add more comments.")){T(!0);try{if(!(await fetch(`${g.current}/api/feedback-sessions/${e.feedbackSessionId}/finalize`,{method:"POST",credentials:"include"})).ok)return;t(a=>a&&{...a,canComment:!1})}finally{T(!1)}}},H=n=>{if(n.pagePath!==window.location.pathname){window.location.assign(n.pagePath);return}let a=null;try{a=document.querySelector(n.cssSelector)}catch{a=null}a&&(a.scrollIntoView({behavior:"smooth",block:"center"}),S(n.id))};if(r||!e)return null;if(!e.canComment)return e.sessionStatus!=="processing"&&e.sessionStatus!=="in_review"?null:c("div",{"data-yns-feedback-ui":"true",children:c(ee,{progress:e.progress,eta:e.eta,status:e.sessionStatus})});let L=e.comments.filter(n=>n.pagePath===window.location.pathname&&n.status!=="done");return b("div",{"data-yns-feedback-ui":"true",children:[L.map((n,a)=>c(te,{pin:n,number:a+1,editing:d===n.id,onStartEdit:()=>S(n.id),onCancelEdit:()=>S(null),onSave:m=>Y(n.id,m),onRemove:()=>j(n.id)},n.id)),i&&c(ne,{pending:i,onCancel:()=>{o(null),u(!0)},onSave:n=>U(n)}),p&&c(oe,{comments:e.comments,currentPath:window.location.pathname,onClose:()=>y(!1),onSelect:H}),b("div",{style:re,children:[c("button",{type:"button",onClick:()=>u(n=>!n),style:s?se:B,children:s?"Cancel":"Add comment"}),c("button",{type:"button",onClick:()=>y(n=>!n),style:ae,children:p?"Hide list":`List (${e.comments.length})`}),c("button",{type:"button",onClick:D,disabled:I,style:le,children:I?"Finalizing\u2026":"Finalize"}),c("span",{style:ce,children:s?"Click any element to comment":`${L.length} on this page`})]})]})}function ee({progress:e,eta:t,status:r}){let[,l]=h(0);v(()=>{let o=window.setInterval(()=>l(d=>d+1),6e4);return()=>window.clearInterval(o)},[]);let s=Q(t);return b("div",{style:Ee,children:[b("div",{style:ke,children:[c("span",{style:Pe,children:"Submitted"}),c("strong",{style:{fontSize:13},children:r==="in_review"?"Feedback under review":"Applying feedback"})]}),b("div",{style:Re,children:[c("span",{children:"Review progress"}),c("span",{style:{opacity:.7},children:e.label})]}),c("div",{style:$e,children:c("div",{style:{...Ie,width:`${e.fillPct}%`}})}),b("p",{style:Te,children:["Estimated delivery: ",c("span",{style:{fontWeight:600},children:s})]})]})}function te({pin:e,number:t,editing:r,onStartEdit:l,onCancelEdit:s,onSave:u,onRemove:i}){let o=ie(e.cssSelector,e.offsetXRatio,e.offsetYRatio);return o?b("div",{style:{position:"absolute",top:o.top,left:o.left,zIndex:2147483600,pointerEvents:"auto"},children:[c("button",{type:"button",onClick:l,style:M,title:e.content,children:t}),r&&c(F,{initial:e.content,onCancel:s,onSave:u,onRemove:i})]}):null}function ne({pending:e,onCancel:t,onSave:r}){return b(Le,{children:[c("div",{style:{position:"absolute",top:e.rect.top+e.rect.height*e.offsetYRatio-12,left:e.rect.left+e.rect.width*e.offsetXRatio-12,zIndex:2147483600,pointerEvents:"none"},children:c("div",{style:M,children:"\u2022"})}),c("div",{style:{position:"absolute",top:e.clickY+10,left:e.clickX+10,zIndex:2147483647},children:c(F,{initial:"",onCancel:t,onSave:r})})]})}function oe({comments:e,currentPath:t,onClose:r,onSelect:l}){let s=new Map;for(let i of e){let o=s.get(i.pagePath)??[];o.push(i),s.set(i.pagePath,o)}let u=Array.from(s.keys()).sort((i,o)=>i===t?-1:o===t?1:i.localeCompare(o));return b("div",{style:be,children:[b("div",{style:ge,children:[b("strong",{style:{fontSize:14},children:["Comments (",e.length,")"]}),c("button",{type:"button",onClick:r,style:X,children:"Close"})]}),c("div",{style:Se,children:e.length===0?c("div",{style:ye,children:"No comments yet. Click \u201CAdd comment\u201D to leave one."}):u.map(i=>b("div",{style:{marginBottom:12},children:[c("div",{style:he,children:i===t?`${i} \xB7 current`:i}),(s.get(i)??[]).map((o,d)=>b("button",{type:"button",onClick:()=>l(o),style:we,disabled:o.status==="done"&&!1,children:[c("span",{style:ve,children:d+1}),c("span",{style:xe,children:o.content.length>140?`${o.content.slice(0,140)}\u2026`:o.content}),o.status==="done"&&c("span",{style:Ce,children:"done"})]},o.id))]},i))})]})}function F({initial:e,onCancel:t,onSave:r,onRemove:l}){let[s,u]=h(e),[i,o]=h(!1),d=A(null);return v(()=>{let p=d.current;if(!p)return;p.focus();let y=p.value.length;p.setSelectionRange(y,y)},[]),b("form",{onSubmit:async p=>{p.preventDefault();let y=s.trim();if(y){o(!0);try{await r(y)}finally{o(!1)}}},style:de,children:[c("textarea",{ref:d,value:s,onChange:p=>u(p.target.value),placeholder:"Leave a comment\u2026",style:ue,rows:3}),b("div",{style:pe,children:[l&&c("button",{type:"button",onClick:()=>l(),style:fe,disabled:i,children:"Delete"}),c("div",{style:{flex:1}}),c("button",{type:"button",onClick:t,style:X,disabled:i,children:"Cancel"}),c("button",{type:"submit",style:me,disabled:i||!s.trim(),children:i?"Saving\u2026":"Save"})]})]})}function ie(e,t,r){let[l,s]=h(null);return v(()=>{let u=()=>{let o=null;try{o=document.querySelector(e)}catch{o=null}if(!o){s(null);return}let d=o.getBoundingClientRect();s({top:d.top+window.scrollY+d.height*r-12,left:d.left+window.scrollX+d.width*t-12})};u();let i=new ResizeObserver(u);try{let o=document.querySelector(e);o&&i.observe(o)}catch{}return window.addEventListener("scroll",u,!0),window.addEventListener("resize",u),()=>{i.disconnect(),window.removeEventListener("scroll",u,!0),window.removeEventListener("resize",u)}},[e,t,r]),l}var re={position:"fixed",bottom:16,left:"50%",transform:"translateX(-50%)",zIndex:2147483646,display:"flex",alignItems:"center",gap:8,padding:"8px 12px",background:"rgba(17, 17, 17, 0.92)",color:"white",borderRadius:999,boxShadow:"0 8px 24px rgba(0,0,0,0.25)",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',fontSize:14,pointerEvents:"auto"},B={border:"none",background:"white",color:"#111",padding:"6px 12px",borderRadius:999,cursor:"pointer",fontWeight:600,fontSize:13},se={...B,background:"#ef4444",color:"white"},ae={border:"1px solid rgba(255,255,255,0.4)",background:"transparent",color:"white",padding:"6px 12px",borderRadius:999,cursor:"pointer",fontWeight:500,fontSize:13},le={border:"none",background:"#10b981",color:"white",padding:"6px 12px",borderRadius:999,cursor:"pointer",fontWeight:600,fontSize:13},ce={opacity:.8,fontSize:12},M={width:24,height:24,borderRadius:999,background:"#10b981",color:"white",border:"2px solid white",cursor:"pointer",fontSize:12,fontWeight:700,boxShadow:"0 2px 6px rgba(0,0,0,0.3)",display:"inline-flex",alignItems:"center",justifyContent:"center",padding:0},de={background:"white",border:"1px solid #e5e7eb",borderRadius:8,padding:12,width:280,boxShadow:"0 12px 32px rgba(0,0,0,0.18)",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',display:"flex",flexDirection:"column",gap:8,color:"#111"},ue={width:"100%",border:"1px solid #d1d5db",borderRadius:6,padding:8,fontSize:14,resize:"vertical",fontFamily:"inherit",color:"#111",background:"white",boxSizing:"border-box"},pe={display:"flex",alignItems:"center",gap:6},$={border:"1px solid transparent",borderRadius:6,padding:"6px 10px",fontSize:13,cursor:"pointer",fontWeight:500},me={...$,background:"#111",color:"white"},X={...$,background:"transparent",color:"#374151",borderColor:"#d1d5db"},fe={...$,background:"transparent",color:"#b91c1c",borderColor:"#fecaca"},be={position:"fixed",top:0,right:0,bottom:0,width:360,maxWidth:"92vw",background:"white",borderLeft:"1px solid #e5e7eb",boxShadow:"-12px 0 32px rgba(0,0,0,0.12)",zIndex:2147483646,display:"flex",flexDirection:"column",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',color:"#111"},ge={display:"flex",alignItems:"center",justifyContent:"space-between",padding:"12px 16px",borderBottom:"1px solid #e5e7eb"},Se={flex:1,overflow:"auto",padding:"12px 12px 24px"},ye={color:"#6b7280",fontSize:13,padding:"24px 8px",textAlign:"center"},he={fontSize:11,textTransform:"uppercase",letterSpacing:.5,color:"#6b7280",padding:"4px 4px 6px",wordBreak:"break-all"},we={display:"flex",alignItems:"flex-start",gap:8,width:"100%",textAlign:"left",background:"white",border:"1px solid #e5e7eb",borderRadius:6,padding:"8px 10px",cursor:"pointer",fontSize:13,marginBottom:6,color:"#111"},ve={flexShrink:0,width:22,height:22,borderRadius:999,background:"#10b981",color:"white",fontWeight:700,fontSize:11,display:"inline-flex",alignItems:"center",justifyContent:"center"},xe={flex:1,whiteSpace:"pre-wrap",wordBreak:"break-word"},Ce={flexShrink:0,background:"#d1fae5",color:"#065f46",fontSize:11,fontWeight:600,padding:"2px 6px",borderRadius:4,alignSelf:"center"},Ee={position:"fixed",bottom:16,left:"50%",transform:"translateX(-50%)",zIndex:2147483646,display:"flex",flexDirection:"column",gap:8,padding:"12px 16px",width:"min(420px, calc(100vw - 32px))",background:"white",border:"1px solid #e5e7eb",borderRadius:12,boxShadow:"0 12px 32px rgba(0,0,0,0.18)",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',color:"#111",pointerEvents:"auto"},ke={display:"flex",alignItems:"center",gap:8},Pe={background:"#d1fae5",color:"#065f46",fontSize:11,fontWeight:700,padding:"2px 8px",borderRadius:999,textTransform:"uppercase",letterSpacing:.5},Re={display:"flex",justifyContent:"space-between",fontSize:12},$e={width:"100%",height:6,background:"#f3f4f6",borderRadius:999,overflow:"hidden"},Ie={height:"100%",background:"#10b981",borderRadius:999,transition:"width 500ms"},Te={margin:0,fontSize:12,color:"#6b7280"};function z(){if(console.log("[YNS Feedback Toolbar] mountFeedbackToolbar() called",{isWindow:typeof window<"u",vercelEnv:process.env.NEXT_PUBLIC_VERCEL_ENV,alreadyMounted:typeof document<"u"&&!!document.getElementById(P)}),typeof window>"u")return null;if(process.env.NEXT_PUBLIC_VERCEL_ENV!=="preview")return console.log("[YNS Feedback Toolbar] gate failed \u2014 NEXT_PUBLIC_VERCEL_ENV is",process.env.NEXT_PUBLIC_VERCEL_ENV),null;if(document.getElementById(P))return null;let e=document.createElement("div");e.id=P,e.dataset.ynsFeedbackUi="true",document.body.appendChild(e);let t=O(e);return t.render(c(Z,{})),{unmount:()=>{t.unmount(),e.remove()}}}typeof window<"u"&&console.log("[YNS Feedback Toolbar] module evaluated",{vercelEnv:process.env.NEXT_PUBLIC_VERCEL_ENV,readyState:document.readyState,willAutoMount:process.env.NEXT_PUBLIC_VERCEL_ENV==="preview"});typeof window<"u"&&process.env.NEXT_PUBLIC_VERCEL_ENV==="preview"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{z()}):z());export{z as mountFeedbackToolbar};
|
|
1
|
+
import{useEffect as v,useRef as A,useState as h}from"react";import{createRoot as O}from"react-dom/client";import{Fragment as Ne,jsx as l,jsxs as b}from"react/jsx-runtime";var P="yns-feedback-toolbar-root",V=()=>{if(typeof window>"u")return null;let e=(process.env.NEXT_PUBLIC_YNS_API_BASE??"").trim();if(e)return e.replace(/\/$/,"");let t=window.location.hostname,r=window.location.protocol;return t.endsWith(".yns.store")?`${r}//yns.store`:t.endsWith(".yns.cx")?`${r}//yns.cx`:window.location.origin},W=e=>{if(e.id)return`#${CSS.escape(e.id)}`;let t=[],r=e;for(;r&&r.nodeType===Node.ELEMENT_NODE&&t.length<6;){let d=r.tagName.toLowerCase(),s=r.getAttribute("class");if(s){let o=s.split(/\s+/).filter(Boolean).slice(0,2).map(c=>`.${CSS.escape(c)}`).join("");d+=o}let u=r.parentElement,i=r.tagName;if(u){let o=Array.from(u.children).filter(c=>c.tagName===i);if(o.length>1){let c=o.indexOf(r)+1;d+=`:nth-of-type(${c})`}}t.unshift(d),r=u}return t.join(" > ")},q=["id","class","data-testid","aria-label","role","name","href","src"],R=e=>{let t=[];for(let r of q){let d=e.getAttribute(r);if(!d)continue;let s=d.length>80?`${d.slice(0,80)}\u2026`:d;t.push(` ${r}="${s.replace(/"/g,""")}"`)}return t.join("")},_=e=>{let t=(e.textContent??"").replace(/\s+/g," ").trim();return t?t.length>100?`${t.slice(0,100)}\u2026`:t:""},G=e=>{let t=[],r=e;for(;r&&r!==document.documentElement&&t.length<5;){let c=r.parentElement;if(!c)break;t.unshift(c),r=c}let d=[],s=0;for(let c of t){let y=" ".repeat(s);d.push(`${y}<${c.tagName.toLowerCase()}${R(c)}>`),s++}let u=" ".repeat(s),i=e.tagName.toLowerCase(),o=_(e);if(d.push(`${u}<${i}${R(e)}>${o?`${o}</${i}>`:""} \u2190 TARGET`),!o){let c=" ".repeat(s+1),y=Array.from(e.children).slice(0,6);for(let p of y){let S=_(p);d.push(`${c}<${p.tagName.toLowerCase()}${R(p)}>${S?`${S}</${p.tagName.toLowerCase()}>`:""}`)}e.children.length>6&&d.push(`${c}\u2026 (${e.children.length-6} more children)`),d.push(`${u}</${i}>`)}for(let c=t.length-1;c>=0;c--){let y=" ".repeat(c),p=t[c];p&&d.push(`${y}</${p.tagName.toLowerCase()}>`)}return d.join(`
|
|
2
|
+
`)},N=e=>{let t=e;for(;t;){if(t instanceof HTMLElement&&t.dataset.ynsFeedbackUi==="true")return!0;t=t.parentElement}return!1},J=new Set(["HTML","HEAD","SCRIPT","STYLE","NOSCRIPT"]),K=1e4,Q=e=>{let t=new Date(e),r=t.getTime()-Date.now(),d=t.toLocaleString(void 0,{weekday:"short",month:"short",day:"numeric",hour:"numeric",minute:"2-digit"});if(r<=0)return`Any moment now (estimated by ${d})`;let s=Math.ceil(r/6e4);if(s<60)return`~${s} minutes (by ${d})`;let u=Math.round(r/36e5);if(u<24)return`~${u} ${u===1?"hour":"hours"} (by ${d})`;let i=Math.round(r/864e5);return`~${i} ${i===1?"day":"days"} (by ${d})`};function Z(){let[e,t]=h(null),[r,d]=h(!0),[s,u]=h(!1),[i,o]=h(null),[c,y]=h(null),[p,S]=h(!1),[I,T]=h(!1),g=A(null);v(()=>{if(g.current=V(),!g.current){d(!1);return}let n=!1,a=new AbortController,m=async()=>{try{let x=await fetch(`${g.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,{credentials:"include",signal:a.signal});if(n)return;if(!x.ok){t(null);return}let w=await x.json();if(n)return;t(w),w.sessionStatus==="done"&&window.location.reload()}catch{n||t(null)}finally{n||d(!1)}};m();let f=window.setInterval(m,K);return()=>{n=!0,a.abort(),window.clearInterval(f)}},[]),v(()=>{!e||e.canComment||(u(!1),o(null),S(!1),y(null))},[e]),v(()=>{if(s)return document.body.style.cursor="crosshair",()=>{document.body.style.cursor=""}},[s]),v(()=>{if(!s)return;let n=document.createElement("div");n.dataset.ynsFeedbackUi="true",n.style.cssText=["position: fixed","pointer-events: none","z-index: 2147483644","border: 2px dashed #10b981","background: rgba(16, 185, 129, 0.08)","border-radius: 3px","display: none","transition: top 0.05s, left 0.05s, width 0.05s, height 0.05s"].join(";");let a=document.createElement("div");a.dataset.ynsFeedbackUi="true",a.textContent="Click to comment",a.style.cssText=["position: fixed","pointer-events: none","z-index: 2147483645","background: #059669","color: #fff","font-size: 11px","font-family: ui-sans-serif, system-ui, sans-serif","padding: 3px 8px","border-radius: 4px","white-space: nowrap","display: none","box-shadow: 0 2px 6px rgba(0,0,0,0.15)"].join(";"),document.documentElement.appendChild(n),document.documentElement.appendChild(a);let m=null,f=w=>{let C=document.elementFromPoint(w.clientX,w.clientY);if(!C||J.has(C.tagName)||N(C)){n.style.display="none",a.style.display="none",m=null;return}m=C,requestAnimationFrame(()=>{if(m!==C)return;let E=C.getBoundingClientRect();n.style.top=`${E.top}px`,n.style.left=`${E.left}px`,n.style.width=`${E.width}px`,n.style.height=`${E.height}px`,n.style.display="block",a.style.left=`${w.clientX+14}px`,a.style.top=`${w.clientY+14}px`,a.style.display="block"})},x=()=>{n.style.display="none",a.style.display="none",m=null};return document.addEventListener("mousemove",f,!0),document.addEventListener("mouseleave",x),()=>{document.removeEventListener("mousemove",f,!0),document.removeEventListener("mouseleave",x),n.remove(),a.remove()}},[s]),v(()=>{if(!s)return;let n=a=>{let m=a.target;if(!(m instanceof Element)||N(m))return;a.preventDefault(),a.stopPropagation();let f=m.getBoundingClientRect(),x=f.width>0?(a.clientX-f.left)/f.width:.5,w=f.height>0?(a.clientY-f.top)/f.height:.5;o({cssSelector:W(m),pagePath:window.location.pathname,surroundingHtml:G(m),rect:{top:f.top+window.scrollY,left:f.left+window.scrollX,width:f.width,height:f.height},clickX:a.clientX+window.scrollX,clickY:a.clientY+window.scrollY,offsetXRatio:Math.min(1,Math.max(0,x)),offsetYRatio:Math.min(1,Math.max(0,w))}),u(!1)};return document.addEventListener("click",n,{capture:!0}),()=>document.removeEventListener("click",n,{capture:!0})},[s]);let k=async()=>{if(!g.current)return;let n=await fetch(`${g.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,{credentials:"include"});if(!n.ok)return;let a=await n.json();t(a)},U=async n=>{!g.current||!e||!i||!(await fetch(`${g.current}/api/feedback-comments`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({feedbackSessionId:e.feedbackSessionId,content:n,pagePath:i.pagePath,cssSelector:i.cssSelector,surroundingHtml:i.surroundingHtml,offsetXRatio:i.offsetXRatio,offsetYRatio:i.offsetYRatio})})).ok||(o(null),u(!0),await k())},Y=async(n,a)=>{!g.current||!(await fetch(`${g.current}/api/feedback-comments/${n}`,{method:"PATCH",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({content:a})})).ok||(y(null),await k())},j=async n=>{!g.current||!(await fetch(`${g.current}/api/feedback-comments/${n}`,{method:"DELETE",credentials:"include"})).ok||await k()},D=async()=>{if(!(!g.current||!e)&&window.confirm("Finalize this feedback session? You won't be able to add more comments.")){T(!0);try{if(!(await fetch(`${g.current}/api/feedback-sessions/${e.feedbackSessionId}/finalize`,{method:"POST",credentials:"include"})).ok)return;t(a=>a&&{...a,canComment:!1})}finally{T(!1)}}},H=n=>{if(n.pagePath!==window.location.pathname){window.location.assign(n.pagePath);return}let a=null;try{a=document.querySelector(n.cssSelector)}catch{a=null}a&&(a.scrollIntoView({behavior:"smooth",block:"center"}),y(n.id))};if(r||!e)return null;if(!e.canComment)return e.sessionStatus!=="processing"&&e.sessionStatus!=="in_review"?null:l("div",{"data-yns-feedback-ui":"true",children:l(ee,{progress:e.progress,eta:e.eta,status:e.sessionStatus})});let L=e.comments.filter(n=>n.pagePath===window.location.pathname&&n.status!=="done");return b("div",{"data-yns-feedback-ui":"true",children:[L.map((n,a)=>l(oe,{pin:n,number:a+1,editing:c===n.id,onStartEdit:()=>y(n.id),onCancelEdit:()=>y(null),onSave:m=>Y(n.id,m),onRemove:()=>j(n.id)},n.id)),i&&l(ie,{pending:i,onCancel:()=>{o(null),u(!0)},onSave:n=>U(n)}),p&&l(re,{comments:e.comments,currentPath:window.location.pathname,onClose:()=>S(!1),onSelect:H}),b("div",{style:ae,children:[l("button",{type:"button",onClick:()=>u(n=>!n),style:s?le:B,children:s?"Cancel":"Add comment"}),l("button",{type:"button",onClick:()=>S(n=>!n),style:de,children:p?"Hide list":`List (${e.comments.length})`}),l("button",{type:"button",onClick:D,disabled:I,style:ce,children:I?"Finalizing\u2026":"Finalize"}),l("span",{style:ue,children:s?"Click any element to comment":`${L.length} on this page`})]})]})}function ee({progress:e,eta:t,status:r}){let[,d]=h(0);v(()=>{let o=window.setInterval(()=>d(c=>c+1),6e4);return()=>window.clearInterval(o)},[]);let s=Q(t);return b("div",{style:Pe,children:[l("style",{children:te}),b("div",{style:Re,children:[l(ne,{}),l("span",{style:$e,children:"Submitted"}),l("strong",{style:{fontSize:13},children:r==="in_review"?"Feedback under review":"Applying feedback"})]}),b("div",{style:Ie,children:[l("span",{children:"Review progress"}),l("span",{style:{opacity:.7},children:e.label})]}),l("div",{style:Te,children:l("div",{style:{...Le,width:`${e.fillPct}%`}})}),b("p",{style:_e,children:["Estimated delivery: ",l("span",{style:{fontWeight:600},children:s})]})]})}var te="@keyframes yns-feedback-spin { to { transform: rotate(360deg); } }";function ne({size:e=14}){return l("span",{"aria-hidden":!0,style:{display:"inline-block",width:e,height:e,border:"2px solid rgba(16, 185, 129, 0.25)",borderTopColor:"#10b981",borderRadius:999,animation:"yns-feedback-spin 0.9s linear infinite"}})}function oe({pin:e,number:t,editing:r,onStartEdit:d,onCancelEdit:s,onSave:u,onRemove:i}){let o=se(e.cssSelector,e.offsetXRatio,e.offsetYRatio);return o?b("div",{style:{position:"absolute",top:o.top,left:o.left,zIndex:2147483600,pointerEvents:"auto"},children:[l("button",{type:"button",onClick:d,style:M,title:e.content,children:t}),r&&l(F,{initial:e.content,onCancel:s,onSave:u,onRemove:i})]}):null}function ie({pending:e,onCancel:t,onSave:r}){return b(Ne,{children:[l("div",{style:{position:"absolute",top:e.rect.top+e.rect.height*e.offsetYRatio-12,left:e.rect.left+e.rect.width*e.offsetXRatio-12,zIndex:2147483600,pointerEvents:"none"},children:l("div",{style:M,children:"\u2022"})}),l("div",{style:{position:"absolute",top:e.clickY+10,left:e.clickX+10,zIndex:2147483647},children:l(F,{initial:"",onCancel:t,onSave:r})})]})}function re({comments:e,currentPath:t,onClose:r,onSelect:d}){let s=new Map;for(let i of e){let o=s.get(i.pagePath)??[];o.push(i),s.set(i.pagePath,o)}let u=Array.from(s.keys()).sort((i,o)=>i===t?-1:o===t?1:i.localeCompare(o));return b("div",{style:ye,children:[b("div",{style:Se,children:[b("strong",{style:{fontSize:14},children:["Comments (",e.length,")"]}),l("button",{type:"button",onClick:r,style:X,children:"Close"})]}),l("div",{style:he,children:e.length===0?l("div",{style:we,children:"No comments yet. Click \u201CAdd comment\u201D to leave one."}):u.map(i=>b("div",{style:{marginBottom:12},children:[l("div",{style:ve,children:i===t?`${i} \xB7 current`:i}),(s.get(i)??[]).map((o,c)=>b("button",{type:"button",onClick:()=>d(o),style:xe,disabled:o.status==="done"&&!1,children:[l("span",{style:Ce,children:c+1}),l("span",{style:Ee,children:o.content.length>140?`${o.content.slice(0,140)}\u2026`:o.content}),o.status==="done"&&l("span",{style:ke,children:"done"})]},o.id))]},i))})]})}function F({initial:e,onCancel:t,onSave:r,onRemove:d}){let[s,u]=h(e),[i,o]=h(!1),c=A(null);return v(()=>{let p=c.current;if(!p)return;p.focus();let S=p.value.length;p.setSelectionRange(S,S)},[]),b("form",{onSubmit:async p=>{p.preventDefault();let S=s.trim();if(S){o(!0);try{await r(S)}finally{o(!1)}}},style:pe,children:[l("textarea",{ref:c,value:s,onChange:p=>u(p.target.value),placeholder:"Leave a comment\u2026",style:me,rows:3}),b("div",{style:fe,children:[d&&l("button",{type:"button",onClick:()=>d(),style:ge,disabled:i,children:"Delete"}),l("div",{style:{flex:1}}),l("button",{type:"button",onClick:t,style:X,disabled:i,children:"Cancel"}),l("button",{type:"submit",style:be,disabled:i||!s.trim(),children:i?"Saving\u2026":"Save"})]})]})}function se(e,t,r){let[d,s]=h(null);return v(()=>{let u=()=>{let o=null;try{o=document.querySelector(e)}catch{o=null}if(!o){s(null);return}let c=o.getBoundingClientRect();s({top:c.top+window.scrollY+c.height*r-12,left:c.left+window.scrollX+c.width*t-12})};u();let i=new ResizeObserver(u);try{let o=document.querySelector(e);o&&i.observe(o)}catch{}return window.addEventListener("scroll",u,!0),window.addEventListener("resize",u),()=>{i.disconnect(),window.removeEventListener("scroll",u,!0),window.removeEventListener("resize",u)}},[e,t,r]),d}var ae={position:"fixed",bottom:16,left:"50%",transform:"translateX(-50%)",zIndex:2147483646,display:"flex",alignItems:"center",gap:8,padding:"8px 12px",background:"rgba(17, 17, 17, 0.92)",color:"white",borderRadius:999,boxShadow:"0 8px 24px rgba(0,0,0,0.25)",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',fontSize:14,pointerEvents:"auto"},B={border:"none",background:"white",color:"#111",padding:"6px 12px",borderRadius:999,cursor:"pointer",fontWeight:600,fontSize:13},le={...B,background:"#ef4444",color:"white"},de={border:"1px solid rgba(255,255,255,0.4)",background:"transparent",color:"white",padding:"6px 12px",borderRadius:999,cursor:"pointer",fontWeight:500,fontSize:13},ce={border:"none",background:"#10b981",color:"white",padding:"6px 12px",borderRadius:999,cursor:"pointer",fontWeight:600,fontSize:13},ue={opacity:.8,fontSize:12},M={width:24,height:24,borderRadius:999,background:"#10b981",color:"white",border:"2px solid white",cursor:"pointer",fontSize:12,fontWeight:700,boxShadow:"0 2px 6px rgba(0,0,0,0.3)",display:"inline-flex",alignItems:"center",justifyContent:"center",padding:0},pe={background:"white",border:"1px solid #e5e7eb",borderRadius:8,padding:12,width:280,boxShadow:"0 12px 32px rgba(0,0,0,0.18)",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',display:"flex",flexDirection:"column",gap:8,color:"#111"},me={width:"100%",border:"1px solid #d1d5db",borderRadius:6,padding:8,fontSize:14,resize:"vertical",fontFamily:"inherit",color:"#111",background:"white",boxSizing:"border-box"},fe={display:"flex",alignItems:"center",gap:6},$={border:"1px solid transparent",borderRadius:6,padding:"6px 10px",fontSize:13,cursor:"pointer",fontWeight:500},be={...$,background:"#111",color:"white"},X={...$,background:"transparent",color:"#374151",borderColor:"#d1d5db"},ge={...$,background:"transparent",color:"#b91c1c",borderColor:"#fecaca"},ye={position:"fixed",top:0,right:0,bottom:0,width:360,maxWidth:"92vw",background:"white",borderLeft:"1px solid #e5e7eb",boxShadow:"-12px 0 32px rgba(0,0,0,0.12)",zIndex:2147483646,display:"flex",flexDirection:"column",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',color:"#111"},Se={display:"flex",alignItems:"center",justifyContent:"space-between",padding:"12px 16px",borderBottom:"1px solid #e5e7eb"},he={flex:1,overflow:"auto",padding:"12px 12px 24px"},we={color:"#6b7280",fontSize:13,padding:"24px 8px",textAlign:"center"},ve={fontSize:11,textTransform:"uppercase",letterSpacing:.5,color:"#6b7280",padding:"4px 4px 6px",wordBreak:"break-all"},xe={display:"flex",alignItems:"flex-start",gap:8,width:"100%",textAlign:"left",background:"white",border:"1px solid #e5e7eb",borderRadius:6,padding:"8px 10px",cursor:"pointer",fontSize:13,marginBottom:6,color:"#111"},Ce={flexShrink:0,width:22,height:22,borderRadius:999,background:"#10b981",color:"white",fontWeight:700,fontSize:11,display:"inline-flex",alignItems:"center",justifyContent:"center"},Ee={flex:1,whiteSpace:"pre-wrap",wordBreak:"break-word"},ke={flexShrink:0,background:"#d1fae5",color:"#065f46",fontSize:11,fontWeight:600,padding:"2px 6px",borderRadius:4,alignSelf:"center"},Pe={position:"fixed",bottom:16,left:"50%",transform:"translateX(-50%)",zIndex:2147483646,display:"flex",flexDirection:"column",gap:8,padding:"12px 16px",width:"min(420px, calc(100vw - 32px))",background:"white",border:"1px solid #e5e7eb",borderRadius:12,boxShadow:"0 12px 32px rgba(0,0,0,0.18)",fontFamily:'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',color:"#111",pointerEvents:"auto"},Re={display:"flex",alignItems:"center",gap:8},$e={background:"#d1fae5",color:"#065f46",fontSize:11,fontWeight:700,padding:"2px 8px",borderRadius:999,textTransform:"uppercase",letterSpacing:.5},Ie={display:"flex",justifyContent:"space-between",fontSize:12},Te={width:"100%",height:6,background:"#f3f4f6",borderRadius:999,overflow:"hidden"},Le={height:"100%",background:"#10b981",borderRadius:999,transition:"width 500ms"},_e={margin:0,fontSize:12,color:"#6b7280"};function z(){if(console.log("[YNS Feedback Toolbar] mountFeedbackToolbar() called",{isWindow:typeof window<"u",vercelEnv:process.env.NEXT_PUBLIC_VERCEL_ENV,alreadyMounted:typeof document<"u"&&!!document.getElementById(P)}),typeof window>"u")return null;if(process.env.NEXT_PUBLIC_VERCEL_ENV!=="preview")return console.log("[YNS Feedback Toolbar] gate failed \u2014 NEXT_PUBLIC_VERCEL_ENV is",process.env.NEXT_PUBLIC_VERCEL_ENV),null;if(document.getElementById(P))return null;let e=document.createElement("div");e.id=P,e.dataset.ynsFeedbackUi="true",document.body.appendChild(e);let t=O(e);return t.render(l(Z,{})),{unmount:()=>{t.unmount(),e.remove()}}}typeof window<"u"&&console.log("[YNS Feedback Toolbar] module evaluated",{vercelEnv:process.env.NEXT_PUBLIC_VERCEL_ENV,readyState:document.readyState,willAutoMount:process.env.NEXT_PUBLIC_VERCEL_ENV==="preview"});typeof window<"u"&&process.env.NEXT_PUBLIC_VERCEL_ENV==="preview"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{z()}):z());export{z as mountFeedbackToolbar};
|
|
3
3
|
//# sourceMappingURL=feedback-toolbar.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/feedback-toolbar.tsx"],"sourcesContent":["/**\n * Feedback session toolbar — side-effect entry.\n *\n * Importing `commerce-kit/feedback-toolbar` (or `commerce-kit/browser` for the\n * combined entry) mounts a floating toolbar onto the page that lets reviewers\n * leave click-anchored comments. Comments persist via YNS API endpoints,\n * authenticated with the `better-auth` session cookie sent through\n * `credentials: \"include\"`.\n *\n * Gated on `process.env.NEXT_PUBLIC_VERCEL_ENV === \"preview\"` — only Vercel\n * preview deploys get the toolbar. Sandbox dev (env undefined) and production\n * (env === \"production\") skip it.\n */\n\nimport { type CSSProperties, type FormEvent, useEffect, useRef, useState } from \"react\";\nimport { createRoot } from \"react-dom/client\";\n\nconst MOUNT_NODE_ID = \"yns-feedback-toolbar-root\";\n\ninterface FeedbackComment {\n\tid: string;\n\tpagePath: string;\n\tcssSelector: string;\n\tcontent: string;\n\tstatus: \"todo\" | \"done\";\n\toffsetXRatio: number;\n\toffsetYRatio: number;\n}\n\ntype SessionStatus = \"created\" | \"in_progress\" | \"processing\" | \"in_review\" | \"done\";\n\ninterface ReviewProgress {\n\tfillPct: number;\n\tlabel: string;\n}\n\ninterface ActiveSession {\n\tfeedbackSessionId: string;\n\tcomments: FeedbackComment[];\n\tcanComment: boolean;\n\tsessionStatus: SessionStatus;\n\tcommentTotal: number;\n\tcommentDone: number;\n\teta: string;\n\tprogress: ReviewProgress;\n}\n\ninterface PendingPin {\n\tcssSelector: string;\n\tpagePath: string;\n\tsurroundingHtml: string;\n\trect: { top: number; left: number; width: number; height: number };\n\tclickX: number;\n\tclickY: number;\n\toffsetXRatio: number;\n\toffsetYRatio: number;\n}\n\nconst buildApiBase = (): string | null => {\n\tif (typeof window === \"undefined\") return null;\n\tconst override = (process.env.NEXT_PUBLIC_YNS_API_BASE ?? \"\").trim();\n\tif (override) return override.replace(/\\/$/, \"\");\n\n\t// Toolbar runs on a per-store preview deploy (mystore-preview.yns.{store|cx})\n\t// served by the merchant's Vercel project — that host has no YNS API. Aim\n\t// at the apex `yns.store` / `yns.cx`, which yns-app serves and where the\n\t// API lives. Cookies scoped to `.yns.store` / `.yns.cx` travel along.\n\tconst host = window.location.hostname;\n\tconst protocol = window.location.protocol;\n\tif (host.endsWith(\".yns.store\")) return `${protocol}//yns.store`;\n\tif (host.endsWith(\".yns.cx\")) return `${protocol}//yns.cx`;\n\treturn window.location.origin;\n};\n\nconst computeCssSelector = (el: Element): string => {\n\tif (el.id) return `#${CSS.escape(el.id)}`;\n\tconst path: string[] = [];\n\tlet node: Element | null = el;\n\twhile (node && node.nodeType === Node.ELEMENT_NODE && path.length < 6) {\n\t\tlet part = node.tagName.toLowerCase();\n\t\tconst className = node.getAttribute(\"class\");\n\t\tif (className) {\n\t\t\tconst classes = className\n\t\t\t\t.split(/\\s+/)\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.slice(0, 2)\n\t\t\t\t.map((c) => `.${CSS.escape(c)}`)\n\t\t\t\t.join(\"\");\n\t\t\tpart += classes;\n\t\t}\n\t\tconst parent: Element | null = node.parentElement;\n\t\tconst tag = node.tagName;\n\t\tif (parent) {\n\t\t\tconst siblings: Element[] = Array.from(parent.children).filter((c) => c.tagName === tag);\n\t\t\tif (siblings.length > 1) {\n\t\t\t\tconst idx = siblings.indexOf(node) + 1;\n\t\t\t\tpart += `:nth-of-type(${idx})`;\n\t\t\t}\n\t\t}\n\t\tpath.unshift(part);\n\t\tnode = parent;\n\t}\n\treturn path.join(\" > \");\n};\n\nconst ATTRS_OF_INTEREST = [\"id\", \"class\", \"data-testid\", \"aria-label\", \"role\", \"name\", \"href\", \"src\"];\n\nconst formatAttrs = (el: Element): string => {\n\tconst parts: string[] = [];\n\tfor (const attr of ATTRS_OF_INTEREST) {\n\t\tconst v = el.getAttribute(attr);\n\t\tif (!v) continue;\n\t\tconst trimmed = v.length > 80 ? `${v.slice(0, 80)}…` : v;\n\t\tparts.push(` ${attr}=\"${trimmed.replace(/\"/g, \""\")}\"`);\n\t}\n\treturn parts.join(\"\");\n};\n\nconst formatTextContent = (el: Element): string => {\n\tconst text = (el.textContent ?? \"\").replace(/\\s+/g, \" \").trim();\n\tif (!text) return \"\";\n\treturn text.length > 100 ? `${text.slice(0, 100)}…` : text;\n};\n\nconst buildSurroundingHtml = (target: Element): string => {\n\tconst ancestors: Element[] = [];\n\tlet cur: Element | null = target;\n\twhile (cur && cur !== document.documentElement && ancestors.length < 5) {\n\t\tconst parent: Element | null = cur.parentElement;\n\t\tif (!parent) break;\n\t\tancestors.unshift(parent);\n\t\tcur = parent;\n\t}\n\n\tconst lines: string[] = [];\n\tlet depth = 0;\n\tfor (const ancestor of ancestors) {\n\t\tconst indent = \" \".repeat(depth);\n\t\tlines.push(`${indent}<${ancestor.tagName.toLowerCase()}${formatAttrs(ancestor)}>`);\n\t\tdepth++;\n\t}\n\n\tconst targetIndent = \" \".repeat(depth);\n\tconst targetTag = target.tagName.toLowerCase();\n\tconst targetText = formatTextContent(target);\n\tlines.push(\n\t\t`${targetIndent}<${targetTag}${formatAttrs(target)}>${\n\t\t\ttargetText ? `${targetText}</${targetTag}>` : \"\"\n\t\t} ← TARGET`,\n\t);\n\n\tif (!targetText) {\n\t\tconst childIndent = \" \".repeat(depth + 1);\n\t\tconst children = Array.from(target.children).slice(0, 6);\n\t\tfor (const child of children) {\n\t\t\tconst childText = formatTextContent(child);\n\t\t\tlines.push(\n\t\t\t\t`${childIndent}<${child.tagName.toLowerCase()}${formatAttrs(child)}>${\n\t\t\t\t\tchildText ? `${childText}</${child.tagName.toLowerCase()}>` : \"\"\n\t\t\t\t}`,\n\t\t\t);\n\t\t}\n\t\tif (target.children.length > 6) {\n\t\t\tlines.push(`${childIndent}… (${target.children.length - 6} more children)`);\n\t\t}\n\t\tlines.push(`${targetIndent}</${targetTag}>`);\n\t}\n\n\tfor (let i = ancestors.length - 1; i >= 0; i--) {\n\t\tconst indent = \" \".repeat(i);\n\t\tconst ancestor = ancestors[i];\n\t\tif (!ancestor) continue;\n\t\tlines.push(`${indent}</${ancestor.tagName.toLowerCase()}>`);\n\t}\n\n\treturn lines.join(\"\\n\");\n};\n\nconst isInsideToolbar = (el: Element | null): boolean => {\n\tlet node = el;\n\twhile (node) {\n\t\tif (node instanceof HTMLElement && node.dataset.ynsFeedbackUi === \"true\") return true;\n\t\tnode = node.parentElement;\n\t}\n\treturn false;\n};\n\nconst IGNORED_TAGS = new Set([\"HTML\", \"HEAD\", \"SCRIPT\", \"STYLE\", \"NOSCRIPT\"]);\n\nconst POLL_INTERVAL_MS = 10_000;\n\n// Format a server-computed ETA as remaining + absolute label. The deadline\n// math itself lives in yns-app's `lib/feedback-comments-api.ts` so the toolbar\n// and YNS lock screen stay in sync.\nconst formatEta = (etaIso: string): string => {\n\tconst eta = new Date(etaIso);\n\tconst remainingMs = eta.getTime() - Date.now();\n\tconst dateLabel = eta.toLocaleString(undefined, {\n\t\tweekday: \"short\",\n\t\tmonth: \"short\",\n\t\tday: \"numeric\",\n\t\thour: \"numeric\",\n\t\tminute: \"2-digit\",\n\t});\n\n\tif (remainingMs <= 0) return `Any moment now (estimated by ${dateLabel})`;\n\tconst remainingMin = Math.ceil(remainingMs / 60_000);\n\tif (remainingMin < 60) return `~${remainingMin} minutes (by ${dateLabel})`;\n\tconst remainingHours = Math.round(remainingMs / 3_600_000);\n\tif (remainingHours < 24) {\n\t\treturn `~${remainingHours} ${remainingHours === 1 ? \"hour\" : \"hours\"} (by ${dateLabel})`;\n\t}\n\tconst remainingDays = Math.round(remainingMs / 86_400_000);\n\treturn `~${remainingDays} ${remainingDays === 1 ? \"day\" : \"days\"} (by ${dateLabel})`;\n};\n\nfunction FeedbackToolbar() {\n\tconst [session, setSession] = useState<ActiveSession | null>(null);\n\tconst [loading, setLoading] = useState(true);\n\tconst [pinMode, setPinMode] = useState(false);\n\tconst [pending, setPending] = useState<PendingPin | null>(null);\n\tconst [editingId, setEditingId] = useState<string | null>(null);\n\tconst [sidebarOpen, setSidebarOpen] = useState(false);\n\tconst [finalizing, setFinalizing] = useState(false);\n\tconst apiBase = useRef<string | null>(null);\n\n\tuseEffect(() => {\n\t\tapiBase.current = buildApiBase();\n\t\tif (!apiBase.current) {\n\t\t\tsetLoading(false);\n\t\t\treturn;\n\t\t}\n\n\t\tlet cancelled = false;\n\t\tconst controller = new AbortController();\n\n\t\tconst fetchOnce = async () => {\n\t\t\ttry {\n\t\t\t\tconst res = await fetch(\n\t\t\t\t\t`${apiBase.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,\n\t\t\t\t\t{ credentials: \"include\", signal: controller.signal },\n\t\t\t\t);\n\t\t\t\tif (cancelled) return;\n\t\t\t\tif (!res.ok) {\n\t\t\t\t\tsetSession(null);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst data = (await res.json()) as ActiveSession;\n\t\t\t\tif (cancelled) return;\n\t\t\t\tsetSession(data);\n\t\t\t\t// Hard-refresh once the AI run lands and the preview is up to date.\n\t\t\t\t// Out of scope to diff anything smarter — the new deploy replaces\n\t\t\t\t// the page entirely so a full reload is appropriate here.\n\t\t\t\tif (data.sessionStatus === \"done\") {\n\t\t\t\t\twindow.location.reload();\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tif (!cancelled) setSession(null);\n\t\t\t} finally {\n\t\t\t\tif (!cancelled) setLoading(false);\n\t\t\t}\n\t\t};\n\n\t\tvoid fetchOnce();\n\t\tconst interval = window.setInterval(fetchOnce, POLL_INTERVAL_MS);\n\n\t\treturn () => {\n\t\t\tcancelled = true;\n\t\t\tcontroller.abort();\n\t\t\twindow.clearInterval(interval);\n\t\t};\n\t}, []);\n\n\t// When the session stops accepting comments (finalized → in_review, or\n\t// closed), tear down any in-flight UI so the hover overlay/popover/sidebar\n\t// don't linger after the toolbar's main render returns null.\n\tuseEffect(() => {\n\t\tif (!session || session.canComment) return;\n\t\tsetPinMode(false);\n\t\tsetPending(null);\n\t\tsetSidebarOpen(false);\n\t\tsetEditingId(null);\n\t}, [session]);\n\n\tuseEffect(() => {\n\t\tif (!pinMode) return;\n\t\tdocument.body.style.cursor = \"crosshair\";\n\t\treturn () => {\n\t\t\tdocument.body.style.cursor = \"\";\n\t\t};\n\t}, [pinMode]);\n\n\tuseEffect(() => {\n\t\tif (!pinMode) return;\n\n\t\tconst overlay = document.createElement(\"div\");\n\t\toverlay.dataset.ynsFeedbackUi = \"true\";\n\t\toverlay.style.cssText = [\n\t\t\t\"position: fixed\",\n\t\t\t\"pointer-events: none\",\n\t\t\t\"z-index: 2147483644\",\n\t\t\t\"border: 2px dashed #10b981\",\n\t\t\t\"background: rgba(16, 185, 129, 0.08)\",\n\t\t\t\"border-radius: 3px\",\n\t\t\t\"display: none\",\n\t\t\t\"transition: top 0.05s, left 0.05s, width 0.05s, height 0.05s\",\n\t\t].join(\";\");\n\n\t\tconst label = document.createElement(\"div\");\n\t\tlabel.dataset.ynsFeedbackUi = \"true\";\n\t\tlabel.textContent = \"Click to comment\";\n\t\tlabel.style.cssText = [\n\t\t\t\"position: fixed\",\n\t\t\t\"pointer-events: none\",\n\t\t\t\"z-index: 2147483645\",\n\t\t\t\"background: #059669\",\n\t\t\t\"color: #fff\",\n\t\t\t\"font-size: 11px\",\n\t\t\t\"font-family: ui-sans-serif, system-ui, sans-serif\",\n\t\t\t\"padding: 3px 8px\",\n\t\t\t\"border-radius: 4px\",\n\t\t\t\"white-space: nowrap\",\n\t\t\t\"display: none\",\n\t\t\t\"box-shadow: 0 2px 6px rgba(0,0,0,0.15)\",\n\t\t].join(\";\");\n\n\t\tdocument.documentElement.appendChild(overlay);\n\t\tdocument.documentElement.appendChild(label);\n\n\t\tlet hovered: Element | null = null;\n\t\tconst handleMove = (e: MouseEvent) => {\n\t\t\tconst el = document.elementFromPoint(e.clientX, e.clientY);\n\t\t\tif (!el || IGNORED_TAGS.has(el.tagName) || isInsideToolbar(el)) {\n\t\t\t\toverlay.style.display = \"none\";\n\t\t\t\tlabel.style.display = \"none\";\n\t\t\t\thovered = null;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\thovered = el;\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\tif (hovered !== el) return;\n\t\t\t\tconst rect = el.getBoundingClientRect();\n\t\t\t\toverlay.style.top = `${rect.top}px`;\n\t\t\t\toverlay.style.left = `${rect.left}px`;\n\t\t\t\toverlay.style.width = `${rect.width}px`;\n\t\t\t\toverlay.style.height = `${rect.height}px`;\n\t\t\t\toverlay.style.display = \"block\";\n\t\t\t\tlabel.style.left = `${e.clientX + 14}px`;\n\t\t\t\tlabel.style.top = `${e.clientY + 14}px`;\n\t\t\t\tlabel.style.display = \"block\";\n\t\t\t});\n\t\t};\n\n\t\tconst handleLeave = () => {\n\t\t\toverlay.style.display = \"none\";\n\t\t\tlabel.style.display = \"none\";\n\t\t\thovered = null;\n\t\t};\n\n\t\tdocument.addEventListener(\"mousemove\", handleMove, true);\n\t\tdocument.addEventListener(\"mouseleave\", handleLeave);\n\n\t\treturn () => {\n\t\t\tdocument.removeEventListener(\"mousemove\", handleMove, true);\n\t\t\tdocument.removeEventListener(\"mouseleave\", handleLeave);\n\t\t\toverlay.remove();\n\t\t\tlabel.remove();\n\t\t};\n\t}, [pinMode]);\n\n\tuseEffect(() => {\n\t\tif (!pinMode) return;\n\t\tconst handleClick = (event: MouseEvent) => {\n\t\t\tconst target = event.target;\n\t\t\tif (!(target instanceof Element)) return;\n\t\t\tif (isInsideToolbar(target)) return;\n\n\t\t\tevent.preventDefault();\n\t\t\tevent.stopPropagation();\n\n\t\t\tconst rect = target.getBoundingClientRect();\n\t\t\tconst offsetXRatio = rect.width > 0 ? (event.clientX - rect.left) / rect.width : 0.5;\n\t\t\tconst offsetYRatio = rect.height > 0 ? (event.clientY - rect.top) / rect.height : 0.5;\n\t\t\tsetPending({\n\t\t\t\tcssSelector: computeCssSelector(target),\n\t\t\t\tpagePath: window.location.pathname,\n\t\t\t\tsurroundingHtml: buildSurroundingHtml(target),\n\t\t\t\trect: {\n\t\t\t\t\ttop: rect.top + window.scrollY,\n\t\t\t\t\tleft: rect.left + window.scrollX,\n\t\t\t\t\twidth: rect.width,\n\t\t\t\t\theight: rect.height,\n\t\t\t\t},\n\t\t\t\tclickX: event.clientX + window.scrollX,\n\t\t\t\tclickY: event.clientY + window.scrollY,\n\t\t\t\toffsetXRatio: Math.min(1, Math.max(0, offsetXRatio)),\n\t\t\t\toffsetYRatio: Math.min(1, Math.max(0, offsetYRatio)),\n\t\t\t});\n\t\t\tsetPinMode(false);\n\t\t};\n\t\tdocument.addEventListener(\"click\", handleClick, { capture: true });\n\t\treturn () => document.removeEventListener(\"click\", handleClick, { capture: true });\n\t}, [pinMode]);\n\n\tconst refreshComments = async () => {\n\t\tif (!apiBase.current) return;\n\t\tconst res = await fetch(\n\t\t\t`${apiBase.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,\n\t\t\t{ credentials: \"include\" },\n\t\t);\n\t\tif (!res.ok) return;\n\t\tconst data = (await res.json()) as ActiveSession;\n\t\tsetSession(data);\n\t};\n\n\tconst submitNewComment = async (content: string) => {\n\t\tif (!apiBase.current || !session || !pending) return;\n\t\tconst res = await fetch(`${apiBase.current}/api/feedback-comments`, {\n\t\t\tmethod: \"POST\",\n\t\t\tcredentials: \"include\",\n\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\tbody: JSON.stringify({\n\t\t\t\tfeedbackSessionId: session.feedbackSessionId,\n\t\t\t\tcontent,\n\t\t\t\tpagePath: pending.pagePath,\n\t\t\t\tcssSelector: pending.cssSelector,\n\t\t\t\tsurroundingHtml: pending.surroundingHtml,\n\t\t\t\toffsetXRatio: pending.offsetXRatio,\n\t\t\t\toffsetYRatio: pending.offsetYRatio,\n\t\t\t}),\n\t\t});\n\t\tif (!res.ok) return;\n\t\tsetPending(null);\n\t\tsetPinMode(true);\n\t\tawait refreshComments();\n\t};\n\n\tconst updateComment = async (id: string, content: string) => {\n\t\tif (!apiBase.current) return;\n\t\tconst res = await fetch(`${apiBase.current}/api/feedback-comments/${id}`, {\n\t\t\tmethod: \"PATCH\",\n\t\t\tcredentials: \"include\",\n\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\tbody: JSON.stringify({ content }),\n\t\t});\n\t\tif (!res.ok) return;\n\t\tsetEditingId(null);\n\t\tawait refreshComments();\n\t};\n\n\tconst removeComment = async (id: string) => {\n\t\tif (!apiBase.current) return;\n\t\tconst res = await fetch(`${apiBase.current}/api/feedback-comments/${id}`, {\n\t\t\tmethod: \"DELETE\",\n\t\t\tcredentials: \"include\",\n\t\t});\n\t\tif (!res.ok) return;\n\t\tawait refreshComments();\n\t};\n\n\tconst finalizeSession = async () => {\n\t\tif (!apiBase.current || !session) return;\n\t\tif (!window.confirm(\"Finalize this feedback session? You won't be able to add more comments.\")) return;\n\t\tsetFinalizing(true);\n\t\ttry {\n\t\t\tconst res = await fetch(\n\t\t\t\t`${apiBase.current}/api/feedback-sessions/${session.feedbackSessionId}/finalize`,\n\t\t\t\t{ method: \"POST\", credentials: \"include\" },\n\t\t\t);\n\t\t\tif (!res.ok) return;\n\t\t\tsetSession((prev) => (prev ? { ...prev, canComment: false } : prev));\n\t\t} finally {\n\t\t\tsetFinalizing(false);\n\t\t}\n\t};\n\n\tconst scrollToComment = (comment: FeedbackComment) => {\n\t\tif (comment.pagePath !== window.location.pathname) {\n\t\t\twindow.location.assign(comment.pagePath);\n\t\t\treturn;\n\t\t}\n\t\tlet el: Element | null = null;\n\t\ttry {\n\t\t\tel = document.querySelector(comment.cssSelector);\n\t\t} catch {\n\t\t\tel = null;\n\t\t}\n\t\tif (!el) return;\n\t\tel.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n\t\tsetEditingId(comment.id);\n\t};\n\n\tif (loading || !session) return null;\n\n\t// Session has been finalized for AI review. Show a small status panel with\n\t// the same progress + ETA shown in YNS, but no commenting controls. Polling\n\t// continues in the background; on `done` we hard-reload to pick up the\n\t// fresh deploy.\n\tif (!session.canComment) {\n\t\tif (session.sessionStatus !== \"processing\" && session.sessionStatus !== \"in_review\") return null;\n\t\treturn (\n\t\t\t<div data-yns-feedback-ui=\"true\">\n\t\t\t\t<SubmittedPanel progress={session.progress} eta={session.eta} status={session.sessionStatus} />\n\t\t\t</div>\n\t\t);\n\t}\n\n\tconst visiblePins = session.comments.filter(\n\t\t(c) => c.pagePath === window.location.pathname && c.status !== \"done\",\n\t);\n\n\treturn (\n\t\t<div data-yns-feedback-ui=\"true\">\n\t\t\t{visiblePins.map((pin, idx) => (\n\t\t\t\t<PinOverlay\n\t\t\t\t\tkey={pin.id}\n\t\t\t\t\tpin={pin}\n\t\t\t\t\tnumber={idx + 1}\n\t\t\t\t\tediting={editingId === pin.id}\n\t\t\t\t\tonStartEdit={() => setEditingId(pin.id)}\n\t\t\t\t\tonCancelEdit={() => setEditingId(null)}\n\t\t\t\t\tonSave={(content) => updateComment(pin.id, content)}\n\t\t\t\t\tonRemove={() => removeComment(pin.id)}\n\t\t\t\t/>\n\t\t\t))}\n\n\t\t\t{pending && (\n\t\t\t\t<PendingCommentPopover\n\t\t\t\t\tpending={pending}\n\t\t\t\t\tonCancel={() => {\n\t\t\t\t\t\tsetPending(null);\n\t\t\t\t\t\tsetPinMode(true);\n\t\t\t\t\t}}\n\t\t\t\t\tonSave={(content) => submitNewComment(content)}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t{sidebarOpen && (\n\t\t\t\t<CommentsSidebar\n\t\t\t\t\tcomments={session.comments}\n\t\t\t\t\tcurrentPath={window.location.pathname}\n\t\t\t\t\tonClose={() => setSidebarOpen(false)}\n\t\t\t\t\tonSelect={scrollToComment}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t<div style={toolbarStyle}>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tonClick={() => setPinMode((v) => !v)}\n\t\t\t\t\tstyle={pinMode ? toolbarButtonActiveStyle : toolbarButtonStyle}\n\t\t\t\t>\n\t\t\t\t\t{pinMode ? \"Cancel\" : \"Add comment\"}\n\t\t\t\t</button>\n\t\t\t\t<button type=\"button\" onClick={() => setSidebarOpen((v) => !v)} style={toolbarButtonGhostStyle}>\n\t\t\t\t\t{sidebarOpen ? \"Hide list\" : `List (${session.comments.length})`}\n\t\t\t\t</button>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tonClick={finalizeSession}\n\t\t\t\t\tdisabled={finalizing}\n\t\t\t\t\tstyle={toolbarButtonFinalizeStyle}\n\t\t\t\t>\n\t\t\t\t\t{finalizing ? \"Finalizing…\" : \"Finalize\"}\n\t\t\t\t</button>\n\t\t\t\t<span style={toolbarHintStyle}>\n\t\t\t\t\t{pinMode ? \"Click any element to comment\" : `${visiblePins.length} on this page`}\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction SubmittedPanel({\n\tprogress,\n\teta: etaIso,\n\tstatus,\n}: {\n\tprogress: ReviewProgress;\n\teta: string;\n\tstatus: SessionStatus;\n}) {\n\t// Re-render every minute so the relative ETA label stays fresh between\n\t// the 10s data polls (which only re-render when the API payload changes).\n\tconst [, setTick] = useState(0);\n\tuseEffect(() => {\n\t\tconst id = window.setInterval(() => setTick((t) => t + 1), 60_000);\n\t\treturn () => window.clearInterval(id);\n\t}, []);\n\n\tconst eta = formatEta(etaIso);\n\tconst isInReview = status === \"in_review\";\n\tconst headline = isInReview ? \"Feedback under review\" : \"Applying feedback\";\n\n\treturn (\n\t\t<div style={submittedPanelStyle}>\n\t\t\t<div style={submittedHeaderStyle}>\n\t\t\t\t<span style={submittedBadgeStyle}>Submitted</span>\n\t\t\t\t<strong style={{ fontSize: 13 }}>{headline}</strong>\n\t\t\t</div>\n\t\t\t<div style={submittedProgressLabelStyle}>\n\t\t\t\t<span>Review progress</span>\n\t\t\t\t<span style={{ opacity: 0.7 }}>{progress.label}</span>\n\t\t\t</div>\n\t\t\t<div style={submittedProgressTrackStyle}>\n\t\t\t\t<div style={{ ...submittedProgressFillStyle, width: `${progress.fillPct}%` }} />\n\t\t\t</div>\n\t\t\t<p style={submittedEtaStyle}>\n\t\t\t\tEstimated delivery: <span style={{ fontWeight: 600 }}>{eta}</span>\n\t\t\t</p>\n\t\t</div>\n\t);\n}\n\nfunction PinOverlay({\n\tpin,\n\tnumber,\n\tediting,\n\tonStartEdit,\n\tonCancelEdit,\n\tonSave,\n\tonRemove,\n}: {\n\tpin: FeedbackComment;\n\tnumber: number;\n\tediting: boolean;\n\tonStartEdit: () => void;\n\tonCancelEdit: () => void;\n\tonSave: (content: string) => Promise<void>;\n\tonRemove: () => Promise<void>;\n}) {\n\tconst target = useTargetPosition(pin.cssSelector, pin.offsetXRatio, pin.offsetYRatio);\n\tif (!target) return null;\n\n\treturn (\n\t\t<div\n\t\t\tstyle={{\n\t\t\t\tposition: \"absolute\",\n\t\t\t\ttop: target.top,\n\t\t\t\tleft: target.left,\n\t\t\t\tzIndex: 2147483600,\n\t\t\t\tpointerEvents: \"auto\",\n\t\t\t}}\n\t\t>\n\t\t\t<button type=\"button\" onClick={onStartEdit} style={pinDotStyle} title={pin.content}>\n\t\t\t\t{number}\n\t\t\t</button>\n\t\t\t{editing && (\n\t\t\t\t<EditPopover initial={pin.content} onCancel={onCancelEdit} onSave={onSave} onRemove={onRemove} />\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nfunction PendingCommentPopover({\n\tpending,\n\tonCancel,\n\tonSave,\n}: {\n\tpending: PendingPin;\n\tonCancel: () => void;\n\tonSave: (content: string) => Promise<void>;\n}) {\n\treturn (\n\t\t<>\n\t\t\t<div\n\t\t\t\tstyle={{\n\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\ttop: pending.rect.top + pending.rect.height * pending.offsetYRatio - 12,\n\t\t\t\t\tleft: pending.rect.left + pending.rect.width * pending.offsetXRatio - 12,\n\t\t\t\t\tzIndex: 2147483600,\n\t\t\t\t\tpointerEvents: \"none\",\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<div style={pinDotStyle}>•</div>\n\t\t\t</div>\n\t\t\t<div\n\t\t\t\tstyle={{\n\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\ttop: pending.clickY + 10,\n\t\t\t\t\tleft: pending.clickX + 10,\n\t\t\t\t\tzIndex: 2147483647,\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<EditPopover initial=\"\" onCancel={onCancel} onSave={onSave} />\n\t\t\t</div>\n\t\t</>\n\t);\n}\n\nfunction CommentsSidebar({\n\tcomments,\n\tcurrentPath,\n\tonClose,\n\tonSelect,\n}: {\n\tcomments: FeedbackComment[];\n\tcurrentPath: string;\n\tonClose: () => void;\n\tonSelect: (comment: FeedbackComment) => void;\n}) {\n\tconst groups = new Map<string, FeedbackComment[]>();\n\tfor (const c of comments) {\n\t\tconst list = groups.get(c.pagePath) ?? [];\n\t\tlist.push(c);\n\t\tgroups.set(c.pagePath, list);\n\t}\n\tconst paths = Array.from(groups.keys()).sort((a, b) => {\n\t\tif (a === currentPath) return -1;\n\t\tif (b === currentPath) return 1;\n\t\treturn a.localeCompare(b);\n\t});\n\n\treturn (\n\t\t<div style={sidebarStyle}>\n\t\t\t<div style={sidebarHeaderStyle}>\n\t\t\t\t<strong style={{ fontSize: 14 }}>Comments ({comments.length})</strong>\n\t\t\t\t<button type=\"button\" onClick={onClose} style={ghostButtonStyle}>\n\t\t\t\t\tClose\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t\t<div style={sidebarScrollStyle}>\n\t\t\t\t{comments.length === 0 ? (\n\t\t\t\t\t<div style={sidebarEmptyStyle}>No comments yet. Click “Add comment” to leave one.</div>\n\t\t\t\t) : (\n\t\t\t\t\tpaths.map((path) => (\n\t\t\t\t\t\t<div key={path} style={{ marginBottom: 12 }}>\n\t\t\t\t\t\t\t<div style={sidebarPathStyle}>{path === currentPath ? `${path} · current` : path}</div>\n\t\t\t\t\t\t\t{(groups.get(path) ?? []).map((c, idx) => (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\tkey={c.id}\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={() => onSelect(c)}\n\t\t\t\t\t\t\t\t\tstyle={sidebarItemStyle}\n\t\t\t\t\t\t\t\t\tdisabled={c.status === \"done\" && false}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<span style={sidebarItemIndexStyle}>{idx + 1}</span>\n\t\t\t\t\t\t\t\t\t<span style={sidebarItemTextStyle}>\n\t\t\t\t\t\t\t\t\t\t{c.content.length > 140 ? `${c.content.slice(0, 140)}…` : c.content}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t{c.status === \"done\" && <span style={sidebarItemDoneStyle}>done</span>}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction EditPopover({\n\tinitial,\n\tonCancel,\n\tonSave,\n\tonRemove,\n}: {\n\tinitial: string;\n\tonCancel: () => void;\n\tonSave: (content: string) => Promise<void>;\n\tonRemove?: () => Promise<void>;\n}) {\n\tconst [value, setValue] = useState(initial);\n\tconst [saving, setSaving] = useState(false);\n\tconst textareaRef = useRef<HTMLTextAreaElement | null>(null);\n\n\tuseEffect(() => {\n\t\tconst el = textareaRef.current;\n\t\tif (!el) return;\n\t\tel.focus();\n\t\tconst len = el.value.length;\n\t\tel.setSelectionRange(len, len);\n\t}, []);\n\n\tconst handleSubmit = async (e: FormEvent) => {\n\t\te.preventDefault();\n\t\tconst content = value.trim();\n\t\tif (!content) return;\n\t\tsetSaving(true);\n\t\ttry {\n\t\t\tawait onSave(content);\n\t\t} finally {\n\t\t\tsetSaving(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t<form onSubmit={handleSubmit} style={popoverStyle}>\n\t\t\t<textarea\n\t\t\t\tref={textareaRef}\n\t\t\t\tvalue={value}\n\t\t\t\tonChange={(e) => setValue(e.target.value)}\n\t\t\t\tplaceholder=\"Leave a comment…\"\n\t\t\t\tstyle={textareaStyle}\n\t\t\t\trows={3}\n\t\t\t/>\n\t\t\t<div style={popoverActionsStyle}>\n\t\t\t\t{onRemove && (\n\t\t\t\t\t<button type=\"button\" onClick={() => onRemove()} style={dangerButtonStyle} disabled={saving}>\n\t\t\t\t\t\tDelete\n\t\t\t\t\t</button>\n\t\t\t\t)}\n\t\t\t\t<div style={{ flex: 1 }} />\n\t\t\t\t<button type=\"button\" onClick={onCancel} style={ghostButtonStyle} disabled={saving}>\n\t\t\t\t\tCancel\n\t\t\t\t</button>\n\t\t\t\t<button type=\"submit\" style={primaryButtonStyle} disabled={saving || !value.trim()}>\n\t\t\t\t\t{saving ? \"Saving…\" : \"Save\"}\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t</form>\n\t);\n}\n\nfunction useTargetPosition(selector: string, offsetXRatio: number, offsetYRatio: number) {\n\tconst [pos, setPos] = useState<{ top: number; left: number } | null>(null);\n\n\tuseEffect(() => {\n\t\tconst update = () => {\n\t\t\tlet el: Element | null = null;\n\t\t\ttry {\n\t\t\t\tel = document.querySelector(selector);\n\t\t\t} catch {\n\t\t\t\tel = null;\n\t\t\t}\n\t\t\tif (!el) {\n\t\t\t\tsetPos(null);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst r = el.getBoundingClientRect();\n\t\t\tsetPos({\n\t\t\t\ttop: r.top + window.scrollY + r.height * offsetYRatio - 12,\n\t\t\t\tleft: r.left + window.scrollX + r.width * offsetXRatio - 12,\n\t\t\t});\n\t\t};\n\n\t\tupdate();\n\t\tconst ro = new ResizeObserver(update);\n\t\ttry {\n\t\t\tconst el = document.querySelector(selector);\n\t\t\tif (el) ro.observe(el);\n\t\t} catch {\n\t\t\t// swallow invalid selector\n\t\t}\n\t\twindow.addEventListener(\"scroll\", update, true);\n\t\twindow.addEventListener(\"resize\", update);\n\t\treturn () => {\n\t\t\tro.disconnect();\n\t\t\twindow.removeEventListener(\"scroll\", update, true);\n\t\t\twindow.removeEventListener(\"resize\", update);\n\t\t};\n\t}, [selector, offsetXRatio, offsetYRatio]);\n\n\treturn pos;\n}\n\n// Inline styles — toolbar is injected into arbitrary stores, so we avoid relying\n// on any CSS framework being present.\nconst toolbarStyle: CSSProperties = {\n\tposition: \"fixed\",\n\tbottom: 16,\n\tleft: \"50%\",\n\ttransform: \"translateX(-50%)\",\n\tzIndex: 2147483646,\n\tdisplay: \"flex\",\n\talignItems: \"center\",\n\tgap: 8,\n\tpadding: \"8px 12px\",\n\tbackground: \"rgba(17, 17, 17, 0.92)\",\n\tcolor: \"white\",\n\tborderRadius: 999,\n\tboxShadow: \"0 8px 24px rgba(0,0,0,0.25)\",\n\tfontFamily:\n\t\t'-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\"',\n\tfontSize: 14,\n\tpointerEvents: \"auto\",\n};\n\nconst toolbarButtonStyle: CSSProperties = {\n\tborder: \"none\",\n\tbackground: \"white\",\n\tcolor: \"#111\",\n\tpadding: \"6px 12px\",\n\tborderRadius: 999,\n\tcursor: \"pointer\",\n\tfontWeight: 600,\n\tfontSize: 13,\n};\n\nconst toolbarButtonActiveStyle: CSSProperties = {\n\t...toolbarButtonStyle,\n\tbackground: \"#ef4444\",\n\tcolor: \"white\",\n};\n\nconst toolbarButtonGhostStyle: CSSProperties = {\n\tborder: \"1px solid rgba(255,255,255,0.4)\",\n\tbackground: \"transparent\",\n\tcolor: \"white\",\n\tpadding: \"6px 12px\",\n\tborderRadius: 999,\n\tcursor: \"pointer\",\n\tfontWeight: 500,\n\tfontSize: 13,\n};\n\nconst toolbarButtonFinalizeStyle: CSSProperties = {\n\tborder: \"none\",\n\tbackground: \"#10b981\",\n\tcolor: \"white\",\n\tpadding: \"6px 12px\",\n\tborderRadius: 999,\n\tcursor: \"pointer\",\n\tfontWeight: 600,\n\tfontSize: 13,\n};\n\nconst toolbarHintStyle: CSSProperties = {\n\topacity: 0.8,\n\tfontSize: 12,\n};\n\nconst pinDotStyle: CSSProperties = {\n\twidth: 24,\n\theight: 24,\n\tborderRadius: 999,\n\tbackground: \"#10b981\",\n\tcolor: \"white\",\n\tborder: \"2px solid white\",\n\tcursor: \"pointer\",\n\tfontSize: 12,\n\tfontWeight: 700,\n\tboxShadow: \"0 2px 6px rgba(0,0,0,0.3)\",\n\tdisplay: \"inline-flex\",\n\talignItems: \"center\",\n\tjustifyContent: \"center\",\n\tpadding: 0,\n};\n\nconst popoverStyle: CSSProperties = {\n\tbackground: \"white\",\n\tborder: \"1px solid #e5e7eb\",\n\tborderRadius: 8,\n\tpadding: 12,\n\twidth: 280,\n\tboxShadow: \"0 12px 32px rgba(0,0,0,0.18)\",\n\tfontFamily:\n\t\t'-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\"',\n\tdisplay: \"flex\",\n\tflexDirection: \"column\",\n\tgap: 8,\n\tcolor: \"#111\",\n};\n\nconst textareaStyle: CSSProperties = {\n\twidth: \"100%\",\n\tborder: \"1px solid #d1d5db\",\n\tborderRadius: 6,\n\tpadding: 8,\n\tfontSize: 14,\n\tresize: \"vertical\",\n\tfontFamily: \"inherit\",\n\tcolor: \"#111\",\n\tbackground: \"white\",\n\tboxSizing: \"border-box\",\n};\n\nconst popoverActionsStyle: CSSProperties = {\n\tdisplay: \"flex\",\n\talignItems: \"center\",\n\tgap: 6,\n};\n\nconst baseButton: CSSProperties = {\n\tborder: \"1px solid transparent\",\n\tborderRadius: 6,\n\tpadding: \"6px 10px\",\n\tfontSize: 13,\n\tcursor: \"pointer\",\n\tfontWeight: 500,\n};\n\nconst primaryButtonStyle: CSSProperties = {\n\t...baseButton,\n\tbackground: \"#111\",\n\tcolor: \"white\",\n};\n\nconst ghostButtonStyle: CSSProperties = {\n\t...baseButton,\n\tbackground: \"transparent\",\n\tcolor: \"#374151\",\n\tborderColor: \"#d1d5db\",\n};\n\nconst dangerButtonStyle: CSSProperties = {\n\t...baseButton,\n\tbackground: \"transparent\",\n\tcolor: \"#b91c1c\",\n\tborderColor: \"#fecaca\",\n};\n\nconst sidebarStyle: CSSProperties = {\n\tposition: \"fixed\",\n\ttop: 0,\n\tright: 0,\n\tbottom: 0,\n\twidth: 360,\n\tmaxWidth: \"92vw\",\n\tbackground: \"white\",\n\tborderLeft: \"1px solid #e5e7eb\",\n\tboxShadow: \"-12px 0 32px rgba(0,0,0,0.12)\",\n\tzIndex: 2147483646,\n\tdisplay: \"flex\",\n\tflexDirection: \"column\",\n\tfontFamily:\n\t\t'-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\"',\n\tcolor: \"#111\",\n};\n\nconst sidebarHeaderStyle: CSSProperties = {\n\tdisplay: \"flex\",\n\talignItems: \"center\",\n\tjustifyContent: \"space-between\",\n\tpadding: \"12px 16px\",\n\tborderBottom: \"1px solid #e5e7eb\",\n};\n\nconst sidebarScrollStyle: CSSProperties = {\n\tflex: 1,\n\toverflow: \"auto\",\n\tpadding: \"12px 12px 24px\",\n};\n\nconst sidebarEmptyStyle: CSSProperties = {\n\tcolor: \"#6b7280\",\n\tfontSize: 13,\n\tpadding: \"24px 8px\",\n\ttextAlign: \"center\",\n};\n\nconst sidebarPathStyle: CSSProperties = {\n\tfontSize: 11,\n\ttextTransform: \"uppercase\",\n\tletterSpacing: 0.5,\n\tcolor: \"#6b7280\",\n\tpadding: \"4px 4px 6px\",\n\twordBreak: \"break-all\",\n};\n\nconst sidebarItemStyle: CSSProperties = {\n\tdisplay: \"flex\",\n\talignItems: \"flex-start\",\n\tgap: 8,\n\twidth: \"100%\",\n\ttextAlign: \"left\",\n\tbackground: \"white\",\n\tborder: \"1px solid #e5e7eb\",\n\tborderRadius: 6,\n\tpadding: \"8px 10px\",\n\tcursor: \"pointer\",\n\tfontSize: 13,\n\tmarginBottom: 6,\n\tcolor: \"#111\",\n};\n\nconst sidebarItemIndexStyle: CSSProperties = {\n\tflexShrink: 0,\n\twidth: 22,\n\theight: 22,\n\tborderRadius: 999,\n\tbackground: \"#10b981\",\n\tcolor: \"white\",\n\tfontWeight: 700,\n\tfontSize: 11,\n\tdisplay: \"inline-flex\",\n\talignItems: \"center\",\n\tjustifyContent: \"center\",\n};\n\nconst sidebarItemTextStyle: CSSProperties = {\n\tflex: 1,\n\twhiteSpace: \"pre-wrap\",\n\twordBreak: \"break-word\",\n};\n\nconst sidebarItemDoneStyle: CSSProperties = {\n\tflexShrink: 0,\n\tbackground: \"#d1fae5\",\n\tcolor: \"#065f46\",\n\tfontSize: 11,\n\tfontWeight: 600,\n\tpadding: \"2px 6px\",\n\tborderRadius: 4,\n\talignSelf: \"center\",\n};\n\nconst submittedPanelStyle: CSSProperties = {\n\tposition: \"fixed\",\n\tbottom: 16,\n\tleft: \"50%\",\n\ttransform: \"translateX(-50%)\",\n\tzIndex: 2147483646,\n\tdisplay: \"flex\",\n\tflexDirection: \"column\",\n\tgap: 8,\n\tpadding: \"12px 16px\",\n\twidth: \"min(420px, calc(100vw - 32px))\",\n\tbackground: \"white\",\n\tborder: \"1px solid #e5e7eb\",\n\tborderRadius: 12,\n\tboxShadow: \"0 12px 32px rgba(0,0,0,0.18)\",\n\tfontFamily:\n\t\t'-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\"',\n\tcolor: \"#111\",\n\tpointerEvents: \"auto\",\n};\n\nconst submittedHeaderStyle: CSSProperties = {\n\tdisplay: \"flex\",\n\talignItems: \"center\",\n\tgap: 8,\n};\n\nconst submittedBadgeStyle: CSSProperties = {\n\tbackground: \"#d1fae5\",\n\tcolor: \"#065f46\",\n\tfontSize: 11,\n\tfontWeight: 700,\n\tpadding: \"2px 8px\",\n\tborderRadius: 999,\n\ttextTransform: \"uppercase\",\n\tletterSpacing: 0.5,\n};\n\nconst submittedProgressLabelStyle: CSSProperties = {\n\tdisplay: \"flex\",\n\tjustifyContent: \"space-between\",\n\tfontSize: 12,\n};\n\nconst submittedProgressTrackStyle: CSSProperties = {\n\twidth: \"100%\",\n\theight: 6,\n\tbackground: \"#f3f4f6\",\n\tborderRadius: 999,\n\toverflow: \"hidden\",\n};\n\nconst submittedProgressFillStyle: CSSProperties = {\n\theight: \"100%\",\n\tbackground: \"#10b981\",\n\tborderRadius: 999,\n\ttransition: \"width 500ms\",\n};\n\nconst submittedEtaStyle: CSSProperties = {\n\tmargin: 0,\n\tfontSize: 12,\n\tcolor: \"#6b7280\",\n};\n\nexport function mountFeedbackToolbar(): { unmount: () => void } | null {\n\tconsole.log(\"[YNS Feedback Toolbar] mountFeedbackToolbar() called\", {\n\t\tisWindow: typeof window !== \"undefined\",\n\t\tvercelEnv: process.env.NEXT_PUBLIC_VERCEL_ENV,\n\t\talreadyMounted: typeof document !== \"undefined\" && Boolean(document.getElementById(MOUNT_NODE_ID)),\n\t});\n\tif (typeof window === \"undefined\") return null;\n\tif (process.env.NEXT_PUBLIC_VERCEL_ENV !== \"preview\") {\n\t\tconsole.log(\n\t\t\t\"[YNS Feedback Toolbar] gate failed — NEXT_PUBLIC_VERCEL_ENV is\",\n\t\t\tprocess.env.NEXT_PUBLIC_VERCEL_ENV,\n\t\t);\n\t\treturn null;\n\t}\n\tif (document.getElementById(MOUNT_NODE_ID)) return null;\n\n\tconst container = document.createElement(\"div\");\n\tcontainer.id = MOUNT_NODE_ID;\n\tcontainer.dataset.ynsFeedbackUi = \"true\";\n\tdocument.body.appendChild(container);\n\n\tconst root = createRoot(container);\n\troot.render(<FeedbackToolbar />);\n\n\treturn {\n\t\tunmount: () => {\n\t\t\troot.unmount();\n\t\t\tcontainer.remove();\n\t\t},\n\t};\n}\n\n// Auto-mount on import. Wait for DOM ready.\nif (typeof window !== \"undefined\") {\n\tconsole.log(\"[YNS Feedback Toolbar] module evaluated\", {\n\t\tvercelEnv: process.env.NEXT_PUBLIC_VERCEL_ENV,\n\t\treadyState: document.readyState,\n\t\twillAutoMount: process.env.NEXT_PUBLIC_VERCEL_ENV === \"preview\",\n\t});\n}\nif (typeof window !== \"undefined\" && process.env.NEXT_PUBLIC_VERCEL_ENV === \"preview\") {\n\tif (document.readyState === \"loading\") {\n\t\tdocument.addEventListener(\"DOMContentLoaded\", () => {\n\t\t\tmountFeedbackToolbar();\n\t\t});\n\t} else {\n\t\tmountFeedbackToolbar();\n\t}\n}\n"],"mappings":"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","names":["useEffect","useRef","useState","createRoot","Fragment","jsx","jsxs","MOUNT_NODE_ID","buildApiBase","override","host","protocol","computeCssSelector","el","path","node","part","className","classes","c","parent","tag","siblings","idx","ATTRS_OF_INTEREST","formatAttrs","parts","attr","v","trimmed","formatTextContent","text","buildSurroundingHtml","target","ancestors","cur","lines","depth","ancestor","indent","targetIndent","targetTag","targetText","childIndent","children","child","childText","i","isInsideToolbar","IGNORED_TAGS","POLL_INTERVAL_MS","formatEta","etaIso","eta","remainingMs","dateLabel","remainingMin","remainingHours","remainingDays","FeedbackToolbar","session","setSession","loading","setLoading","pinMode","setPinMode","pending","setPending","editingId","setEditingId","sidebarOpen","setSidebarOpen","finalizing","setFinalizing","apiBase","cancelled","controller","fetchOnce","res","data","interval","overlay","label","hovered","handleMove","e","rect","handleLeave","handleClick","event","offsetXRatio","offsetYRatio","refreshComments","submitNewComment","content","updateComment","id","removeComment","finalizeSession","prev","scrollToComment","comment","SubmittedPanel","visiblePins","pin","PinOverlay","PendingCommentPopover","CommentsSidebar","toolbarStyle","toolbarButtonActiveStyle","toolbarButtonStyle","toolbarButtonGhostStyle","toolbarButtonFinalizeStyle","toolbarHintStyle","progress","status","setTick","t","submittedPanelStyle","submittedHeaderStyle","submittedBadgeStyle","submittedProgressLabelStyle","submittedProgressTrackStyle","submittedProgressFillStyle","submittedEtaStyle","number","editing","onStartEdit","onCancelEdit","onSave","onRemove","useTargetPosition","pinDotStyle","EditPopover","onCancel","comments","currentPath","onClose","onSelect","groups","list","paths","a","b","sidebarStyle","sidebarHeaderStyle","ghostButtonStyle","sidebarScrollStyle","sidebarEmptyStyle","sidebarPathStyle","sidebarItemStyle","sidebarItemIndexStyle","sidebarItemTextStyle","sidebarItemDoneStyle","initial","value","setValue","saving","setSaving","textareaRef","len","popoverStyle","textareaStyle","popoverActionsStyle","dangerButtonStyle","primaryButtonStyle","selector","pos","setPos","update","r","ro","baseButton","mountFeedbackToolbar","container","root"]}
|
|
1
|
+
{"version":3,"sources":["../src/feedback-toolbar.tsx"],"sourcesContent":["/**\n * Feedback session toolbar — side-effect entry.\n *\n * Importing `commerce-kit/feedback-toolbar` (or `commerce-kit/browser` for the\n * combined entry) mounts a floating toolbar onto the page that lets reviewers\n * leave click-anchored comments. Comments persist via YNS API endpoints,\n * authenticated with the `better-auth` session cookie sent through\n * `credentials: \"include\"`.\n *\n * Gated on `process.env.NEXT_PUBLIC_VERCEL_ENV === \"preview\"` — only Vercel\n * preview deploys get the toolbar. Sandbox dev (env undefined) and production\n * (env === \"production\") skip it.\n */\n\nimport { type CSSProperties, type FormEvent, useEffect, useRef, useState } from \"react\";\nimport { createRoot } from \"react-dom/client\";\n\nconst MOUNT_NODE_ID = \"yns-feedback-toolbar-root\";\n\ninterface FeedbackComment {\n\tid: string;\n\tpagePath: string;\n\tcssSelector: string;\n\tcontent: string;\n\tstatus: \"todo\" | \"done\";\n\toffsetXRatio: number;\n\toffsetYRatio: number;\n}\n\ntype SessionStatus = \"created\" | \"in_progress\" | \"processing\" | \"in_review\" | \"done\";\n\ninterface ReviewProgress {\n\tfillPct: number;\n\tlabel: string;\n}\n\ninterface ActiveSession {\n\tfeedbackSessionId: string;\n\tcomments: FeedbackComment[];\n\tcanComment: boolean;\n\tsessionStatus: SessionStatus;\n\tcommentTotal: number;\n\tcommentDone: number;\n\teta: string;\n\tprogress: ReviewProgress;\n}\n\ninterface PendingPin {\n\tcssSelector: string;\n\tpagePath: string;\n\tsurroundingHtml: string;\n\trect: { top: number; left: number; width: number; height: number };\n\tclickX: number;\n\tclickY: number;\n\toffsetXRatio: number;\n\toffsetYRatio: number;\n}\n\nconst buildApiBase = (): string | null => {\n\tif (typeof window === \"undefined\") return null;\n\tconst override = (process.env.NEXT_PUBLIC_YNS_API_BASE ?? \"\").trim();\n\tif (override) return override.replace(/\\/$/, \"\");\n\n\t// Toolbar runs on a per-store preview deploy (mystore-preview.yns.{store|cx})\n\t// served by the merchant's Vercel project — that host has no YNS API. Aim\n\t// at the apex `yns.store` / `yns.cx`, which yns-app serves and where the\n\t// API lives. Cookies scoped to `.yns.store` / `.yns.cx` travel along.\n\tconst host = window.location.hostname;\n\tconst protocol = window.location.protocol;\n\tif (host.endsWith(\".yns.store\")) return `${protocol}//yns.store`;\n\tif (host.endsWith(\".yns.cx\")) return `${protocol}//yns.cx`;\n\treturn window.location.origin;\n};\n\nconst computeCssSelector = (el: Element): string => {\n\tif (el.id) return `#${CSS.escape(el.id)}`;\n\tconst path: string[] = [];\n\tlet node: Element | null = el;\n\twhile (node && node.nodeType === Node.ELEMENT_NODE && path.length < 6) {\n\t\tlet part = node.tagName.toLowerCase();\n\t\tconst className = node.getAttribute(\"class\");\n\t\tif (className) {\n\t\t\tconst classes = className\n\t\t\t\t.split(/\\s+/)\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.slice(0, 2)\n\t\t\t\t.map((c) => `.${CSS.escape(c)}`)\n\t\t\t\t.join(\"\");\n\t\t\tpart += classes;\n\t\t}\n\t\tconst parent: Element | null = node.parentElement;\n\t\tconst tag = node.tagName;\n\t\tif (parent) {\n\t\t\tconst siblings: Element[] = Array.from(parent.children).filter((c) => c.tagName === tag);\n\t\t\tif (siblings.length > 1) {\n\t\t\t\tconst idx = siblings.indexOf(node) + 1;\n\t\t\t\tpart += `:nth-of-type(${idx})`;\n\t\t\t}\n\t\t}\n\t\tpath.unshift(part);\n\t\tnode = parent;\n\t}\n\treturn path.join(\" > \");\n};\n\nconst ATTRS_OF_INTEREST = [\"id\", \"class\", \"data-testid\", \"aria-label\", \"role\", \"name\", \"href\", \"src\"];\n\nconst formatAttrs = (el: Element): string => {\n\tconst parts: string[] = [];\n\tfor (const attr of ATTRS_OF_INTEREST) {\n\t\tconst v = el.getAttribute(attr);\n\t\tif (!v) continue;\n\t\tconst trimmed = v.length > 80 ? `${v.slice(0, 80)}…` : v;\n\t\tparts.push(` ${attr}=\"${trimmed.replace(/\"/g, \""\")}\"`);\n\t}\n\treturn parts.join(\"\");\n};\n\nconst formatTextContent = (el: Element): string => {\n\tconst text = (el.textContent ?? \"\").replace(/\\s+/g, \" \").trim();\n\tif (!text) return \"\";\n\treturn text.length > 100 ? `${text.slice(0, 100)}…` : text;\n};\n\nconst buildSurroundingHtml = (target: Element): string => {\n\tconst ancestors: Element[] = [];\n\tlet cur: Element | null = target;\n\twhile (cur && cur !== document.documentElement && ancestors.length < 5) {\n\t\tconst parent: Element | null = cur.parentElement;\n\t\tif (!parent) break;\n\t\tancestors.unshift(parent);\n\t\tcur = parent;\n\t}\n\n\tconst lines: string[] = [];\n\tlet depth = 0;\n\tfor (const ancestor of ancestors) {\n\t\tconst indent = \" \".repeat(depth);\n\t\tlines.push(`${indent}<${ancestor.tagName.toLowerCase()}${formatAttrs(ancestor)}>`);\n\t\tdepth++;\n\t}\n\n\tconst targetIndent = \" \".repeat(depth);\n\tconst targetTag = target.tagName.toLowerCase();\n\tconst targetText = formatTextContent(target);\n\tlines.push(\n\t\t`${targetIndent}<${targetTag}${formatAttrs(target)}>${\n\t\t\ttargetText ? `${targetText}</${targetTag}>` : \"\"\n\t\t} ← TARGET`,\n\t);\n\n\tif (!targetText) {\n\t\tconst childIndent = \" \".repeat(depth + 1);\n\t\tconst children = Array.from(target.children).slice(0, 6);\n\t\tfor (const child of children) {\n\t\t\tconst childText = formatTextContent(child);\n\t\t\tlines.push(\n\t\t\t\t`${childIndent}<${child.tagName.toLowerCase()}${formatAttrs(child)}>${\n\t\t\t\t\tchildText ? `${childText}</${child.tagName.toLowerCase()}>` : \"\"\n\t\t\t\t}`,\n\t\t\t);\n\t\t}\n\t\tif (target.children.length > 6) {\n\t\t\tlines.push(`${childIndent}… (${target.children.length - 6} more children)`);\n\t\t}\n\t\tlines.push(`${targetIndent}</${targetTag}>`);\n\t}\n\n\tfor (let i = ancestors.length - 1; i >= 0; i--) {\n\t\tconst indent = \" \".repeat(i);\n\t\tconst ancestor = ancestors[i];\n\t\tif (!ancestor) continue;\n\t\tlines.push(`${indent}</${ancestor.tagName.toLowerCase()}>`);\n\t}\n\n\treturn lines.join(\"\\n\");\n};\n\nconst isInsideToolbar = (el: Element | null): boolean => {\n\tlet node = el;\n\twhile (node) {\n\t\tif (node instanceof HTMLElement && node.dataset.ynsFeedbackUi === \"true\") return true;\n\t\tnode = node.parentElement;\n\t}\n\treturn false;\n};\n\nconst IGNORED_TAGS = new Set([\"HTML\", \"HEAD\", \"SCRIPT\", \"STYLE\", \"NOSCRIPT\"]);\n\nconst POLL_INTERVAL_MS = 10_000;\n\n// Format a server-computed ETA as remaining + absolute label. The deadline\n// math itself lives in yns-app's `lib/feedback-comments-api.ts` so the toolbar\n// and YNS lock screen stay in sync.\nconst formatEta = (etaIso: string): string => {\n\tconst eta = new Date(etaIso);\n\tconst remainingMs = eta.getTime() - Date.now();\n\tconst dateLabel = eta.toLocaleString(undefined, {\n\t\tweekday: \"short\",\n\t\tmonth: \"short\",\n\t\tday: \"numeric\",\n\t\thour: \"numeric\",\n\t\tminute: \"2-digit\",\n\t});\n\n\tif (remainingMs <= 0) return `Any moment now (estimated by ${dateLabel})`;\n\tconst remainingMin = Math.ceil(remainingMs / 60_000);\n\tif (remainingMin < 60) return `~${remainingMin} minutes (by ${dateLabel})`;\n\tconst remainingHours = Math.round(remainingMs / 3_600_000);\n\tif (remainingHours < 24) {\n\t\treturn `~${remainingHours} ${remainingHours === 1 ? \"hour\" : \"hours\"} (by ${dateLabel})`;\n\t}\n\tconst remainingDays = Math.round(remainingMs / 86_400_000);\n\treturn `~${remainingDays} ${remainingDays === 1 ? \"day\" : \"days\"} (by ${dateLabel})`;\n};\n\nfunction FeedbackToolbar() {\n\tconst [session, setSession] = useState<ActiveSession | null>(null);\n\tconst [loading, setLoading] = useState(true);\n\tconst [pinMode, setPinMode] = useState(false);\n\tconst [pending, setPending] = useState<PendingPin | null>(null);\n\tconst [editingId, setEditingId] = useState<string | null>(null);\n\tconst [sidebarOpen, setSidebarOpen] = useState(false);\n\tconst [finalizing, setFinalizing] = useState(false);\n\tconst apiBase = useRef<string | null>(null);\n\n\tuseEffect(() => {\n\t\tapiBase.current = buildApiBase();\n\t\tif (!apiBase.current) {\n\t\t\tsetLoading(false);\n\t\t\treturn;\n\t\t}\n\n\t\tlet cancelled = false;\n\t\tconst controller = new AbortController();\n\n\t\tconst fetchOnce = async () => {\n\t\t\ttry {\n\t\t\t\tconst res = await fetch(\n\t\t\t\t\t`${apiBase.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,\n\t\t\t\t\t{ credentials: \"include\", signal: controller.signal },\n\t\t\t\t);\n\t\t\t\tif (cancelled) return;\n\t\t\t\tif (!res.ok) {\n\t\t\t\t\tsetSession(null);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst data = (await res.json()) as ActiveSession;\n\t\t\t\tif (cancelled) return;\n\t\t\t\tsetSession(data);\n\t\t\t\t// Hard-refresh once the AI run lands and the preview is up to date.\n\t\t\t\t// Out of scope to diff anything smarter — the new deploy replaces\n\t\t\t\t// the page entirely so a full reload is appropriate here.\n\t\t\t\tif (data.sessionStatus === \"done\") {\n\t\t\t\t\twindow.location.reload();\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tif (!cancelled) setSession(null);\n\t\t\t} finally {\n\t\t\t\tif (!cancelled) setLoading(false);\n\t\t\t}\n\t\t};\n\n\t\tvoid fetchOnce();\n\t\tconst interval = window.setInterval(fetchOnce, POLL_INTERVAL_MS);\n\n\t\treturn () => {\n\t\t\tcancelled = true;\n\t\t\tcontroller.abort();\n\t\t\twindow.clearInterval(interval);\n\t\t};\n\t}, []);\n\n\t// When the session stops accepting comments (finalized → in_review, or\n\t// closed), tear down any in-flight UI so the hover overlay/popover/sidebar\n\t// don't linger after the toolbar's main render returns null.\n\tuseEffect(() => {\n\t\tif (!session || session.canComment) return;\n\t\tsetPinMode(false);\n\t\tsetPending(null);\n\t\tsetSidebarOpen(false);\n\t\tsetEditingId(null);\n\t}, [session]);\n\n\tuseEffect(() => {\n\t\tif (!pinMode) return;\n\t\tdocument.body.style.cursor = \"crosshair\";\n\t\treturn () => {\n\t\t\tdocument.body.style.cursor = \"\";\n\t\t};\n\t}, [pinMode]);\n\n\tuseEffect(() => {\n\t\tif (!pinMode) return;\n\n\t\tconst overlay = document.createElement(\"div\");\n\t\toverlay.dataset.ynsFeedbackUi = \"true\";\n\t\toverlay.style.cssText = [\n\t\t\t\"position: fixed\",\n\t\t\t\"pointer-events: none\",\n\t\t\t\"z-index: 2147483644\",\n\t\t\t\"border: 2px dashed #10b981\",\n\t\t\t\"background: rgba(16, 185, 129, 0.08)\",\n\t\t\t\"border-radius: 3px\",\n\t\t\t\"display: none\",\n\t\t\t\"transition: top 0.05s, left 0.05s, width 0.05s, height 0.05s\",\n\t\t].join(\";\");\n\n\t\tconst label = document.createElement(\"div\");\n\t\tlabel.dataset.ynsFeedbackUi = \"true\";\n\t\tlabel.textContent = \"Click to comment\";\n\t\tlabel.style.cssText = [\n\t\t\t\"position: fixed\",\n\t\t\t\"pointer-events: none\",\n\t\t\t\"z-index: 2147483645\",\n\t\t\t\"background: #059669\",\n\t\t\t\"color: #fff\",\n\t\t\t\"font-size: 11px\",\n\t\t\t\"font-family: ui-sans-serif, system-ui, sans-serif\",\n\t\t\t\"padding: 3px 8px\",\n\t\t\t\"border-radius: 4px\",\n\t\t\t\"white-space: nowrap\",\n\t\t\t\"display: none\",\n\t\t\t\"box-shadow: 0 2px 6px rgba(0,0,0,0.15)\",\n\t\t].join(\";\");\n\n\t\tdocument.documentElement.appendChild(overlay);\n\t\tdocument.documentElement.appendChild(label);\n\n\t\tlet hovered: Element | null = null;\n\t\tconst handleMove = (e: MouseEvent) => {\n\t\t\tconst el = document.elementFromPoint(e.clientX, e.clientY);\n\t\t\tif (!el || IGNORED_TAGS.has(el.tagName) || isInsideToolbar(el)) {\n\t\t\t\toverlay.style.display = \"none\";\n\t\t\t\tlabel.style.display = \"none\";\n\t\t\t\thovered = null;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\thovered = el;\n\t\t\trequestAnimationFrame(() => {\n\t\t\t\tif (hovered !== el) return;\n\t\t\t\tconst rect = el.getBoundingClientRect();\n\t\t\t\toverlay.style.top = `${rect.top}px`;\n\t\t\t\toverlay.style.left = `${rect.left}px`;\n\t\t\t\toverlay.style.width = `${rect.width}px`;\n\t\t\t\toverlay.style.height = `${rect.height}px`;\n\t\t\t\toverlay.style.display = \"block\";\n\t\t\t\tlabel.style.left = `${e.clientX + 14}px`;\n\t\t\t\tlabel.style.top = `${e.clientY + 14}px`;\n\t\t\t\tlabel.style.display = \"block\";\n\t\t\t});\n\t\t};\n\n\t\tconst handleLeave = () => {\n\t\t\toverlay.style.display = \"none\";\n\t\t\tlabel.style.display = \"none\";\n\t\t\thovered = null;\n\t\t};\n\n\t\tdocument.addEventListener(\"mousemove\", handleMove, true);\n\t\tdocument.addEventListener(\"mouseleave\", handleLeave);\n\n\t\treturn () => {\n\t\t\tdocument.removeEventListener(\"mousemove\", handleMove, true);\n\t\t\tdocument.removeEventListener(\"mouseleave\", handleLeave);\n\t\t\toverlay.remove();\n\t\t\tlabel.remove();\n\t\t};\n\t}, [pinMode]);\n\n\tuseEffect(() => {\n\t\tif (!pinMode) return;\n\t\tconst handleClick = (event: MouseEvent) => {\n\t\t\tconst target = event.target;\n\t\t\tif (!(target instanceof Element)) return;\n\t\t\tif (isInsideToolbar(target)) return;\n\n\t\t\tevent.preventDefault();\n\t\t\tevent.stopPropagation();\n\n\t\t\tconst rect = target.getBoundingClientRect();\n\t\t\tconst offsetXRatio = rect.width > 0 ? (event.clientX - rect.left) / rect.width : 0.5;\n\t\t\tconst offsetYRatio = rect.height > 0 ? (event.clientY - rect.top) / rect.height : 0.5;\n\t\t\tsetPending({\n\t\t\t\tcssSelector: computeCssSelector(target),\n\t\t\t\tpagePath: window.location.pathname,\n\t\t\t\tsurroundingHtml: buildSurroundingHtml(target),\n\t\t\t\trect: {\n\t\t\t\t\ttop: rect.top + window.scrollY,\n\t\t\t\t\tleft: rect.left + window.scrollX,\n\t\t\t\t\twidth: rect.width,\n\t\t\t\t\theight: rect.height,\n\t\t\t\t},\n\t\t\t\tclickX: event.clientX + window.scrollX,\n\t\t\t\tclickY: event.clientY + window.scrollY,\n\t\t\t\toffsetXRatio: Math.min(1, Math.max(0, offsetXRatio)),\n\t\t\t\toffsetYRatio: Math.min(1, Math.max(0, offsetYRatio)),\n\t\t\t});\n\t\t\tsetPinMode(false);\n\t\t};\n\t\tdocument.addEventListener(\"click\", handleClick, { capture: true });\n\t\treturn () => document.removeEventListener(\"click\", handleClick, { capture: true });\n\t}, [pinMode]);\n\n\tconst refreshComments = async () => {\n\t\tif (!apiBase.current) return;\n\t\tconst res = await fetch(\n\t\t\t`${apiBase.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,\n\t\t\t{ credentials: \"include\" },\n\t\t);\n\t\tif (!res.ok) return;\n\t\tconst data = (await res.json()) as ActiveSession;\n\t\tsetSession(data);\n\t};\n\n\tconst submitNewComment = async (content: string) => {\n\t\tif (!apiBase.current || !session || !pending) return;\n\t\tconst res = await fetch(`${apiBase.current}/api/feedback-comments`, {\n\t\t\tmethod: \"POST\",\n\t\t\tcredentials: \"include\",\n\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\tbody: JSON.stringify({\n\t\t\t\tfeedbackSessionId: session.feedbackSessionId,\n\t\t\t\tcontent,\n\t\t\t\tpagePath: pending.pagePath,\n\t\t\t\tcssSelector: pending.cssSelector,\n\t\t\t\tsurroundingHtml: pending.surroundingHtml,\n\t\t\t\toffsetXRatio: pending.offsetXRatio,\n\t\t\t\toffsetYRatio: pending.offsetYRatio,\n\t\t\t}),\n\t\t});\n\t\tif (!res.ok) return;\n\t\tsetPending(null);\n\t\tsetPinMode(true);\n\t\tawait refreshComments();\n\t};\n\n\tconst updateComment = async (id: string, content: string) => {\n\t\tif (!apiBase.current) return;\n\t\tconst res = await fetch(`${apiBase.current}/api/feedback-comments/${id}`, {\n\t\t\tmethod: \"PATCH\",\n\t\t\tcredentials: \"include\",\n\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\tbody: JSON.stringify({ content }),\n\t\t});\n\t\tif (!res.ok) return;\n\t\tsetEditingId(null);\n\t\tawait refreshComments();\n\t};\n\n\tconst removeComment = async (id: string) => {\n\t\tif (!apiBase.current) return;\n\t\tconst res = await fetch(`${apiBase.current}/api/feedback-comments/${id}`, {\n\t\t\tmethod: \"DELETE\",\n\t\t\tcredentials: \"include\",\n\t\t});\n\t\tif (!res.ok) return;\n\t\tawait refreshComments();\n\t};\n\n\tconst finalizeSession = async () => {\n\t\tif (!apiBase.current || !session) return;\n\t\tif (!window.confirm(\"Finalize this feedback session? You won't be able to add more comments.\")) return;\n\t\tsetFinalizing(true);\n\t\ttry {\n\t\t\tconst res = await fetch(\n\t\t\t\t`${apiBase.current}/api/feedback-sessions/${session.feedbackSessionId}/finalize`,\n\t\t\t\t{ method: \"POST\", credentials: \"include\" },\n\t\t\t);\n\t\t\tif (!res.ok) return;\n\t\t\tsetSession((prev) => (prev ? { ...prev, canComment: false } : prev));\n\t\t} finally {\n\t\t\tsetFinalizing(false);\n\t\t}\n\t};\n\n\tconst scrollToComment = (comment: FeedbackComment) => {\n\t\tif (comment.pagePath !== window.location.pathname) {\n\t\t\twindow.location.assign(comment.pagePath);\n\t\t\treturn;\n\t\t}\n\t\tlet el: Element | null = null;\n\t\ttry {\n\t\t\tel = document.querySelector(comment.cssSelector);\n\t\t} catch {\n\t\t\tel = null;\n\t\t}\n\t\tif (!el) return;\n\t\tel.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n\t\tsetEditingId(comment.id);\n\t};\n\n\tif (loading || !session) return null;\n\n\t// Session has been finalized for AI review. Show a small status panel with\n\t// the same progress + ETA shown in YNS, but no commenting controls. Polling\n\t// continues in the background; on `done` we hard-reload to pick up the\n\t// fresh deploy.\n\tif (!session.canComment) {\n\t\tif (session.sessionStatus !== \"processing\" && session.sessionStatus !== \"in_review\") return null;\n\t\treturn (\n\t\t\t<div data-yns-feedback-ui=\"true\">\n\t\t\t\t<SubmittedPanel progress={session.progress} eta={session.eta} status={session.sessionStatus} />\n\t\t\t</div>\n\t\t);\n\t}\n\n\tconst visiblePins = session.comments.filter(\n\t\t(c) => c.pagePath === window.location.pathname && c.status !== \"done\",\n\t);\n\n\treturn (\n\t\t<div data-yns-feedback-ui=\"true\">\n\t\t\t{visiblePins.map((pin, idx) => (\n\t\t\t\t<PinOverlay\n\t\t\t\t\tkey={pin.id}\n\t\t\t\t\tpin={pin}\n\t\t\t\t\tnumber={idx + 1}\n\t\t\t\t\tediting={editingId === pin.id}\n\t\t\t\t\tonStartEdit={() => setEditingId(pin.id)}\n\t\t\t\t\tonCancelEdit={() => setEditingId(null)}\n\t\t\t\t\tonSave={(content) => updateComment(pin.id, content)}\n\t\t\t\t\tonRemove={() => removeComment(pin.id)}\n\t\t\t\t/>\n\t\t\t))}\n\n\t\t\t{pending && (\n\t\t\t\t<PendingCommentPopover\n\t\t\t\t\tpending={pending}\n\t\t\t\t\tonCancel={() => {\n\t\t\t\t\t\tsetPending(null);\n\t\t\t\t\t\tsetPinMode(true);\n\t\t\t\t\t}}\n\t\t\t\t\tonSave={(content) => submitNewComment(content)}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t{sidebarOpen && (\n\t\t\t\t<CommentsSidebar\n\t\t\t\t\tcomments={session.comments}\n\t\t\t\t\tcurrentPath={window.location.pathname}\n\t\t\t\t\tonClose={() => setSidebarOpen(false)}\n\t\t\t\t\tonSelect={scrollToComment}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t<div style={toolbarStyle}>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tonClick={() => setPinMode((v) => !v)}\n\t\t\t\t\tstyle={pinMode ? toolbarButtonActiveStyle : toolbarButtonStyle}\n\t\t\t\t>\n\t\t\t\t\t{pinMode ? \"Cancel\" : \"Add comment\"}\n\t\t\t\t</button>\n\t\t\t\t<button type=\"button\" onClick={() => setSidebarOpen((v) => !v)} style={toolbarButtonGhostStyle}>\n\t\t\t\t\t{sidebarOpen ? \"Hide list\" : `List (${session.comments.length})`}\n\t\t\t\t</button>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tonClick={finalizeSession}\n\t\t\t\t\tdisabled={finalizing}\n\t\t\t\t\tstyle={toolbarButtonFinalizeStyle}\n\t\t\t\t>\n\t\t\t\t\t{finalizing ? \"Finalizing…\" : \"Finalize\"}\n\t\t\t\t</button>\n\t\t\t\t<span style={toolbarHintStyle}>\n\t\t\t\t\t{pinMode ? \"Click any element to comment\" : `${visiblePins.length} on this page`}\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction SubmittedPanel({\n\tprogress,\n\teta: etaIso,\n\tstatus,\n}: {\n\tprogress: ReviewProgress;\n\teta: string;\n\tstatus: SessionStatus;\n}) {\n\t// Re-render every minute so the relative ETA label stays fresh between\n\t// the 10s data polls (which only re-render when the API payload changes).\n\tconst [, setTick] = useState(0);\n\tuseEffect(() => {\n\t\tconst id = window.setInterval(() => setTick((t) => t + 1), 60_000);\n\t\treturn () => window.clearInterval(id);\n\t}, []);\n\n\tconst eta = formatEta(etaIso);\n\tconst isInReview = status === \"in_review\";\n\tconst headline = isInReview ? \"Feedback under review\" : \"Applying feedback\";\n\n\treturn (\n\t\t<div style={submittedPanelStyle}>\n\t\t\t<style>{spinnerKeyframes}</style>\n\t\t\t<div style={submittedHeaderStyle}>\n\t\t\t\t<Spinner />\n\t\t\t\t<span style={submittedBadgeStyle}>Submitted</span>\n\t\t\t\t<strong style={{ fontSize: 13 }}>{headline}</strong>\n\t\t\t</div>\n\t\t\t<div style={submittedProgressLabelStyle}>\n\t\t\t\t<span>Review progress</span>\n\t\t\t\t<span style={{ opacity: 0.7 }}>{progress.label}</span>\n\t\t\t</div>\n\t\t\t<div style={submittedProgressTrackStyle}>\n\t\t\t\t<div style={{ ...submittedProgressFillStyle, width: `${progress.fillPct}%` }} />\n\t\t\t</div>\n\t\t\t<p style={submittedEtaStyle}>\n\t\t\t\tEstimated delivery: <span style={{ fontWeight: 600 }}>{eta}</span>\n\t\t\t</p>\n\t\t</div>\n\t);\n}\n\n// Inline keyframes — toolbar avoids relying on a global stylesheet.\nconst spinnerKeyframes = `@keyframes yns-feedback-spin { to { transform: rotate(360deg); } }`;\n\nfunction Spinner({ size = 14 }: { size?: number }) {\n\treturn (\n\t\t<span\n\t\t\taria-hidden\n\t\t\tstyle={{\n\t\t\t\tdisplay: \"inline-block\",\n\t\t\t\twidth: size,\n\t\t\t\theight: size,\n\t\t\t\tborder: \"2px solid rgba(16, 185, 129, 0.25)\",\n\t\t\t\tborderTopColor: \"#10b981\",\n\t\t\t\tborderRadius: 999,\n\t\t\t\tanimation: \"yns-feedback-spin 0.9s linear infinite\",\n\t\t\t}}\n\t\t/>\n\t);\n}\n\nfunction PinOverlay({\n\tpin,\n\tnumber,\n\tediting,\n\tonStartEdit,\n\tonCancelEdit,\n\tonSave,\n\tonRemove,\n}: {\n\tpin: FeedbackComment;\n\tnumber: number;\n\tediting: boolean;\n\tonStartEdit: () => void;\n\tonCancelEdit: () => void;\n\tonSave: (content: string) => Promise<void>;\n\tonRemove: () => Promise<void>;\n}) {\n\tconst target = useTargetPosition(pin.cssSelector, pin.offsetXRatio, pin.offsetYRatio);\n\tif (!target) return null;\n\n\treturn (\n\t\t<div\n\t\t\tstyle={{\n\t\t\t\tposition: \"absolute\",\n\t\t\t\ttop: target.top,\n\t\t\t\tleft: target.left,\n\t\t\t\tzIndex: 2147483600,\n\t\t\t\tpointerEvents: \"auto\",\n\t\t\t}}\n\t\t>\n\t\t\t<button type=\"button\" onClick={onStartEdit} style={pinDotStyle} title={pin.content}>\n\t\t\t\t{number}\n\t\t\t</button>\n\t\t\t{editing && (\n\t\t\t\t<EditPopover initial={pin.content} onCancel={onCancelEdit} onSave={onSave} onRemove={onRemove} />\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nfunction PendingCommentPopover({\n\tpending,\n\tonCancel,\n\tonSave,\n}: {\n\tpending: PendingPin;\n\tonCancel: () => void;\n\tonSave: (content: string) => Promise<void>;\n}) {\n\treturn (\n\t\t<>\n\t\t\t<div\n\t\t\t\tstyle={{\n\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\ttop: pending.rect.top + pending.rect.height * pending.offsetYRatio - 12,\n\t\t\t\t\tleft: pending.rect.left + pending.rect.width * pending.offsetXRatio - 12,\n\t\t\t\t\tzIndex: 2147483600,\n\t\t\t\t\tpointerEvents: \"none\",\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<div style={pinDotStyle}>•</div>\n\t\t\t</div>\n\t\t\t<div\n\t\t\t\tstyle={{\n\t\t\t\t\tposition: \"absolute\",\n\t\t\t\t\ttop: pending.clickY + 10,\n\t\t\t\t\tleft: pending.clickX + 10,\n\t\t\t\t\tzIndex: 2147483647,\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<EditPopover initial=\"\" onCancel={onCancel} onSave={onSave} />\n\t\t\t</div>\n\t\t</>\n\t);\n}\n\nfunction CommentsSidebar({\n\tcomments,\n\tcurrentPath,\n\tonClose,\n\tonSelect,\n}: {\n\tcomments: FeedbackComment[];\n\tcurrentPath: string;\n\tonClose: () => void;\n\tonSelect: (comment: FeedbackComment) => void;\n}) {\n\tconst groups = new Map<string, FeedbackComment[]>();\n\tfor (const c of comments) {\n\t\tconst list = groups.get(c.pagePath) ?? [];\n\t\tlist.push(c);\n\t\tgroups.set(c.pagePath, list);\n\t}\n\tconst paths = Array.from(groups.keys()).sort((a, b) => {\n\t\tif (a === currentPath) return -1;\n\t\tif (b === currentPath) return 1;\n\t\treturn a.localeCompare(b);\n\t});\n\n\treturn (\n\t\t<div style={sidebarStyle}>\n\t\t\t<div style={sidebarHeaderStyle}>\n\t\t\t\t<strong style={{ fontSize: 14 }}>Comments ({comments.length})</strong>\n\t\t\t\t<button type=\"button\" onClick={onClose} style={ghostButtonStyle}>\n\t\t\t\t\tClose\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t\t<div style={sidebarScrollStyle}>\n\t\t\t\t{comments.length === 0 ? (\n\t\t\t\t\t<div style={sidebarEmptyStyle}>No comments yet. Click “Add comment” to leave one.</div>\n\t\t\t\t) : (\n\t\t\t\t\tpaths.map((path) => (\n\t\t\t\t\t\t<div key={path} style={{ marginBottom: 12 }}>\n\t\t\t\t\t\t\t<div style={sidebarPathStyle}>{path === currentPath ? `${path} · current` : path}</div>\n\t\t\t\t\t\t\t{(groups.get(path) ?? []).map((c, idx) => (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\tkey={c.id}\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={() => onSelect(c)}\n\t\t\t\t\t\t\t\t\tstyle={sidebarItemStyle}\n\t\t\t\t\t\t\t\t\tdisabled={c.status === \"done\" && false}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<span style={sidebarItemIndexStyle}>{idx + 1}</span>\n\t\t\t\t\t\t\t\t\t<span style={sidebarItemTextStyle}>\n\t\t\t\t\t\t\t\t\t\t{c.content.length > 140 ? `${c.content.slice(0, 140)}…` : c.content}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t{c.status === \"done\" && <span style={sidebarItemDoneStyle}>done</span>}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction EditPopover({\n\tinitial,\n\tonCancel,\n\tonSave,\n\tonRemove,\n}: {\n\tinitial: string;\n\tonCancel: () => void;\n\tonSave: (content: string) => Promise<void>;\n\tonRemove?: () => Promise<void>;\n}) {\n\tconst [value, setValue] = useState(initial);\n\tconst [saving, setSaving] = useState(false);\n\tconst textareaRef = useRef<HTMLTextAreaElement | null>(null);\n\n\tuseEffect(() => {\n\t\tconst el = textareaRef.current;\n\t\tif (!el) return;\n\t\tel.focus();\n\t\tconst len = el.value.length;\n\t\tel.setSelectionRange(len, len);\n\t}, []);\n\n\tconst handleSubmit = async (e: FormEvent) => {\n\t\te.preventDefault();\n\t\tconst content = value.trim();\n\t\tif (!content) return;\n\t\tsetSaving(true);\n\t\ttry {\n\t\t\tawait onSave(content);\n\t\t} finally {\n\t\t\tsetSaving(false);\n\t\t}\n\t};\n\n\treturn (\n\t\t<form onSubmit={handleSubmit} style={popoverStyle}>\n\t\t\t<textarea\n\t\t\t\tref={textareaRef}\n\t\t\t\tvalue={value}\n\t\t\t\tonChange={(e) => setValue(e.target.value)}\n\t\t\t\tplaceholder=\"Leave a comment…\"\n\t\t\t\tstyle={textareaStyle}\n\t\t\t\trows={3}\n\t\t\t/>\n\t\t\t<div style={popoverActionsStyle}>\n\t\t\t\t{onRemove && (\n\t\t\t\t\t<button type=\"button\" onClick={() => onRemove()} style={dangerButtonStyle} disabled={saving}>\n\t\t\t\t\t\tDelete\n\t\t\t\t\t</button>\n\t\t\t\t)}\n\t\t\t\t<div style={{ flex: 1 }} />\n\t\t\t\t<button type=\"button\" onClick={onCancel} style={ghostButtonStyle} disabled={saving}>\n\t\t\t\t\tCancel\n\t\t\t\t</button>\n\t\t\t\t<button type=\"submit\" style={primaryButtonStyle} disabled={saving || !value.trim()}>\n\t\t\t\t\t{saving ? \"Saving…\" : \"Save\"}\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t</form>\n\t);\n}\n\nfunction useTargetPosition(selector: string, offsetXRatio: number, offsetYRatio: number) {\n\tconst [pos, setPos] = useState<{ top: number; left: number } | null>(null);\n\n\tuseEffect(() => {\n\t\tconst update = () => {\n\t\t\tlet el: Element | null = null;\n\t\t\ttry {\n\t\t\t\tel = document.querySelector(selector);\n\t\t\t} catch {\n\t\t\t\tel = null;\n\t\t\t}\n\t\t\tif (!el) {\n\t\t\t\tsetPos(null);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst r = el.getBoundingClientRect();\n\t\t\tsetPos({\n\t\t\t\ttop: r.top + window.scrollY + r.height * offsetYRatio - 12,\n\t\t\t\tleft: r.left + window.scrollX + r.width * offsetXRatio - 12,\n\t\t\t});\n\t\t};\n\n\t\tupdate();\n\t\tconst ro = new ResizeObserver(update);\n\t\ttry {\n\t\t\tconst el = document.querySelector(selector);\n\t\t\tif (el) ro.observe(el);\n\t\t} catch {\n\t\t\t// swallow invalid selector\n\t\t}\n\t\twindow.addEventListener(\"scroll\", update, true);\n\t\twindow.addEventListener(\"resize\", update);\n\t\treturn () => {\n\t\t\tro.disconnect();\n\t\t\twindow.removeEventListener(\"scroll\", update, true);\n\t\t\twindow.removeEventListener(\"resize\", update);\n\t\t};\n\t}, [selector, offsetXRatio, offsetYRatio]);\n\n\treturn pos;\n}\n\n// Inline styles — toolbar is injected into arbitrary stores, so we avoid relying\n// on any CSS framework being present.\nconst toolbarStyle: CSSProperties = {\n\tposition: \"fixed\",\n\tbottom: 16,\n\tleft: \"50%\",\n\ttransform: \"translateX(-50%)\",\n\tzIndex: 2147483646,\n\tdisplay: \"flex\",\n\talignItems: \"center\",\n\tgap: 8,\n\tpadding: \"8px 12px\",\n\tbackground: \"rgba(17, 17, 17, 0.92)\",\n\tcolor: \"white\",\n\tborderRadius: 999,\n\tboxShadow: \"0 8px 24px rgba(0,0,0,0.25)\",\n\tfontFamily:\n\t\t'-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\"',\n\tfontSize: 14,\n\tpointerEvents: \"auto\",\n};\n\nconst toolbarButtonStyle: CSSProperties = {\n\tborder: \"none\",\n\tbackground: \"white\",\n\tcolor: \"#111\",\n\tpadding: \"6px 12px\",\n\tborderRadius: 999,\n\tcursor: \"pointer\",\n\tfontWeight: 600,\n\tfontSize: 13,\n};\n\nconst toolbarButtonActiveStyle: CSSProperties = {\n\t...toolbarButtonStyle,\n\tbackground: \"#ef4444\",\n\tcolor: \"white\",\n};\n\nconst toolbarButtonGhostStyle: CSSProperties = {\n\tborder: \"1px solid rgba(255,255,255,0.4)\",\n\tbackground: \"transparent\",\n\tcolor: \"white\",\n\tpadding: \"6px 12px\",\n\tborderRadius: 999,\n\tcursor: \"pointer\",\n\tfontWeight: 500,\n\tfontSize: 13,\n};\n\nconst toolbarButtonFinalizeStyle: CSSProperties = {\n\tborder: \"none\",\n\tbackground: \"#10b981\",\n\tcolor: \"white\",\n\tpadding: \"6px 12px\",\n\tborderRadius: 999,\n\tcursor: \"pointer\",\n\tfontWeight: 600,\n\tfontSize: 13,\n};\n\nconst toolbarHintStyle: CSSProperties = {\n\topacity: 0.8,\n\tfontSize: 12,\n};\n\nconst pinDotStyle: CSSProperties = {\n\twidth: 24,\n\theight: 24,\n\tborderRadius: 999,\n\tbackground: \"#10b981\",\n\tcolor: \"white\",\n\tborder: \"2px solid white\",\n\tcursor: \"pointer\",\n\tfontSize: 12,\n\tfontWeight: 700,\n\tboxShadow: \"0 2px 6px rgba(0,0,0,0.3)\",\n\tdisplay: \"inline-flex\",\n\talignItems: \"center\",\n\tjustifyContent: \"center\",\n\tpadding: 0,\n};\n\nconst popoverStyle: CSSProperties = {\n\tbackground: \"white\",\n\tborder: \"1px solid #e5e7eb\",\n\tborderRadius: 8,\n\tpadding: 12,\n\twidth: 280,\n\tboxShadow: \"0 12px 32px rgba(0,0,0,0.18)\",\n\tfontFamily:\n\t\t'-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\"',\n\tdisplay: \"flex\",\n\tflexDirection: \"column\",\n\tgap: 8,\n\tcolor: \"#111\",\n};\n\nconst textareaStyle: CSSProperties = {\n\twidth: \"100%\",\n\tborder: \"1px solid #d1d5db\",\n\tborderRadius: 6,\n\tpadding: 8,\n\tfontSize: 14,\n\tresize: \"vertical\",\n\tfontFamily: \"inherit\",\n\tcolor: \"#111\",\n\tbackground: \"white\",\n\tboxSizing: \"border-box\",\n};\n\nconst popoverActionsStyle: CSSProperties = {\n\tdisplay: \"flex\",\n\talignItems: \"center\",\n\tgap: 6,\n};\n\nconst baseButton: CSSProperties = {\n\tborder: \"1px solid transparent\",\n\tborderRadius: 6,\n\tpadding: \"6px 10px\",\n\tfontSize: 13,\n\tcursor: \"pointer\",\n\tfontWeight: 500,\n};\n\nconst primaryButtonStyle: CSSProperties = {\n\t...baseButton,\n\tbackground: \"#111\",\n\tcolor: \"white\",\n};\n\nconst ghostButtonStyle: CSSProperties = {\n\t...baseButton,\n\tbackground: \"transparent\",\n\tcolor: \"#374151\",\n\tborderColor: \"#d1d5db\",\n};\n\nconst dangerButtonStyle: CSSProperties = {\n\t...baseButton,\n\tbackground: \"transparent\",\n\tcolor: \"#b91c1c\",\n\tborderColor: \"#fecaca\",\n};\n\nconst sidebarStyle: CSSProperties = {\n\tposition: \"fixed\",\n\ttop: 0,\n\tright: 0,\n\tbottom: 0,\n\twidth: 360,\n\tmaxWidth: \"92vw\",\n\tbackground: \"white\",\n\tborderLeft: \"1px solid #e5e7eb\",\n\tboxShadow: \"-12px 0 32px rgba(0,0,0,0.12)\",\n\tzIndex: 2147483646,\n\tdisplay: \"flex\",\n\tflexDirection: \"column\",\n\tfontFamily:\n\t\t'-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\"',\n\tcolor: \"#111\",\n};\n\nconst sidebarHeaderStyle: CSSProperties = {\n\tdisplay: \"flex\",\n\talignItems: \"center\",\n\tjustifyContent: \"space-between\",\n\tpadding: \"12px 16px\",\n\tborderBottom: \"1px solid #e5e7eb\",\n};\n\nconst sidebarScrollStyle: CSSProperties = {\n\tflex: 1,\n\toverflow: \"auto\",\n\tpadding: \"12px 12px 24px\",\n};\n\nconst sidebarEmptyStyle: CSSProperties = {\n\tcolor: \"#6b7280\",\n\tfontSize: 13,\n\tpadding: \"24px 8px\",\n\ttextAlign: \"center\",\n};\n\nconst sidebarPathStyle: CSSProperties = {\n\tfontSize: 11,\n\ttextTransform: \"uppercase\",\n\tletterSpacing: 0.5,\n\tcolor: \"#6b7280\",\n\tpadding: \"4px 4px 6px\",\n\twordBreak: \"break-all\",\n};\n\nconst sidebarItemStyle: CSSProperties = {\n\tdisplay: \"flex\",\n\talignItems: \"flex-start\",\n\tgap: 8,\n\twidth: \"100%\",\n\ttextAlign: \"left\",\n\tbackground: \"white\",\n\tborder: \"1px solid #e5e7eb\",\n\tborderRadius: 6,\n\tpadding: \"8px 10px\",\n\tcursor: \"pointer\",\n\tfontSize: 13,\n\tmarginBottom: 6,\n\tcolor: \"#111\",\n};\n\nconst sidebarItemIndexStyle: CSSProperties = {\n\tflexShrink: 0,\n\twidth: 22,\n\theight: 22,\n\tborderRadius: 999,\n\tbackground: \"#10b981\",\n\tcolor: \"white\",\n\tfontWeight: 700,\n\tfontSize: 11,\n\tdisplay: \"inline-flex\",\n\talignItems: \"center\",\n\tjustifyContent: \"center\",\n};\n\nconst sidebarItemTextStyle: CSSProperties = {\n\tflex: 1,\n\twhiteSpace: \"pre-wrap\",\n\twordBreak: \"break-word\",\n};\n\nconst sidebarItemDoneStyle: CSSProperties = {\n\tflexShrink: 0,\n\tbackground: \"#d1fae5\",\n\tcolor: \"#065f46\",\n\tfontSize: 11,\n\tfontWeight: 600,\n\tpadding: \"2px 6px\",\n\tborderRadius: 4,\n\talignSelf: \"center\",\n};\n\nconst submittedPanelStyle: CSSProperties = {\n\tposition: \"fixed\",\n\tbottom: 16,\n\tleft: \"50%\",\n\ttransform: \"translateX(-50%)\",\n\tzIndex: 2147483646,\n\tdisplay: \"flex\",\n\tflexDirection: \"column\",\n\tgap: 8,\n\tpadding: \"12px 16px\",\n\twidth: \"min(420px, calc(100vw - 32px))\",\n\tbackground: \"white\",\n\tborder: \"1px solid #e5e7eb\",\n\tborderRadius: 12,\n\tboxShadow: \"0 12px 32px rgba(0,0,0,0.18)\",\n\tfontFamily:\n\t\t'-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\"',\n\tcolor: \"#111\",\n\tpointerEvents: \"auto\",\n};\n\nconst submittedHeaderStyle: CSSProperties = {\n\tdisplay: \"flex\",\n\talignItems: \"center\",\n\tgap: 8,\n};\n\nconst submittedBadgeStyle: CSSProperties = {\n\tbackground: \"#d1fae5\",\n\tcolor: \"#065f46\",\n\tfontSize: 11,\n\tfontWeight: 700,\n\tpadding: \"2px 8px\",\n\tborderRadius: 999,\n\ttextTransform: \"uppercase\",\n\tletterSpacing: 0.5,\n};\n\nconst submittedProgressLabelStyle: CSSProperties = {\n\tdisplay: \"flex\",\n\tjustifyContent: \"space-between\",\n\tfontSize: 12,\n};\n\nconst submittedProgressTrackStyle: CSSProperties = {\n\twidth: \"100%\",\n\theight: 6,\n\tbackground: \"#f3f4f6\",\n\tborderRadius: 999,\n\toverflow: \"hidden\",\n};\n\nconst submittedProgressFillStyle: CSSProperties = {\n\theight: \"100%\",\n\tbackground: \"#10b981\",\n\tborderRadius: 999,\n\ttransition: \"width 500ms\",\n};\n\nconst submittedEtaStyle: CSSProperties = {\n\tmargin: 0,\n\tfontSize: 12,\n\tcolor: \"#6b7280\",\n};\n\nexport function mountFeedbackToolbar(): { unmount: () => void } | null {\n\tconsole.log(\"[YNS Feedback Toolbar] mountFeedbackToolbar() called\", {\n\t\tisWindow: typeof window !== \"undefined\",\n\t\tvercelEnv: process.env.NEXT_PUBLIC_VERCEL_ENV,\n\t\talreadyMounted: typeof document !== \"undefined\" && Boolean(document.getElementById(MOUNT_NODE_ID)),\n\t});\n\tif (typeof window === \"undefined\") return null;\n\tif (process.env.NEXT_PUBLIC_VERCEL_ENV !== \"preview\") {\n\t\tconsole.log(\n\t\t\t\"[YNS Feedback Toolbar] gate failed — NEXT_PUBLIC_VERCEL_ENV is\",\n\t\t\tprocess.env.NEXT_PUBLIC_VERCEL_ENV,\n\t\t);\n\t\treturn null;\n\t}\n\tif (document.getElementById(MOUNT_NODE_ID)) return null;\n\n\tconst container = document.createElement(\"div\");\n\tcontainer.id = MOUNT_NODE_ID;\n\tcontainer.dataset.ynsFeedbackUi = \"true\";\n\tdocument.body.appendChild(container);\n\n\tconst root = createRoot(container);\n\troot.render(<FeedbackToolbar />);\n\n\treturn {\n\t\tunmount: () => {\n\t\t\troot.unmount();\n\t\t\tcontainer.remove();\n\t\t},\n\t};\n}\n\n// Auto-mount on import. Wait for DOM ready.\nif (typeof window !== \"undefined\") {\n\tconsole.log(\"[YNS Feedback Toolbar] module evaluated\", {\n\t\tvercelEnv: process.env.NEXT_PUBLIC_VERCEL_ENV,\n\t\treadyState: document.readyState,\n\t\twillAutoMount: process.env.NEXT_PUBLIC_VERCEL_ENV === \"preview\",\n\t});\n}\nif (typeof window !== \"undefined\" && process.env.NEXT_PUBLIC_VERCEL_ENV === \"preview\") {\n\tif (document.readyState === \"loading\") {\n\t\tdocument.addEventListener(\"DOMContentLoaded\", () => {\n\t\t\tmountFeedbackToolbar();\n\t\t});\n\t} else {\n\t\tmountFeedbackToolbar();\n\t}\n}\n"],"mappings":"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","names":["useEffect","useRef","useState","createRoot","Fragment","jsx","jsxs","MOUNT_NODE_ID","buildApiBase","override","host","protocol","computeCssSelector","el","path","node","part","className","classes","parent","tag","siblings","idx","ATTRS_OF_INTEREST","formatAttrs","parts","attr","v","trimmed","formatTextContent","text","buildSurroundingHtml","target","ancestors","cur","lines","depth","ancestor","indent","targetIndent","targetTag","targetText","childIndent","children","child","childText","i","isInsideToolbar","IGNORED_TAGS","POLL_INTERVAL_MS","formatEta","etaIso","eta","remainingMs","dateLabel","remainingMin","remainingHours","remainingDays","FeedbackToolbar","session","setSession","loading","setLoading","pinMode","setPinMode","pending","setPending","editingId","setEditingId","sidebarOpen","setSidebarOpen","finalizing","setFinalizing","apiBase","cancelled","controller","fetchOnce","res","data","interval","overlay","label","hovered","handleMove","e","rect","handleLeave","handleClick","event","offsetXRatio","offsetYRatio","refreshComments","submitNewComment","content","updateComment","id","removeComment","finalizeSession","prev","scrollToComment","comment","SubmittedPanel","visiblePins","c","pin","PinOverlay","PendingCommentPopover","CommentsSidebar","toolbarStyle","toolbarButtonActiveStyle","toolbarButtonStyle","toolbarButtonGhostStyle","toolbarButtonFinalizeStyle","toolbarHintStyle","progress","status","setTick","t","submittedPanelStyle","spinnerKeyframes","submittedHeaderStyle","Spinner","submittedBadgeStyle","submittedProgressLabelStyle","submittedProgressTrackStyle","submittedProgressFillStyle","submittedEtaStyle","size","number","editing","onStartEdit","onCancelEdit","onSave","onRemove","useTargetPosition","pinDotStyle","EditPopover","onCancel","comments","currentPath","onClose","onSelect","groups","list","paths","a","b","sidebarStyle","sidebarHeaderStyle","ghostButtonStyle","sidebarScrollStyle","sidebarEmptyStyle","sidebarPathStyle","sidebarItemStyle","sidebarItemIndexStyle","sidebarItemTextStyle","sidebarItemDoneStyle","initial","value","setValue","saving","setSaving","textareaRef","len","popoverStyle","textareaStyle","popoverActionsStyle","dangerButtonStyle","primaryButtonStyle","selector","pos","setPos","update","r","ro","baseButton","mountFeedbackToolbar","container","root"]}
|