commerce-kit 0.35.0 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/browser.d.ts CHANGED
@@ -1,16 +1,2 @@
1
- /**
2
- * Feedback session toolbar — side-effect entry.
3
- *
4
- * Importing `commerce-kit/browser` mounts a floating toolbar onto the page that
5
- * lets reviewers leave click-anchored comments. Comments persist via YNS API
6
- * endpoints, authenticated with the `better-auth` session cookie sent through
7
- * `credentials: "include"`.
8
- *
9
- * Disabled in production (`process.env.NEXT_PUBLIC_VERCEL_ENV === "production"`)
10
- * and when not running in a browser.
11
- */
12
- declare function mountFeedbackToolbar(): {
13
- unmount: () => void;
14
- } | null;
15
1
 
16
- export { mountFeedbackToolbar };
2
+ export { }
package/dist/browser.js CHANGED
@@ -1,3 +1 @@
1
- import{useEffect as y,useRef as L,useState as g}from"react";import{createRoot as N}from"react-dom/client";import{jsx as m,jsxs as h}from"react/jsx-runtime";var E="yns-feedback-toolbar-root",A=()=>{if(typeof window>"u")return null;let e=(process.env.NEXT_PUBLIC_YNS_API_BASE??"").trim();return e?e.replace(/\/$/,""):window.location.origin},B=e=>{if(e.id)return`#${CSS.escape(e.id)}`;let t=[],r=e;for(;r&&r.nodeType===Node.ELEMENT_NODE&&t.length<6;){let n=r.tagName.toLowerCase(),i=r.getAttribute("class");if(i){let d=i.split(/\s+/).filter(Boolean).slice(0,2).map(s=>`.${CSS.escape(s)}`).join("");n+=d}let c=r.parentElement,l=r.tagName;if(c){let d=Array.from(c.children).filter(s=>s.tagName===l);if(d.length>1){let s=d.indexOf(r)+1;n+=`:nth-of-type(${s})`}}t.unshift(n),r=c}return t.join(" > ")},F=["id","class","data-testid","aria-label","role","name","href","src"],v=e=>{let t=[];for(let r of F){let n=e.getAttribute(r);if(!n)continue;let i=n.length>80?`${n.slice(0,80)}\u2026`:n;t.push(` ${r}="${i.replace(/"/g,"&quot;")}"`)}return t.join("")},k=e=>{let t=(e.textContent??"").replace(/\s+/g," ").trim();return t?t.length>100?`${t.slice(0,100)}\u2026`:t:""},_=e=>{let t=[],r=e;for(;r&&r!==document.documentElement&&t.length<5;){let s=r.parentElement;if(!s)break;t.unshift(s),r=s}let n=[],i=0;for(let s of t){let p=" ".repeat(i);n.push(`${p}<${s.tagName.toLowerCase()}${v(s)}>`),i++}let c=" ".repeat(i),l=e.tagName.toLowerCase(),d=k(e);if(n.push(`${c}<${l}${v(e)}>${d?`${d}</${l}>`:""} \u2190 TARGET`),!d){let s=" ".repeat(i+1),p=Array.from(e.children).slice(0,6);for(let a of p){let b=k(a);n.push(`${s}<${a.tagName.toLowerCase()}${v(a)}>${b?`${b}</${a.tagName.toLowerCase()}>`:""}`)}e.children.length>6&&n.push(`${s}\u2026 (${e.children.length-6} more children)`),n.push(`${c}</${l}>`)}for(let s=t.length-1;s>=0;s--){let p=" ".repeat(s),a=t[s];a&&n.push(`${p}</${a.tagName.toLowerCase()}>`)}return n.join(`
2
- `)},z=e=>{let t=e;for(;t;){if(t instanceof HTMLElement&&t.dataset.ynsFeedbackUi==="true")return!0;t=t.parentElement}return!1};function j(){let[e,t]=g(null),[r,n]=g(!0),[i,c]=g(!1),[l,d]=g(null),[s,p]=g(null),a=L(null);y(()=>{if(a.current=A(),!a.current){n(!1);return}let o=new AbortController;return fetch(`${a.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,{credentials:"include",signal:o.signal}).then(async u=>{if(!u.ok){t(null);return}let f=await u.json();t(f)}).catch(()=>{t(null)}).finally(()=>n(!1)),()=>o.abort()},[]),y(()=>{if(i)return document.body.style.cursor="crosshair",()=>{document.body.style.cursor=""}},[i]),y(()=>{if(!i)return;let o=u=>{let f=u.target;if(!(f instanceof Element)||z(f))return;u.preventDefault(),u.stopPropagation();let S=f.getBoundingClientRect();d({cssSelector:B(f),pagePath:window.location.pathname,surroundingHtml:_(f),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}),c(!1)};return document.addEventListener("click",o,{capture:!0}),()=>document.removeEventListener("click",o,{capture:!0})},[i]);let b=async()=>{if(!a.current)return;let o=await fetch(`${a.current}/api/feedback-comments?host=${encodeURIComponent(window.location.host)}`,{credentials:"include"});if(!o.ok)return;let u=await o.json();t(u)},I=async o=>{!a.current||!e||!l||!(await fetch(`${a.current}/api/feedback-comments`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({feedbackSessionId:e.feedbackSessionId,content:o,pagePath:l.pagePath,cssSelector:l.cssSelector,surroundingHtml:l.surroundingHtml})})).ok||(d(null),await b())},T=async(o,u)=>{!a.current||!(await fetch(`${a.current}/api/feedback-comments/${o}`,{method:"PATCH",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({content:u})})).ok||(p(null),await b())},R=async o=>{!a.current||!(await fetch(`${a.current}/api/feedback-comments/${o}`,{method:"DELETE",credentials:"include"})).ok||await b()};if(r||!e||!e.canComment)return null;let C=e.comments.filter(o=>o.pagePath===window.location.pathname&&o.status!=="done");return h("div",{"data-yns-feedback-ui":"true",children:[C.map((o,u)=>m(U,{pin:o,number:u+1,editing:s===o.id,onStartEdit:()=>p(o.id),onCancelEdit:()=>p(null),onSave:f=>T(o.id,f),onRemove:()=>R(o.id)},o.id)),l&&m(O,{pending:l,onCancel:()=>d(null),onSave:o=>I(o)}),h("div",{style:D,children:[m("button",{type:"button",onClick:()=>c(o=>!o),style:i?H:$,children:i?"Cancel":"Add comment"}),m("span",{style:M,children:i?"Click any element to comment":`${C.length} on this page`})]})]})}function U({pin:e,number:t,editing:r,onStartEdit:n,onCancelEdit:i,onSave:c,onRemove:l}){let d=X(e.cssSelector);return d?h("div",{style:{position:"absolute",top:d.top,left:d.left,zIndex:2147483600,pointerEvents:"auto"},children:[m("button",{type:"button",onClick:n,style:Y,title:e.content,children:t}),r&&m(x,{initial:e.content,onCancel:i,onSave:c,onRemove:l})]}):null}function O({pending:e,onCancel:t,onSave:r}){return m("div",{style:{position:"absolute",top:e.clickY+10,left:e.clickX+10,zIndex:2147483647},children:m(x,{initial:"",onCancel:t,onSave:r})})}function x({initial:e,onCancel:t,onSave:r,onRemove:n}){let[i,c]=g(e),[l,d]=g(!1);return h("form",{onSubmit:async p=>{p.preventDefault();let a=i.trim();if(a){d(!0);try{await r(a)}finally{d(!1)}}},style:V,children:[m("textarea",{value:i,onChange:p=>c(p.target.value),placeholder:"Leave a comment\u2026",style:q,rows:3}),h("div",{style:W,children:[n&&m("button",{type:"button",onClick:()=>n(),style:K,disabled:l,children:"Delete"}),m("div",{style:{flex:1}}),m("button",{type:"button",onClick:t,style:G,disabled:l,children:"Cancel"}),m("button",{type:"submit",style:J,disabled:l||!i.trim(),children:l?"Saving\u2026":"Save"})]})]})}function X(e){let[t,r]=g(null);return y(()=>{let n=()=>{let c=null;try{c=document.querySelector(e)}catch{c=null}if(!c){r(null);return}let l=c.getBoundingClientRect();r({top:l.top+window.scrollY-12,left:l.left+window.scrollX-12})};n();let i=new ResizeObserver(n);try{let c=document.querySelector(e);c&&i.observe(c)}catch{}return window.addEventListener("scroll",n,!0),window.addEventListener("resize",n),()=>{i.disconnect(),window.removeEventListener("scroll",n,!0),window.removeEventListener("resize",n)}},[e]),t}var D={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},H={...$,background:"#ef4444",color:"white"},M={opacity:.8,fontSize:12},Y={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},V={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"},q={width:"100%",border:"1px solid #d1d5db",borderRadius:6,padding:8,fontSize:14,resize:"vertical",fontFamily:"inherit",color:"#111",background:"white",boxSizing:"border-box"},W={display:"flex",alignItems:"center",gap:6},w={border:"1px solid transparent",borderRadius:6,padding:"6px 10px",fontSize:13,cursor:"pointer",fontWeight:500},J={...w,background:"#111",color:"white"},G={...w,background:"transparent",color:"#374151",borderColor:"#d1d5db"},K={...w,background:"transparent",color:"#b91c1c",borderColor:"#fecaca"};function P(){if(typeof window>"u"||process.env.NEXT_PUBLIC_VERCEL_ENV==="production"||document.getElementById(E))return null;let e=document.createElement("div");e.id=E,e.dataset.ynsFeedbackUi="true",document.body.appendChild(e);let t=N(e);return t.render(m(j,{})),{unmount:()=>{t.unmount(),e.remove()}}}typeof window<"u"&&process.env.NEXT_PUBLIC_VERCEL_ENV!=="production"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{P()}):P());export{P as mountFeedbackToolbar};
3
1
  //# sourceMappingURL=browser.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/browser.tsx"],"sourcesContent":["/**\n * Feedback session toolbar — side-effect entry.\n *\n * Importing `commerce-kit/browser` mounts a floating toolbar onto the page that\n * lets reviewers leave click-anchored comments. Comments persist via YNS API\n * endpoints, authenticated with the `better-auth` session cookie sent through\n * `credentials: \"include\"`.\n *\n * Disabled in production (`process.env.NEXT_PUBLIC_VERCEL_ENV === \"production\"`)\n * and when not running in a browser.\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\t// Same origin by default. Stores can override at build time via\n\t// NEXT_PUBLIC_YNS_API_BASE if their API lives on a different host.\n\tconst override = (process.env.NEXT_PUBLIC_YNS_API_BASE ?? \"\").trim();\n\tif (override) return override.replace(/\\/$/, \"\");\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, \"&quot;\")}\"`);\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\tif (typeof window === \"undefined\") return null;\n\tif (process.env.NEXT_PUBLIC_VERCEL_ENV === \"production\") return null;\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\" && process.env.NEXT_PUBLIC_VERCEL_ENV !== \"production\") {\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","jsx","jsxs","MOUNT_NODE_ID","buildApiBase","override","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":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Feedback session toolbar — side-effect entry.
3
+ *
4
+ * Importing `commerce-kit/feedback-toolbar` (or `commerce-kit/browser` for the
5
+ * combined entry) mounts a floating toolbar onto the page that lets reviewers
6
+ * leave click-anchored comments. Comments persist via YNS API endpoints,
7
+ * authenticated with the `better-auth` session cookie sent through
8
+ * `credentials: "include"`.
9
+ *
10
+ * Gated on `process.env.NEXT_PUBLIC_VERCEL_ENV === "preview"` — only Vercel
11
+ * preview deploys get the toolbar. Sandbox dev (env undefined) and production
12
+ * (env === "production") skip it.
13
+ */
14
+ declare function mountFeedbackToolbar(): {
15
+ unmount: () => void;
16
+ } | null;
17
+
18
+ export { mountFeedbackToolbar };
@@ -0,0 +1,3 @@
1
+ import{useEffect as y,useRef as L,useState as g}from"react";import{createRoot as N}from"react-dom/client";import{jsx as p,jsxs as h}from"react/jsx-runtime";var E="yns-feedback-toolbar-root",A=()=>{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.host,o=window.location.protocol,n=t.indexOf(".");if(n>0){let r=t.slice(0,n);if(r.endsWith("-preview")){let s=r.slice(0,-8);return`${o}//${s}${t.slice(n)}`}}return window.location.origin},B=e=>{if(e.id)return`#${CSS.escape(e.id)}`;let t=[],o=e;for(;o&&o.nodeType===Node.ELEMENT_NODE&&t.length<6;){let n=o.tagName.toLowerCase(),r=o.getAttribute("class");if(r){let d=r.split(/\s+/).filter(Boolean).slice(0,2).map(l=>`.${CSS.escape(l)}`).join("");n+=d}let s=o.parentElement,c=o.tagName;if(s){let d=Array.from(s.children).filter(l=>l.tagName===c);if(d.length>1){let l=d.indexOf(o)+1;n+=`:nth-of-type(${l})`}}t.unshift(n),o=s}return t.join(" > ")},F=["id","class","data-testid","aria-label","role","name","href","src"],w=e=>{let t=[];for(let o of F){let n=e.getAttribute(o);if(!n)continue;let r=n.length>80?`${n.slice(0,80)}\u2026`:n;t.push(` ${o}="${r.replace(/"/g,"&quot;")}"`)}return t.join("")},k=e=>{let t=(e.textContent??"").replace(/\s+/g," ").trim();return t?t.length>100?`${t.slice(0,100)}\u2026`:t:""},_=e=>{let t=[],o=e;for(;o&&o!==document.documentElement&&t.length<5;){let l=o.parentElement;if(!l)break;t.unshift(l),o=l}let n=[],r=0;for(let l of t){let m=" ".repeat(r);n.push(`${m}<${l.tagName.toLowerCase()}${w(l)}>`),r++}let s=" ".repeat(r),c=e.tagName.toLowerCase(),d=k(e);if(n.push(`${s}<${c}${w(e)}>${d?`${d}</${c}>`:""} \u2190 TARGET`),!d){let l=" ".repeat(r+1),m=Array.from(e.children).slice(0,6);for(let a of m){let b=k(a);n.push(`${l}<${a.tagName.toLowerCase()}${w(a)}>${b?`${b}</${a.tagName.toLowerCase()}>`:""}`)}e.children.length>6&&n.push(`${l}\u2026 (${e.children.length-6} more children)`),n.push(`${s}</${c}>`)}for(let l=t.length-1;l>=0;l--){let m=" ".repeat(l),a=t[l];a&&n.push(`${m}</${a.tagName.toLowerCase()}>`)}return n.join(`
2
+ `)},z=e=>{let t=e;for(;t;){if(t instanceof HTMLElement&&t.dataset.ynsFeedbackUi==="true")return!0;t=t.parentElement}return!1};function j(){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(),!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)||z(f))return;u.preventDefault(),u.stopPropagation();let S=f.getBoundingClientRect();d({cssSelector:B(f),pagePath:window.location.pathname,surroundingHtml:_(f),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}),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)},I=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())},R=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 h("div",{"data-yns-feedback-ui":"true",children:[C.map((i,u)=>p(O,{pin:i,number:u+1,editing:l===i.id,onStartEdit:()=>m(i.id),onCancelEdit:()=>m(null),onSave:f=>T(i.id,f),onRemove:()=>R(i.id)},i.id)),c&&p(U,{pending:c,onCancel:()=>d(null),onSave:i=>I(i)}),h("div",{style:D,children:[p("button",{type:"button",onClick:()=>s(i=>!i),style:r?H:$,children:r?"Cancel":"Add comment"}),p("span",{style:M,children:r?"Click any element to comment":`${C.length} on this page`})]})]})}function O({pin:e,number:t,editing:o,onStartEdit:n,onCancelEdit:r,onSave:s,onRemove:c}){let d=X(e.cssSelector);return d?h("div",{style:{position:"absolute",top:d.top,left:d.left,zIndex:2147483600,pointerEvents:"auto"},children:[p("button",{type:"button",onClick:n,style:Y,title:e.content,children:t}),o&&p(x,{initial:e.content,onCancel:r,onSave:s,onRemove:c})]}):null}function U({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 h("form",{onSubmit:async m=>{m.preventDefault();let a=r.trim();if(a){d(!0);try{await o(a)}finally{d(!1)}}},style:V,children:[p("textarea",{value:r,onChange:m=>s(m.target.value),placeholder:"Leave a comment\u2026",style:W,rows:3}),h("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 X(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 D={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},H={...$,background:"#ef4444",color:"white"},M={opacity:.8,fontSize:12},Y={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},V={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},v={border:"1px solid transparent",borderRadius:6,padding:"6px 10px",fontSize:13,cursor:"pointer",fontWeight:500},J={...v,background:"#111",color:"white"},G={...v,background:"transparent",color:"#374151",borderColor:"#d1d5db"},K={...v,background:"transparent",color:"#b91c1c",borderColor:"#fecaca"};function P(){if(typeof window>"u"||process.env.NEXT_PUBLIC_VERCEL_ENV!=="preview"||document.getElementById(E))return null;let e=document.createElement("div");e.id=E,e.dataset.ynsFeedbackUi="true",document.body.appendChild(e);let t=N(e);return t.render(p(j,{})),{unmount:()=>{t.unmount(),e.remove()}}}typeof window<"u"&&process.env.NEXT_PUBLIC_VERCEL_ENV==="preview"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{P()}):P());export{P as mountFeedbackToolbar};
3
+ //# sourceMappingURL=feedback-toolbar.js.map
@@ -0,0 +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, \"&quot;\")}\"`);\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\tif (typeof window === \"undefined\") return null;\n\tif (process.env.NEXT_PUBLIC_VERCEL_ENV !== \"preview\") return null;\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\" && 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","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"]}
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=sandbox-inspectors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "commerce-kit",
4
- "version": "0.35.0",
4
+ "version": "0.36.0",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-only",
7
7
  "keywords": [
@@ -28,6 +28,16 @@
28
28
  "import": "./dist/browser.js",
29
29
  "types": "./dist/browser.d.ts",
30
30
  "default": "./dist/browser.js"
31
+ },
32
+ "./feedback-toolbar": {
33
+ "import": "./dist/feedback-toolbar.js",
34
+ "types": "./dist/feedback-toolbar.d.ts",
35
+ "default": "./dist/feedback-toolbar.js"
36
+ },
37
+ "./sandbox-inspectors": {
38
+ "import": "./dist/sandbox-inspectors.js",
39
+ "types": "./dist/sandbox-inspectors.d.ts",
40
+ "default": "./dist/sandbox-inspectors.js"
31
41
  }
32
42
  },
33
43
  "files": [
@@ -37,7 +47,9 @@
37
47
  "LICENSE.md"
38
48
  ],
39
49
  "sideEffects": [
40
- "./dist/browser.js"
50
+ "./dist/browser.js",
51
+ "./dist/feedback-toolbar.js",
52
+ "./dist/sandbox-inspectors.js"
41
53
  ],
42
54
  "scripts": {
43
55
  "dev": "tsup --config tsup.config.ts --watch",