commerce-kit 0.36.2 → 0.36.4
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
|
|
2
|
-
`)},X=e=>{let n=e;for(;n;){if(n instanceof HTMLElement&&n.dataset.ynsFeedbackUi==="true")return!0;n=n.parentElement}return!1};function D(){let[e,n]=v(null),[o,t]=v(!0),[i,r]=v(!1),[d,c]=v(null),[l,f]=v(null),u=M(null);C(()=>{if(u.current=B(),!u.current){t(!1);return}let s=new AbortController;return fetch(`${u.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,{credentials:"include",signal:s.signal}).then(async a=>{if(!a.ok){n(null);return}let y=await a.json();n(y)}).catch(()=>{n(null)}).finally(()=>t(!1)),()=>s.abort()},[]),C(()=>{if(i)return document.body.style.cursor="crosshair",()=>{document.body.style.cursor=""}},[i]),C(()=>{if(!i)return;let s=a=>{let y=a.target;if(!(y instanceof Element)||X(y))return;a.preventDefault(),a.stopPropagation();let S=y.getBoundingClientRect();c({cssSelector:F(y),pagePath:window.location.pathname,surroundingHtml:j(y),rect:{top:S.top+window.scrollY,left:S.left+window.scrollX,width:S.width,height:S.height},clickX:a.clientX+window.scrollX,clickY:a.clientY+window.scrollY}),r(!1)};return document.addEventListener("click",s,{capture:!0}),()=>document.removeEventListener("click",s,{capture:!0})},[i]);let h=async()=>{if(!u.current)return;let s=await fetch(`${u.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,{credentials:"include"});if(!s.ok)return;let a=await s.json();n(a)},w=async s=>{!u.current||!e||!d||!(await fetch(`${u.current}/api/feedback-comments`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({feedbackSessionId:e.feedbackSessionId,content:s,pagePath:d.pagePath,cssSelector:d.cssSelector,surroundingHtml:d.surroundingHtml})})).ok||(c(null),await h())},g=async(s,a)=>{!u.current||!(await fetch(`${u.current}/api/feedback-comments/${s}`,{method:"PATCH",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({content:a})})).ok||(f(null),await h())},m=async s=>{!u.current||!(await fetch(`${u.current}/api/feedback-comments/${s}`,{method:"DELETE",credentials:"include"})).ok||await h()};if(o||!e||!e.canComment)return null;let p=e.comments.filter(s=>s.pagePath===window.location.pathname&&s.status!=="done");return E("div",{"data-yns-feedback-ui":"true",children:[p.map((s,a)=>b(U,{pin:s,number:a+1,editing:l===s.id,onStartEdit:()=>f(s.id),onCancelEdit:()=>f(null),onSave:y=>g(s.id,y),onRemove:()=>m(s.id)},s.id)),d&&b(Y,{pending:d,onCancel:()=>c(null),onSave:s=>w(s)}),E("div",{style:V,children:[b("button",{type:"button",onClick:()=>r(s=>!s),style:i?H:T,children:i?"Cancel":"Add comment"}),b("span",{style:W,children:i?"Click any element to comment":`${p.length} on this page`})]})]})}function U({pin:e,number:n,editing:o,onStartEdit:t,onCancelEdit:i,onSave:r,onRemove:d}){let c=O(e.cssSelector);return c?E("div",{style:{position:"absolute",top:c.top,left:c.left,zIndex:2147483600,pointerEvents:"auto"},children:[b("button",{type:"button",onClick:t,style:q,title:e.content,children:n}),o&&b(N,{initial:e.content,onCancel:i,onSave:r,onRemove:d})]}):null}function Y({pending:e,onCancel:n,onSave:o}){return b("div",{style:{position:"absolute",top:e.clickY+10,left:e.clickX+10,zIndex:2147483647},children:b(N,{initial:"",onCancel:n,onSave:o})})}function N({initial:e,onCancel:n,onSave:o,onRemove:t}){let[i,r]=v(e),[d,c]=v(!1);return E("form",{onSubmit:async f=>{f.preventDefault();let u=i.trim();if(u){c(!0);try{await o(u)}finally{c(!1)}}},style:K,children:[b("textarea",{value:i,onChange:f=>r(f.target.value),placeholder:"Leave a comment\u2026",style:G,rows:3}),E("div",{style:J,children:[t&&b("button",{type:"button",onClick:()=>t(),style:ee,disabled:d,children:"Delete"}),b("div",{style:{flex:1}}),b("button",{type:"button",onClick:n,style:Z,disabled:d,children:"Cancel"}),b("button",{type:"submit",style:Q,disabled:d||!i.trim(),children:d?"Saving\u2026":"Save"})]})]})}function O(e){let[n,o]=v(null);return C(()=>{let t=()=>{let r=null;try{r=document.querySelector(e)}catch{r=null}if(!r){o(null);return}let d=r.getBoundingClientRect();o({top:d.top+window.scrollY-12,left:d.left+window.scrollX-12})};t();let i=new ResizeObserver(t);try{let r=document.querySelector(e);r&&i.observe(r)}catch{}return window.addEventListener("scroll",t,!0),window.addEventListener("resize",t),()=>{i.disconnect(),window.removeEventListener("scroll",t,!0),window.removeEventListener("resize",t)}},[e]),n}var V={position:"fixed",bottom:16,left:"50%",transform:"translateX(-50%)",zIndex:2147483646,display:"flex",alignItems:"center",gap:12,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"},T={border:"none",background:"white",color:"#111",padding:"6px 12px",borderRadius:999,cursor:"pointer",fontWeight:600,fontSize:13},H={...T,background:"#ef4444",color:"white"},W={opacity:.8,fontSize:12},q={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},K={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"},G={width:"100%",border:"1px solid #d1d5db",borderRadius:6,padding:8,fontSize:14,resize:"vertical",fontFamily:"inherit",color:"#111",background:"white",boxSizing:"border-box"},J={display:"flex",alignItems:"center",gap:6},$={border:"1px solid transparent",borderRadius:6,padding:"6px 10px",fontSize:13,cursor:"pointer",fontWeight:500},Q={...$,background:"#111",color:"white"},Z={...$,background:"transparent",color:"#374151",borderColor:"#d1d5db"},ee={...$,background:"transparent",color:"#b91c1c",borderColor:"#fecaca"};function I(){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(k)}),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(k))return null;let e=document.createElement("div");e.id=k,e.dataset.ynsFeedbackUi="true",document.body.appendChild(e);let n=_(e);return n.render(b(D,{})),{unmount:()=>{n.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",()=>{I()}):I());function A(){typeof window>"u"||(console.log("[YNS Sandbox Inspectors] module evaluated"),oe(),ie())}A();var te=new Set(["HEAD","SCRIPT","STYLE","NOSCRIPT","HTML"]);function ne(e){if(e.id)return`${e.tagName.toLowerCase()}#${CSS.escape(e.id)}`;let n=[],o=e;for(;o&&o!==document.body&&o!==document.documentElement;){let t=o.tagName.toLowerCase();if(o.id){n.unshift(`${t}#${CSS.escape(o.id)}`);break}let i=Array.from(o.classList).filter(c=>!c.startsWith("data-yns-"));i.length>0&&(t+=`.${i.map(c=>CSS.escape(c)).join(".")}`);let r=o.parentElement,d=o.tagName;if(r&&Array.from(r.children).filter(l=>l.tagName===d&&(i.length===0||i.every(f=>l.classList.contains(f)))).length>1){let l=Array.from(r.children).indexOf(o)+1;t+=`:nth-child(${l})`}n.unshift(t),o=o.parentElement}return n.join(" > ")}function R(e,n){let o=e.getBoundingClientRect(),t=window.getComputedStyle(e),i=(e.textContent??"").trim();return{tag:e.tagName.toLowerCase(),id:e.id||void 0,classes:Array.from(e.classList).filter(r=>!r.startsWith("data-yns-")),textContent:i.length>n?`${i.slice(0,n)}\u2026`:i,cssSelector:ne(e),boundingRect:{top:o.top,left:o.left,width:o.width,height:o.height},computedStyles:{color:t.color,backgroundColor:t.backgroundColor,fontSize:t.fontSize,fontFamily:t.fontFamily,padding:t.padding,margin:t.margin}}}function x(e,n){if(!e||!e.tagName||te.has(e.tagName))return!0;for(let o of n)if(e.hasAttribute?.(o))return!0;return!1}function oe(){let e=!1,n=null,o=null,t=document.createElement("div");t.setAttribute("data-yns-design-overlay","hover"),t.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(t),document.documentElement.appendChild(i);let r=document.createElement("style");r.setAttribute("data-yns-design-overlay","style"),r.textContent="[data-yns-selected] { outline: 2px solid #3b82f6 !important; outline-offset: 1px; }",document.head.appendChild(r);function d(g){let m=g.getBoundingClientRect();t.style.top=`${m.top}px`,t.style.left=`${m.left}px`,t.style.width=`${m.width}px`,t.style.height=`${m.height}px`,t.style.display="block";let p=Array.from(g.classList).filter(a=>!a.startsWith("data-yns-")),s=g.tagName.toLowerCase()+(p.length?`.${p[0]}`:"");i.textContent=s,i.style.left=`${m.left}px`,i.style.top=`${Math.max(0,m.top-22)}px`,i.style.display="block"}let c=g=>{let m=document.elementFromPoint(g.clientX,g.clientY);if(x(m,["data-yns-design-overlay"])){t.style.display="none",i.style.display="none",n=null;return}n=m,requestAnimationFrame(()=>{n===m&&e&&m&&d(m)})},l=()=>{t.style.display="none",i.style.display="none",n=null},f=g=>{if(!e||!n)return;g.preventDefault(),g.stopPropagation(),g.stopImmediatePropagation();let m=n;if(x(m,["data-yns-design-overlay"]))return;let p=R(m,120),s=p.cssSelector;o&&o.el.removeAttribute("data-yns-selected"),o&&o.cssSelector===s?(o=null,window.parent.postMessage({type:"element-deselected",data:p},"*")):(o={el:m,cssSelector:s},m.setAttribute("data-yns-selected",""),window.parent.postMessage({type:"element-selected",data:p},"*"))},u=g=>{g.key==="Escape"&&e&&(w(),window.parent.postMessage({type:"design-mode-cleared"},"*"))};function h(){e=!0,document.addEventListener("mousemove",c,!0),document.addEventListener("mouseleave",l),document.addEventListener("click",f,!0),document.addEventListener("keydown",u,!0)}function w(){e=!1,document.removeEventListener("mousemove",c,!0),document.removeEventListener("mouseleave",l),document.removeEventListener("click",f,!0),document.removeEventListener("keydown",u,!0),t.style.display="none",i.style.display="none",n=null,o&&(o.el.removeAttribute("data-yns-selected"),o=null)}window.addEventListener("message",g=>{let m=g.data;!m||typeof m!="object"||(m.type==="design-mode-toggle"&&(m.enabled?h():(w(),window.parent.postMessage({type:"design-mode-cleared"},"*"))),m.type==="design-mode-deselect"&&o&&(o.el.removeAttribute("data-yns-selected"),o=null))})}function ie(){let e=!1,n=null,o=[],t=document.createElement("div");t.setAttribute("data-yns-comment-overlay","hover"),t.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 r=document.createElement("div");r.setAttribute("data-yns-comment-overlay","pins"),r.style.cssText="position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 2147483643;",document.documentElement.appendChild(t),document.documentElement.appendChild(i),document.documentElement.appendChild(r);function d(){r.innerHTML="";for(let p of o){let s=null;try{s=document.querySelector(p.selector)}catch{s=null}if(!s)continue;let a=s.getBoundingClientRect(),y=document.createElement("div");y.style.cssText=["position: fixed",`top: ${a.top-12}px`,`left: ${a.left+a.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(";"),y.textContent=String(p.number),r.appendChild(y)}}let c=p=>{let s=document.elementFromPoint(p.clientX,p.clientY);if(x(s,["data-yns-comment-overlay","data-yns-design-overlay"])){t.style.display="none",i.style.display="none",n=null;return}n=s,requestAnimationFrame(()=>{if(n===s&&e&&s){let a=s.getBoundingClientRect();t.style.top=`${a.top}px`,t.style.left=`${a.left}px`,t.style.width=`${a.width}px`,t.style.height=`${a.height}px`,t.style.display="block",i.style.left=`${p.clientX+14}px`,i.style.top=`${p.clientY+14}px`,i.style.display="block"}})},l=()=>{t.style.display="none",i.style.display="none",n=null},f=p=>{if(!e||!n)return;p.preventDefault(),p.stopPropagation(),p.stopImmediatePropagation();let s=n;if(x(s,["data-yns-comment-overlay","data-yns-design-overlay"]))return;let a=R(s,200),y=s.getBoundingClientRect();window.parent.postMessage({type:"comment-click",data:{element:a,clickPosition:{x:p.clientX,y:p.clientY},elementRect:{top:y.top,left:y.left,width:y.width,height:y.height},pagePath:window.location.pathname}},"*")},u=p=>{p.key==="Escape"&&e&&(w(),window.parent.postMessage({type:"comment-mode-cleared"},"*"))};function h(){e=!0,document.body.style.cursor="crosshair",document.addEventListener("mousemove",c,!0),document.addEventListener("mouseleave",l),document.addEventListener("click",f,!0),document.addEventListener("keydown",u,!0)}function w(){e=!1,document.body.style.cursor="",document.removeEventListener("mousemove",c,!0),document.removeEventListener("mouseleave",l),document.removeEventListener("click",f,!0),document.removeEventListener("keydown",u,!0),t.style.display="none",i.style.display="none",n=null}let g=!1,m=()=>{g||(g=!0,requestAnimationFrame(()=>{g=!1,d()}))};window.addEventListener("scroll",m,{passive:!0}),window.addEventListener("resize",m,{passive:!0}),window.addEventListener("message",p=>{let s=p.data;!s||typeof s!="object"||(s.type==="comment-mode-toggle"&&(s.enabled?h():w()),s.type==="comment-pins-update"&&(o=s.pins??[],d()),s.type==="comment-pin-remove"&&(o=o.filter(a=>a.id!==s.pinId),d()))})}export{I as mountFeedbackToolbar,A as startSandboxInspectors};
|
|
1
|
+
import{useEffect as $,useRef as W,useState as E}from"react";import{createRoot as q}from"react-dom/client";import{Fragment as Ce,jsx as f,jsxs as w}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(a=>`.${CSS.escape(a)}`).join("");o+=s}let c=n.parentElement,l=n.tagName;if(c){let s=Array.from(c.children).filter(a=>a.tagName===l);if(s.length>1){let a=s.indexOf(n)+1;o+=`:nth-of-type(${a})`}}t.unshift(o),n=c}return t.join(" > ")},J=["id","class","data-testid","aria-label","role","name","href","src"],N=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 a=n.parentElement;if(!a)break;t.unshift(a),n=a}let o=[],i=0;for(let a of t){let g=" ".repeat(i);o.push(`${g}<${a.tagName.toLowerCase()}${N(a)}>`),i++}let c=" ".repeat(i),l=e.tagName.toLowerCase(),s=F(e);if(o.push(`${c}<${l}${N(e)}>${s?`${s}</${l}>`:""} \u2190 TARGET`),!s){let a=" ".repeat(i+1),g=Array.from(e.children).slice(0,6);for(let b of g){let C=F(b);o.push(`${a}<${b.tagName.toLowerCase()}${N(b)}>${C?`${C}</${b.tagName.toLowerCase()}>`:""}`)}e.children.length>6&&o.push(`${a}\u2026 (${e.children.length-6} more children)`),o.push(`${c}</${l}>`)}for(let a=t.length-1;a>=0;a--){let g=" ".repeat(a),b=t[a];b&&o.push(`${g}</${b.tagName.toLowerCase()}>`)}return o.join(`
|
|
2
|
+
`)},B=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"]);function ee(){let[e,t]=E(null),[n,o]=E(!0),[i,c]=E(!1),[l,s]=E(null),[a,g]=E(null),[b,C]=E(!1),[k,y]=E(!1),d=W(null);$(()=>{if(d.current=G(),!d.current){o(!1);return}let r=new AbortController;return fetch(`${d.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,{credentials:"include",signal:r.signal}).then(async u=>{if(!u.ok){t(null);return}let v=await u.json();t(v)}).catch(()=>{t(null)}).finally(()=>o(!1)),()=>r.abort()},[]),$(()=>{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 v=null,S=P=>{let L=document.elementFromPoint(P.clientX,P.clientY);if(!L||Z.has(L.tagName)||B(L)){r.style.display="none",u.style.display="none",v=null;return}v=L,requestAnimationFrame(()=>{if(v!==L)return;let R=L.getBoundingClientRect();r.style.top=`${R.top}px`,r.style.left=`${R.left}px`,r.style.width=`${R.width}px`,r.style.height=`${R.height}px`,r.style.display="block",u.style.left=`${P.clientX+14}px`,u.style.top=`${P.clientY+14}px`,u.style.display="block"})},I=()=>{r.style.display="none",u.style.display="none",v=null};return document.addEventListener("mousemove",S,!0),document.addEventListener("mouseleave",I),()=>{document.removeEventListener("mousemove",S,!0),document.removeEventListener("mouseleave",I),r.remove(),u.remove()}},[i]),$(()=>{if(!i)return;let r=u=>{let v=u.target;if(!(v instanceof Element)||B(v))return;u.preventDefault(),u.stopPropagation();let S=v.getBoundingClientRect(),I=S.width>0?(u.clientX-S.left)/S.width:.5,P=S.height>0?(u.clientY-S.top)/S.height:.5;s({cssSelector:K(v),pagePath:window.location.pathname,surroundingHtml:Q(v),rect:{top:S.top+window.scrollY,left:S.left+window.scrollX,width:S.width,height:S.height},clickX:u.clientX+window.scrollX,clickY:u.clientY+window.scrollY,offsetXRatio:Math.min(1,Math.max(0,I)),offsetYRatio:Math.min(1,Math.max(0,P))}),c(!1)};return document.addEventListener("click",r,{capture:!0}),()=>document.removeEventListener("click",r,{capture:!0})},[i]);let m=async()=>{if(!d.current)return;let r=await fetch(`${d.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,{credentials:"include"});if(!r.ok)return;let u=await r.json();t(u)},p=async r=>{!d.current||!e||!l||!(await fetch(`${d.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),await m())},h=async(r,u)=>{!d.current||!(await fetch(`${d.current}/api/feedback-comments/${r}`,{method:"PATCH",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({content:u})})).ok||(g(null),await m())},x=async r=>{!d.current||!(await fetch(`${d.current}/api/feedback-comments/${r}`,{method:"DELETE",credentials:"include"})).ok||await m()},H=async()=>{if(!(!d.current||!e)&&window.confirm("Finalize this feedback session? You won't be able to add more comments.")){y(!0);try{if(!(await fetch(`${d.current}/api/feedback-sessions/${e.feedbackSessionId}/finalize`,{method:"POST",credentials:"include"})).ok)return;t(u=>u&&{...u,canComment:!1})}finally{y(!1)}}},V=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"}),g(r.id))};if(n||!e||!e.canComment)return null;let M=e.comments.filter(r=>r.pagePath===window.location.pathname&&r.status!=="done");return w("div",{"data-yns-feedback-ui":"true",children:[M.map((r,u)=>f(te,{pin:r,number:u+1,editing:a===r.id,onStartEdit:()=>g(r.id),onCancelEdit:()=>g(null),onSave:v=>h(r.id,v),onRemove:()=>x(r.id)},r.id)),l&&f(ne,{pending:l,onCancel:()=>s(null),onSave:r=>p(r)}),b&&f(oe,{comments:e.comments,currentPath:window.location.pathname,onClose:()=>C(!1),onSelect:V}),w("div",{style:se,children:[f("button",{type:"button",onClick:()=>c(r=>!r),style:i?re:Y,children:i?"Cancel":"Add comment"}),f("button",{type:"button",onClick:()=>C(r=>!r),style:le,children:b?"Hide list":`List (${e.comments.length})`}),f("button",{type:"button",onClick:H,disabled:k,style:ae,children:k?"Finalizing\u2026":"Finalize"}),f("span",{style:de,children:i?"Click any element to comment":`${M.length} on this page`})]})]})}function te({pin:e,number:t,editing:n,onStartEdit:o,onCancelEdit:i,onSave:c,onRemove:l}){let s=ie(e.cssSelector,e.offsetXRatio,e.offsetYRatio);return s?w("div",{style:{position:"absolute",top:s.top,left:s.left,zIndex:2147483600,pointerEvents:"auto"},children:[f("button",{type:"button",onClick:o,style:j,title:e.content,children:t}),n&&f(X,{initial:e.content,onCancel:i,onSave:c,onRemove:l})]}):null}function ne({pending:e,onCancel:t,onSave:n}){return w(Ce,{children:[f("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:f("div",{style:j,children:"\u2022"})}),f("div",{style:{position:"absolute",top:e.clickY+10,left:e.clickX+10,zIndex:2147483647},children:f(X,{initial:"",onCancel:t,onSave:n})})]})}function oe({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 c=Array.from(i.keys()).sort((l,s)=>l===t?-1:s===t?1:l.localeCompare(s));return w("div",{style:ge,children:[w("div",{style:ye,children:[w("strong",{style:{fontSize:14},children:["Comments (",e.length,")"]}),f("button",{type:"button",onClick:n,style:D,children:"Close"})]}),f("div",{style:be,children:e.length===0?f("div",{style:he,children:"No comments yet. Click \u201CAdd comment\u201D to leave one."}):c.map(l=>w("div",{style:{marginBottom:12},children:[f("div",{style:ve,children:l===t?`${l} \xB7 current`:l}),(i.get(l)??[]).map((s,a)=>w("button",{type:"button",onClick:()=>o(s),style:Se,disabled:s.status==="done"&&!1,children:[f("span",{style:we,children:a+1}),f("span",{style:xe,children:s.content.length>140?`${s.content.slice(0,140)}\u2026`:s.content}),s.status==="done"&&f("span",{style:Ee,children:"done"})]},s.id))]},l))})]})}function X({initial:e,onCancel:t,onSave:n,onRemove:o}){let[i,c]=E(e),[l,s]=E(!1);return w("form",{onSubmit:async g=>{g.preventDefault();let b=i.trim();if(b){s(!0);try{await n(b)}finally{s(!1)}}},style:ce,children:[f("textarea",{value:i,onChange:g=>c(g.target.value),placeholder:"Leave a comment\u2026",style:ue,rows:3}),w("div",{style:pe,children:[o&&f("button",{type:"button",onClick:()=>o(),style:fe,disabled:l,children:"Delete"}),f("div",{style:{flex:1}}),f("button",{type:"button",onClick:t,style:D,disabled:l,children:"Cancel"}),f("button",{type:"submit",style:me,disabled:l||!i.trim(),children:l?"Saving\u2026":"Save"})]})]})}function ie(e,t,n){let[o,i]=E(null);return $(()=>{let c=()=>{let s=null;try{s=document.querySelector(e)}catch{s=null}if(!s){i(null);return}let a=s.getBoundingClientRect();i({top:a.top+window.scrollY+a.height*n-12,left:a.left+window.scrollX+a.width*t-12})};c();let l=new ResizeObserver(c);try{let s=document.querySelector(e);s&&l.observe(s)}catch{}return window.addEventListener("scroll",c,!0),window.addEventListener("resize",c),()=>{l.disconnect(),window.removeEventListener("scroll",c,!0),window.removeEventListener("resize",c)}},[e,t,n]),o}var se={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},re={...Y,background:"#ef4444",color:"white"},le={border:"1px solid rgba(255,255,255,0.4)",background:"transparent",color:"white",padding:"6px 12px",borderRadius:999,cursor:"pointer",fontWeight:500,fontSize:13},ae={border:"none",background:"#10b981",color:"white",padding:"6px 12px",borderRadius:999,cursor:"pointer",fontWeight:600,fontSize:13},de={opacity:.8,fontSize:12},j={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},ce={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},z={border:"1px solid transparent",borderRadius:6,padding:"6px 10px",fontSize:13,cursor:"pointer",fontWeight:500},me={...z,background:"#111",color:"white"},D={...z,background:"transparent",color:"#374151",borderColor:"#d1d5db"},fe={...z,background:"transparent",color:"#b91c1c",borderColor:"#fecaca"},ge={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"},ye={display:"flex",alignItems:"center",justifyContent:"space-between",padding:"12px 16px",borderBottom:"1px solid #e5e7eb"},be={flex:1,overflow:"auto",padding:"12px 12px 24px"},he={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"},Se={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"},we={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"},Ee={flexShrink:0,background:"#d1fae5",color:"#065f46",fontSize:11,fontWeight:600,padding:"2px 6px",borderRadius:4,alignSelf:"center"};function _(){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(f(ee,{})),{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",()=>{_()}):_());function U(){typeof window>"u"||(console.log("[YNS Sandbox Inspectors] module evaluated"),Le(),$e())}U();var ke=new Set(["HEAD","SCRIPT","STYLE","NOSCRIPT","HTML"]);function Pe(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 c=n.parentElement,l=n.tagName;if(c&&Array.from(c.children).filter(a=>a.tagName===l&&(i.length===0||i.every(g=>a.classList.contains(g)))).length>1){let a=Array.from(c.children).indexOf(n)+1;o+=`:nth-child(${a})`}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(c=>!c.startsWith("data-yns-")),textContent:i.length>t?`${i.slice(0,t)}\u2026`:i,cssSelector:Pe(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||ke.has(e.tagName))return!0;for(let n of t)if(e.hasAttribute?.(n))return!0;return!1}function Le(){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 c=document.createElement("style");c.setAttribute("data-yns-design-overlay","style"),c.textContent="[data-yns-selected] { outline: 2px solid #3b82f6 !important; outline-offset: 1px; }",document.head.appendChild(c);function l(y){let d=y.getBoundingClientRect();o.style.top=`${d.top}px`,o.style.left=`${d.left}px`,o.style.width=`${d.width}px`,o.style.height=`${d.height}px`,o.style.display="block";let m=Array.from(y.classList).filter(h=>!h.startsWith("data-yns-")),p=y.tagName.toLowerCase()+(m.length?`.${m[0]}`:"");i.textContent=p,i.style.left=`${d.left}px`,i.style.top=`${Math.max(0,d.top-22)}px`,i.style.display="block"}let s=y=>{let d=document.elementFromPoint(y.clientX,y.clientY);if(T(d,["data-yns-design-overlay"])){o.style.display="none",i.style.display="none",t=null;return}t=d,requestAnimationFrame(()=>{t===d&&e&&d&&l(d)})},a=()=>{o.style.display="none",i.style.display="none",t=null},g=y=>{if(!e||!t)return;y.preventDefault(),y.stopPropagation(),y.stopImmediatePropagation();let d=t;if(T(d,["data-yns-design-overlay"]))return;let m=O(d,120),p=m.cssSelector;n&&n.el.removeAttribute("data-yns-selected"),n&&n.cssSelector===p?(n=null,window.parent.postMessage({type:"element-deselected",data:m},"*")):(n={el:d,cssSelector:p},d.setAttribute("data-yns-selected",""),window.parent.postMessage({type:"element-selected",data:m},"*"))},b=y=>{y.key==="Escape"&&e&&(k(),window.parent.postMessage({type:"design-mode-cleared"},"*"))};function C(){e=!0,document.addEventListener("mousemove",s,!0),document.addEventListener("mouseleave",a),document.addEventListener("click",g,!0),document.addEventListener("keydown",b,!0)}function k(){e=!1,document.removeEventListener("mousemove",s,!0),document.removeEventListener("mouseleave",a),document.removeEventListener("click",g,!0),document.removeEventListener("keydown",b,!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 d=y.data;!d||typeof d!="object"||(d.type==="design-mode-toggle"&&(d.enabled?C():(k(),window.parent.postMessage({type:"design-mode-cleared"},"*"))),d.type==="design-mode-deselect"&&n&&(n.el.removeAttribute("data-yns-selected"),n=null))})}function $e(){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 c=document.createElement("div");c.setAttribute("data-yns-comment-overlay","pins"),c.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(c);function l(){c.innerHTML="";for(let m of n){let p=null;try{p=document.querySelector(m.selector)}catch{p=null}if(!p)continue;let h=p.getBoundingClientRect(),x=document.createElement("div");x.style.cssText=["position: fixed",`top: ${h.top-12}px`,`left: ${h.left+h.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(";"),x.textContent=String(m.number),c.appendChild(x)}}let s=m=>{let p=document.elementFromPoint(m.clientX,m.clientY);if(T(p,["data-yns-comment-overlay","data-yns-design-overlay"])){o.style.display="none",i.style.display="none",t=null;return}t=p,requestAnimationFrame(()=>{if(t===p&&e&&p){let h=p.getBoundingClientRect();o.style.top=`${h.top}px`,o.style.left=`${h.left}px`,o.style.width=`${h.width}px`,o.style.height=`${h.height}px`,o.style.display="block",i.style.left=`${m.clientX+14}px`,i.style.top=`${m.clientY+14}px`,i.style.display="block"}})},a=()=>{o.style.display="none",i.style.display="none",t=null},g=m=>{if(!e||!t)return;m.preventDefault(),m.stopPropagation(),m.stopImmediatePropagation();let p=t;if(T(p,["data-yns-comment-overlay","data-yns-design-overlay"]))return;let h=O(p,200),x=p.getBoundingClientRect();window.parent.postMessage({type:"comment-click",data:{element:h,clickPosition:{x:m.clientX,y:m.clientY},elementRect:{top:x.top,left:x.left,width:x.width,height:x.height},pagePath:window.location.pathname}},"*")},b=m=>{m.key==="Escape"&&e&&(k(),window.parent.postMessage({type:"comment-mode-cleared"},"*"))};function C(){e=!0,document.body.style.cursor="crosshair",document.addEventListener("mousemove",s,!0),document.addEventListener("mouseleave",a),document.addEventListener("click",g,!0),document.addEventListener("keydown",b,!0)}function k(){e=!1,document.body.style.cursor="",document.removeEventListener("mousemove",s,!0),document.removeEventListener("mouseleave",a),document.removeEventListener("click",g,!0),document.removeEventListener("keydown",b,!0),o.style.display="none",i.style.display="none",t=null}let y=!1,d=()=>{y||(y=!0,requestAnimationFrame(()=>{y=!1,l()}))};window.addEventListener("scroll",d,{passive:!0}),window.addEventListener("resize",d,{passive:!0}),window.addEventListener("message",m=>{let p=m.data;!p||typeof p!="object"||(p.type==="comment-mode-toggle"&&(p.enabled?C():k()),p.type==="comment-pins-update"&&(n=p.pins??[],l()),p.type==="comment-pin-remove"&&(n=n.filter(h=>h.id!==p.pinId),l()))})}export{_ as mountFeedbackToolbar,U 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}\n\ninterface ActiveSession {\n\tfeedbackSessionId: string;\n\tcomments: FeedbackComment[];\n\tcanComment: boolean;\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}\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 the per-store preview deploy (e.g. mystore-preview.yns.store)\n\t// which is served by the merchant's own Vercel project — that host does NOT\n\t// host the YNS API. Strip the `-preview` suffix so the call lands on the\n\t// multi-tenant YNS app (mystore.yns.store) where the API lives. Same eTLD+1\n\t// means better-auth cookies scoped to `.yns.store` travel along.\n\tconst host = window.location.host;\n\tconst protocol = window.location.protocol;\n\tconst dotIdx = host.indexOf(\".\");\n\tif (dotIdx > 0) {\n\t\tconst sub = host.slice(0, dotIdx);\n\t\tif (sub.endsWith(\"-preview\")) {\n\t\t\tconst stripped = sub.slice(0, -\"-preview\".length);\n\t\t\treturn `${protocol}//${stripped}${host.slice(dotIdx)}`;\n\t\t}\n\t}\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\n/**\n * Build a compact HTML-like outline of the clicked element + its ancestors and a\n * sample of nearby children. Marks the target with `← TARGET` so the AI agent\n * can locate it later. Output stays small (< 2KB typical).\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\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 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\tconst controller = new AbortController();\n\t\tfetch(`${apiBase.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`, {\n\t\t\tcredentials: \"include\",\n\t\t\tsignal: controller.signal,\n\t\t})\n\t\t\t.then(async (res) => {\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\tsetSession(data);\n\t\t\t})\n\t\t\t.catch(() => {\n\t\t\t\tsetSession(null);\n\t\t\t})\n\t\t\t.finally(() => setLoading(false));\n\n\t\treturn () => controller.abort();\n\t}, []);\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\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\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});\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}),\n\t\t});\n\t\tif (!res.ok) return;\n\t\tsetPending(null);\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\tif (loading || !session || !session.canComment) return null;\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={() => setPending(null)}\n\t\t\t\t\tonSave={(content) => submitNewComment(content)}\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<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 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 = useTargetRect(pin.cssSelector);\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<div\n\t\t\tstyle={{\n\t\t\t\tposition: \"absolute\",\n\t\t\t\ttop: pending.clickY + 10,\n\t\t\t\tleft: pending.clickX + 10,\n\t\t\t\tzIndex: 2147483647,\n\t\t\t}}\n\t\t>\n\t\t\t<EditPopover initial=\"\" onCancel={onCancel} onSave={onSave} />\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\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\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 useTargetRect(selector: string) {\n\tconst [rect, setRect] = 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\tsetRect(null);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst r = el.getBoundingClientRect();\n\t\t\tsetRect({ top: r.top + window.scrollY - 12, left: r.left + window.scrollX - 12 });\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]);\n\n\treturn rect;\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: 12,\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 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\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":"AAcA,OAA6C,aAAAA,EAAW,UAAAC,EAAQ,YAAAC,MAAgB,QAChF,OAAS,cAAAC,MAAkB,mBAgTvB,cAAAC,EAoBD,QAAAC,MApBC,oBA9SJ,IAAMC,EAAgB,4BAyBhBC,EAAe,IAAqB,CACzC,GAAI,OAAO,OAAW,IAAa,OAAO,KAC1C,IAAMC,GAAY,QAAQ,IAAI,0BAA4B,IAAI,KAAK,EACnE,GAAIA,EAAU,OAAOA,EAAS,QAAQ,MAAO,EAAE,EAO/C,IAAMC,EAAO,OAAO,SAAS,KACvBC,EAAW,OAAO,SAAS,SAC3BC,EAASF,EAAK,QAAQ,GAAG,EAC/B,GAAIE,EAAS,EAAG,CACf,IAAMC,EAAMH,EAAK,MAAM,EAAGE,CAAM,EAChC,GAAIC,EAAI,SAAS,UAAU,EAAG,CAC7B,IAAMC,EAAWD,EAAI,MAAM,EAAG,EAAkB,EAChD,MAAO,GAAGF,CAAQ,KAAKG,CAAQ,GAAGJ,EAAK,MAAME,CAAM,CAAC,EACrD,CACD,CACA,OAAO,OAAO,SAAS,MACxB,EAEMG,EAAsBC,GAAwB,CACnD,GAAIA,EAAG,GAAI,MAAO,IAAI,IAAI,OAAOA,EAAG,EAAE,CAAC,GACvC,IAAMC,EAAiB,CAAC,EACpBC,EAAuBF,EAC3B,KAAOE,GAAQA,EAAK,WAAa,KAAK,cAAgBD,EAAK,OAAS,GAAG,CACtE,IAAIE,EAAOD,EAAK,QAAQ,YAAY,EAC9BE,EAAYF,EAAK,aAAa,OAAO,EAC3C,GAAIE,EAAW,CACd,IAAMC,EAAUD,EACd,MAAM,KAAK,EACX,OAAO,OAAO,EACd,MAAM,EAAG,CAAC,EACV,IAAKE,GAAM,IAAI,IAAI,OAAOA,CAAC,CAAC,EAAE,EAC9B,KAAK,EAAE,EACTH,GAAQE,CACT,CACA,IAAME,EAAyBL,EAAK,cAC9BM,EAAMN,EAAK,QACjB,GAAIK,EAAQ,CACX,IAAME,EAAsB,MAAM,KAAKF,EAAO,QAAQ,EAAE,OAAQD,GAAMA,EAAE,UAAYE,CAAG,EACvF,GAAIC,EAAS,OAAS,EAAG,CACxB,IAAMC,EAAMD,EAAS,QAAQP,CAAI,EAAI,EACrCC,GAAQ,gBAAgBO,CAAG,GAC5B,CACD,CACAT,EAAK,QAAQE,CAAI,EACjBD,EAAOK,CACR,CACA,OAAON,EAAK,KAAK,KAAK,CACvB,EAEMU,EAAoB,CAAC,KAAM,QAAS,cAAe,aAAc,OAAQ,OAAQ,OAAQ,KAAK,EAE9FC,EAAeZ,GAAwB,CAC5C,IAAMa,EAAkB,CAAC,EACzB,QAAWC,KAAQH,EAAmB,CACrC,IAAMI,EAAIf,EAAG,aAAac,CAAI,EAC9B,GAAI,CAACC,EAAG,SACR,IAAMC,EAAUD,EAAE,OAAS,GAAK,GAAGA,EAAE,MAAM,EAAG,EAAE,CAAC,SAAMA,EACvDF,EAAM,KAAK,IAAIC,CAAI,KAAKE,EAAQ,QAAQ,KAAM,QAAQ,CAAC,GAAG,CAC3D,CACA,OAAOH,EAAM,KAAK,EAAE,CACrB,EAEMI,EAAqBjB,GAAwB,CAClD,IAAMkB,GAAQlB,EAAG,aAAe,IAAI,QAAQ,OAAQ,GAAG,EAAE,KAAK,EAC9D,OAAKkB,EACEA,EAAK,OAAS,IAAM,GAAGA,EAAK,MAAM,EAAG,GAAG,CAAC,SAAMA,EADpC,EAEnB,EAOMC,EAAwBC,GAA4B,CACzD,IAAMC,EAAuB,CAAC,EAC1BC,EAAsBF,EAC1B,KAAOE,GAAOA,IAAQ,SAAS,iBAAmBD,EAAU,OAAS,GAAG,CACvE,IAAMd,EAAyBe,EAAI,cACnC,GAAI,CAACf,EAAQ,MACbc,EAAU,QAAQd,CAAM,EACxBe,EAAMf,CACP,CAEA,IAAMgB,EAAkB,CAAC,EACrBC,EAAQ,EACZ,QAAWC,KAAYJ,EAAW,CACjC,IAAMK,EAAS,KAAK,OAAOF,CAAK,EAChCD,EAAM,KAAK,GAAGG,CAAM,IAAID,EAAS,QAAQ,YAAY,CAAC,GAAGb,EAAYa,CAAQ,CAAC,GAAG,EACjFD,GACD,CAEA,IAAMG,EAAe,KAAK,OAAOH,CAAK,EAChCI,EAAYR,EAAO,QAAQ,YAAY,EACvCS,EAAaZ,EAAkBG,CAAM,EAO3C,GANAG,EAAM,KACL,GAAGI,CAAY,IAAIC,CAAS,GAAGhB,EAAYQ,CAAM,CAAC,IACjDS,EAAa,GAAGA,CAAU,KAAKD,CAAS,IAAM,EAC/C,iBACD,EAEI,CAACC,EAAY,CAChB,IAAMC,EAAc,KAAK,OAAON,EAAQ,CAAC,EACnCO,EAAW,MAAM,KAAKX,EAAO,QAAQ,EAAE,MAAM,EAAG,CAAC,EACvD,QAAWY,KAASD,EAAU,CAC7B,IAAME,EAAYhB,EAAkBe,CAAK,EACzCT,EAAM,KACL,GAAGO,CAAW,IAAIE,EAAM,QAAQ,YAAY,CAAC,GAAGpB,EAAYoB,CAAK,CAAC,IACjEC,EAAY,GAAGA,CAAS,KAAKD,EAAM,QAAQ,YAAY,CAAC,IAAM,EAC/D,EACD,CACD,CACIZ,EAAO,SAAS,OAAS,GAC5BG,EAAM,KAAK,GAAGO,CAAW,WAAMV,EAAO,SAAS,OAAS,CAAC,iBAAiB,EAE3EG,EAAM,KAAK,GAAGI,CAAY,KAAKC,CAAS,GAAG,CAC5C,CAEA,QAASM,EAAIb,EAAU,OAAS,EAAGa,GAAK,EAAGA,IAAK,CAC/C,IAAMR,EAAS,KAAK,OAAOQ,CAAC,EACtBT,EAAWJ,EAAUa,CAAC,EACvBT,GACLF,EAAM,KAAK,GAAGG,CAAM,KAAKD,EAAS,QAAQ,YAAY,CAAC,GAAG,CAC3D,CAEA,OAAOF,EAAM,KAAK;AAAA,CAAI,CACvB,EAEMY,EAAmBnC,GAAgC,CACxD,IAAIE,EAAOF,EACX,KAAOE,GAAM,CACZ,GAAIA,aAAgB,aAAeA,EAAK,QAAQ,gBAAkB,OAAQ,MAAO,GACjFA,EAAOA,EAAK,aACb,CACA,MAAO,EACR,EAEA,SAASkC,GAAkB,CAC1B,GAAM,CAACC,EAASC,CAAU,EAAInD,EAA+B,IAAI,EAC3D,CAACoD,EAASC,CAAU,EAAIrD,EAAS,EAAI,EACrC,CAACsD,EAASC,CAAU,EAAIvD,EAAS,EAAK,EACtC,CAACwD,EAASC,CAAU,EAAIzD,EAA4B,IAAI,EACxD,CAAC0D,EAAWC,CAAY,EAAI3D,EAAwB,IAAI,EACxD4D,EAAU7D,EAAsB,IAAI,EAE1CD,EAAU,IAAM,CAEf,GADA8D,EAAQ,QAAUvD,EAAa,EAC3B,CAACuD,EAAQ,QAAS,CACrBP,EAAW,EAAK,EAChB,MACD,CAEA,IAAMQ,EAAa,IAAI,gBACvB,aAAM,GAAGD,EAAQ,OAAO,+BAA+B,mBAAmB,OAAO,SAAS,IAAI,CAAC,GAAI,CAClG,YAAa,UACb,OAAQC,EAAW,MACpB,CAAC,EACC,KAAK,MAAOC,GAAQ,CACpB,GAAI,CAACA,EAAI,GAAI,CACZX,EAAW,IAAI,EACf,MACD,CACA,IAAMY,EAAQ,MAAMD,EAAI,KAAK,EAC7BX,EAAWY,CAAI,CAChB,CAAC,EACA,MAAM,IAAM,CACZZ,EAAW,IAAI,CAChB,CAAC,EACA,QAAQ,IAAME,EAAW,EAAK,CAAC,EAE1B,IAAMQ,EAAW,MAAM,CAC/B,EAAG,CAAC,CAAC,EAEL/D,EAAU,IAAM,CACf,GAAKwD,EACL,gBAAS,KAAK,MAAM,OAAS,YACtB,IAAM,CACZ,SAAS,KAAK,MAAM,OAAS,EAC9B,CACD,EAAG,CAACA,CAAO,CAAC,EAEZxD,EAAU,IAAM,CACf,GAAI,CAACwD,EAAS,OACd,IAAMU,EAAeC,GAAsB,CAC1C,IAAMhC,EAASgC,EAAM,OAErB,GADI,EAAEhC,aAAkB,UACpBe,EAAgBf,CAAM,EAAG,OAE7BgC,EAAM,eAAe,EACrBA,EAAM,gBAAgB,EAEtB,IAAMC,EAAOjC,EAAO,sBAAsB,EAC1CwB,EAAW,CACV,YAAa7C,EAAmBqB,CAAM,EACtC,SAAU,OAAO,SAAS,SAC1B,gBAAiBD,EAAqBC,CAAM,EAC5C,KAAM,CACL,IAAKiC,EAAK,IAAM,OAAO,QACvB,KAAMA,EAAK,KAAO,OAAO,QACzB,MAAOA,EAAK,MACZ,OAAQA,EAAK,MACd,EACA,OAAQD,EAAM,QAAU,OAAO,QAC/B,OAAQA,EAAM,QAAU,OAAO,OAChC,CAAC,EACDV,EAAW,EAAK,CACjB,EACA,gBAAS,iBAAiB,QAASS,EAAa,CAAE,QAAS,EAAK,CAAC,EAC1D,IAAM,SAAS,oBAAoB,QAASA,EAAa,CAAE,QAAS,EAAK,CAAC,CAClF,EAAG,CAACV,CAAO,CAAC,EAEZ,IAAMa,EAAkB,SAAY,CACnC,GAAI,CAACP,EAAQ,QAAS,OACtB,IAAME,EAAM,MAAM,MACjB,GAAGF,EAAQ,OAAO,+BAA+B,mBAAmB,OAAO,SAAS,IAAI,CAAC,GACzF,CAAE,YAAa,SAAU,CAC1B,EACA,GAAI,CAACE,EAAI,GAAI,OACb,IAAMC,EAAQ,MAAMD,EAAI,KAAK,EAC7BX,EAAWY,CAAI,CAChB,EAEMK,EAAmB,MAAOC,GAAoB,CAC/C,CAACT,EAAQ,SAAW,CAACV,GAAW,CAACM,GAajC,EAZQ,MAAM,MAAM,GAAGI,EAAQ,OAAO,yBAA0B,CACnE,OAAQ,OACR,YAAa,UACb,QAAS,CAAE,eAAgB,kBAAmB,EAC9C,KAAM,KAAK,UAAU,CACpB,kBAAmBV,EAAQ,kBAC3B,QAAAmB,EACA,SAAUb,EAAQ,SAClB,YAAaA,EAAQ,YACrB,gBAAiBA,EAAQ,eAC1B,CAAC,CACF,CAAC,GACQ,KACTC,EAAW,IAAI,EACf,MAAMU,EAAgB,EACvB,EAEMG,EAAgB,MAAOC,EAAYF,IAAoB,CACxD,CAACT,EAAQ,SAOT,EANQ,MAAM,MAAM,GAAGA,EAAQ,OAAO,0BAA0BW,CAAE,GAAI,CACzE,OAAQ,QACR,YAAa,UACb,QAAS,CAAE,eAAgB,kBAAmB,EAC9C,KAAM,KAAK,UAAU,CAAE,QAAAF,CAAQ,CAAC,CACjC,CAAC,GACQ,KACTV,EAAa,IAAI,EACjB,MAAMQ,EAAgB,EACvB,EAEMK,EAAgB,MAAOD,GAAe,CACvC,CAACX,EAAQ,SAKT,EAJQ,MAAM,MAAM,GAAGA,EAAQ,OAAO,0BAA0BW,CAAE,GAAI,CACzE,OAAQ,SACR,YAAa,SACd,CAAC,GACQ,IACT,MAAMJ,EAAgB,CACvB,EAEA,GAAIf,GAAW,CAACF,GAAW,CAACA,EAAQ,WAAY,OAAO,KAEvD,IAAMuB,EAAcvB,EAAQ,SAAS,OACnC/B,GAAMA,EAAE,WAAa,OAAO,SAAS,UAAYA,EAAE,SAAW,MAChE,EAEA,OACChB,EAAC,OAAI,uBAAqB,OACxB,UAAAsE,EAAY,IAAI,CAACC,EAAKnD,IACtBrB,EAACyE,EAAA,CAEA,IAAKD,EACL,OAAQnD,EAAM,EACd,QAASmC,IAAcgB,EAAI,GAC3B,YAAa,IAAMf,EAAae,EAAI,EAAE,EACtC,aAAc,IAAMf,EAAa,IAAI,EACrC,OAASU,GAAYC,EAAcI,EAAI,GAAIL,CAAO,EAClD,SAAU,IAAMG,EAAcE,EAAI,EAAE,GAP/BA,EAAI,EAQV,CACA,EAEAlB,GACAtD,EAAC0E,EAAA,CACA,QAASpB,EACT,SAAU,IAAMC,EAAW,IAAI,EAC/B,OAASY,GAAYD,EAAiBC,CAAO,EAC9C,EAGDlE,EAAC,OAAI,MAAO0E,EACX,UAAA3E,EAAC,UACA,KAAK,SACL,QAAS,IAAMqD,EAAY3B,GAAM,CAACA,CAAC,EACnC,MAAO0B,EAAUwB,EAA2BC,EAE3C,SAAAzB,EAAU,SAAW,cACvB,EACApD,EAAC,QAAK,MAAO8E,EACX,SAAA1B,EAAU,+BAAiC,GAAGmB,EAAY,MAAM,gBAClE,GACD,GACD,CAEF,CAEA,SAASE,EAAW,CACnB,IAAAD,EACA,OAAAO,EACA,QAAAC,EACA,YAAAC,EACA,aAAAC,EACA,OAAAC,EACA,SAAAC,CACD,EAQG,CACF,IAAMrD,EAASsD,EAAcb,EAAI,WAAW,EAC5C,OAAKzC,EAGJ9B,EAAC,OACA,MAAO,CACN,SAAU,WACV,IAAK8B,EAAO,IACZ,KAAMA,EAAO,KACb,OAAQ,WACR,cAAe,MAChB,EAEA,UAAA/B,EAAC,UAAO,KAAK,SAAS,QAASiF,EAAa,MAAOK,EAAa,MAAOd,EAAI,QACzE,SAAAO,EACF,EACCC,GACAhF,EAACuF,EAAA,CAAY,QAASf,EAAI,QAAS,SAAUU,EAAc,OAAQC,EAAQ,SAAUC,EAAU,GAEjG,EAlBmB,IAoBrB,CAEA,SAASV,EAAsB,CAC9B,QAAApB,EACA,SAAAkC,EACA,OAAAL,CACD,EAIG,CACF,OACCnF,EAAC,OACA,MAAO,CACN,SAAU,WACV,IAAKsD,EAAQ,OAAS,GACtB,KAAMA,EAAQ,OAAS,GACvB,OAAQ,UACT,EAEA,SAAAtD,EAACuF,EAAA,CAAY,QAAQ,GAAG,SAAUC,EAAU,OAAQL,EAAQ,EAC7D,CAEF,CAEA,SAASI,EAAY,CACpB,QAAAE,EACA,SAAAD,EACA,OAAAL,EACA,SAAAC,CACD,EAKG,CACF,GAAM,CAACM,EAAOC,CAAQ,EAAI7F,EAAS2F,CAAO,EACpC,CAACG,EAAQC,CAAS,EAAI/F,EAAS,EAAK,EAc1C,OACCG,EAAC,QAAK,SAbc,MAAO6F,GAAiB,CAC5CA,EAAE,eAAe,EACjB,IAAM3B,EAAUuB,EAAM,KAAK,EAC3B,GAAKvB,EACL,CAAA0B,EAAU,EAAI,EACd,GAAI,CACH,MAAMV,EAAOhB,CAAO,CACrB,QAAE,CACD0B,EAAU,EAAK,CAChB,EACD,EAG+B,MAAOE,EACpC,UAAA/F,EAAC,YACA,MAAO0F,EACP,SAAWI,GAAMH,EAASG,EAAE,OAAO,KAAK,EACxC,YAAY,wBACZ,MAAOE,EACP,KAAM,EACP,EACA/F,EAAC,OAAI,MAAOgG,EACV,UAAAb,GACApF,EAAC,UAAO,KAAK,SAAS,QAAS,IAAMoF,EAAS,EAAG,MAAOc,GAAmB,SAAUN,EAAQ,kBAE7F,EAED5F,EAAC,OAAI,MAAO,CAAE,KAAM,CAAE,EAAG,EACzBA,EAAC,UAAO,KAAK,SAAS,QAASwF,EAAU,MAAOW,EAAkB,SAAUP,EAAQ,kBAEpF,EACA5F,EAAC,UAAO,KAAK,SAAS,MAAOoG,EAAoB,SAAUR,GAAU,CAACF,EAAM,KAAK,EAC/E,SAAAE,EAAS,eAAY,OACvB,GACD,GACD,CAEF,CAEA,SAASP,EAAcgB,EAAkB,CACxC,GAAM,CAACrC,EAAMsC,CAAO,EAAIxG,EAA+C,IAAI,EAE3E,OAAAF,EAAU,IAAM,CACf,IAAM2G,EAAS,IAAM,CACpB,IAAI5F,EAAqB,KACzB,GAAI,CACHA,EAAK,SAAS,cAAc0F,CAAQ,CACrC,MAAQ,CACP1F,EAAK,IACN,CACA,GAAI,CAACA,EAAI,CACR2F,EAAQ,IAAI,EACZ,MACD,CACA,IAAME,EAAI7F,EAAG,sBAAsB,EACnC2F,EAAQ,CAAE,IAAKE,EAAE,IAAM,OAAO,QAAU,GAAI,KAAMA,EAAE,KAAO,OAAO,QAAU,EAAG,CAAC,CACjF,EAEAD,EAAO,EACP,IAAME,EAAK,IAAI,eAAeF,CAAM,EACpC,GAAI,CACH,IAAM5F,EAAK,SAAS,cAAc0F,CAAQ,EACtC1F,GAAI8F,EAAG,QAAQ9F,CAAE,CACtB,MAAQ,CAER,CACA,cAAO,iBAAiB,SAAU4F,EAAQ,EAAI,EAC9C,OAAO,iBAAiB,SAAUA,CAAM,EACjC,IAAM,CACZE,EAAG,WAAW,EACd,OAAO,oBAAoB,SAAUF,EAAQ,EAAI,EACjD,OAAO,oBAAoB,SAAUA,CAAM,CAC5C,CACD,EAAG,CAACF,CAAQ,CAAC,EAENrC,CACR,CAIA,IAAMW,EAA8B,CACnC,SAAU,QACV,OAAQ,GACR,KAAM,MACN,UAAW,mBACX,OAAQ,WACR,QAAS,OACT,WAAY,SACZ,IAAK,GACL,QAAS,WACT,WAAY,yBACZ,MAAO,QACP,aAAc,IACd,UAAW,8BACX,WACC,6HACD,SAAU,GACV,cAAe,MAChB,EAEME,EAAoC,CACzC,OAAQ,OACR,WAAY,QACZ,MAAO,OACP,QAAS,WACT,aAAc,IACd,OAAQ,UACR,WAAY,IACZ,SAAU,EACX,EAEMD,EAA0C,CAC/C,GAAGC,EACH,WAAY,UACZ,MAAO,OACR,EAEMC,EAAkC,CACvC,QAAS,GACT,SAAU,EACX,EAEMQ,EAA6B,CAClC,MAAO,GACP,OAAQ,GACR,aAAc,IACd,WAAY,UACZ,MAAO,QACP,OAAQ,kBACR,OAAQ,UACR,SAAU,GACV,WAAY,IACZ,UAAW,4BACX,QAAS,cACT,WAAY,SACZ,eAAgB,SAChB,QAAS,CACV,EAEMS,EAA8B,CACnC,WAAY,QACZ,OAAQ,oBACR,aAAc,EACd,QAAS,GACT,MAAO,IACP,UAAW,+BACX,WACC,6HACD,QAAS,OACT,cAAe,SACf,IAAK,EACL,MAAO,MACR,EAEMC,EAA+B,CACpC,MAAO,OACP,OAAQ,oBACR,aAAc,EACd,QAAS,EACT,SAAU,GACV,OAAQ,WACR,WAAY,UACZ,MAAO,OACP,WAAY,QACZ,UAAW,YACZ,EAEMC,EAAqC,CAC1C,QAAS,OACT,WAAY,SACZ,IAAK,CACN,EAEMS,EAA4B,CACjC,OAAQ,wBACR,aAAc,EACd,QAAS,WACT,SAAU,GACV,OAAQ,UACR,WAAY,GACb,EAEMN,EAAoC,CACzC,GAAGM,EACH,WAAY,OACZ,MAAO,OACR,EAEMP,EAAkC,CACvC,GAAGO,EACH,WAAY,cACZ,MAAO,UACP,YAAa,SACd,EAEMR,GAAmC,CACxC,GAAGQ,EACH,WAAY,cACZ,MAAO,UACP,YAAa,SACd,EAEO,SAASC,GAAuD,CAMtE,GALA,QAAQ,IAAI,uDAAwD,CACnE,SAAU,OAAO,OAAW,IAC5B,UAAW,QAAQ,IAAI,uBACvB,eAAgB,OAAO,SAAa,KAAe,EAAQ,SAAS,eAAezG,CAAa,CACjG,CAAC,EACG,OAAO,OAAW,IAAa,OAAO,KAC1C,GAAI,QAAQ,IAAI,yBAA2B,UAC1C,eAAQ,IACP,sEACA,QAAQ,IAAI,sBACb,EACO,KAER,GAAI,SAAS,eAAeA,CAAa,EAAG,OAAO,KAEnD,IAAM0G,EAAY,SAAS,cAAc,KAAK,EAC9CA,EAAU,GAAK1G,EACf0G,EAAU,QAAQ,cAAgB,OAClC,SAAS,KAAK,YAAYA,CAAS,EAEnC,IAAMC,EAAO9G,EAAW6G,CAAS,EACjC,OAAAC,EAAK,OAAO7G,EAAC+C,EAAA,EAAgB,CAAE,EAExB,CACN,QAAS,IAAM,CACd8D,EAAK,QAAQ,EACbD,EAAU,OAAO,CAClB,CACD,CACD,CAGI,OAAO,OAAW,KACrB,QAAQ,IAAI,0CAA2C,CACtD,UAAW,QAAQ,IAAI,uBACvB,WAAY,SAAS,WACrB,cAAe,QAAQ,IAAI,yBAA2B,SACvD,CAAC,EAEE,OAAO,OAAW,KAAe,QAAQ,IAAI,yBAA2B,YACvE,SAAS,aAAe,UAC3B,SAAS,iBAAiB,mBAAoB,IAAM,CACnDD,EAAqB,CACtB,CAAC,EAEDA,EAAqB,GCrpBhB,SAASG,GAA+B,CAC1C,OAAO,OAAW,MACtB,QAAQ,IAAI,2CAA2C,EACvDC,GAAwB,EACxBC,GAAyB,EAC1B,CAGAF,EAAuB,EAmBvB,IAAMG,GAAe,IAAI,IAAI,CAAC,OAAQ,SAAU,QAAS,WAAY,MAAM,CAAC,EAE5E,SAASC,GAAoBC,EAAqB,CACjD,GAAIA,EAAG,GAAI,MAAO,GAAGA,EAAG,QAAQ,YAAY,CAAC,IAAI,IAAI,OAAOA,EAAG,EAAE,CAAC,GAElE,IAAMC,EAAkB,CAAC,EACrBC,EAA0BF,EAC9B,KAAOE,GAAWA,IAAY,SAAS,MAAQA,IAAY,SAAS,iBAAiB,CACpF,IAAIC,EAAWD,EAAQ,QAAQ,YAAY,EAE3C,GAAIA,EAAQ,GAAI,CACfD,EAAM,QAAQ,GAAGE,CAAQ,IAAI,IAAI,OAAOD,EAAQ,EAAE,CAAC,EAAE,EACrD,KACD,CAEA,IAAME,EAAU,MAAM,KAAKF,EAAQ,SAAS,EAAE,OAAQ,GAAM,CAAC,EAAE,WAAW,WAAW,CAAC,EAClFE,EAAQ,OAAS,IACpBD,GAAY,IAAIC,EAAQ,IAAK,GAAM,IAAI,OAAO,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,IAG5D,IAAMC,EAAyBH,EAAQ,cACjCI,EAAMJ,EAAQ,QACpB,GAAIG,GACc,MAAM,KAAKA,EAAO,QAAQ,EAAE,OAC3CE,GAAMA,EAAE,UAAYD,IAAQF,EAAQ,SAAW,GAAKA,EAAQ,MAAOI,GAAMD,EAAE,UAAU,SAASC,CAAC,CAAC,EAClG,EACa,OAAS,EAAG,CACxB,IAAMC,EAAQ,MAAM,KAAKJ,EAAO,QAAQ,EAAE,QAAQH,CAAO,EAAI,EAC7DC,GAAY,cAAcM,CAAK,GAChC,CAGDR,EAAM,QAAQE,CAAQ,EACtBD,EAAUA,EAAQ,aACnB,CACA,OAAOD,EAAM,KAAK,KAAK,CACxB,CAEA,SAASS,EAAiBV,EAAaW,EAAgC,CACtE,IAAMC,EAAOZ,EAAG,sBAAsB,EAChCa,EAAW,OAAO,iBAAiBb,CAAE,EACrCc,GAAQd,EAAG,aAAe,IAAI,KAAK,EACzC,MAAO,CACN,IAAKA,EAAG,QAAQ,YAAY,EAC5B,GAAIA,EAAG,IAAM,OACb,QAAS,MAAM,KAAKA,EAAG,SAAS,EAAE,OAAQQ,GAAM,CAACA,EAAE,WAAW,WAAW,CAAC,EAC1E,YAAaM,EAAK,OAASH,EAAY,GAAGG,EAAK,MAAM,EAAGH,CAAS,CAAC,SAAMG,EACxE,YAAaf,GAAoBC,CAAE,EACnC,aAAc,CAAE,IAAKY,EAAK,IAAK,KAAMA,EAAK,KAAM,MAAOA,EAAK,MAAO,OAAQA,EAAK,MAAO,EACvF,eAAgB,CACf,MAAOC,EAAS,MAChB,gBAAiBA,EAAS,gBAC1B,SAAUA,EAAS,SACnB,WAAYA,EAAS,WACrB,QAASA,EAAS,QAClB,OAAQA,EAAS,MAClB,CACD,CACD,CAEA,SAASE,EAAaf,EAAoBgB,EAAoC,CAE7E,GADI,CAAChB,GAAM,CAACA,EAAG,SACXF,GAAa,IAAIE,EAAG,OAAO,EAAG,MAAO,GACzC,QAAWiB,KAAQD,EAClB,GAAIhB,EAAG,eAAeiB,CAAI,EAAG,MAAO,GAErC,MAAO,EACR,CAEA,SAASrB,IAA0B,CAClC,IAAIsB,EAAU,GACVC,EAAiC,KACjCC,EAA+D,KAE7DC,EAAU,SAAS,cAAc,KAAK,EAC5CA,EAAQ,aAAa,0BAA2B,OAAO,EACvDA,EAAQ,MAAM,QAAU,CACvB,kBACA,uBACA,sBACA,4BACA,uCACA,qBACA,gBACA,8DACD,EAAE,KAAK,GAAG,EAEV,IAAMC,EAAQ,SAAS,cAAc,KAAK,EAC1CA,EAAM,aAAa,0BAA2B,OAAO,EACrDA,EAAM,MAAM,QAAU,CACrB,kBACA,uBACA,sBACA,sBACA,cACA,kBACA,uCACA,mBACA,qBACA,sBACA,eACD,EAAE,KAAK,GAAG,EAEV,SAAS,gBAAgB,YAAYD,CAAO,EAC5C,SAAS,gBAAgB,YAAYC,CAAK,EAE1C,IAAMC,EAAQ,SAAS,cAAc,OAAO,EAC5CA,EAAM,aAAa,0BAA2B,OAAO,EACrDA,EAAM,YAAc,sFACpB,SAAS,KAAK,YAAYA,CAAK,EAE/B,SAASC,EAAgBxB,EAAa,CACrC,IAAMY,EAAOZ,EAAG,sBAAsB,EACtCqB,EAAQ,MAAM,IAAM,GAAGT,EAAK,GAAG,KAC/BS,EAAQ,MAAM,KAAO,GAAGT,EAAK,IAAI,KACjCS,EAAQ,MAAM,MAAQ,GAAGT,EAAK,KAAK,KACnCS,EAAQ,MAAM,OAAS,GAAGT,EAAK,MAAM,KACrCS,EAAQ,MAAM,QAAU,QAExB,IAAMjB,EAAU,MAAM,KAAKJ,EAAG,SAAS,EAAE,OAAQQ,GAAM,CAACA,EAAE,WAAW,WAAW,CAAC,EAC3EiB,EAAYzB,EAAG,QAAQ,YAAY,GAAKI,EAAQ,OAAS,IAAIA,EAAQ,CAAC,CAAC,GAAK,IAClFkB,EAAM,YAAcG,EACpBH,EAAM,MAAM,KAAO,GAAGV,EAAK,IAAI,KAC/BU,EAAM,MAAM,IAAM,GAAG,KAAK,IAAI,EAAGV,EAAK,IAAM,EAAE,CAAC,KAC/CU,EAAM,MAAM,QAAU,OACvB,CAEA,IAAMI,EAAmBC,GAAkB,CAC1C,IAAM3B,EAAK,SAAS,iBAAiB2B,EAAE,QAASA,EAAE,OAAO,EACzD,GAAIZ,EAAaf,EAAI,CAAC,yBAAyB,CAAC,EAAG,CAClDqB,EAAQ,MAAM,QAAU,OACxBC,EAAM,MAAM,QAAU,OACtBH,EAAiB,KACjB,MACD,CACAA,EAAiBnB,EACjB,sBAAsB,IAAM,CACvBmB,IAAmBnB,GAAMkB,GAAWlB,GACvCwB,EAAgBxB,CAAE,CAEpB,CAAC,CACF,EAEM4B,EAAmB,IAAM,CAC9BP,EAAQ,MAAM,QAAU,OACxBC,EAAM,MAAM,QAAU,OACtBH,EAAiB,IAClB,EAEMU,EAAeF,GAAkB,CACtC,GAAI,CAACT,GAAW,CAACC,EAAgB,OACjCQ,EAAE,eAAe,EACjBA,EAAE,gBAAgB,EAClBA,EAAE,yBAAyB,EAE3B,IAAM3B,EAAKmB,EACX,GAAIJ,EAAaf,EAAI,CAAC,yBAAyB,CAAC,EAAG,OAEnD,IAAM8B,EAAOpB,EAAiBV,EAAI,GAAG,EAC/BG,EAAW2B,EAAK,YAElBV,GACHA,EAAgB,GAAG,gBAAgB,mBAAmB,EAGnDA,GAAmBA,EAAgB,cAAgBjB,GACtDiB,EAAkB,KAClB,OAAO,OAAO,YAAY,CAAE,KAAM,qBAAsB,KAAMU,CAAK,EAAG,GAAG,IAEzEV,EAAkB,CAAE,GAAApB,EAAI,YAAaG,CAAS,EAC9CH,EAAG,aAAa,oBAAqB,EAAE,EACvC,OAAO,OAAO,YAAY,CAAE,KAAM,mBAAoB,KAAM8B,CAAK,EAAG,GAAG,EAEzE,EAEMC,EAAiBJ,GAAqB,CACvCA,EAAE,MAAQ,UAAYT,IACzBc,EAAkB,EAClB,OAAO,OAAO,YAAY,CAAE,KAAM,qBAAsB,EAAG,GAAG,EAEhE,EAEA,SAASC,GAAmB,CAC3Bf,EAAU,GACV,SAAS,iBAAiB,YAAaQ,EAAiB,EAAI,EAC5D,SAAS,iBAAiB,aAAcE,CAAgB,EACxD,SAAS,iBAAiB,QAASC,EAAa,EAAI,EACpD,SAAS,iBAAiB,UAAWE,EAAe,EAAI,CACzD,CAEA,SAASC,GAAoB,CAC5Bd,EAAU,GACV,SAAS,oBAAoB,YAAaQ,EAAiB,EAAI,EAC/D,SAAS,oBAAoB,aAAcE,CAAgB,EAC3D,SAAS,oBAAoB,QAASC,EAAa,EAAI,EACvD,SAAS,oBAAoB,UAAWE,EAAe,EAAI,EAE3DV,EAAQ,MAAM,QAAU,OACxBC,EAAM,MAAM,QAAU,OACtBH,EAAiB,KAEbC,IACHA,EAAgB,GAAG,gBAAgB,mBAAmB,EACtDA,EAAkB,KAEpB,CAEA,OAAO,iBAAiB,UAAYc,GAAU,CAC7C,IAAMC,EAAMD,EAAM,KACd,CAACC,GAAO,OAAOA,GAAQ,WAEvBA,EAAI,OAAS,uBACZA,EAAI,QACPF,EAAiB,GAEjBD,EAAkB,EAClB,OAAO,OAAO,YAAY,CAAE,KAAM,qBAAsB,EAAG,GAAG,IAI5DG,EAAI,OAAS,wBACZf,IACHA,EAAgB,GAAG,gBAAgB,mBAAmB,EACtDA,EAAkB,MAGrB,CAAC,CACF,CAEA,SAASvB,IAA2B,CACnC,IAAIqB,EAAU,GACVC,EAAiC,KACjCiB,EAAgE,CAAC,EAE/Df,EAAU,SAAS,cAAc,KAAK,EAC5CA,EAAQ,aAAa,2BAA4B,OAAO,EACxDA,EAAQ,MAAM,QAAU,CACvB,kBACA,uBACA,sBACA,6BACA,uCACA,qBACA,gBACA,8DACD,EAAE,KAAK,GAAG,EAEV,IAAMgB,EAAc,SAAS,cAAc,KAAK,EAChDA,EAAY,aAAa,2BAA4B,cAAc,EACnEA,EAAY,YAAc,mBAC1BA,EAAY,MAAM,QAAU,CAC3B,kBACA,uBACA,sBACA,sBACA,cACA,kBACA,oDACA,mBACA,qBACA,sBACA,gBACA,wCACD,EAAE,KAAK,GAAG,EAEV,IAAMC,EAAe,SAAS,cAAc,KAAK,EACjDA,EAAa,aAAa,2BAA4B,MAAM,EAC5DA,EAAa,MAAM,QAClB,0GAED,SAAS,gBAAgB,YAAYjB,CAAO,EAC5C,SAAS,gBAAgB,YAAYgB,CAAW,EAChD,SAAS,gBAAgB,YAAYC,CAAY,EAEjD,SAASC,GAAa,CACrBD,EAAa,UAAY,GACzB,QAAWE,KAAOJ,EAAM,CACvB,IAAIpC,EAAqB,KACzB,GAAI,CACHA,EAAK,SAAS,cAAcwC,EAAI,QAAQ,CACzC,MAAQ,CACPxC,EAAK,IACN,CACA,GAAI,CAACA,EAAI,SAET,IAAMyC,EAAIzC,EAAG,sBAAsB,EAC7B0C,EAAQ,SAAS,cAAc,KAAK,EAC1CA,EAAM,MAAM,QAAU,CACrB,kBACA,QAAQD,EAAE,IAAM,EAAE,KAClB,SAASA,EAAE,KAAOA,EAAE,MAAQ,EAAI,EAAE,KAClC,cACA,eACA,qBACA,sBACA,cACA,gBACA,sBACA,0BACA,kBACA,mBACA,oDACA,wCACA,sBACD,EAAE,KAAK,GAAG,EACVC,EAAM,YAAc,OAAOF,EAAI,MAAM,EACrCF,EAAa,YAAYI,CAAK,CAC/B,CACD,CAEA,IAAMhB,EAAmBC,GAAkB,CAC1C,IAAM3B,EAAK,SAAS,iBAAiB2B,EAAE,QAASA,EAAE,OAAO,EACzD,GAAIZ,EAAaf,EAAI,CAAC,2BAA4B,yBAAyB,CAAC,EAAG,CAC9EqB,EAAQ,MAAM,QAAU,OACxBgB,EAAY,MAAM,QAAU,OAC5BlB,EAAiB,KACjB,MACD,CACAA,EAAiBnB,EACjB,sBAAsB,IAAM,CAC3B,GAAImB,IAAmBnB,GAAMkB,GAAWlB,EAAI,CAC3C,IAAMY,EAAOZ,EAAG,sBAAsB,EACtCqB,EAAQ,MAAM,IAAM,GAAGT,EAAK,GAAG,KAC/BS,EAAQ,MAAM,KAAO,GAAGT,EAAK,IAAI,KACjCS,EAAQ,MAAM,MAAQ,GAAGT,EAAK,KAAK,KACnCS,EAAQ,MAAM,OAAS,GAAGT,EAAK,MAAM,KACrCS,EAAQ,MAAM,QAAU,QAExBgB,EAAY,MAAM,KAAO,GAAGV,EAAE,QAAU,EAAE,KAC1CU,EAAY,MAAM,IAAM,GAAGV,EAAE,QAAU,EAAE,KACzCU,EAAY,MAAM,QAAU,OAC7B,CACD,CAAC,CACF,EAEMT,EAAmB,IAAM,CAC9BP,EAAQ,MAAM,QAAU,OACxBgB,EAAY,MAAM,QAAU,OAC5BlB,EAAiB,IAClB,EAEMU,EAAeF,GAAkB,CACtC,GAAI,CAACT,GAAW,CAACC,EAAgB,OACjCQ,EAAE,eAAe,EACjBA,EAAE,gBAAgB,EAClBA,EAAE,yBAAyB,EAE3B,IAAM3B,EAAKmB,EACX,GAAIJ,EAAaf,EAAI,CAAC,2BAA4B,yBAAyB,CAAC,EAAG,OAE/E,IAAM8B,EAAOpB,EAAiBV,EAAI,GAAG,EAC/BY,EAAOZ,EAAG,sBAAsB,EAEtC,OAAO,OAAO,YACb,CACC,KAAM,gBACN,KAAM,CACL,QAAS8B,EACT,cAAe,CAAE,EAAGH,EAAE,QAAS,EAAGA,EAAE,OAAQ,EAC5C,YAAa,CAAE,IAAKf,EAAK,IAAK,KAAMA,EAAK,KAAM,MAAOA,EAAK,MAAO,OAAQA,EAAK,MAAO,EACtF,SAAU,OAAO,SAAS,QAC3B,CACD,EACA,GACD,CACD,EAEMmB,EAAiBJ,GAAqB,CACvCA,EAAE,MAAQ,UAAYT,IACzByB,EAAmB,EACnB,OAAO,OAAO,YAAY,CAAE,KAAM,sBAAuB,EAAG,GAAG,EAEjE,EAEA,SAASC,GAAoB,CAC5B1B,EAAU,GACV,SAAS,KAAK,MAAM,OAAS,YAC7B,SAAS,iBAAiB,YAAaQ,EAAiB,EAAI,EAC5D,SAAS,iBAAiB,aAAcE,CAAgB,EACxD,SAAS,iBAAiB,QAASC,EAAa,EAAI,EACpD,SAAS,iBAAiB,UAAWE,EAAe,EAAI,CACzD,CAEA,SAASY,GAAqB,CAC7BzB,EAAU,GACV,SAAS,KAAK,MAAM,OAAS,GAC7B,SAAS,oBAAoB,YAAaQ,EAAiB,EAAI,EAC/D,SAAS,oBAAoB,aAAcE,CAAgB,EAC3D,SAAS,oBAAoB,QAASC,EAAa,EAAI,EACvD,SAAS,oBAAoB,UAAWE,EAAe,EAAI,EAE3DV,EAAQ,MAAM,QAAU,OACxBgB,EAAY,MAAM,QAAU,OAC5BlB,EAAiB,IAClB,CAEA,IAAI0B,EAAa,GACXC,EAAiB,IAAM,CACxBD,IACJA,EAAa,GACb,sBAAsB,IAAM,CAC3BA,EAAa,GACbN,EAAW,CACZ,CAAC,EACF,EACA,OAAO,iBAAiB,SAAUO,EAAgB,CAAE,QAAS,EAAK,CAAC,EACnE,OAAO,iBAAiB,SAAUA,EAAgB,CAAE,QAAS,EAAK,CAAC,EAEnE,OAAO,iBAAiB,UAAYZ,GAAU,CAC7C,IAAMC,EAAMD,EAAM,KACd,CAACC,GAAO,OAAOA,GAAQ,WAEvBA,EAAI,OAAS,wBACZA,EAAI,QAASS,EAAkB,EAC9BD,EAAmB,GAGrBR,EAAI,OAAS,wBAChBC,EAAOD,EAAI,MAAQ,CAAC,EACpBI,EAAW,GAGRJ,EAAI,OAAS,uBAChBC,EAAOA,EAAK,OAAQW,GAAMA,EAAE,KAAOZ,EAAI,KAAK,EAC5CI,EAAW,GAEb,CAAC,CACF","names":["useEffect","useRef","useState","createRoot","jsx","jsxs","MOUNT_NODE_ID","buildApiBase","override","host","protocol","dotIdx","sub","stripped","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","FeedbackToolbar","session","setSession","loading","setLoading","pinMode","setPinMode","pending","setPending","editingId","setEditingId","apiBase","controller","res","data","handleClick","event","rect","refreshComments","submitNewComment","content","updateComment","id","removeComment","visiblePins","pin","PinOverlay","PendingCommentPopover","toolbarStyle","toolbarButtonActiveStyle","toolbarButtonStyle","toolbarHintStyle","number","editing","onStartEdit","onCancelEdit","onSave","onRemove","useTargetRect","pinDotStyle","EditPopover","onCancel","initial","value","setValue","saving","setSaving","e","popoverStyle","textareaStyle","popoverActionsStyle","dangerButtonStyle","ghostButtonStyle","primaryButtonStyle","selector","setRect","update","r","ro","baseButton","mountFeedbackToolbar","container","root","startSandboxInspectors","initDesignModeInspector","initCommentModeInspector","IGNORED_TAGS","generateCssSelector","el","parts","current","selector","classes","parent","tag","s","c","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\ninterface ActiveSession {\n\tfeedbackSessionId: string;\n\tcomments: FeedbackComment[];\n\tcanComment: boolean;\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\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\tconst controller = new AbortController();\n\t\tfetch(`${apiBase.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`, {\n\t\t\tcredentials: \"include\",\n\t\t\tsignal: controller.signal,\n\t\t})\n\t\t\t.then(async (res) => {\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\tsetSession(data);\n\t\t\t})\n\t\t\t.catch(() => {\n\t\t\t\tsetSession(null);\n\t\t\t})\n\t\t\t.finally(() => setLoading(false));\n\n\t\treturn () => controller.abort();\n\t}, []);\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\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 || !session.canComment) return null;\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={() => setPending(null)}\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 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\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\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\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":"AAcA,OAA6C,aAAAA,EAAW,UAAAC,EAAQ,YAAAC,MAAgB,QAChF,OAAS,cAAAC,MAAkB,mBAgavB,OA0GF,YAAAC,GA1GE,OAAAC,EA6BD,QAAAC,MA7BC,oBA9ZJ,IAAMC,EAAgB,4BA6BhBC,EAAe,IAAqB,CACzC,GAAI,OAAO,OAAW,IAAa,OAAO,KAC1C,IAAMC,GAAY,QAAQ,IAAI,0BAA4B,IAAI,KAAK,EACnE,GAAIA,EAAU,OAAOA,EAAS,QAAQ,MAAO,EAAE,EAM/C,IAAMC,EAAO,OAAO,SAAS,SACvBC,EAAW,OAAO,SAAS,SACjC,OAAID,EAAK,SAAS,YAAY,EAAU,GAAGC,CAAQ,cAC/CD,EAAK,SAAS,SAAS,EAAU,GAAGC,CAAQ,WACzC,OAAO,SAAS,MACxB,EAEMC,EAAsBC,GAAwB,CACnD,GAAIA,EAAG,GAAI,MAAO,IAAI,IAAI,OAAOA,EAAG,EAAE,CAAC,GACvC,IAAMC,EAAiB,CAAC,EACpBC,EAAuBF,EAC3B,KAAOE,GAAQA,EAAK,WAAa,KAAK,cAAgBD,EAAK,OAAS,GAAG,CACtE,IAAIE,EAAOD,EAAK,QAAQ,YAAY,EAC9BE,EAAYF,EAAK,aAAa,OAAO,EAC3C,GAAIE,EAAW,CACd,IAAMC,EAAUD,EACd,MAAM,KAAK,EACX,OAAO,OAAO,EACd,MAAM,EAAG,CAAC,EACV,IAAKE,GAAM,IAAI,IAAI,OAAOA,CAAC,CAAC,EAAE,EAC9B,KAAK,EAAE,EACTH,GAAQE,CACT,CACA,IAAME,EAAyBL,EAAK,cAC9BM,EAAMN,EAAK,QACjB,GAAIK,EAAQ,CACX,IAAME,EAAsB,MAAM,KAAKF,EAAO,QAAQ,EAAE,OAAQD,GAAMA,EAAE,UAAYE,CAAG,EACvF,GAAIC,EAAS,OAAS,EAAG,CACxB,IAAMC,EAAMD,EAAS,QAAQP,CAAI,EAAI,EACrCC,GAAQ,gBAAgBO,CAAG,GAC5B,CACD,CACAT,EAAK,QAAQE,CAAI,EACjBD,EAAOK,CACR,CACA,OAAON,EAAK,KAAK,KAAK,CACvB,EAEMU,EAAoB,CAAC,KAAM,QAAS,cAAe,aAAc,OAAQ,OAAQ,OAAQ,KAAK,EAE9FC,EAAeZ,GAAwB,CAC5C,IAAMa,EAAkB,CAAC,EACzB,QAAWC,KAAQH,EAAmB,CACrC,IAAMI,EAAIf,EAAG,aAAac,CAAI,EAC9B,GAAI,CAACC,EAAG,SACR,IAAMC,EAAUD,EAAE,OAAS,GAAK,GAAGA,EAAE,MAAM,EAAG,EAAE,CAAC,SAAMA,EACvDF,EAAM,KAAK,IAAIC,CAAI,KAAKE,EAAQ,QAAQ,KAAM,QAAQ,CAAC,GAAG,CAC3D,CACA,OAAOH,EAAM,KAAK,EAAE,CACrB,EAEMI,EAAqBjB,GAAwB,CAClD,IAAMkB,GAAQlB,EAAG,aAAe,IAAI,QAAQ,OAAQ,GAAG,EAAE,KAAK,EAC9D,OAAKkB,EACEA,EAAK,OAAS,IAAM,GAAGA,EAAK,MAAM,EAAG,GAAG,CAAC,SAAMA,EADpC,EAEnB,EAEMC,EAAwBC,GAA4B,CACzD,IAAMC,EAAuB,CAAC,EAC1BC,EAAsBF,EAC1B,KAAOE,GAAOA,IAAQ,SAAS,iBAAmBD,EAAU,OAAS,GAAG,CACvE,IAAMd,EAAyBe,EAAI,cACnC,GAAI,CAACf,EAAQ,MACbc,EAAU,QAAQd,CAAM,EACxBe,EAAMf,CACP,CAEA,IAAMgB,EAAkB,CAAC,EACrBC,EAAQ,EACZ,QAAWC,KAAYJ,EAAW,CACjC,IAAMK,EAAS,KAAK,OAAOF,CAAK,EAChCD,EAAM,KAAK,GAAGG,CAAM,IAAID,EAAS,QAAQ,YAAY,CAAC,GAAGb,EAAYa,CAAQ,CAAC,GAAG,EACjFD,GACD,CAEA,IAAMG,EAAe,KAAK,OAAOH,CAAK,EAChCI,EAAYR,EAAO,QAAQ,YAAY,EACvCS,EAAaZ,EAAkBG,CAAM,EAO3C,GANAG,EAAM,KACL,GAAGI,CAAY,IAAIC,CAAS,GAAGhB,EAAYQ,CAAM,CAAC,IACjDS,EAAa,GAAGA,CAAU,KAAKD,CAAS,IAAM,EAC/C,iBACD,EAEI,CAACC,EAAY,CAChB,IAAMC,EAAc,KAAK,OAAON,EAAQ,CAAC,EACnCO,EAAW,MAAM,KAAKX,EAAO,QAAQ,EAAE,MAAM,EAAG,CAAC,EACvD,QAAWY,KAASD,EAAU,CAC7B,IAAME,EAAYhB,EAAkBe,CAAK,EACzCT,EAAM,KACL,GAAGO,CAAW,IAAIE,EAAM,QAAQ,YAAY,CAAC,GAAGpB,EAAYoB,CAAK,CAAC,IACjEC,EAAY,GAAGA,CAAS,KAAKD,EAAM,QAAQ,YAAY,CAAC,IAAM,EAC/D,EACD,CACD,CACIZ,EAAO,SAAS,OAAS,GAC5BG,EAAM,KAAK,GAAGO,CAAW,WAAMV,EAAO,SAAS,OAAS,CAAC,iBAAiB,EAE3EG,EAAM,KAAK,GAAGI,CAAY,KAAKC,CAAS,GAAG,CAC5C,CAEA,QAASM,EAAIb,EAAU,OAAS,EAAGa,GAAK,EAAGA,IAAK,CAC/C,IAAMR,EAAS,KAAK,OAAOQ,CAAC,EACtBT,EAAWJ,EAAUa,CAAC,EACvBT,GACLF,EAAM,KAAK,GAAGG,CAAM,KAAKD,EAAS,QAAQ,YAAY,CAAC,GAAG,CAC3D,CAEA,OAAOF,EAAM,KAAK;AAAA,CAAI,CACvB,EAEMY,EAAmBnC,GAAgC,CACxD,IAAIE,EAAOF,EACX,KAAOE,GAAM,CACZ,GAAIA,aAAgB,aAAeA,EAAK,QAAQ,gBAAkB,OAAQ,MAAO,GACjFA,EAAOA,EAAK,aACb,CACA,MAAO,EACR,EAEMkC,EAAe,IAAI,IAAI,CAAC,OAAQ,OAAQ,SAAU,QAAS,UAAU,CAAC,EAE5E,SAASC,IAAkB,CAC1B,GAAM,CAACC,EAASC,CAAU,EAAIlD,EAA+B,IAAI,EAC3D,CAACmD,EAASC,CAAU,EAAIpD,EAAS,EAAI,EACrC,CAACqD,EAASC,CAAU,EAAItD,EAAS,EAAK,EACtC,CAACuD,EAASC,CAAU,EAAIxD,EAA4B,IAAI,EACxD,CAACyD,EAAWC,CAAY,EAAI1D,EAAwB,IAAI,EACxD,CAAC2D,EAAaC,CAAc,EAAI5D,EAAS,EAAK,EAC9C,CAAC6D,EAAYC,CAAa,EAAI9D,EAAS,EAAK,EAC5C+D,EAAUhE,EAAsB,IAAI,EAE1CD,EAAU,IAAM,CAEf,GADAiE,EAAQ,QAAUzD,EAAa,EAC3B,CAACyD,EAAQ,QAAS,CACrBX,EAAW,EAAK,EAChB,MACD,CAEA,IAAMY,EAAa,IAAI,gBACvB,aAAM,GAAGD,EAAQ,OAAO,+BAA+B,mBAAmB,OAAO,SAAS,IAAI,CAAC,GAAI,CAClG,YAAa,UACb,OAAQC,EAAW,MACpB,CAAC,EACC,KAAK,MAAOC,GAAQ,CACpB,GAAI,CAACA,EAAI,GAAI,CACZf,EAAW,IAAI,EACf,MACD,CACA,IAAMgB,EAAQ,MAAMD,EAAI,KAAK,EAC7Bf,EAAWgB,CAAI,CAChB,CAAC,EACA,MAAM,IAAM,CACZhB,EAAW,IAAI,CAChB,CAAC,EACA,QAAQ,IAAME,EAAW,EAAK,CAAC,EAE1B,IAAMY,EAAW,MAAM,CAC/B,EAAG,CAAC,CAAC,EAELlE,EAAU,IAAM,CACf,GAAKuD,EACL,gBAAS,KAAK,MAAM,OAAS,YACtB,IAAM,CACZ,SAAS,KAAK,MAAM,OAAS,EAC9B,CACD,EAAG,CAACA,CAAO,CAAC,EAEZvD,EAAU,IAAM,CACf,GAAI,CAACuD,EAAS,OAEd,IAAMc,EAAU,SAAS,cAAc,KAAK,EAC5CA,EAAQ,QAAQ,cAAgB,OAChCA,EAAQ,MAAM,QAAU,CACvB,kBACA,uBACA,sBACA,6BACA,uCACA,qBACA,gBACA,8DACD,EAAE,KAAK,GAAG,EAEV,IAAMC,EAAQ,SAAS,cAAc,KAAK,EAC1CA,EAAM,QAAQ,cAAgB,OAC9BA,EAAM,YAAc,mBACpBA,EAAM,MAAM,QAAU,CACrB,kBACA,uBACA,sBACA,sBACA,cACA,kBACA,oDACA,mBACA,qBACA,sBACA,gBACA,wCACD,EAAE,KAAK,GAAG,EAEV,SAAS,gBAAgB,YAAYD,CAAO,EAC5C,SAAS,gBAAgB,YAAYC,CAAK,EAE1C,IAAIC,EAA0B,KACxBC,EAAcC,GAAkB,CACrC,IAAM5D,EAAK,SAAS,iBAAiB4D,EAAE,QAASA,EAAE,OAAO,EACzD,GAAI,CAAC5D,GAAMoC,EAAa,IAAIpC,EAAG,OAAO,GAAKmC,EAAgBnC,CAAE,EAAG,CAC/DwD,EAAQ,MAAM,QAAU,OACxBC,EAAM,MAAM,QAAU,OACtBC,EAAU,KACV,MACD,CACAA,EAAU1D,EACV,sBAAsB,IAAM,CAC3B,GAAI0D,IAAY1D,EAAI,OACpB,IAAM6D,EAAO7D,EAAG,sBAAsB,EACtCwD,EAAQ,MAAM,IAAM,GAAGK,EAAK,GAAG,KAC/BL,EAAQ,MAAM,KAAO,GAAGK,EAAK,IAAI,KACjCL,EAAQ,MAAM,MAAQ,GAAGK,EAAK,KAAK,KACnCL,EAAQ,MAAM,OAAS,GAAGK,EAAK,MAAM,KACrCL,EAAQ,MAAM,QAAU,QACxBC,EAAM,MAAM,KAAO,GAAGG,EAAE,QAAU,EAAE,KACpCH,EAAM,MAAM,IAAM,GAAGG,EAAE,QAAU,EAAE,KACnCH,EAAM,MAAM,QAAU,OACvB,CAAC,CACF,EAEMK,EAAc,IAAM,CACzBN,EAAQ,MAAM,QAAU,OACxBC,EAAM,MAAM,QAAU,OACtBC,EAAU,IACX,EAEA,gBAAS,iBAAiB,YAAaC,EAAY,EAAI,EACvD,SAAS,iBAAiB,aAAcG,CAAW,EAE5C,IAAM,CACZ,SAAS,oBAAoB,YAAaH,EAAY,EAAI,EAC1D,SAAS,oBAAoB,aAAcG,CAAW,EACtDN,EAAQ,OAAO,EACfC,EAAM,OAAO,CACd,CACD,EAAG,CAACf,CAAO,CAAC,EAEZvD,EAAU,IAAM,CACf,GAAI,CAACuD,EAAS,OACd,IAAMqB,EAAeC,GAAsB,CAC1C,IAAM5C,EAAS4C,EAAM,OAErB,GADI,EAAE5C,aAAkB,UACpBe,EAAgBf,CAAM,EAAG,OAE7B4C,EAAM,eAAe,EACrBA,EAAM,gBAAgB,EAEtB,IAAMH,EAAOzC,EAAO,sBAAsB,EACpC6C,EAAeJ,EAAK,MAAQ,GAAKG,EAAM,QAAUH,EAAK,MAAQA,EAAK,MAAQ,GAC3EK,EAAeL,EAAK,OAAS,GAAKG,EAAM,QAAUH,EAAK,KAAOA,EAAK,OAAS,GAClFhB,EAAW,CACV,YAAa9C,EAAmBqB,CAAM,EACtC,SAAU,OAAO,SAAS,SAC1B,gBAAiBD,EAAqBC,CAAM,EAC5C,KAAM,CACL,IAAKyC,EAAK,IAAM,OAAO,QACvB,KAAMA,EAAK,KAAO,OAAO,QACzB,MAAOA,EAAK,MACZ,OAAQA,EAAK,MACd,EACA,OAAQG,EAAM,QAAU,OAAO,QAC/B,OAAQA,EAAM,QAAU,OAAO,QAC/B,aAAc,KAAK,IAAI,EAAG,KAAK,IAAI,EAAGC,CAAY,CAAC,EACnD,aAAc,KAAK,IAAI,EAAG,KAAK,IAAI,EAAGC,CAAY,CAAC,CACpD,CAAC,EACDvB,EAAW,EAAK,CACjB,EACA,gBAAS,iBAAiB,QAASoB,EAAa,CAAE,QAAS,EAAK,CAAC,EAC1D,IAAM,SAAS,oBAAoB,QAASA,EAAa,CAAE,QAAS,EAAK,CAAC,CAClF,EAAG,CAACrB,CAAO,CAAC,EAEZ,IAAMyB,EAAkB,SAAY,CACnC,GAAI,CAACf,EAAQ,QAAS,OACtB,IAAME,EAAM,MAAM,MACjB,GAAGF,EAAQ,OAAO,+BAA+B,mBAAmB,OAAO,SAAS,IAAI,CAAC,GACzF,CAAE,YAAa,SAAU,CAC1B,EACA,GAAI,CAACE,EAAI,GAAI,OACb,IAAMC,EAAQ,MAAMD,EAAI,KAAK,EAC7Bf,EAAWgB,CAAI,CAChB,EAEMa,EAAmB,MAAOC,GAAoB,CAC/C,CAACjB,EAAQ,SAAW,CAACd,GAAW,CAACM,GAejC,EAdQ,MAAM,MAAM,GAAGQ,EAAQ,OAAO,yBAA0B,CACnE,OAAQ,OACR,YAAa,UACb,QAAS,CAAE,eAAgB,kBAAmB,EAC9C,KAAM,KAAK,UAAU,CACpB,kBAAmBd,EAAQ,kBAC3B,QAAA+B,EACA,SAAUzB,EAAQ,SAClB,YAAaA,EAAQ,YACrB,gBAAiBA,EAAQ,gBACzB,aAAcA,EAAQ,aACtB,aAAcA,EAAQ,YACvB,CAAC,CACF,CAAC,GACQ,KACTC,EAAW,IAAI,EACf,MAAMsB,EAAgB,EACvB,EAEMG,EAAgB,MAAOC,EAAYF,IAAoB,CACxD,CAACjB,EAAQ,SAOT,EANQ,MAAM,MAAM,GAAGA,EAAQ,OAAO,0BAA0BmB,CAAE,GAAI,CACzE,OAAQ,QACR,YAAa,UACb,QAAS,CAAE,eAAgB,kBAAmB,EAC9C,KAAM,KAAK,UAAU,CAAE,QAAAF,CAAQ,CAAC,CACjC,CAAC,GACQ,KACTtB,EAAa,IAAI,EACjB,MAAMoB,EAAgB,EACvB,EAEMK,EAAgB,MAAOD,GAAe,CACvC,CAACnB,EAAQ,SAKT,EAJQ,MAAM,MAAM,GAAGA,EAAQ,OAAO,0BAA0BmB,CAAE,GAAI,CACzE,OAAQ,SACR,YAAa,SACd,CAAC,GACQ,IACT,MAAMJ,EAAgB,CACvB,EAEMM,EAAkB,SAAY,CACnC,GAAI,GAACrB,EAAQ,SAAW,CAACd,IACpB,OAAO,QAAQ,yEAAyE,EAC7F,CAAAa,EAAc,EAAI,EAClB,GAAI,CAKH,GAAI,EAJQ,MAAM,MACjB,GAAGC,EAAQ,OAAO,0BAA0Bd,EAAQ,iBAAiB,YACrE,CAAE,OAAQ,OAAQ,YAAa,SAAU,CAC1C,GACS,GAAI,OACbC,EAAYmC,GAAUA,GAAO,CAAE,GAAGA,EAAM,WAAY,EAAM,CAAS,CACpE,QAAE,CACDvB,EAAc,EAAK,CACpB,EACD,EAEMwB,EAAmBC,GAA6B,CACrD,GAAIA,EAAQ,WAAa,OAAO,SAAS,SAAU,CAClD,OAAO,SAAS,OAAOA,EAAQ,QAAQ,EACvC,MACD,CACA,IAAI5E,EAAqB,KACzB,GAAI,CACHA,EAAK,SAAS,cAAc4E,EAAQ,WAAW,CAChD,MAAQ,CACP5E,EAAK,IACN,CACKA,IACLA,EAAG,eAAe,CAAE,SAAU,SAAU,MAAO,QAAS,CAAC,EACzD+C,EAAa6B,EAAQ,EAAE,EACxB,EAEA,GAAIpC,GAAW,CAACF,GAAW,CAACA,EAAQ,WAAY,OAAO,KAEvD,IAAMuC,EAAcvC,EAAQ,SAAS,OACnChC,GAAMA,EAAE,WAAa,OAAO,SAAS,UAAYA,EAAE,SAAW,MAChE,EAEA,OACCb,EAAC,OAAI,uBAAqB,OACxB,UAAAoF,EAAY,IAAI,CAACC,EAAKpE,IACtBlB,EAACuF,GAAA,CAEA,IAAKD,EACL,OAAQpE,EAAM,EACd,QAASoC,IAAcgC,EAAI,GAC3B,YAAa,IAAM/B,EAAa+B,EAAI,EAAE,EACtC,aAAc,IAAM/B,EAAa,IAAI,EACrC,OAASsB,GAAYC,EAAcQ,EAAI,GAAIT,CAAO,EAClD,SAAU,IAAMG,EAAcM,EAAI,EAAE,GAP/BA,EAAI,EAQV,CACA,EAEAlC,GACApD,EAACwF,GAAA,CACA,QAASpC,EACT,SAAU,IAAMC,EAAW,IAAI,EAC/B,OAASwB,GAAYD,EAAiBC,CAAO,EAC9C,EAGArB,GACAxD,EAACyF,GAAA,CACA,SAAU3C,EAAQ,SAClB,YAAa,OAAO,SAAS,SAC7B,QAAS,IAAMW,EAAe,EAAK,EACnC,SAAU0B,EACX,EAGDlF,EAAC,OAAI,MAAOyF,GACX,UAAA1F,EAAC,UACA,KAAK,SACL,QAAS,IAAMmD,EAAY5B,GAAM,CAACA,CAAC,EACnC,MAAO2B,EAAUyC,GAA2BC,EAE3C,SAAA1C,EAAU,SAAW,cACvB,EACAlD,EAAC,UAAO,KAAK,SAAS,QAAS,IAAMyD,EAAgBlC,GAAM,CAACA,CAAC,EAAG,MAAOsE,GACrE,SAAArC,EAAc,YAAc,SAASV,EAAQ,SAAS,MAAM,IAC9D,EACA9C,EAAC,UACA,KAAK,SACL,QAASiF,EACT,SAAUvB,EACV,MAAOoC,GAEN,SAAApC,EAAa,mBAAgB,WAC/B,EACA1D,EAAC,QAAK,MAAO+F,GACX,SAAA7C,EAAU,+BAAiC,GAAGmC,EAAY,MAAM,gBAClE,GACD,GACD,CAEF,CAEA,SAASE,GAAW,CACnB,IAAAD,EACA,OAAAU,EACA,QAAAC,EACA,YAAAC,EACA,aAAAC,EACA,OAAAC,EACA,SAAAC,CACD,EAQG,CACF,IAAMzE,EAAS0E,GAAkBhB,EAAI,YAAaA,EAAI,aAAcA,EAAI,YAAY,EACpF,OAAK1D,EAGJ3B,EAAC,OACA,MAAO,CACN,SAAU,WACV,IAAK2B,EAAO,IACZ,KAAMA,EAAO,KACb,OAAQ,WACR,cAAe,MAChB,EAEA,UAAA5B,EAAC,UAAO,KAAK,SAAS,QAASkG,EAAa,MAAOK,EAAa,MAAOjB,EAAI,QACzE,SAAAU,EACF,EACCC,GACAjG,EAACwG,EAAA,CAAY,QAASlB,EAAI,QAAS,SAAUa,EAAc,OAAQC,EAAQ,SAAUC,EAAU,GAEjG,EAlBmB,IAoBrB,CAEA,SAASb,GAAsB,CAC9B,QAAApC,EACA,SAAAqD,EACA,OAAAL,CACD,EAIG,CACF,OACCnG,EAAAF,GAAA,CACC,UAAAC,EAAC,OACA,MAAO,CACN,SAAU,WACV,IAAKoD,EAAQ,KAAK,IAAMA,EAAQ,KAAK,OAASA,EAAQ,aAAe,GACrE,KAAMA,EAAQ,KAAK,KAAOA,EAAQ,KAAK,MAAQA,EAAQ,aAAe,GACtE,OAAQ,WACR,cAAe,MAChB,EAEA,SAAApD,EAAC,OAAI,MAAOuG,EAAa,kBAAC,EAC3B,EACAvG,EAAC,OACA,MAAO,CACN,SAAU,WACV,IAAKoD,EAAQ,OAAS,GACtB,KAAMA,EAAQ,OAAS,GACvB,OAAQ,UACT,EAEA,SAAApD,EAACwG,EAAA,CAAY,QAAQ,GAAG,SAAUC,EAAU,OAAQL,EAAQ,EAC7D,GACD,CAEF,CAEA,SAASX,GAAgB,CACxB,SAAAiB,EACA,YAAAC,EACA,QAAAC,EACA,SAAAC,CACD,EAKG,CACF,IAAMC,EAAS,IAAI,IACnB,QAAWhG,KAAK4F,EAAU,CACzB,IAAMK,EAAOD,EAAO,IAAIhG,EAAE,QAAQ,GAAK,CAAC,EACxCiG,EAAK,KAAKjG,CAAC,EACXgG,EAAO,IAAIhG,EAAE,SAAUiG,CAAI,CAC5B,CACA,IAAMC,EAAQ,MAAM,KAAKF,EAAO,KAAK,CAAC,EAAE,KAAK,CAACG,EAAGC,IAC5CD,IAAMN,EAAoB,GAC1BO,IAAMP,EAAoB,EACvBM,EAAE,cAAcC,CAAC,CACxB,EAED,OACCjH,EAAC,OAAI,MAAOkH,GACX,UAAAlH,EAAC,OAAI,MAAOmH,GACX,UAAAnH,EAAC,UAAO,MAAO,CAAE,SAAU,EAAG,EAAG,uBAAWyG,EAAS,OAAO,KAAC,EAC7D1G,EAAC,UAAO,KAAK,SAAS,QAAS4G,EAAS,MAAOS,EAAkB,iBAEjE,GACD,EACArH,EAAC,OAAI,MAAOsH,GACV,SAAAZ,EAAS,SAAW,EACpB1G,EAAC,OAAI,MAAOuH,GAAmB,wEAAkD,EAEjFP,EAAM,IAAKvG,GACVR,EAAC,OAAe,MAAO,CAAE,aAAc,EAAG,EACzC,UAAAD,EAAC,OAAI,MAAOwH,GAAmB,SAAA/G,IAASkG,EAAc,GAAGlG,CAAI,gBAAeA,EAAK,GAC/EqG,EAAO,IAAIrG,CAAI,GAAK,CAAC,GAAG,IAAI,CAACK,EAAGI,IACjCjB,EAAC,UAEA,KAAK,SACL,QAAS,IAAM4G,EAAS/F,CAAC,EACzB,MAAO2G,GACP,SAAU3G,EAAE,SAAW,QAAU,GAEjC,UAAAd,EAAC,QAAK,MAAO0H,GAAwB,SAAAxG,EAAM,EAAE,EAC7ClB,EAAC,QAAK,MAAO2H,GACX,SAAA7G,EAAE,QAAQ,OAAS,IAAM,GAAGA,EAAE,QAAQ,MAAM,EAAG,GAAG,CAAC,SAAMA,EAAE,QAC7D,EACCA,EAAE,SAAW,QAAUd,EAAC,QAAK,MAAO4H,GAAsB,gBAAI,IAV1D9G,EAAE,EAWR,CACA,IAhBQL,CAiBV,CACA,EAEH,GACD,CAEF,CAEA,SAAS+F,EAAY,CACpB,QAAAqB,EACA,SAAApB,EACA,OAAAL,EACA,SAAAC,CACD,EAKG,CACF,GAAM,CAACyB,EAAOC,CAAQ,EAAIlI,EAASgI,CAAO,EACpC,CAACG,EAAQC,CAAS,EAAIpI,EAAS,EAAK,EAc1C,OACCI,EAAC,QAAK,SAbc,MAAOmE,GAAiB,CAC5CA,EAAE,eAAe,EACjB,IAAMS,EAAUiD,EAAM,KAAK,EAC3B,GAAKjD,EACL,CAAAoD,EAAU,EAAI,EACd,GAAI,CACH,MAAM7B,EAAOvB,CAAO,CACrB,QAAE,CACDoD,EAAU,EAAK,CAChB,EACD,EAG+B,MAAOC,GACpC,UAAAlI,EAAC,YACA,MAAO8H,EACP,SAAW1D,GAAM2D,EAAS3D,EAAE,OAAO,KAAK,EACxC,YAAY,wBACZ,MAAO+D,GACP,KAAM,EACP,EACAlI,EAAC,OAAI,MAAOmI,GACV,UAAA/B,GACArG,EAAC,UAAO,KAAK,SAAS,QAAS,IAAMqG,EAAS,EAAG,MAAOgC,GAAmB,SAAUL,EAAQ,kBAE7F,EAEDhI,EAAC,OAAI,MAAO,CAAE,KAAM,CAAE,EAAG,EACzBA,EAAC,UAAO,KAAK,SAAS,QAASyG,EAAU,MAAOY,EAAkB,SAAUW,EAAQ,kBAEpF,EACAhI,EAAC,UAAO,KAAK,SAAS,MAAOsI,GAAoB,SAAUN,GAAU,CAACF,EAAM,KAAK,EAC/E,SAAAE,EAAS,eAAY,OACvB,GACD,GACD,CAEF,CAEA,SAAS1B,GAAkBiC,EAAkB9D,EAAsBC,EAAsB,CACxF,GAAM,CAAC8D,EAAKC,CAAM,EAAI5I,EAA+C,IAAI,EAEzE,OAAAF,EAAU,IAAM,CACf,IAAM+I,EAAS,IAAM,CACpB,IAAIlI,EAAqB,KACzB,GAAI,CACHA,EAAK,SAAS,cAAc+H,CAAQ,CACrC,MAAQ,CACP/H,EAAK,IACN,CACA,GAAI,CAACA,EAAI,CACRiI,EAAO,IAAI,EACX,MACD,CACA,IAAME,EAAInI,EAAG,sBAAsB,EACnCiI,EAAO,CACN,IAAKE,EAAE,IAAM,OAAO,QAAUA,EAAE,OAASjE,EAAe,GACxD,KAAMiE,EAAE,KAAO,OAAO,QAAUA,EAAE,MAAQlE,EAAe,EAC1D,CAAC,CACF,EAEAiE,EAAO,EACP,IAAME,EAAK,IAAI,eAAeF,CAAM,EACpC,GAAI,CACH,IAAMlI,EAAK,SAAS,cAAc+H,CAAQ,EACtC/H,GAAIoI,EAAG,QAAQpI,CAAE,CACtB,MAAQ,CAER,CACA,cAAO,iBAAiB,SAAUkI,EAAQ,EAAI,EAC9C,OAAO,iBAAiB,SAAUA,CAAM,EACjC,IAAM,CACZE,EAAG,WAAW,EACd,OAAO,oBAAoB,SAAUF,EAAQ,EAAI,EACjD,OAAO,oBAAoB,SAAUA,CAAM,CAC5C,CACD,EAAG,CAACH,EAAU9D,EAAcC,CAAY,CAAC,EAElC8D,CACR,CAIA,IAAM9C,GAA8B,CACnC,SAAU,QACV,OAAQ,GACR,KAAM,MACN,UAAW,mBACX,OAAQ,WACR,QAAS,OACT,WAAY,SACZ,IAAK,EACL,QAAS,WACT,WAAY,yBACZ,MAAO,QACP,aAAc,IACd,UAAW,8BACX,WACC,6HACD,SAAU,GACV,cAAe,MAChB,EAEME,EAAoC,CACzC,OAAQ,OACR,WAAY,QACZ,MAAO,OACP,QAAS,WACT,aAAc,IACd,OAAQ,UACR,WAAY,IACZ,SAAU,EACX,EAEMD,GAA0C,CAC/C,GAAGC,EACH,WAAY,UACZ,MAAO,OACR,EAEMC,GAAyC,CAC9C,OAAQ,kCACR,WAAY,cACZ,MAAO,QACP,QAAS,WACT,aAAc,IACd,OAAQ,UACR,WAAY,IACZ,SAAU,EACX,EAEMC,GAA4C,CACjD,OAAQ,OACR,WAAY,UACZ,MAAO,QACP,QAAS,WACT,aAAc,IACd,OAAQ,UACR,WAAY,IACZ,SAAU,EACX,EAEMC,GAAkC,CACvC,QAAS,GACT,SAAU,EACX,EAEMQ,EAA6B,CAClC,MAAO,GACP,OAAQ,GACR,aAAc,IACd,WAAY,UACZ,MAAO,QACP,OAAQ,kBACR,OAAQ,UACR,SAAU,GACV,WAAY,IACZ,UAAW,4BACX,QAAS,cACT,WAAY,SACZ,eAAgB,SAChB,QAAS,CACV,EAEM2B,GAA8B,CACnC,WAAY,QACZ,OAAQ,oBACR,aAAc,EACd,QAAS,GACT,MAAO,IACP,UAAW,+BACX,WACC,6HACD,QAAS,OACT,cAAe,SACf,IAAK,EACL,MAAO,MACR,EAEMC,GAA+B,CACpC,MAAO,OACP,OAAQ,oBACR,aAAc,EACd,QAAS,EACT,SAAU,GACV,OAAQ,WACR,WAAY,UACZ,MAAO,OACP,WAAY,QACZ,UAAW,YACZ,EAEMC,GAAqC,CAC1C,QAAS,OACT,WAAY,SACZ,IAAK,CACN,EAEMS,EAA4B,CACjC,OAAQ,wBACR,aAAc,EACd,QAAS,WACT,SAAU,GACV,OAAQ,UACR,WAAY,GACb,EAEMP,GAAoC,CACzC,GAAGO,EACH,WAAY,OACZ,MAAO,OACR,EAEMxB,EAAkC,CACvC,GAAGwB,EACH,WAAY,cACZ,MAAO,UACP,YAAa,SACd,EAEMR,GAAmC,CACxC,GAAGQ,EACH,WAAY,cACZ,MAAO,UACP,YAAa,SACd,EAEM1B,GAA8B,CACnC,SAAU,QACV,IAAK,EACL,MAAO,EACP,OAAQ,EACR,MAAO,IACP,SAAU,OACV,WAAY,QACZ,WAAY,oBACZ,UAAW,gCACX,OAAQ,WACR,QAAS,OACT,cAAe,SACf,WACC,6HACD,MAAO,MACR,EAEMC,GAAoC,CACzC,QAAS,OACT,WAAY,SACZ,eAAgB,gBAChB,QAAS,YACT,aAAc,mBACf,EAEME,GAAoC,CACzC,KAAM,EACN,SAAU,OACV,QAAS,gBACV,EAEMC,GAAmC,CACxC,MAAO,UACP,SAAU,GACV,QAAS,WACT,UAAW,QACZ,EAEMC,GAAkC,CACvC,SAAU,GACV,cAAe,YACf,cAAe,GACf,MAAO,UACP,QAAS,cACT,UAAW,WACZ,EAEMC,GAAkC,CACvC,QAAS,OACT,WAAY,aACZ,IAAK,EACL,MAAO,OACP,UAAW,OACX,WAAY,QACZ,OAAQ,oBACR,aAAc,EACd,QAAS,WACT,OAAQ,UACR,SAAU,GACV,aAAc,EACd,MAAO,MACR,EAEMC,GAAuC,CAC5C,WAAY,EACZ,MAAO,GACP,OAAQ,GACR,aAAc,IACd,WAAY,UACZ,MAAO,QACP,WAAY,IACZ,SAAU,GACV,QAAS,cACT,WAAY,SACZ,eAAgB,QACjB,EAEMC,GAAsC,CAC3C,KAAM,EACN,WAAY,WACZ,UAAW,YACZ,EAEMC,GAAsC,CAC3C,WAAY,EACZ,WAAY,UACZ,MAAO,UACP,SAAU,GACV,WAAY,IACZ,QAAS,UACT,aAAc,EACd,UAAW,QACZ,EAEO,SAASkB,GAAuD,CAMtE,GALA,QAAQ,IAAI,uDAAwD,CACnE,SAAU,OAAO,OAAW,IAC5B,UAAW,QAAQ,IAAI,uBACvB,eAAgB,OAAO,SAAa,KAAe,EAAQ,SAAS,eAAe5I,CAAa,CACjG,CAAC,EACG,OAAO,OAAW,IAAa,OAAO,KAC1C,GAAI,QAAQ,IAAI,yBAA2B,UAC1C,eAAQ,IACP,sEACA,QAAQ,IAAI,sBACb,EACO,KAER,GAAI,SAAS,eAAeA,CAAa,EAAG,OAAO,KAEnD,IAAM6I,EAAY,SAAS,cAAc,KAAK,EAC9CA,EAAU,GAAK7I,EACf6I,EAAU,QAAQ,cAAgB,OAClC,SAAS,KAAK,YAAYA,CAAS,EAEnC,IAAMC,EAAOlJ,EAAWiJ,CAAS,EACjC,OAAAC,EAAK,OAAOhJ,EAAC6C,GAAA,EAAgB,CAAE,EAExB,CACN,QAAS,IAAM,CACdmG,EAAK,QAAQ,EACbD,EAAU,OAAO,CAClB,CACD,CACD,CAGI,OAAO,OAAW,KACrB,QAAQ,IAAI,0CAA2C,CACtD,UAAW,QAAQ,IAAI,uBACvB,WAAY,SAAS,WACrB,cAAe,QAAQ,IAAI,yBAA2B,SACvD,CAAC,EAEE,OAAO,OAAW,KAAe,QAAQ,IAAI,yBAA2B,YACvE,SAAS,aAAe,UAC3B,SAAS,iBAAiB,mBAAoB,IAAM,CACnDD,EAAqB,CACtB,CAAC,EAEDA,EAAqB,GC39BhB,SAASG,GAA+B,CAC1C,OAAO,OAAW,MACtB,QAAQ,IAAI,2CAA2C,EACvDC,GAAwB,EACxBC,GAAyB,EAC1B,CAGAF,EAAuB,EAmBvB,IAAMG,GAAe,IAAI,IAAI,CAAC,OAAQ,SAAU,QAAS,WAAY,MAAM,CAAC,EAE5E,SAASC,GAAoBC,EAAqB,CACjD,GAAIA,EAAG,GAAI,MAAO,GAAGA,EAAG,QAAQ,YAAY,CAAC,IAAI,IAAI,OAAOA,EAAG,EAAE,CAAC,GAElE,IAAMC,EAAkB,CAAC,EACrBC,EAA0BF,EAC9B,KAAOE,GAAWA,IAAY,SAAS,MAAQA,IAAY,SAAS,iBAAiB,CACpF,IAAIC,EAAWD,EAAQ,QAAQ,YAAY,EAE3C,GAAIA,EAAQ,GAAI,CACfD,EAAM,QAAQ,GAAGE,CAAQ,IAAI,IAAI,OAAOD,EAAQ,EAAE,CAAC,EAAE,EACrD,KACD,CAEA,IAAME,EAAU,MAAM,KAAKF,EAAQ,SAAS,EAAE,OAAQG,GAAM,CAACA,EAAE,WAAW,WAAW,CAAC,EAClFD,EAAQ,OAAS,IACpBD,GAAY,IAAIC,EAAQ,IAAKC,GAAM,IAAI,OAAOA,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,IAG5D,IAAMC,EAAyBJ,EAAQ,cACjCK,EAAML,EAAQ,QACpB,GAAII,GACc,MAAM,KAAKA,EAAO,QAAQ,EAAE,OAC3CE,GAAMA,EAAE,UAAYD,IAAQH,EAAQ,SAAW,GAAKA,EAAQ,MAAOC,GAAMG,EAAE,UAAU,SAASH,CAAC,CAAC,EAClG,EACa,OAAS,EAAG,CACxB,IAAMI,EAAQ,MAAM,KAAKH,EAAO,QAAQ,EAAE,QAAQJ,CAAO,EAAI,EAC7DC,GAAY,cAAcM,CAAK,GAChC,CAGDR,EAAM,QAAQE,CAAQ,EACtBD,EAAUA,EAAQ,aACnB,CACA,OAAOD,EAAM,KAAK,KAAK,CACxB,CAEA,SAASS,EAAiBV,EAAaW,EAAgC,CACtE,IAAMC,EAAOZ,EAAG,sBAAsB,EAChCa,EAAW,OAAO,iBAAiBb,CAAE,EACrCc,GAAQd,EAAG,aAAe,IAAI,KAAK,EACzC,MAAO,CACN,IAAKA,EAAG,QAAQ,YAAY,EAC5B,GAAIA,EAAG,IAAM,OACb,QAAS,MAAM,KAAKA,EAAG,SAAS,EAAE,OAAQ,GAAM,CAAC,EAAE,WAAW,WAAW,CAAC,EAC1E,YAAac,EAAK,OAASH,EAAY,GAAGG,EAAK,MAAM,EAAGH,CAAS,CAAC,SAAMG,EACxE,YAAaf,GAAoBC,CAAE,EACnC,aAAc,CAAE,IAAKY,EAAK,IAAK,KAAMA,EAAK,KAAM,MAAOA,EAAK,MAAO,OAAQA,EAAK,MAAO,EACvF,eAAgB,CACf,MAAOC,EAAS,MAChB,gBAAiBA,EAAS,gBAC1B,SAAUA,EAAS,SACnB,WAAYA,EAAS,WACrB,QAASA,EAAS,QAClB,OAAQA,EAAS,MAClB,CACD,CACD,CAEA,SAASE,EAAaf,EAAoBgB,EAAoC,CAE7E,GADI,CAAChB,GAAM,CAACA,EAAG,SACXF,GAAa,IAAIE,EAAG,OAAO,EAAG,MAAO,GACzC,QAAWiB,KAAQD,EAClB,GAAIhB,EAAG,eAAeiB,CAAI,EAAG,MAAO,GAErC,MAAO,EACR,CAEA,SAASrB,IAA0B,CAClC,IAAIsB,EAAU,GACVC,EAAiC,KACjCC,EAA+D,KAE7DC,EAAU,SAAS,cAAc,KAAK,EAC5CA,EAAQ,aAAa,0BAA2B,OAAO,EACvDA,EAAQ,MAAM,QAAU,CACvB,kBACA,uBACA,sBACA,4BACA,uCACA,qBACA,gBACA,8DACD,EAAE,KAAK,GAAG,EAEV,IAAMC,EAAQ,SAAS,cAAc,KAAK,EAC1CA,EAAM,aAAa,0BAA2B,OAAO,EACrDA,EAAM,MAAM,QAAU,CACrB,kBACA,uBACA,sBACA,sBACA,cACA,kBACA,uCACA,mBACA,qBACA,sBACA,eACD,EAAE,KAAK,GAAG,EAEV,SAAS,gBAAgB,YAAYD,CAAO,EAC5C,SAAS,gBAAgB,YAAYC,CAAK,EAE1C,IAAMC,EAAQ,SAAS,cAAc,OAAO,EAC5CA,EAAM,aAAa,0BAA2B,OAAO,EACrDA,EAAM,YAAc,sFACpB,SAAS,KAAK,YAAYA,CAAK,EAE/B,SAASC,EAAgBxB,EAAa,CACrC,IAAMY,EAAOZ,EAAG,sBAAsB,EACtCqB,EAAQ,MAAM,IAAM,GAAGT,EAAK,GAAG,KAC/BS,EAAQ,MAAM,KAAO,GAAGT,EAAK,IAAI,KACjCS,EAAQ,MAAM,MAAQ,GAAGT,EAAK,KAAK,KACnCS,EAAQ,MAAM,OAAS,GAAGT,EAAK,MAAM,KACrCS,EAAQ,MAAM,QAAU,QAExB,IAAMjB,EAAU,MAAM,KAAKJ,EAAG,SAAS,EAAE,OAAQK,GAAM,CAACA,EAAE,WAAW,WAAW,CAAC,EAC3EoB,EAAYzB,EAAG,QAAQ,YAAY,GAAKI,EAAQ,OAAS,IAAIA,EAAQ,CAAC,CAAC,GAAK,IAClFkB,EAAM,YAAcG,EACpBH,EAAM,MAAM,KAAO,GAAGV,EAAK,IAAI,KAC/BU,EAAM,MAAM,IAAM,GAAG,KAAK,IAAI,EAAGV,EAAK,IAAM,EAAE,CAAC,KAC/CU,EAAM,MAAM,QAAU,OACvB,CAEA,IAAMI,EAAmBC,GAAkB,CAC1C,IAAM3B,EAAK,SAAS,iBAAiB2B,EAAE,QAASA,EAAE,OAAO,EACzD,GAAIZ,EAAaf,EAAI,CAAC,yBAAyB,CAAC,EAAG,CAClDqB,EAAQ,MAAM,QAAU,OACxBC,EAAM,MAAM,QAAU,OACtBH,EAAiB,KACjB,MACD,CACAA,EAAiBnB,EACjB,sBAAsB,IAAM,CACvBmB,IAAmBnB,GAAMkB,GAAWlB,GACvCwB,EAAgBxB,CAAE,CAEpB,CAAC,CACF,EAEM4B,EAAmB,IAAM,CAC9BP,EAAQ,MAAM,QAAU,OACxBC,EAAM,MAAM,QAAU,OACtBH,EAAiB,IAClB,EAEMU,EAAeF,GAAkB,CACtC,GAAI,CAACT,GAAW,CAACC,EAAgB,OACjCQ,EAAE,eAAe,EACjBA,EAAE,gBAAgB,EAClBA,EAAE,yBAAyB,EAE3B,IAAM3B,EAAKmB,EACX,GAAIJ,EAAaf,EAAI,CAAC,yBAAyB,CAAC,EAAG,OAEnD,IAAM8B,EAAOpB,EAAiBV,EAAI,GAAG,EAC/BG,EAAW2B,EAAK,YAElBV,GACHA,EAAgB,GAAG,gBAAgB,mBAAmB,EAGnDA,GAAmBA,EAAgB,cAAgBjB,GACtDiB,EAAkB,KAClB,OAAO,OAAO,YAAY,CAAE,KAAM,qBAAsB,KAAMU,CAAK,EAAG,GAAG,IAEzEV,EAAkB,CAAE,GAAApB,EAAI,YAAaG,CAAS,EAC9CH,EAAG,aAAa,oBAAqB,EAAE,EACvC,OAAO,OAAO,YAAY,CAAE,KAAM,mBAAoB,KAAM8B,CAAK,EAAG,GAAG,EAEzE,EAEMC,EAAiBJ,GAAqB,CACvCA,EAAE,MAAQ,UAAYT,IACzBc,EAAkB,EAClB,OAAO,OAAO,YAAY,CAAE,KAAM,qBAAsB,EAAG,GAAG,EAEhE,EAEA,SAASC,GAAmB,CAC3Bf,EAAU,GACV,SAAS,iBAAiB,YAAaQ,EAAiB,EAAI,EAC5D,SAAS,iBAAiB,aAAcE,CAAgB,EACxD,SAAS,iBAAiB,QAASC,EAAa,EAAI,EACpD,SAAS,iBAAiB,UAAWE,EAAe,EAAI,CACzD,CAEA,SAASC,GAAoB,CAC5Bd,EAAU,GACV,SAAS,oBAAoB,YAAaQ,EAAiB,EAAI,EAC/D,SAAS,oBAAoB,aAAcE,CAAgB,EAC3D,SAAS,oBAAoB,QAASC,EAAa,EAAI,EACvD,SAAS,oBAAoB,UAAWE,EAAe,EAAI,EAE3DV,EAAQ,MAAM,QAAU,OACxBC,EAAM,MAAM,QAAU,OACtBH,EAAiB,KAEbC,IACHA,EAAgB,GAAG,gBAAgB,mBAAmB,EACtDA,EAAkB,KAEpB,CAEA,OAAO,iBAAiB,UAAYc,GAAU,CAC7C,IAAMC,EAAMD,EAAM,KACd,CAACC,GAAO,OAAOA,GAAQ,WAEvBA,EAAI,OAAS,uBACZA,EAAI,QACPF,EAAiB,GAEjBD,EAAkB,EAClB,OAAO,OAAO,YAAY,CAAE,KAAM,qBAAsB,EAAG,GAAG,IAI5DG,EAAI,OAAS,wBACZf,IACHA,EAAgB,GAAG,gBAAgB,mBAAmB,EACtDA,EAAkB,MAGrB,CAAC,CACF,CAEA,SAASvB,IAA2B,CACnC,IAAIqB,EAAU,GACVC,EAAiC,KACjCiB,EAAgE,CAAC,EAE/Df,EAAU,SAAS,cAAc,KAAK,EAC5CA,EAAQ,aAAa,2BAA4B,OAAO,EACxDA,EAAQ,MAAM,QAAU,CACvB,kBACA,uBACA,sBACA,6BACA,uCACA,qBACA,gBACA,8DACD,EAAE,KAAK,GAAG,EAEV,IAAMgB,EAAc,SAAS,cAAc,KAAK,EAChDA,EAAY,aAAa,2BAA4B,cAAc,EACnEA,EAAY,YAAc,mBAC1BA,EAAY,MAAM,QAAU,CAC3B,kBACA,uBACA,sBACA,sBACA,cACA,kBACA,oDACA,mBACA,qBACA,sBACA,gBACA,wCACD,EAAE,KAAK,GAAG,EAEV,IAAMC,EAAe,SAAS,cAAc,KAAK,EACjDA,EAAa,aAAa,2BAA4B,MAAM,EAC5DA,EAAa,MAAM,QAClB,0GAED,SAAS,gBAAgB,YAAYjB,CAAO,EAC5C,SAAS,gBAAgB,YAAYgB,CAAW,EAChD,SAAS,gBAAgB,YAAYC,CAAY,EAEjD,SAASC,GAAa,CACrBD,EAAa,UAAY,GACzB,QAAWE,KAAOJ,EAAM,CACvB,IAAIpC,EAAqB,KACzB,GAAI,CACHA,EAAK,SAAS,cAAcwC,EAAI,QAAQ,CACzC,MAAQ,CACPxC,EAAK,IACN,CACA,GAAI,CAACA,EAAI,SAET,IAAMyC,EAAIzC,EAAG,sBAAsB,EAC7B0C,EAAQ,SAAS,cAAc,KAAK,EAC1CA,EAAM,MAAM,QAAU,CACrB,kBACA,QAAQD,EAAE,IAAM,EAAE,KAClB,SAASA,EAAE,KAAOA,EAAE,MAAQ,EAAI,EAAE,KAClC,cACA,eACA,qBACA,sBACA,cACA,gBACA,sBACA,0BACA,kBACA,mBACA,oDACA,wCACA,sBACD,EAAE,KAAK,GAAG,EACVC,EAAM,YAAc,OAAOF,EAAI,MAAM,EACrCF,EAAa,YAAYI,CAAK,CAC/B,CACD,CAEA,IAAMhB,EAAmBC,GAAkB,CAC1C,IAAM3B,EAAK,SAAS,iBAAiB2B,EAAE,QAASA,EAAE,OAAO,EACzD,GAAIZ,EAAaf,EAAI,CAAC,2BAA4B,yBAAyB,CAAC,EAAG,CAC9EqB,EAAQ,MAAM,QAAU,OACxBgB,EAAY,MAAM,QAAU,OAC5BlB,EAAiB,KACjB,MACD,CACAA,EAAiBnB,EACjB,sBAAsB,IAAM,CAC3B,GAAImB,IAAmBnB,GAAMkB,GAAWlB,EAAI,CAC3C,IAAMY,EAAOZ,EAAG,sBAAsB,EACtCqB,EAAQ,MAAM,IAAM,GAAGT,EAAK,GAAG,KAC/BS,EAAQ,MAAM,KAAO,GAAGT,EAAK,IAAI,KACjCS,EAAQ,MAAM,MAAQ,GAAGT,EAAK,KAAK,KACnCS,EAAQ,MAAM,OAAS,GAAGT,EAAK,MAAM,KACrCS,EAAQ,MAAM,QAAU,QAExBgB,EAAY,MAAM,KAAO,GAAGV,EAAE,QAAU,EAAE,KAC1CU,EAAY,MAAM,IAAM,GAAGV,EAAE,QAAU,EAAE,KACzCU,EAAY,MAAM,QAAU,OAC7B,CACD,CAAC,CACF,EAEMT,EAAmB,IAAM,CAC9BP,EAAQ,MAAM,QAAU,OACxBgB,EAAY,MAAM,QAAU,OAC5BlB,EAAiB,IAClB,EAEMU,EAAeF,GAAkB,CACtC,GAAI,CAACT,GAAW,CAACC,EAAgB,OACjCQ,EAAE,eAAe,EACjBA,EAAE,gBAAgB,EAClBA,EAAE,yBAAyB,EAE3B,IAAM3B,EAAKmB,EACX,GAAIJ,EAAaf,EAAI,CAAC,2BAA4B,yBAAyB,CAAC,EAAG,OAE/E,IAAM8B,EAAOpB,EAAiBV,EAAI,GAAG,EAC/BY,EAAOZ,EAAG,sBAAsB,EAEtC,OAAO,OAAO,YACb,CACC,KAAM,gBACN,KAAM,CACL,QAAS8B,EACT,cAAe,CAAE,EAAGH,EAAE,QAAS,EAAGA,EAAE,OAAQ,EAC5C,YAAa,CAAE,IAAKf,EAAK,IAAK,KAAMA,EAAK,KAAM,MAAOA,EAAK,MAAO,OAAQA,EAAK,MAAO,EACtF,SAAU,OAAO,SAAS,QAC3B,CACD,EACA,GACD,CACD,EAEMmB,EAAiBJ,GAAqB,CACvCA,EAAE,MAAQ,UAAYT,IACzByB,EAAmB,EACnB,OAAO,OAAO,YAAY,CAAE,KAAM,sBAAuB,EAAG,GAAG,EAEjE,EAEA,SAASC,GAAoB,CAC5B1B,EAAU,GACV,SAAS,KAAK,MAAM,OAAS,YAC7B,SAAS,iBAAiB,YAAaQ,EAAiB,EAAI,EAC5D,SAAS,iBAAiB,aAAcE,CAAgB,EACxD,SAAS,iBAAiB,QAASC,EAAa,EAAI,EACpD,SAAS,iBAAiB,UAAWE,EAAe,EAAI,CACzD,CAEA,SAASY,GAAqB,CAC7BzB,EAAU,GACV,SAAS,KAAK,MAAM,OAAS,GAC7B,SAAS,oBAAoB,YAAaQ,EAAiB,EAAI,EAC/D,SAAS,oBAAoB,aAAcE,CAAgB,EAC3D,SAAS,oBAAoB,QAASC,EAAa,EAAI,EACvD,SAAS,oBAAoB,UAAWE,EAAe,EAAI,EAE3DV,EAAQ,MAAM,QAAU,OACxBgB,EAAY,MAAM,QAAU,OAC5BlB,EAAiB,IAClB,CAEA,IAAI0B,EAAa,GACXC,EAAiB,IAAM,CACxBD,IACJA,EAAa,GACb,sBAAsB,IAAM,CAC3BA,EAAa,GACbN,EAAW,CACZ,CAAC,EACF,EACA,OAAO,iBAAiB,SAAUO,EAAgB,CAAE,QAAS,EAAK,CAAC,EACnE,OAAO,iBAAiB,SAAUA,EAAgB,CAAE,QAAS,EAAK,CAAC,EAEnE,OAAO,iBAAiB,UAAYZ,GAAU,CAC7C,IAAMC,EAAMD,EAAM,KACd,CAACC,GAAO,OAAOA,GAAQ,WAEvBA,EAAI,OAAS,wBACZA,EAAI,QAASS,EAAkB,EAC9BD,EAAmB,GAGrBR,EAAI,OAAS,wBAChBC,EAAOD,EAAI,MAAQ,CAAC,EACpBI,EAAW,GAGRJ,EAAI,OAAS,uBAChBC,EAAOA,EAAK,OAAQW,GAAMA,EAAE,KAAOZ,EAAI,KAAK,EAC5CI,EAAW,GAEb,CAAC,CACF","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","FeedbackToolbar","session","setSession","loading","setLoading","pinMode","setPinMode","pending","setPending","editingId","setEditingId","sidebarOpen","setSidebarOpen","finalizing","setFinalizing","apiBase","controller","res","data","overlay","label","hovered","handleMove","e","rect","handleLeave","handleClick","event","offsetXRatio","offsetYRatio","refreshComments","submitNewComment","content","updateComment","id","removeComment","finalizeSession","prev","scrollToComment","comment","visiblePins","pin","PinOverlay","PendingCommentPopover","CommentsSidebar","toolbarStyle","toolbarButtonActiveStyle","toolbarButtonStyle","toolbarButtonGhostStyle","toolbarButtonFinalizeStyle","toolbarHintStyle","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","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
|
|
2
|
-
`)},U=e=>{let t=e;for(;t;){if(t instanceof HTMLElement&&t.dataset.ynsFeedbackUi==="true")return!0;t=t.parentElement}return!1};function X(){let[e,t]=g(null),[o,n]=g(!0),[r,s]=g(!1),[c,d]=g(null),[l,m]=g(null),a=L(null);y(()=>{if(a.current=_(),!a.current){n(!1);return}let i=new AbortController;return fetch(`${a.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,{credentials:"include",signal:i.signal}).then(async u=>{if(!u.ok){t(null);return}let f=await u.json();t(f)}).catch(()=>{t(null)}).finally(()=>n(!1)),()=>i.abort()},[]),y(()=>{if(r)return document.body.style.cursor="crosshair",()=>{document.body.style.cursor=""}},[r]),y(()=>{if(!r)return;let i=u=>{let f=u.target;if(!(f instanceof Element)||U(f))return;u.preventDefault(),u.stopPropagation();let h=f.getBoundingClientRect();d({cssSelector:B(f),pagePath:window.location.pathname,surroundingHtml:F(f),rect:{top:h.top+window.scrollY,left:h.left+window.scrollX,width:h.width,height:h.height},clickX:u.clientX+window.scrollX,clickY:u.clientY+window.scrollY}),s(!1)};return document.addEventListener("click",i,{capture:!0}),()=>document.removeEventListener("click",i,{capture:!0})},[r]);let b=async()=>{if(!a.current)return;let i=await fetch(`${a.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,{credentials:"include"});if(!i.ok)return;let u=await i.json();t(u)},N=async i=>{!a.current||!e||!c||!(await fetch(`${a.current}/api/feedback-comments`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({feedbackSessionId:e.feedbackSessionId,content:i,pagePath:c.pagePath,cssSelector:c.cssSelector,surroundingHtml:c.surroundingHtml})})).ok||(d(null),await b())},T=async(i,u)=>{!a.current||!(await fetch(`${a.current}/api/feedback-comments/${i}`,{method:"PATCH",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({content:u})})).ok||(m(null),await b())},I=async i=>{!a.current||!(await fetch(`${a.current}/api/feedback-comments/${i}`,{method:"DELETE",credentials:"include"})).ok||await b()};if(o||!e||!e.canComment)return null;let C=e.comments.filter(i=>i.pagePath===window.location.pathname&&i.status!=="done");return S("div",{"data-yns-feedback-ui":"true",children:[C.map((i,u)=>p(V,{pin:i,number:u+1,editing:l===i.id,onStartEdit:()=>m(i.id),onCancelEdit:()=>m(null),onSave:f=>T(i.id,f),onRemove:()=>I(i.id)},i.id)),c&&p(z,{pending:c,onCancel:()=>d(null),onSave:i=>N(i)}),S("div",{style:O,children:[p("button",{type:"button",onClick:()=>s(i=>!i),style:r?M:$,children:r?"Cancel":"Add comment"}),p("span",{style:Y,children:r?"Click any element to comment":`${C.length} on this page`})]})]})}function V({pin:e,number:t,editing:o,onStartEdit:n,onCancelEdit:r,onSave:s,onRemove:c}){let d=j(e.cssSelector);return d?S("div",{style:{position:"absolute",top:d.top,left:d.left,zIndex:2147483600,pointerEvents:"auto"},children:[p("button",{type:"button",onClick:n,style:D,title:e.content,children:t}),o&&p(x,{initial:e.content,onCancel:r,onSave:s,onRemove:c})]}):null}function z({pending:e,onCancel:t,onSave:o}){return p("div",{style:{position:"absolute",top:e.clickY+10,left:e.clickX+10,zIndex:2147483647},children:p(x,{initial:"",onCancel:t,onSave:o})})}function x({initial:e,onCancel:t,onSave:o,onRemove:n}){let[r,s]=g(e),[c,d]=g(!1);return S("form",{onSubmit:async m=>{m.preventDefault();let a=r.trim();if(a){d(!0);try{await o(a)}finally{d(!1)}}},style:H,children:[p("textarea",{value:r,onChange:m=>s(m.target.value),placeholder:"Leave a comment\u2026",style:W,rows:3}),S("div",{style:q,children:[n&&p("button",{type:"button",onClick:()=>n(),style:K,disabled:c,children:"Delete"}),p("div",{style:{flex:1}}),p("button",{type:"button",onClick:t,style:G,disabled:c,children:"Cancel"}),p("button",{type:"submit",style:J,disabled:c||!r.trim(),children:c?"Saving\u2026":"Save"})]})]})}function j(e){let[t,o]=g(null);return y(()=>{let n=()=>{let s=null;try{s=document.querySelector(e)}catch{s=null}if(!s){o(null);return}let c=s.getBoundingClientRect();o({top:c.top+window.scrollY-12,left:c.left+window.scrollX-12})};n();let r=new ResizeObserver(n);try{let s=document.querySelector(e);s&&r.observe(s)}catch{}return window.addEventListener("scroll",n,!0),window.addEventListener("resize",n),()=>{r.disconnect(),window.removeEventListener("scroll",n,!0),window.removeEventListener("resize",n)}},[e]),t}var O={position:"fixed",bottom:16,left:"50%",transform:"translateX(-50%)",zIndex:2147483646,display:"flex",alignItems:"center",gap:12,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"},$={border:"none",background:"white",color:"#111",padding:"6px 12px",borderRadius:999,cursor:"pointer",fontWeight:600,fontSize:13},M={...$,background:"#ef4444",color:"white"},Y={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},H={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"},W={width:"100%",border:"1px solid #d1d5db",borderRadius:6,padding:8,fontSize:14,resize:"vertical",fontFamily:"inherit",color:"#111",background:"white",boxSizing:"border-box"},q={display:"flex",alignItems:"center",gap:6},E={border:"1px solid transparent",borderRadius:6,padding:"6px 10px",fontSize:13,cursor:"pointer",fontWeight:500},J={...E,background:"#111",color:"white"},G={...E,background:"transparent",color:"#374151",borderColor:"#d1d5db"},K={...E,background:"transparent",color:"#b91c1c",borderColor:"#fecaca"};function P(){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(w)}),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(w))return null;let e=document.createElement("div");e.id=w,e.dataset.ynsFeedbackUi="true",document.body.appendChild(e);let t=R(e);return t.render(p(X,{})),{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",()=>{P()}):P());export{P as mountFeedbackToolbar};
|
|
1
|
+
import{useEffect as C,useRef as V,useState as h}from"react";import{createRoot as H}from"react-dom/client";import{Fragment as we,jsx as d,jsxs as y}from"react/jsx-runtime";var P="yns-feedback-toolbar-root",D=()=>{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,l=window.location.protocol;return t.endsWith(".yns.store")?`${l}//yns.store`:t.endsWith(".yns.cx")?`${l}//yns.cx`:window.location.origin},W=e=>{if(e.id)return`#${CSS.escape(e.id)}`;let t=[],l=e;for(;l&&l.nodeType===Node.ELEMENT_NODE&&t.length<6;){let c=l.tagName.toLowerCase(),s=l.getAttribute("class");if(s){let o=s.split(/\s+/).filter(Boolean).slice(0,2).map(a=>`.${CSS.escape(a)}`).join("");c+=o}let u=l.parentElement,r=l.tagName;if(u){let o=Array.from(u.children).filter(a=>a.tagName===r);if(o.length>1){let a=o.indexOf(l)+1;c+=`:nth-of-type(${a})`}}t.unshift(c),l=u}return t.join(" > ")},q=["id","class","data-testid","aria-label","role","name","href","src"],R=e=>{let t=[];for(let l of q){let c=e.getAttribute(l);if(!c)continue;let s=c.length>80?`${c.slice(0,80)}\u2026`:c;t.push(` ${l}="${s.replace(/"/g,""")}"`)}return t.join("")},N=e=>{let t=(e.textContent??"").replace(/\s+/g," ").trim();return t?t.length>100?`${t.slice(0,100)}\u2026`:t:""},G=e=>{let t=[],l=e;for(;l&&l!==document.documentElement&&t.length<5;){let a=l.parentElement;if(!a)break;t.unshift(a),l=a}let c=[],s=0;for(let a of t){let m=" ".repeat(s);c.push(`${m}<${a.tagName.toLowerCase()}${R(a)}>`),s++}let u=" ".repeat(s),r=e.tagName.toLowerCase(),o=N(e);if(c.push(`${u}<${r}${R(e)}>${o?`${o}</${r}>`:""} \u2190 TARGET`),!o){let a=" ".repeat(s+1),m=Array.from(e.children).slice(0,6);for(let f of m){let w=N(f);c.push(`${a}<${f.tagName.toLowerCase()}${R(f)}>${w?`${w}</${f.tagName.toLowerCase()}>`:""}`)}e.children.length>6&&c.push(`${a}\u2026 (${e.children.length-6} more children)`),c.push(`${u}</${r}>`)}for(let a=t.length-1;a>=0;a--){let m=" ".repeat(a),f=t[a];f&&c.push(`${m}</${f.tagName.toLowerCase()}>`)}return c.join(`
|
|
2
|
+
`)},z=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"]);function K(){let[e,t]=h(null),[l,c]=h(!0),[s,u]=h(!1),[r,o]=h(null),[a,m]=h(null),[f,w]=h(!1),[I,T]=h(!1),b=V(null);C(()=>{if(b.current=D(),!b.current){c(!1);return}let n=new AbortController;return fetch(`${b.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,{credentials:"include",signal:n.signal}).then(async i=>{if(!i.ok){t(null);return}let p=await i.json();t(p)}).catch(()=>{t(null)}).finally(()=>c(!1)),()=>n.abort()},[]),C(()=>{if(s)return document.body.style.cursor="crosshair",()=>{document.body.style.cursor=""}},[s]),C(()=>{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 i=document.createElement("div");i.dataset.ynsFeedbackUi="true",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(";"),document.documentElement.appendChild(n),document.documentElement.appendChild(i);let p=null,g=S=>{let v=document.elementFromPoint(S.clientX,S.clientY);if(!v||J.has(v.tagName)||z(v)){n.style.display="none",i.style.display="none",p=null;return}p=v,requestAnimationFrame(()=>{if(p!==v)return;let E=v.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",i.style.left=`${S.clientX+14}px`,i.style.top=`${S.clientY+14}px`,i.style.display="block"})},x=()=>{n.style.display="none",i.style.display="none",p=null};return document.addEventListener("mousemove",g,!0),document.addEventListener("mouseleave",x),()=>{document.removeEventListener("mousemove",g,!0),document.removeEventListener("mouseleave",x),n.remove(),i.remove()}},[s]),C(()=>{if(!s)return;let n=i=>{let p=i.target;if(!(p instanceof Element)||z(p))return;i.preventDefault(),i.stopPropagation();let g=p.getBoundingClientRect(),x=g.width>0?(i.clientX-g.left)/g.width:.5,S=g.height>0?(i.clientY-g.top)/g.height:.5;o({cssSelector:W(p),pagePath:window.location.pathname,surroundingHtml:G(p),rect:{top:g.top+window.scrollY,left:g.left+window.scrollX,width:g.width,height:g.height},clickX:i.clientX+window.scrollX,clickY:i.clientY+window.scrollY,offsetXRatio:Math.min(1,Math.max(0,x)),offsetYRatio:Math.min(1,Math.max(0,S))}),u(!1)};return document.addEventListener("click",n,{capture:!0}),()=>document.removeEventListener("click",n,{capture:!0})},[s]);let k=async()=>{if(!b.current)return;let n=await fetch(`${b.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,{credentials:"include"});if(!n.ok)return;let i=await n.json();t(i)},Y=async n=>{!b.current||!e||!r||!(await fetch(`${b.current}/api/feedback-comments`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({feedbackSessionId:e.feedbackSessionId,content:n,pagePath:r.pagePath,cssSelector:r.cssSelector,surroundingHtml:r.surroundingHtml,offsetXRatio:r.offsetXRatio,offsetYRatio:r.offsetYRatio})})).ok||(o(null),await k())},U=async(n,i)=>{!b.current||!(await fetch(`${b.current}/api/feedback-comments/${n}`,{method:"PATCH",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({content:i})})).ok||(m(null),await k())},M=async n=>{!b.current||!(await fetch(`${b.current}/api/feedback-comments/${n}`,{method:"DELETE",credentials:"include"})).ok||await k()},j=async()=>{if(!(!b.current||!e)&&window.confirm("Finalize this feedback session? You won't be able to add more comments.")){T(!0);try{if(!(await fetch(`${b.current}/api/feedback-sessions/${e.feedbackSessionId}/finalize`,{method:"POST",credentials:"include"})).ok)return;t(i=>i&&{...i,canComment:!1})}finally{T(!1)}}},O=n=>{if(n.pagePath!==window.location.pathname){window.location.assign(n.pagePath);return}let i=null;try{i=document.querySelector(n.cssSelector)}catch{i=null}i&&(i.scrollIntoView({behavior:"smooth",block:"center"}),m(n.id))};if(l||!e||!e.canComment)return null;let L=e.comments.filter(n=>n.pagePath===window.location.pathname&&n.status!=="done");return y("div",{"data-yns-feedback-ui":"true",children:[L.map((n,i)=>d(Q,{pin:n,number:i+1,editing:a===n.id,onStartEdit:()=>m(n.id),onCancelEdit:()=>m(null),onSave:p=>U(n.id,p),onRemove:()=>M(n.id)},n.id)),r&&d(Z,{pending:r,onCancel:()=>o(null),onSave:n=>Y(n)}),f&&d(ee,{comments:e.comments,currentPath:window.location.pathname,onClose:()=>w(!1),onSelect:O}),y("div",{style:ne,children:[d("button",{type:"button",onClick:()=>u(n=>!n),style:s?oe:A,children:s?"Cancel":"Add comment"}),d("button",{type:"button",onClick:()=>w(n=>!n),style:ie,children:f?"Hide list":`List (${e.comments.length})`}),d("button",{type:"button",onClick:j,disabled:I,style:re,children:I?"Finalizing\u2026":"Finalize"}),d("span",{style:se,children:s?"Click any element to comment":`${L.length} on this page`})]})]})}function Q({pin:e,number:t,editing:l,onStartEdit:c,onCancelEdit:s,onSave:u,onRemove:r}){let o=te(e.cssSelector,e.offsetXRatio,e.offsetYRatio);return o?y("div",{style:{position:"absolute",top:o.top,left:o.left,zIndex:2147483600,pointerEvents:"auto"},children:[d("button",{type:"button",onClick:c,style:_,title:e.content,children:t}),l&&d(F,{initial:e.content,onCancel:s,onSave:u,onRemove:r})]}):null}function Z({pending:e,onCancel:t,onSave:l}){return y(we,{children:[d("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:d("div",{style:_,children:"\u2022"})}),d("div",{style:{position:"absolute",top:e.clickY+10,left:e.clickX+10,zIndex:2147483647},children:d(F,{initial:"",onCancel:t,onSave:l})})]})}function ee({comments:e,currentPath:t,onClose:l,onSelect:c}){let s=new Map;for(let r of e){let o=s.get(r.pagePath)??[];o.push(r),s.set(r.pagePath,o)}let u=Array.from(s.keys()).sort((r,o)=>r===t?-1:o===t?1:r.localeCompare(o));return y("div",{style:pe,children:[y("div",{style:me,children:[y("strong",{style:{fontSize:14},children:["Comments (",e.length,")"]}),d("button",{type:"button",onClick:l,style:X,children:"Close"})]}),d("div",{style:fe,children:e.length===0?d("div",{style:be,children:"No comments yet. Click \u201CAdd comment\u201D to leave one."}):u.map(r=>y("div",{style:{marginBottom:12},children:[d("div",{style:ge,children:r===t?`${r} \xB7 current`:r}),(s.get(r)??[]).map((o,a)=>y("button",{type:"button",onClick:()=>c(o),style:ye,disabled:o.status==="done"&&!1,children:[d("span",{style:he,children:a+1}),d("span",{style:Se,children:o.content.length>140?`${o.content.slice(0,140)}\u2026`:o.content}),o.status==="done"&&d("span",{style:ve,children:"done"})]},o.id))]},r))})]})}function F({initial:e,onCancel:t,onSave:l,onRemove:c}){let[s,u]=h(e),[r,o]=h(!1);return y("form",{onSubmit:async m=>{m.preventDefault();let f=s.trim();if(f){o(!0);try{await l(f)}finally{o(!1)}}},style:le,children:[d("textarea",{value:s,onChange:m=>u(m.target.value),placeholder:"Leave a comment\u2026",style:ae,rows:3}),y("div",{style:ce,children:[c&&d("button",{type:"button",onClick:()=>c(),style:ue,disabled:r,children:"Delete"}),d("div",{style:{flex:1}}),d("button",{type:"button",onClick:t,style:X,disabled:r,children:"Cancel"}),d("button",{type:"submit",style:de,disabled:r||!s.trim(),children:r?"Saving\u2026":"Save"})]})]})}function te(e,t,l){let[c,s]=h(null);return C(()=>{let u=()=>{let o=null;try{o=document.querySelector(e)}catch{o=null}if(!o){s(null);return}let a=o.getBoundingClientRect();s({top:a.top+window.scrollY+a.height*l-12,left:a.left+window.scrollX+a.width*t-12})};u();let r=new ResizeObserver(u);try{let o=document.querySelector(e);o&&r.observe(o)}catch{}return window.addEventListener("scroll",u,!0),window.addEventListener("resize",u),()=>{r.disconnect(),window.removeEventListener("scroll",u,!0),window.removeEventListener("resize",u)}},[e,t,l]),c}var ne={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"},A={border:"none",background:"white",color:"#111",padding:"6px 12px",borderRadius:999,cursor:"pointer",fontWeight:600,fontSize:13},oe={...A,background:"#ef4444",color:"white"},ie={border:"1px solid rgba(255,255,255,0.4)",background:"transparent",color:"white",padding:"6px 12px",borderRadius:999,cursor:"pointer",fontWeight:500,fontSize:13},re={border:"none",background:"#10b981",color:"white",padding:"6px 12px",borderRadius:999,cursor:"pointer",fontWeight:600,fontSize:13},se={opacity:.8,fontSize:12},_={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},le={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"},ae={width:"100%",border:"1px solid #d1d5db",borderRadius:6,padding:8,fontSize:14,resize:"vertical",fontFamily:"inherit",color:"#111",background:"white",boxSizing:"border-box"},ce={display:"flex",alignItems:"center",gap:6},$={border:"1px solid transparent",borderRadius:6,padding:"6px 10px",fontSize:13,cursor:"pointer",fontWeight:500},de={...$,background:"#111",color:"white"},X={...$,background:"transparent",color:"#374151",borderColor:"#d1d5db"},ue={...$,background:"transparent",color:"#b91c1c",borderColor:"#fecaca"},pe={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"},me={display:"flex",alignItems:"center",justifyContent:"space-between",padding:"12px 16px",borderBottom:"1px solid #e5e7eb"},fe={flex:1,overflow:"auto",padding:"12px 12px 24px"},be={color:"#6b7280",fontSize:13,padding:"24px 8px",textAlign:"center"},ge={fontSize:11,textTransform:"uppercase",letterSpacing:.5,color:"#6b7280",padding:"4px 4px 6px",wordBreak:"break-all"},ye={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"},he={flexShrink:0,width:22,height:22,borderRadius:999,background:"#10b981",color:"white",fontWeight:700,fontSize:11,display:"inline-flex",alignItems:"center",justifyContent:"center"},Se={flex:1,whiteSpace:"pre-wrap",wordBreak:"break-word"},ve={flexShrink:0,background:"#d1fae5",color:"#065f46",fontSize:11,fontWeight:600,padding:"2px 6px",borderRadius:4,alignSelf:"center"};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(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=H(e);return t.render(d(K,{})),{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());export{B 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}\n\ninterface ActiveSession {\n\tfeedbackSessionId: string;\n\tcomments: FeedbackComment[];\n\tcanComment: boolean;\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}\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 the per-store preview deploy (e.g. mystore-preview.yns.store)\n\t// which is served by the merchant's own Vercel project — that host does NOT\n\t// host the YNS API. Strip the `-preview` suffix so the call lands on the\n\t// multi-tenant YNS app (mystore.yns.store) where the API lives. Same eTLD+1\n\t// means better-auth cookies scoped to `.yns.store` travel along.\n\tconst host = window.location.host;\n\tconst protocol = window.location.protocol;\n\tconst dotIdx = host.indexOf(\".\");\n\tif (dotIdx > 0) {\n\t\tconst sub = host.slice(0, dotIdx);\n\t\tif (sub.endsWith(\"-preview\")) {\n\t\t\tconst stripped = sub.slice(0, -\"-preview\".length);\n\t\t\treturn `${protocol}//${stripped}${host.slice(dotIdx)}`;\n\t\t}\n\t}\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\n/**\n * Build a compact HTML-like outline of the clicked element + its ancestors and a\n * sample of nearby children. Marks the target with `← TARGET` so the AI agent\n * can locate it later. Output stays small (< 2KB typical).\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\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 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\tconst controller = new AbortController();\n\t\tfetch(`${apiBase.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`, {\n\t\t\tcredentials: \"include\",\n\t\t\tsignal: controller.signal,\n\t\t})\n\t\t\t.then(async (res) => {\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\tsetSession(data);\n\t\t\t})\n\t\t\t.catch(() => {\n\t\t\t\tsetSession(null);\n\t\t\t})\n\t\t\t.finally(() => setLoading(false));\n\n\t\treturn () => controller.abort();\n\t}, []);\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\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\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});\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}),\n\t\t});\n\t\tif (!res.ok) return;\n\t\tsetPending(null);\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\tif (loading || !session || !session.canComment) return null;\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={() => setPending(null)}\n\t\t\t\t\tonSave={(content) => submitNewComment(content)}\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<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 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 = useTargetRect(pin.cssSelector);\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<div\n\t\t\tstyle={{\n\t\t\t\tposition: \"absolute\",\n\t\t\t\ttop: pending.clickY + 10,\n\t\t\t\tleft: pending.clickX + 10,\n\t\t\t\tzIndex: 2147483647,\n\t\t\t}}\n\t\t>\n\t\t\t<EditPopover initial=\"\" onCancel={onCancel} onSave={onSave} />\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\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\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 useTargetRect(selector: string) {\n\tconst [rect, setRect] = 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\tsetRect(null);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst r = el.getBoundingClientRect();\n\t\t\tsetRect({ top: r.top + window.scrollY - 12, left: r.left + window.scrollX - 12 });\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]);\n\n\treturn rect;\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: 12,\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 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\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":"AAcA,OAA6C,aAAAA,EAAW,UAAAC,EAAQ,YAAAC,MAAgB,QAChF,OAAS,cAAAC,MAAkB,mBAgTvB,cAAAC,EAoBD,QAAAC,MApBC,oBA9SJ,IAAMC,EAAgB,4BAyBhBC,EAAe,IAAqB,CACzC,GAAI,OAAO,OAAW,IAAa,OAAO,KAC1C,IAAMC,GAAY,QAAQ,IAAI,0BAA4B,IAAI,KAAK,EACnE,GAAIA,EAAU,OAAOA,EAAS,QAAQ,MAAO,EAAE,EAO/C,IAAMC,EAAO,OAAO,SAAS,KACvBC,EAAW,OAAO,SAAS,SAC3BC,EAASF,EAAK,QAAQ,GAAG,EAC/B,GAAIE,EAAS,EAAG,CACf,IAAMC,EAAMH,EAAK,MAAM,EAAGE,CAAM,EAChC,GAAIC,EAAI,SAAS,UAAU,EAAG,CAC7B,IAAMC,EAAWD,EAAI,MAAM,EAAG,EAAkB,EAChD,MAAO,GAAGF,CAAQ,KAAKG,CAAQ,GAAGJ,EAAK,MAAME,CAAM,CAAC,EACrD,CACD,CACA,OAAO,OAAO,SAAS,MACxB,EAEMG,EAAsBC,GAAwB,CACnD,GAAIA,EAAG,GAAI,MAAO,IAAI,IAAI,OAAOA,EAAG,EAAE,CAAC,GACvC,IAAMC,EAAiB,CAAC,EACpBC,EAAuBF,EAC3B,KAAOE,GAAQA,EAAK,WAAa,KAAK,cAAgBD,EAAK,OAAS,GAAG,CACtE,IAAIE,EAAOD,EAAK,QAAQ,YAAY,EAC9BE,EAAYF,EAAK,aAAa,OAAO,EAC3C,GAAIE,EAAW,CACd,IAAMC,EAAUD,EACd,MAAM,KAAK,EACX,OAAO,OAAO,EACd,MAAM,EAAG,CAAC,EACV,IAAKE,GAAM,IAAI,IAAI,OAAOA,CAAC,CAAC,EAAE,EAC9B,KAAK,EAAE,EACTH,GAAQE,CACT,CACA,IAAME,EAAyBL,EAAK,cAC9BM,EAAMN,EAAK,QACjB,GAAIK,EAAQ,CACX,IAAME,EAAsB,MAAM,KAAKF,EAAO,QAAQ,EAAE,OAAQD,GAAMA,EAAE,UAAYE,CAAG,EACvF,GAAIC,EAAS,OAAS,EAAG,CACxB,IAAMC,EAAMD,EAAS,QAAQP,CAAI,EAAI,EACrCC,GAAQ,gBAAgBO,CAAG,GAC5B,CACD,CACAT,EAAK,QAAQE,CAAI,EACjBD,EAAOK,CACR,CACA,OAAON,EAAK,KAAK,KAAK,CACvB,EAEMU,EAAoB,CAAC,KAAM,QAAS,cAAe,aAAc,OAAQ,OAAQ,OAAQ,KAAK,EAE9FC,EAAeZ,GAAwB,CAC5C,IAAMa,EAAkB,CAAC,EACzB,QAAWC,KAAQH,EAAmB,CACrC,IAAMI,EAAIf,EAAG,aAAac,CAAI,EAC9B,GAAI,CAACC,EAAG,SACR,IAAMC,EAAUD,EAAE,OAAS,GAAK,GAAGA,EAAE,MAAM,EAAG,EAAE,CAAC,SAAMA,EACvDF,EAAM,KAAK,IAAIC,CAAI,KAAKE,EAAQ,QAAQ,KAAM,QAAQ,CAAC,GAAG,CAC3D,CACA,OAAOH,EAAM,KAAK,EAAE,CACrB,EAEMI,EAAqBjB,GAAwB,CAClD,IAAMkB,GAAQlB,EAAG,aAAe,IAAI,QAAQ,OAAQ,GAAG,EAAE,KAAK,EAC9D,OAAKkB,EACEA,EAAK,OAAS,IAAM,GAAGA,EAAK,MAAM,EAAG,GAAG,CAAC,SAAMA,EADpC,EAEnB,EAOMC,EAAwBC,GAA4B,CACzD,IAAMC,EAAuB,CAAC,EAC1BC,EAAsBF,EAC1B,KAAOE,GAAOA,IAAQ,SAAS,iBAAmBD,EAAU,OAAS,GAAG,CACvE,IAAMd,EAAyBe,EAAI,cACnC,GAAI,CAACf,EAAQ,MACbc,EAAU,QAAQd,CAAM,EACxBe,EAAMf,CACP,CAEA,IAAMgB,EAAkB,CAAC,EACrBC,EAAQ,EACZ,QAAWC,KAAYJ,EAAW,CACjC,IAAMK,EAAS,KAAK,OAAOF,CAAK,EAChCD,EAAM,KAAK,GAAGG,CAAM,IAAID,EAAS,QAAQ,YAAY,CAAC,GAAGb,EAAYa,CAAQ,CAAC,GAAG,EACjFD,GACD,CAEA,IAAMG,EAAe,KAAK,OAAOH,CAAK,EAChCI,EAAYR,EAAO,QAAQ,YAAY,EACvCS,EAAaZ,EAAkBG,CAAM,EAO3C,GANAG,EAAM,KACL,GAAGI,CAAY,IAAIC,CAAS,GAAGhB,EAAYQ,CAAM,CAAC,IACjDS,EAAa,GAAGA,CAAU,KAAKD,CAAS,IAAM,EAC/C,iBACD,EAEI,CAACC,EAAY,CAChB,IAAMC,EAAc,KAAK,OAAON,EAAQ,CAAC,EACnCO,EAAW,MAAM,KAAKX,EAAO,QAAQ,EAAE,MAAM,EAAG,CAAC,EACvD,QAAWY,KAASD,EAAU,CAC7B,IAAME,EAAYhB,EAAkBe,CAAK,EACzCT,EAAM,KACL,GAAGO,CAAW,IAAIE,EAAM,QAAQ,YAAY,CAAC,GAAGpB,EAAYoB,CAAK,CAAC,IACjEC,EAAY,GAAGA,CAAS,KAAKD,EAAM,QAAQ,YAAY,CAAC,IAAM,EAC/D,EACD,CACD,CACIZ,EAAO,SAAS,OAAS,GAC5BG,EAAM,KAAK,GAAGO,CAAW,WAAMV,EAAO,SAAS,OAAS,CAAC,iBAAiB,EAE3EG,EAAM,KAAK,GAAGI,CAAY,KAAKC,CAAS,GAAG,CAC5C,CAEA,QAASM,EAAIb,EAAU,OAAS,EAAGa,GAAK,EAAGA,IAAK,CAC/C,IAAMR,EAAS,KAAK,OAAOQ,CAAC,EACtBT,EAAWJ,EAAUa,CAAC,EACvBT,GACLF,EAAM,KAAK,GAAGG,CAAM,KAAKD,EAAS,QAAQ,YAAY,CAAC,GAAG,CAC3D,CAEA,OAAOF,EAAM,KAAK;AAAA,CAAI,CACvB,EAEMY,EAAmBnC,GAAgC,CACxD,IAAIE,EAAOF,EACX,KAAOE,GAAM,CACZ,GAAIA,aAAgB,aAAeA,EAAK,QAAQ,gBAAkB,OAAQ,MAAO,GACjFA,EAAOA,EAAK,aACb,CACA,MAAO,EACR,EAEA,SAASkC,GAAkB,CAC1B,GAAM,CAACC,EAASC,CAAU,EAAInD,EAA+B,IAAI,EAC3D,CAACoD,EAASC,CAAU,EAAIrD,EAAS,EAAI,EACrC,CAACsD,EAASC,CAAU,EAAIvD,EAAS,EAAK,EACtC,CAACwD,EAASC,CAAU,EAAIzD,EAA4B,IAAI,EACxD,CAAC0D,EAAWC,CAAY,EAAI3D,EAAwB,IAAI,EACxD4D,EAAU7D,EAAsB,IAAI,EAE1CD,EAAU,IAAM,CAEf,GADA8D,EAAQ,QAAUvD,EAAa,EAC3B,CAACuD,EAAQ,QAAS,CACrBP,EAAW,EAAK,EAChB,MACD,CAEA,IAAMQ,EAAa,IAAI,gBACvB,aAAM,GAAGD,EAAQ,OAAO,+BAA+B,mBAAmB,OAAO,SAAS,IAAI,CAAC,GAAI,CAClG,YAAa,UACb,OAAQC,EAAW,MACpB,CAAC,EACC,KAAK,MAAOC,GAAQ,CACpB,GAAI,CAACA,EAAI,GAAI,CACZX,EAAW,IAAI,EACf,MACD,CACA,IAAMY,EAAQ,MAAMD,EAAI,KAAK,EAC7BX,EAAWY,CAAI,CAChB,CAAC,EACA,MAAM,IAAM,CACZZ,EAAW,IAAI,CAChB,CAAC,EACA,QAAQ,IAAME,EAAW,EAAK,CAAC,EAE1B,IAAMQ,EAAW,MAAM,CAC/B,EAAG,CAAC,CAAC,EAEL/D,EAAU,IAAM,CACf,GAAKwD,EACL,gBAAS,KAAK,MAAM,OAAS,YACtB,IAAM,CACZ,SAAS,KAAK,MAAM,OAAS,EAC9B,CACD,EAAG,CAACA,CAAO,CAAC,EAEZxD,EAAU,IAAM,CACf,GAAI,CAACwD,EAAS,OACd,IAAMU,EAAeC,GAAsB,CAC1C,IAAMhC,EAASgC,EAAM,OAErB,GADI,EAAEhC,aAAkB,UACpBe,EAAgBf,CAAM,EAAG,OAE7BgC,EAAM,eAAe,EACrBA,EAAM,gBAAgB,EAEtB,IAAMC,EAAOjC,EAAO,sBAAsB,EAC1CwB,EAAW,CACV,YAAa7C,EAAmBqB,CAAM,EACtC,SAAU,OAAO,SAAS,SAC1B,gBAAiBD,EAAqBC,CAAM,EAC5C,KAAM,CACL,IAAKiC,EAAK,IAAM,OAAO,QACvB,KAAMA,EAAK,KAAO,OAAO,QACzB,MAAOA,EAAK,MACZ,OAAQA,EAAK,MACd,EACA,OAAQD,EAAM,QAAU,OAAO,QAC/B,OAAQA,EAAM,QAAU,OAAO,OAChC,CAAC,EACDV,EAAW,EAAK,CACjB,EACA,gBAAS,iBAAiB,QAASS,EAAa,CAAE,QAAS,EAAK,CAAC,EAC1D,IAAM,SAAS,oBAAoB,QAASA,EAAa,CAAE,QAAS,EAAK,CAAC,CAClF,EAAG,CAACV,CAAO,CAAC,EAEZ,IAAMa,EAAkB,SAAY,CACnC,GAAI,CAACP,EAAQ,QAAS,OACtB,IAAME,EAAM,MAAM,MACjB,GAAGF,EAAQ,OAAO,+BAA+B,mBAAmB,OAAO,SAAS,IAAI,CAAC,GACzF,CAAE,YAAa,SAAU,CAC1B,EACA,GAAI,CAACE,EAAI,GAAI,OACb,IAAMC,EAAQ,MAAMD,EAAI,KAAK,EAC7BX,EAAWY,CAAI,CAChB,EAEMK,EAAmB,MAAOC,GAAoB,CAC/C,CAACT,EAAQ,SAAW,CAACV,GAAW,CAACM,GAajC,EAZQ,MAAM,MAAM,GAAGI,EAAQ,OAAO,yBAA0B,CACnE,OAAQ,OACR,YAAa,UACb,QAAS,CAAE,eAAgB,kBAAmB,EAC9C,KAAM,KAAK,UAAU,CACpB,kBAAmBV,EAAQ,kBAC3B,QAAAmB,EACA,SAAUb,EAAQ,SAClB,YAAaA,EAAQ,YACrB,gBAAiBA,EAAQ,eAC1B,CAAC,CACF,CAAC,GACQ,KACTC,EAAW,IAAI,EACf,MAAMU,EAAgB,EACvB,EAEMG,EAAgB,MAAOC,EAAYF,IAAoB,CACxD,CAACT,EAAQ,SAOT,EANQ,MAAM,MAAM,GAAGA,EAAQ,OAAO,0BAA0BW,CAAE,GAAI,CACzE,OAAQ,QACR,YAAa,UACb,QAAS,CAAE,eAAgB,kBAAmB,EAC9C,KAAM,KAAK,UAAU,CAAE,QAAAF,CAAQ,CAAC,CACjC,CAAC,GACQ,KACTV,EAAa,IAAI,EACjB,MAAMQ,EAAgB,EACvB,EAEMK,EAAgB,MAAOD,GAAe,CACvC,CAACX,EAAQ,SAKT,EAJQ,MAAM,MAAM,GAAGA,EAAQ,OAAO,0BAA0BW,CAAE,GAAI,CACzE,OAAQ,SACR,YAAa,SACd,CAAC,GACQ,IACT,MAAMJ,EAAgB,CACvB,EAEA,GAAIf,GAAW,CAACF,GAAW,CAACA,EAAQ,WAAY,OAAO,KAEvD,IAAMuB,EAAcvB,EAAQ,SAAS,OACnC/B,GAAMA,EAAE,WAAa,OAAO,SAAS,UAAYA,EAAE,SAAW,MAChE,EAEA,OACChB,EAAC,OAAI,uBAAqB,OACxB,UAAAsE,EAAY,IAAI,CAACC,EAAKnD,IACtBrB,EAACyE,EAAA,CAEA,IAAKD,EACL,OAAQnD,EAAM,EACd,QAASmC,IAAcgB,EAAI,GAC3B,YAAa,IAAMf,EAAae,EAAI,EAAE,EACtC,aAAc,IAAMf,EAAa,IAAI,EACrC,OAASU,GAAYC,EAAcI,EAAI,GAAIL,CAAO,EAClD,SAAU,IAAMG,EAAcE,EAAI,EAAE,GAP/BA,EAAI,EAQV,CACA,EAEAlB,GACAtD,EAAC0E,EAAA,CACA,QAASpB,EACT,SAAU,IAAMC,EAAW,IAAI,EAC/B,OAASY,GAAYD,EAAiBC,CAAO,EAC9C,EAGDlE,EAAC,OAAI,MAAO0E,EACX,UAAA3E,EAAC,UACA,KAAK,SACL,QAAS,IAAMqD,EAAY3B,GAAM,CAACA,CAAC,EACnC,MAAO0B,EAAUwB,EAA2BC,EAE3C,SAAAzB,EAAU,SAAW,cACvB,EACApD,EAAC,QAAK,MAAO8E,EACX,SAAA1B,EAAU,+BAAiC,GAAGmB,EAAY,MAAM,gBAClE,GACD,GACD,CAEF,CAEA,SAASE,EAAW,CACnB,IAAAD,EACA,OAAAO,EACA,QAAAC,EACA,YAAAC,EACA,aAAAC,EACA,OAAAC,EACA,SAAAC,CACD,EAQG,CACF,IAAMrD,EAASsD,EAAcb,EAAI,WAAW,EAC5C,OAAKzC,EAGJ9B,EAAC,OACA,MAAO,CACN,SAAU,WACV,IAAK8B,EAAO,IACZ,KAAMA,EAAO,KACb,OAAQ,WACR,cAAe,MAChB,EAEA,UAAA/B,EAAC,UAAO,KAAK,SAAS,QAASiF,EAAa,MAAOK,EAAa,MAAOd,EAAI,QACzE,SAAAO,EACF,EACCC,GACAhF,EAACuF,EAAA,CAAY,QAASf,EAAI,QAAS,SAAUU,EAAc,OAAQC,EAAQ,SAAUC,EAAU,GAEjG,EAlBmB,IAoBrB,CAEA,SAASV,EAAsB,CAC9B,QAAApB,EACA,SAAAkC,EACA,OAAAL,CACD,EAIG,CACF,OACCnF,EAAC,OACA,MAAO,CACN,SAAU,WACV,IAAKsD,EAAQ,OAAS,GACtB,KAAMA,EAAQ,OAAS,GACvB,OAAQ,UACT,EAEA,SAAAtD,EAACuF,EAAA,CAAY,QAAQ,GAAG,SAAUC,EAAU,OAAQL,EAAQ,EAC7D,CAEF,CAEA,SAASI,EAAY,CACpB,QAAAE,EACA,SAAAD,EACA,OAAAL,EACA,SAAAC,CACD,EAKG,CACF,GAAM,CAACM,EAAOC,CAAQ,EAAI7F,EAAS2F,CAAO,EACpC,CAACG,EAAQC,CAAS,EAAI/F,EAAS,EAAK,EAc1C,OACCG,EAAC,QAAK,SAbc,MAAO6F,GAAiB,CAC5CA,EAAE,eAAe,EACjB,IAAM3B,EAAUuB,EAAM,KAAK,EAC3B,GAAKvB,EACL,CAAA0B,EAAU,EAAI,EACd,GAAI,CACH,MAAMV,EAAOhB,CAAO,CACrB,QAAE,CACD0B,EAAU,EAAK,CAChB,EACD,EAG+B,MAAOE,EACpC,UAAA/F,EAAC,YACA,MAAO0F,EACP,SAAWI,GAAMH,EAASG,EAAE,OAAO,KAAK,EACxC,YAAY,wBACZ,MAAOE,EACP,KAAM,EACP,EACA/F,EAAC,OAAI,MAAOgG,EACV,UAAAb,GACApF,EAAC,UAAO,KAAK,SAAS,QAAS,IAAMoF,EAAS,EAAG,MAAOc,EAAmB,SAAUN,EAAQ,kBAE7F,EAED5F,EAAC,OAAI,MAAO,CAAE,KAAM,CAAE,EAAG,EACzBA,EAAC,UAAO,KAAK,SAAS,QAASwF,EAAU,MAAOW,EAAkB,SAAUP,EAAQ,kBAEpF,EACA5F,EAAC,UAAO,KAAK,SAAS,MAAOoG,EAAoB,SAAUR,GAAU,CAACF,EAAM,KAAK,EAC/E,SAAAE,EAAS,eAAY,OACvB,GACD,GACD,CAEF,CAEA,SAASP,EAAcgB,EAAkB,CACxC,GAAM,CAACrC,EAAMsC,CAAO,EAAIxG,EAA+C,IAAI,EAE3E,OAAAF,EAAU,IAAM,CACf,IAAM2G,EAAS,IAAM,CACpB,IAAI5F,EAAqB,KACzB,GAAI,CACHA,EAAK,SAAS,cAAc0F,CAAQ,CACrC,MAAQ,CACP1F,EAAK,IACN,CACA,GAAI,CAACA,EAAI,CACR2F,EAAQ,IAAI,EACZ,MACD,CACA,IAAME,EAAI7F,EAAG,sBAAsB,EACnC2F,EAAQ,CAAE,IAAKE,EAAE,IAAM,OAAO,QAAU,GAAI,KAAMA,EAAE,KAAO,OAAO,QAAU,EAAG,CAAC,CACjF,EAEAD,EAAO,EACP,IAAME,EAAK,IAAI,eAAeF,CAAM,EACpC,GAAI,CACH,IAAM5F,EAAK,SAAS,cAAc0F,CAAQ,EACtC1F,GAAI8F,EAAG,QAAQ9F,CAAE,CACtB,MAAQ,CAER,CACA,cAAO,iBAAiB,SAAU4F,EAAQ,EAAI,EAC9C,OAAO,iBAAiB,SAAUA,CAAM,EACjC,IAAM,CACZE,EAAG,WAAW,EACd,OAAO,oBAAoB,SAAUF,EAAQ,EAAI,EACjD,OAAO,oBAAoB,SAAUA,CAAM,CAC5C,CACD,EAAG,CAACF,CAAQ,CAAC,EAENrC,CACR,CAIA,IAAMW,EAA8B,CACnC,SAAU,QACV,OAAQ,GACR,KAAM,MACN,UAAW,mBACX,OAAQ,WACR,QAAS,OACT,WAAY,SACZ,IAAK,GACL,QAAS,WACT,WAAY,yBACZ,MAAO,QACP,aAAc,IACd,UAAW,8BACX,WACC,6HACD,SAAU,GACV,cAAe,MAChB,EAEME,EAAoC,CACzC,OAAQ,OACR,WAAY,QACZ,MAAO,OACP,QAAS,WACT,aAAc,IACd,OAAQ,UACR,WAAY,IACZ,SAAU,EACX,EAEMD,EAA0C,CAC/C,GAAGC,EACH,WAAY,UACZ,MAAO,OACR,EAEMC,EAAkC,CACvC,QAAS,GACT,SAAU,EACX,EAEMQ,EAA6B,CAClC,MAAO,GACP,OAAQ,GACR,aAAc,IACd,WAAY,UACZ,MAAO,QACP,OAAQ,kBACR,OAAQ,UACR,SAAU,GACV,WAAY,IACZ,UAAW,4BACX,QAAS,cACT,WAAY,SACZ,eAAgB,SAChB,QAAS,CACV,EAEMS,EAA8B,CACnC,WAAY,QACZ,OAAQ,oBACR,aAAc,EACd,QAAS,GACT,MAAO,IACP,UAAW,+BACX,WACC,6HACD,QAAS,OACT,cAAe,SACf,IAAK,EACL,MAAO,MACR,EAEMC,EAA+B,CACpC,MAAO,OACP,OAAQ,oBACR,aAAc,EACd,QAAS,EACT,SAAU,GACV,OAAQ,WACR,WAAY,UACZ,MAAO,OACP,WAAY,QACZ,UAAW,YACZ,EAEMC,EAAqC,CAC1C,QAAS,OACT,WAAY,SACZ,IAAK,CACN,EAEMS,EAA4B,CACjC,OAAQ,wBACR,aAAc,EACd,QAAS,WACT,SAAU,GACV,OAAQ,UACR,WAAY,GACb,EAEMN,EAAoC,CACzC,GAAGM,EACH,WAAY,OACZ,MAAO,OACR,EAEMP,EAAkC,CACvC,GAAGO,EACH,WAAY,cACZ,MAAO,UACP,YAAa,SACd,EAEMR,EAAmC,CACxC,GAAGQ,EACH,WAAY,cACZ,MAAO,UACP,YAAa,SACd,EAEO,SAASC,GAAuD,CAMtE,GALA,QAAQ,IAAI,uDAAwD,CACnE,SAAU,OAAO,OAAW,IAC5B,UAAW,QAAQ,IAAI,uBACvB,eAAgB,OAAO,SAAa,KAAe,EAAQ,SAAS,eAAezG,CAAa,CACjG,CAAC,EACG,OAAO,OAAW,IAAa,OAAO,KAC1C,GAAI,QAAQ,IAAI,yBAA2B,UAC1C,eAAQ,IACP,sEACA,QAAQ,IAAI,sBACb,EACO,KAER,GAAI,SAAS,eAAeA,CAAa,EAAG,OAAO,KAEnD,IAAM0G,EAAY,SAAS,cAAc,KAAK,EAC9CA,EAAU,GAAK1G,EACf0G,EAAU,QAAQ,cAAgB,OAClC,SAAS,KAAK,YAAYA,CAAS,EAEnC,IAAMC,EAAO9G,EAAW6G,CAAS,EACjC,OAAAC,EAAK,OAAO7G,EAAC+C,EAAA,EAAgB,CAAE,EAExB,CACN,QAAS,IAAM,CACd8D,EAAK,QAAQ,EACbD,EAAU,OAAO,CAClB,CACD,CACD,CAGI,OAAO,OAAW,KACrB,QAAQ,IAAI,0CAA2C,CACtD,UAAW,QAAQ,IAAI,uBACvB,WAAY,SAAS,WACrB,cAAe,QAAQ,IAAI,yBAA2B,SACvD,CAAC,EAEE,OAAO,OAAW,KAAe,QAAQ,IAAI,yBAA2B,YACvE,SAAS,aAAe,UAC3B,SAAS,iBAAiB,mBAAoB,IAAM,CACnDD,EAAqB,CACtB,CAAC,EAEDA,EAAqB","names":["useEffect","useRef","useState","createRoot","jsx","jsxs","MOUNT_NODE_ID","buildApiBase","override","host","protocol","dotIdx","sub","stripped","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","FeedbackToolbar","session","setSession","loading","setLoading","pinMode","setPinMode","pending","setPending","editingId","setEditingId","apiBase","controller","res","data","handleClick","event","rect","refreshComments","submitNewComment","content","updateComment","id","removeComment","visiblePins","pin","PinOverlay","PendingCommentPopover","toolbarStyle","toolbarButtonActiveStyle","toolbarButtonStyle","toolbarHintStyle","number","editing","onStartEdit","onCancelEdit","onSave","onRemove","useTargetRect","pinDotStyle","EditPopover","onCancel","initial","value","setValue","saving","setSaving","e","popoverStyle","textareaStyle","popoverActionsStyle","dangerButtonStyle","ghostButtonStyle","primaryButtonStyle","selector","setRect","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\ninterface ActiveSession {\n\tfeedbackSessionId: string;\n\tcomments: FeedbackComment[];\n\tcanComment: boolean;\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\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\tconst controller = new AbortController();\n\t\tfetch(`${apiBase.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`, {\n\t\t\tcredentials: \"include\",\n\t\t\tsignal: controller.signal,\n\t\t})\n\t\t\t.then(async (res) => {\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\tsetSession(data);\n\t\t\t})\n\t\t\t.catch(() => {\n\t\t\t\tsetSession(null);\n\t\t\t})\n\t\t\t.finally(() => setLoading(false));\n\n\t\treturn () => controller.abort();\n\t}, []);\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\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 || !session.canComment) return null;\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={() => setPending(null)}\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 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\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\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\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","FeedbackToolbar","session","setSession","loading","setLoading","pinMode","setPinMode","pending","setPending","editingId","setEditingId","sidebarOpen","setSidebarOpen","finalizing","setFinalizing","apiBase","controller","res","data","overlay","label","hovered","handleMove","e","rect","handleLeave","handleClick","event","offsetXRatio","offsetYRatio","refreshComments","submitNewComment","content","updateComment","id","removeComment","finalizeSession","prev","scrollToComment","comment","visiblePins","pin","PinOverlay","PendingCommentPopover","CommentsSidebar","toolbarStyle","toolbarButtonActiveStyle","toolbarButtonStyle","toolbarButtonGhostStyle","toolbarButtonFinalizeStyle","toolbarHintStyle","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","popoverStyle","textareaStyle","popoverActionsStyle","dangerButtonStyle","primaryButtonStyle","selector","pos","setPos","update","r","ro","baseButton","mountFeedbackToolbar","container","root"]}
|