@coframe-gtm/annotations 1.1.0 → 1.2.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/package.json +1 -1
- package/src/inject/bundle-source.generated.ts +1 -1
- package/src/output.ts +1 -1
- package/src/picker.ts +23 -5
- package/src/store.ts +34 -0
- package/src/ui/Avatar.ts +62 -0
- package/src/ui/Composer.ts +51 -11
- package/src/ui/Cursors.ts +1 -5
- package/src/ui/Pins.ts +91 -21
- package/src/ui/ThreadPanel.ts +22 -10
- package/src/ui/Toolbar.ts +202 -20
- package/src/ui/brand.ts +16 -0
- package/src/ui/helpers.ts +89 -0
- package/src/ui/overlay.ts +8 -0
- package/src/ui/styles.ts +194 -39
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coframe-gtm/annotations",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Clean-room AFS v1.1 annotation overlay (frontend) + transport-agnostic ingest core (backend). Injects into any page; humans + agents annotate bidirectionally.",
|
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
// This file is generated by `pnpm build`.
|
|
3
3
|
// Do not edit by hand. Regenerate by running the build script.
|
|
4
4
|
export const ANNOTATIONS_V1_BUNDLE_SOURCE: string =
|
|
5
|
-
"\"use strict\";(()=>{var Ye=Object.defineProperty;var De=(e,t,n)=>t in e?Ye(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var z=(e,t,n)=>De(e,typeof t!=\"symbol\"?t+\"\":t,n);var re=\"0123456789ABCDEFGHJKMNPQRSTVWXYZ\";function O(e=Date.now()){let t=e,n=\"\";for(let r=0;r<10;r++)n=re[t&31]+n,t=Math.floor(t/32);let o=\"\";for(let r=0;r<16;r++)o+=re[Math.floor(Math.random()*32)];return n+o}var Y=class{constructor(t){z(this,\"_value\");z(this,\"_subs\",new Set);this._value=t}get value(){return this._value}set value(t){if(!Object.is(this._value,t)){this._value=t;for(let n of Array.from(this._subs))n(t)}}peek(){return this._value}subscribe(t){return this._subs.add(t),()=>{this._subs.delete(t)}}};function d(e){return new Y(e)}var l=d([]),u=d(\"view\"),T=d(\"dark\"),_=d(null),$=d(\"\"),ie=d(0),ae=d(!1),y=d({kind:\"human\",id:\"human\",displayName:\"You\"}),p=d(\"element\"),h=d(null),f=d(null),m=d([]),b=d(null),P=d(0),v=d([]);function w(e,t){let n=_.value,o={type:e,timestamp:new Date().toISOString(),sessionId:$.value,sequence:++ie.value,payload:t};if(n)try{fetch(n,{method:\"POST\",headers:{\"Content-Type\":\"application/json\"},body:JSON.stringify(o),keepalive:!0}).catch(r=>console.warn(\"[annotations] webhook emit failed\",r))}catch(r){console.warn(\"[annotations] webhook emit threw synchronously\",r)}}var A={version:\"0.1.0\",schema:\"afs-1.1\",addAnnotation(e){let t=e.id??`ann_${O().slice(-12).toLowerCase()}`,n=Date.now(),o={id:t,comment:e.comment,elementPath:e.elementPath,element:e.element,x:e.x,y:e.y,timestamp:e.timestamp??n,url:e.url,boundingBox:e.boundingBox,reactComponents:e.reactComponents,cssClasses:e.cssClasses,computedStyles:e.computedStyles,accessibility:e.accessibility,nearbyText:e.nearbyText,selectedText:e.selectedText,isFixed:e.isFixed,isMultiSelect:e.isMultiSelect,fullPath:e.fullPath,nearbyElements:e.nearbyElements,elementBoundingBoxes:e.elementBoundingBoxes,intent:e.intent,severity:e.severity,kind:e.kind??\"feedback\",placement:e.placement,rearrange:e.rearrange,status:e.status??\"pending\",thread:[],author:e.author??{kind:\"agent\",id:\"agent\",displayName:\"Agent\"},createdAt:new Date(n).toISOString(),updatedAt:new Date(n).toISOString()};return l.value=[...l.value,o],w(\"annotation.created\",o),t},replyToAnnotation(e,t){if(!l.value.find(r=>r.id===e))return null;let o={id:`msg_${O().slice(-10).toLowerCase()}`,role:t.role,content:t.content,timestamp:Date.now()};return l.value=l.value.map(r=>r.id===e?{...r,thread:[...r.thread??[],o],updatedAt:new Date().toISOString()}:r),w(\"thread.message\",{annotationId:e,message:o}),o.id},updateAnnotation(e,t){let n=!1;return l.value=l.value.map(o=>o.id!==e?o:(n=!0,{...o,...t,updatedAt:new Date().toISOString()})),n&&w(\"annotation.updated\",{id:e,patch:t}),n},acknowledgeAnnotation(e){return this.updateAnnotation(e,{status:\"acknowledged\"})},resolveAnnotation(e,t=\"agent\"){return this.updateAnnotation(e,{status:\"resolved\",resolvedAt:new Date().toISOString(),resolvedBy:t})},dismissAnnotation(e){return this.updateAnnotation(e,{status:\"dismissed\"})},removeAnnotation(e){let t=l.value.length;l.value=l.value.filter(o=>o.id!==e);let n=l.value.length<t;return n&&w(\"annotation.deleted\",{id:e}),n},clearAnnotations(){let e=l.value.length;return l.value=[],b.value=null,w(\"session.updated\",{cleared:e}),e},focusAnnotation(e){let t=l.value.some(n=>n.id===e);return t&&(b.value=e),t},closeThread(){b.value=null},setCursors(e){v.value=e},setCursor(e){let t=v.value.filter(n=>n.id!==e.id);v.value=[...t,e]},removeCursor(e){let t=v.value.length;return v.value=v.value.filter(n=>n.id!==e),v.value.length<t},clearCursors(){v.value=[]},setMode(e){u.value!==e&&(u.value=e,w(\"session.updated\",{mode:e}))},getMode(){return u.value},subscribeMode(e){return u.subscribe(e)},setTheme(e){T.value=e},getAnnotations(){return l.value}};function se(e){e.webhookUrl!==void 0&&(_.value=e.webhookUrl),$.value=e.sessionId??`cf_${O().slice(-12).toLowerCase()}`,w(\"session.created\",{sessionId:$.value,pageUrl:typeof location<\"u\"?location.href:\"\"})}var Fe=[\"display\",\"position\",\"width\",\"height\",\"margin\",\"padding\",\"color\",\"background-color\",\"background\",\"font-family\",\"font-size\",\"font-weight\",\"line-height\",\"border\",\"border-radius\",\"box-shadow\",\"flex-direction\",\"justify-content\",\"align-items\",\"gap\",\"grid-template-columns\",\"z-index\",\"opacity\",\"text-align\"],Ue=[\"role\",\"aria-label\",\"aria-labelledby\",\"aria-describedby\",\"aria-hidden\",\"aria-expanded\",\"aria-checked\",\"aria-selected\",\"alt\",\"title\",\"tabindex\",\"type\",\"name\",\"for\",\"href\"];function N(e){let t=e.getBoundingClientRect(),n=tt(e),o=window.scrollX,r=window.scrollY,s={x:t.left+(n?0:o),y:t.top+(n?0:r),width:t.width,height:t.height};return{element:e.tagName.toLowerCase(),elementPath:Ke(e),fullPath:Ve(e),x:nt((t.left+t.width/2)/window.innerWidth*100),y:n?t.top:t.top+r,url:location.href,boundingBox:s,isFixed:n,cssClasses:We(e),computedStyles:qe(e),accessibility:Ge(e),nearbyText:Xe(e),nearbyElements:Je(e),reactComponents:Qe(e)}}function Ke(e){if(e.id&&le(e.id))return`#${F(e.id)}`;let t=e.getAttribute(\"data-testid\");if(t)return`[data-testid=\"${t}\"]`;let n=e.tagName.toLowerCase(),o=ce(e);return o?`${n}.${F(o)}`:n}function Ve(e){let t=[],n=e;for(;n&&n.nodeType===1&&n.tagName.toLowerCase()!==\"html\";){if(n.id&&le(n.id)){t.unshift(`#${F(n.id)}`);break}let o=n.tagName.toLowerCase(),r=n.parentElement;if(r){let s=Array.from(r.children).filter(a=>a.tagName===n.tagName);s.length>1&&(o+=`:nth-of-type(${s.indexOf(n)+1})`)}t.unshift(o),n=r}return t.join(\" > \")}function U(e){if(!e)return null;try{return document.querySelector(e)}catch{return null}}function le(e){return!(e.length>40||/^[:]?r[a-z0-9]+[:]?$/i.test(e)||/^(radix|headlessui|mui|react-aria)[-:]/i.test(e))}function ce(e){for(let t of Array.from(e.classList))if(!(/^[a-z0-9_-]*[a-f0-9]{6,}$/i.test(t)&&/\\d/.test(t))&&!t.startsWith(\"cf-\"))return t;return e.classList[0]??null}function F(e){return typeof CSS<\"u\"&&typeof CSS.escape==\"function\"?CSS.escape(e):e.replace(/([^\\w-])/g,\"\\\\$1\")}function We(e){let t=Array.from(e.classList).filter(n=>!n.startsWith(\"cf-\"));return t.length?t.join(\" \"):void 0}function qe(e){let t=window.getComputedStyle(e),n=[];for(let o of Fe){let r=t.getPropertyValue(o);r&&r!==\"none\"&&r!==\"normal\"&&r!==\"auto\"&&n.push(`${o}: ${r};`)}return n.length?n.join(`\n`):void 0}function Ge(e){let t=[];for(let o of Ue){let r=e.getAttribute(o);r!==null&&r!==\"\"&&t.push(`${o}=\"${r}\"`)}let n=Ze(e);return n&&t.push(`text=\"${K(n,60)}\"`),t.length?t.join(\" \"):void 0}function Xe(e){let t=(e.textContent??\"\").replace(/\\s+/g,\" \").trim();if(t)return K(t,200)}function Je(e){let t=[];e.parentElement&&t.push(`parent: ${D(e.parentElement)}`);let n=e.previousElementSibling;n&&t.push(`prev: ${D(n)}`);let o=e.nextElementSibling;return o&&t.push(`next: ${D(o)}`),t.length?t.join(\" | \"):void 0}function D(e){let t=e.tagName.toLowerCase(),n=ce(e),o=(e.textContent??\"\").replace(/\\s+/g,\" \").trim().slice(0,30);return`${t}${n?`.${n}`:\"\"}${o?` \"${o}\"`:\"\"}`}function Ze(e){let t=\"\";for(let n of Array.from(e.childNodes))n.nodeType===3&&(t+=n.textContent??\"\");return t.replace(/\\s+/g,\" \").trim()}function Qe(e){let t=Object.keys(e).find(x=>x.startsWith(\"__reactFiber$\")||x.startsWith(\"__reactInternalInstance$\"));if(!t)return;let n=e[t],o=[],r,s=0;for(;n&&s<30;){let x=n.type,k=typeof x==\"function\"?x.displayName??x.name:void 0;if(k&&/^[A-Z]/.test(k)&&!o.includes(k)&&(o.push(k),!r&&n.memoizedProps&&(r=n.memoizedProps),o.length>=3))break;n=n.return,s++}if(!o.length)return;let a=o[0],E=r?et(r):\"\";return E?`${a} (${E})`:a}function et(e){let t=[];for(let[n,o]of Object.entries(e))if(n!==\"children\"&&(typeof o==\"string\"?t.push(`${n}=${JSON.stringify(K(o,40))}`):(typeof o==\"number\"||typeof o==\"boolean\")&&t.push(`${n}=${o}`),t.length>=5))break;return t.join(\", \")}function tt(e){let t=e,n=0;for(;t&&n<20;){if(window.getComputedStyle(t).position===\"fixed\")return!0;t=t.parentElement,n++}return!1}function K(e,t){return e.length>t?`${e.slice(0,t-1)}\\u2026`:e}function nt(e){return Math.max(0,Math.min(100,Math.round(e*10)/10))}var V=null,ue=!1,de=0;function ot(e){let t=Date.now();t-de<120||(de=t,w(\"presence.cursor\",{id:y.value.id??\"human\",label:y.value.displayName??\"You\",kind:y.value.kind,x:e.clientX/window.innerWidth*100,y:e.clientY+window.scrollY}))}function pe(e){V=e}function W(e){return V?e.composedPath().includes(V):!1}function fe(e){return!(e instanceof Element)||e.tagName===\"HTML\"||e.tagName===\"BODY\"?null:e}function rt(e){if(W(e)||ot(e),u.value!==\"feedback\"||f.value){h.value=null;return}if(W(e)){h.value=null;return}if(p.value===\"text\"){h.value=null;return}let t=fe(e.target);if(!t){h.value=null;return}let n=t.getBoundingClientRect();h.value={top:n.top,left:n.left,width:n.width,height:n.height}}function it(e){if(u.value!==\"feedback\"||W(e)||p.value===\"text\"||f.value)return;let t=fe(e.target);if(t){if(e.preventDefault(),e.stopPropagation(),p.value===\"multi\"){lt(t);return}q(N(t))}}function at(){if(u.value!==\"feedback\"||p.value!==\"text\"||f.value)return;let e=window.getSelection(),t=e?.toString().trim();if(!t||!e||e.rangeCount===0)return;let n=e.getRangeAt(0),o=n.commonAncestorContainer.nodeType===1?n.commonAncestorContainer:n.commonAncestorContainer.parentElement;if(!o)return;let r=N(o);q({...r,selectedText:t})}function st(e){if(e.key===\"Escape\"){if(f.value){f.value=null;return}if(m.value.length){m.value=[],h.value=null;return}u.value===\"feedback\"&&(u.value=\"view\",h.value=null)}}function lt(e){let t=m.value;m.value=t.includes(e)?t.filter(n=>n!==e):[...t,e]}function me(){let e=m.value;if(!e.length)return;let t=N(e[0]),n=e.map(r=>{let s=r.getBoundingClientRect();return{x:s.left+window.scrollX,y:s.top+window.scrollY,width:s.width,height:s.height}}),o=e.map(r=>r.tagName.toLowerCase()).join(\", \");q({...t,isMultiSelect:!0,elementBoundingBoxes:n,nearbyElements:`${e.length} elements: ${o}`})}function q(e){m.value=[],h.value=null,f.value=e}function ge(){ue||(ue=!0,document.addEventListener(\"mousemove\",rt,!0),document.addEventListener(\"click\",it,!0),document.addEventListener(\"mouseup\",at,!0),document.addEventListener(\"keydown\",st,!0))}var he=\"annotations-v1-host\",ct=`\n:host {\n all: initial;\n contain: layout style;\n font-family: -apple-system, \"SF Pro Text\", system-ui, sans-serif;\n font-size: 14px;\n line-height: 1.45;\n color: #f4f4f4;\n}\n*, *::before, *::after { box-sizing: border-box; }\nbutton { font: inherit; cursor: pointer; }\n[hidden] { display: none !important; }\n`,I=null;function be(){if(I&&document.contains(I.host))return I;let e=document.getElementById(he);e&&e.remove();let t=document.createElement(\"div\");t.id=he,t.style.cssText=[\"all: initial\",\"position: fixed\",\"inset: 0\",\"pointer-events: none\",\"z-index: 2147483647\"].join(\";\");let n=t.attachShadow({mode:\"closed\"}),o=document.createElement(\"style\");o.textContent=ct,n.appendChild(o);let r=document.createElement(\"div\");return r.id=\"cf-app\",r.style.cssText=\"pointer-events: auto;\",n.appendChild(r),document.documentElement.appendChild(t),I={host:t,shadow:n,appRoot:r},I}function j(e){return`top:${e.top}px;left:${e.left}px;width:${e.width}px;height:${e.height}px;`}function G(e){let t=e.trim(),n=t.indexOf(`\n`);return n>0?t.slice(0,n):t}function ve(e,t){return e.length>t?`${e.slice(0,t-1)}\\u2026`:e}function xe(e){let t=0;for(let n=0;n<e.length;n++)t=(t<<5)-t+e.charCodeAt(n);return t}function ye(e){let t=U(e.fullPath??\"\")??U(e.elementPath??\"\");if(t){let n=t.getBoundingClientRect();return{top:n.top-11,left:n.left-11}}return e.boundingBox?{top:e.boundingBox.y-(e.isFixed?0:window.scrollY)-11,left:e.boundingBox.x-(e.isFixed?0:window.scrollX)-11}:null}var ut=\"http://www.w3.org/2000/svg\";function we(e,t){for(let[n,o]of Object.entries(t))if(o!=null){if(n===\"class\"){e.setAttribute(\"class\",String(o));continue}if(n===\"style\"){e.setAttribute(\"style\",String(o));continue}if(n===\"dataset\"){let r=o;if(e instanceof HTMLElement)for(let[s,a]of Object.entries(r))e.dataset[s]=a;continue}if(n.startsWith(\"on\")&&typeof o==\"function\"){let r=n.slice(2).toLowerCase();e.addEventListener(r,o);continue}if(typeof o==\"boolean\"){o&&e.setAttribute(n,\"\");continue}e.setAttribute(n,String(o))}}function X(e,t){for(let n of t)if(!(n===null||n===!1||n===void 0)){if(Array.isArray(n)){X(e,n);continue}if(n instanceof Node){e.appendChild(n);continue}e.appendChild(document.createTextNode(String(n)))}}function i(e,t,...n){let o=document.createElement(e);return t&&we(o,t),X(o,n),o}function J(e,t,...n){let o=document.createElementNS(ut,e);return t&&we(o,t),X(o,n),o}function dt(e){return e===\"text\"?\"Select text on the page to annotate it.\":e===\"multi\"?\"Click elements to group them, then commit.\":\"Click any element to comment. Esc to exit.\"}function B(e,t,n){return i(\"button\",{class:`cf-seg-btn${t?\" on\":\"\"}`,onClick:n},e)}function ke(){let e=u.value===\"feedback\",t=l.value.length,n=m.value.length;return i(\"div\",{class:\"cf-toolbar\"},i(\"div\",{class:\"cf-toolbar-head\"},i(\"span\",{class:\"cf-logo-dot\"}),i(\"span\",null,\"Annotations\"),i(\"span\",{class:\"cf-count\"},String(t))),i(\"div\",{class:\"cf-seg\"},B(\"View\",u.value===\"view\",()=>{u.value=\"view\",h.value=null,m.value=[]}),B(\"Comment\",e,()=>{u.value=\"feedback\"})),e?i(\"div\",{class:\"cf-seg cf-tools\"},B(\"Element\",p.value===\"element\",()=>{p.value=\"element\",m.value=[]}),B(\"Text\",p.value===\"text\",()=>{p.value=\"text\",m.value=[]}),B(\"Multi\",p.value===\"multi\",()=>{p.value=\"multi\"})):null,e&&p.value===\"multi\"&&n?i(\"button\",{class:\"cf-btn cf-btn-primary cf-commit\",onClick:me},`Comment ${n} element${n===1?\"\":\"s\"}`):null,e?i(\"div\",{class:\"cf-hint\"},dt(p.value)):null)}var L={blocking:\"#ef4444\",important:\"#f59e0b\",suggestion:\"#3ecf8e\"},Te=\"#a78bfa\",Ee=[\"fix\",\"change\",\"question\",\"approve\"],Se=[\"blocking\",\"important\",\"suggestion\"],Ae=\"#8b94a3\",Z=[\"#a78bfa\",\"#3ecf8e\",\"#f59e0b\",\"#38bdf8\",\"#f472b6\"];function Ce(e,t){let n=ye(e);if(!n)return null;let o=e.severity?L[e.severity]:Te,r=e.status===\"resolved\"||e.status===\"dismissed\",s=i(\"button\",{class:`cf-pin${r?\" resolved\":\"\"}${e.author?.kind===\"agent\"?\" agent\":\"\"}`,style:`top:${n.top}px;left:${n.left}px;--cf-pin:${o};`,title:G(e.comment),onClick:()=>{b.value=b.value===e.id?null:e.id}},e.author?.kind===\"agent\"?\"\\u2605\":String(t)),a=null;return s.addEventListener(\"mouseenter\",()=>{a||(a=i(\"div\",{class:\"cf-pin-preview\"},G(e.comment)||e.comment),s.appendChild(a))}),s.addEventListener(\"mouseleave\",()=>{a?.remove(),a=null}),s}function Le(e){let t=e.getBoundingClientRect();return i(\"div\",{class:\"cf-multi-box\",style:j({top:t.top,left:t.left,width:t.width,height:t.height})})}function Me(e){let t=e.x/100*window.innerWidth,n=e.y-window.scrollY,o=e.color??(e.kind===\"agent\"?Z[Math.abs(xe(e.id))%Z.length]:\"#ffffff\"),r=J(\"svg\",{width:\"18\",height:\"18\",viewBox:\"0 0 18 18\",class:\"cf-cursor-arrow\"},J(\"path\",{d:\"M2 2 L2 14 L6 10 L9 16 L11 15 L8 9 L14 9 Z\",fill:o,stroke:\"rgba(0,0,0,0.35)\",\"stroke-width\":\"0.75\"}));return i(\"div\",{class:`cf-cursor cf-cursor-${e.kind}`,style:`top:${n}px;left:${t}px;--cf-cursor:${o};`},r,i(\"span\",{class:\"cf-cursor-label\"},`${e.kind===\"agent\"?\"\\u{1F916} \":\"\"}${e.label}`))}function $e(e){let t=\"change\",n=\"important\",o=i(\"textarea\",{class:\"cf-textarea\",placeholder:\"Describe the change\\u2026 (Markdown supported)\",autofocus:!0,onKeyDown:c=>{(c.metaKey||c.ctrlKey)&&c.key===\"Enter\"&&r()},onInput:()=>g()}),r=()=>{let c=o.value.trim();c&&(A.addAnnotation({...e,comment:c,intent:t,severity:n,kind:\"feedback\",author:y.value}),f.value=null)},s=()=>{f.value=null};function a(c,Q,ee,He){let te=c.map(S=>{let R=ee(S);return i(\"button\",{class:`cf-chip${R?\" on\":\"\"}`,style:R?`--cf-chip:${Q(S)};`:void 0,onClick:()=>{He(S),ne()}},S)}),ze=i(\"div\",{class:\"cf-chipset\"},...te);function ne(){c.forEach((S,R)=>{let H=te[R],oe=ee(S);H.className=`cf-chip${oe?\" on\":\"\"}`,oe?H.setAttribute(\"style\",`--cf-chip:${Q(S)};`):H.removeAttribute(\"style\")})}return{group:ze,refresh:ne}}let E=a(Ee,()=>Ae,c=>t===c,c=>{t=c}),x=a(Se,c=>L[c],c=>n===c,c=>{n=c}),k=e.isMultiSelect?`${e.elementBoundingBoxes?.length??0} elements`:e.selectedText?`\"${ve(e.selectedText,40)}\"`:`<${e.element}>`,M=i(\"button\",{class:\"cf-btn cf-btn-primary\",disabled:!0,onClick:r},\"Comment\");function g(){M.disabled=!o.value.trim()}return i(\"div\",{class:\"cf-popup cf-composer\"},i(\"div\",{class:\"cf-popup-head\"},i(\"span\",{class:\"cf-popup-target\"},k),i(\"button\",{class:\"cf-icon-btn\",onClick:s,title:\"Cancel\"},\"\\u2715\")),o,i(\"div\",{class:\"cf-field\"},i(\"span\",{class:\"cf-field-label\"},\"Intent\"),E.group),i(\"div\",{class:\"cf-field\"},i(\"span\",{class:\"cf-field-label\"},\"Severity\"),x.group),i(\"div\",{class:\"cf-popup-actions\"},i(\"button\",{class:\"cf-btn\",onClick:s},\"Cancel\"),M))}function Pe(e,t,n){return i(\"div\",{class:`cf-msg cf-msg-${e}`},i(\"div\",{class:\"cf-msg-meta\"},i(\"span\",{class:`cf-avatar cf-avatar-${e}`},e===\"agent\"?\"\\u{1F916}\":\"\\u{1F9D1}\"),i(\"span\",{class:\"cf-msg-name\"},t)),i(\"div\",{class:\"cf-msg-body\"},n))}function Ie(e){let t=l.value.find(a=>a.id===e);if(!t)return null;let n=i(\"textarea\",{class:\"cf-textarea cf-textarea-sm\",placeholder:\"Reply\\u2026\",onKeyDown:a=>{(a.metaKey||a.ctrlKey)&&a.key===\"Enter\"&&o()},onInput:()=>s()}),o=()=>{let a=n.value.trim();a&&A.replyToAnnotation(e,{role:y.value.kind,content:a})},r=i(\"button\",{class:\"cf-btn cf-btn-primary\",disabled:!0,onClick:o},\"Reply\");function s(){r.disabled=!n.value.trim()}return i(\"div\",{class:\"cf-popup cf-thread\"},i(\"div\",{class:\"cf-popup-head\"},i(\"span\",{class:\"cf-popup-target\"},`<${t.element}>`),t.severity?i(\"span\",{class:\"cf-tag\",style:`--cf-tag:${L[t.severity]};`},t.severity):null,i(\"button\",{class:\"cf-icon-btn\",onClick:()=>{b.value=null},title:\"Close\"},\"\\u2715\")),i(\"div\",{class:\"cf-thread-body\"},Pe(t.author?.kind??\"human\",t.author?.displayName??\"You\",t.comment),...(t.thread??[]).map(a=>Pe(a.role,a.role===\"agent\"?\"Agent\":\"You\",a.content))),i(\"div\",{class:\"cf-thread-compose\"},n,i(\"div\",{class:\"cf-thread-actions\"},t.status!==\"resolved\"?i(\"button\",{class:\"cf-btn cf-btn-ghost\",onClick:()=>A.resolveAnnotation(e,y.value.kind)},\"Resolve\"):i(\"span\",{class:\"cf-resolved-tag\"},\"\\u2713 resolved\"),r)))}var Be=`\n.cf-overlay {\n position: fixed;\n inset: 0;\n pointer-events: none;\n font-family: -apple-system, \"SF Pro Text\", system-ui, sans-serif;\n font-size: 13px;\n color: #f4f4f5;\n --cf-bg: rgba(18, 18, 22, 0.96);\n --cf-border: rgba(255, 255, 255, 0.1);\n --cf-muted: rgba(255, 255, 255, 0.55);\n --cf-accent: #a78bfa;\n}\n.cf-overlay[data-theme=\"light\"] {\n color: #18181b;\n --cf-bg: rgba(252, 252, 253, 0.97);\n --cf-border: rgba(0, 0, 0, 0.1);\n --cf-muted: rgba(0, 0, 0, 0.5);\n}\n\n/* Hover highlight + multi-select boxes */\n.cf-hover {\n position: fixed;\n pointer-events: none;\n border: 2px solid var(--cf-accent);\n background: rgba(167, 139, 250, 0.1);\n border-radius: 4px;\n box-shadow: 0 0 0 1px rgba(167, 139, 250, 0.4);\n transition: top .06s ease, left .06s ease, width .06s ease, height .06s ease;\n z-index: 1;\n}\n.cf-multi-box {\n position: fixed;\n pointer-events: none;\n border: 2px solid #3ecf8e;\n background: rgba(62, 207, 142, 0.12);\n border-radius: 4px;\n z-index: 1;\n animation: cf-multi-pulse 1.4s ease-in-out infinite;\n}\n@keyframes cf-multi-pulse {\n 0%, 100% { box-shadow: 0 0 0 0 rgba(62,207,142,0.0); }\n 50% { box-shadow: 0 0 0 3px rgba(62,207,142,0.25); }\n}\n\n/* Live presence cursors (multi-cursor collaboration) */\n.cf-cursor {\n position: fixed;\n z-index: 6;\n pointer-events: none;\n display: flex;\n align-items: flex-start;\n gap: 4px;\n /* Glide to new positions so multiple actors read as \"live\". */\n transition: top .12s ease, left .12s ease;\n will-change: top, left;\n}\n.cf-cursor-arrow { display: block; filter: drop-shadow(0 1px 2px rgba(0,0,0,0.4)); }\n.cf-cursor-label {\n transform: translateY(2px);\n background: var(--cf-cursor, #a78bfa);\n color: #0b0b0f;\n font: 700 11px -apple-system, system-ui;\n padding: 2px 7px;\n border-radius: 8px;\n white-space: nowrap;\n box-shadow: 0 2px 8px rgba(0,0,0,0.35);\n}\n.cf-cursor-human .cf-cursor-label { color: #18181b; }\n.cf-cursor-agent .cf-cursor-label { color: #0b0b0f; }\n\n/* Pins */\n.cf-pin {\n position: fixed;\n width: 22px; height: 22px;\n border-radius: 50% 50% 50% 2px;\n background: var(--cf-pin, #a78bfa);\n color: #fff;\n border: 2px solid rgba(255,255,255,0.9);\n font: 700 11px -apple-system, system-ui;\n display: flex; align-items: center; justify-content: center;\n cursor: pointer;\n pointer-events: auto;\n box-shadow: 0 2px 8px rgba(0,0,0,0.35);\n z-index: 3;\n transition: transform .12s cubic-bezier(.34,1.56,.64,1);\n padding: 0;\n /* Pop in when a pin first renders so new annotations are obvious. */\n animation: cf-pin-pop .32s cubic-bezier(.34,1.56,.64,1) both;\n}\n.cf-pin:hover { transform: scale(1.2); }\n.cf-pin.resolved { opacity: 0.45; }\n/* Agent-pushed pins pulse a ring so reviewers spot what the agent added. */\n.cf-pin.agent { border-style: dashed; }\n.cf-pin.agent::after {\n content: \"\";\n position: absolute; inset: -4px;\n border-radius: inherit;\n border: 2px solid var(--cf-pin, #a78bfa);\n animation: cf-pin-ring 1.6s ease-out 3;\n pointer-events: none;\n}\n\n/* Hover-to-preview card \\u2014 shown next to a pin on mouseenter. */\n.cf-pin-preview {\n position: absolute;\n top: 26px; left: 0;\n max-width: 240px;\n width: max-content;\n pointer-events: none;\n background: var(--cf-bg);\n border: 1px solid var(--cf-border);\n border-radius: 9px;\n padding: 7px 10px;\n font: 500 12px -apple-system, system-ui;\n line-height: 1.4;\n color: #f4f4f5;\n white-space: normal;\n overflow-wrap: anywhere;\n backdrop-filter: blur(16px) saturate(140%);\n box-shadow: 0 8px 28px rgba(0,0,0,0.5);\n z-index: 7;\n animation: cf-rise .14s cubic-bezier(.16,1,.3,1) both;\n}\n.cf-overlay[data-theme=\"light\"] .cf-pin-preview { color: #18181b; }\n\n@keyframes cf-pin-pop {\n 0% { transform: scale(0); opacity: 0; }\n 60% { transform: scale(1.25); opacity: 1; }\n 100% { transform: scale(1); opacity: 1; }\n}\n@keyframes cf-pin-ring {\n 0% { transform: scale(1); opacity: .7; }\n 100% { transform: scale(2.4); opacity: 0; }\n}\n\n/* Toolbar */\n.cf-toolbar {\n position: fixed;\n left: 16px; bottom: 16px;\n pointer-events: auto;\n background: var(--cf-bg);\n border: 1px solid var(--cf-border);\n border-radius: 14px;\n padding: 10px;\n display: flex; flex-direction: column; gap: 8px;\n backdrop-filter: blur(16px) saturate(140%);\n box-shadow: 0 16px 48px rgba(0,0,0,0.5);\n min-width: 220px;\n z-index: 4;\n}\n.cf-toolbar-head {\n display: flex; align-items: center; gap: 7px;\n font-weight: 700; letter-spacing: 0.02em;\n}\n.cf-logo-dot {\n width: 8px; height: 8px; border-radius: 50%;\n background: var(--cf-accent);\n box-shadow: 0 0 10px var(--cf-accent);\n}\n.cf-count {\n margin-left: auto;\n font: 700 11px -apple-system, system-ui;\n color: var(--cf-muted);\n background: rgba(255,255,255,0.08);\n padding: 1px 7px; border-radius: 8px;\n}\n.cf-overlay[data-theme=\"light\"] .cf-count { background: rgba(0,0,0,0.06); }\n\n.cf-seg {\n display: flex; gap: 2px;\n background: rgba(255,255,255,0.06);\n border-radius: 9px; padding: 2px;\n}\n.cf-overlay[data-theme=\"light\"] .cf-seg { background: rgba(0,0,0,0.05); }\n.cf-seg-btn {\n flex: 1; border: none; background: transparent; color: var(--cf-muted);\n padding: 5px 10px; border-radius: 7px; font: 600 12px -apple-system, system-ui;\n}\n.cf-seg-btn.on {\n background: var(--cf-accent); color: #fff;\n box-shadow: 0 1px 4px rgba(0,0,0,0.3);\n}\n.cf-tools .cf-seg-btn.on { background: rgba(255,255,255,0.16); color: #fff; }\n.cf-overlay[data-theme=\"light\"] .cf-tools .cf-seg-btn.on { background: rgba(0,0,0,0.12); color: #111; }\n.cf-hint { font-size: 11px; color: var(--cf-muted); padding: 0 2px; }\n.cf-commit { width: 100%; }\n\n/* Buttons */\n.cf-btn {\n border: 1px solid var(--cf-border); background: transparent; color: inherit;\n padding: 6px 12px; border-radius: 8px; font: 600 12px -apple-system, system-ui;\n}\n.cf-btn:hover { background: rgba(255,255,255,0.06); }\n.cf-overlay[data-theme=\"light\"] .cf-btn:hover { background: rgba(0,0,0,0.04); }\n.cf-btn-primary {\n background: var(--cf-accent); border-color: var(--cf-accent); color: #fff;\n}\n.cf-btn-primary:hover { background: #9173f0; }\n.cf-btn-primary:disabled { opacity: 0.4; cursor: not-allowed; }\n.cf-btn-ghost { border-color: transparent; color: var(--cf-muted); }\n\n/* Popups (composer + thread) */\n.cf-popup {\n position: fixed;\n left: 16px; bottom: 92px;\n width: 320px;\n pointer-events: auto;\n background: var(--cf-bg);\n border: 1px solid var(--cf-border);\n border-radius: 14px;\n padding: 12px;\n display: flex; flex-direction: column; gap: 10px;\n backdrop-filter: blur(16px) saturate(140%);\n box-shadow: 0 16px 48px rgba(0,0,0,0.55);\n z-index: 5;\n animation: cf-rise .2s cubic-bezier(.16,1,.3,1) both;\n}\n@keyframes cf-rise {\n from { transform: translateY(10px); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n}\n.cf-popup-head {\n display: flex; align-items: center; gap: 8px;\n}\n.cf-popup-target {\n font: 600 12px ui-monospace, \"SF Mono\", monospace;\n color: var(--cf-accent);\n overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n}\n.cf-icon-btn {\n margin-left: auto; border: none; background: transparent;\n color: var(--cf-muted); font-size: 13px; cursor: pointer;\n width: 22px; height: 22px; border-radius: 6px;\n}\n.cf-icon-btn:hover { background: rgba(255,255,255,0.08); }\n\n.cf-textarea {\n width: 100%; min-height: 72px; resize: vertical;\n background: rgba(255,255,255,0.04);\n border: 1px solid var(--cf-border); border-radius: 9px;\n padding: 8px 10px; color: inherit; font: 400 13px -apple-system, system-ui;\n line-height: 1.45;\n}\n.cf-overlay[data-theme=\"light\"] .cf-textarea { background: rgba(0,0,0,0.03); }\n.cf-textarea:focus { outline: none; border-color: var(--cf-accent); }\n.cf-textarea-sm { min-height: 48px; }\n\n.cf-field { display: flex; flex-direction: column; gap: 5px; }\n.cf-field-label {\n font: 700 10px -apple-system, system-ui; letter-spacing: 0.06em;\n text-transform: uppercase; color: var(--cf-muted);\n}\n.cf-chipset { display: flex; flex-wrap: wrap; gap: 5px; }\n.cf-chip {\n border: 1px solid var(--cf-border); background: transparent; color: var(--cf-muted);\n padding: 4px 10px; border-radius: 20px; font: 600 11px -apple-system, system-ui;\n text-transform: capitalize;\n}\n.cf-chip.on {\n background: var(--cf-chip, var(--cf-accent));\n border-color: var(--cf-chip, var(--cf-accent));\n color: #fff;\n}\n.cf-popup-actions { display: flex; gap: 8px; justify-content: flex-end; }\n\n/* Thread */\n.cf-thread-body {\n display: flex; flex-direction: column; gap: 12px;\n max-height: 280px; overflow-y: auto; padding-right: 2px;\n}\n.cf-msg { display: flex; flex-direction: column; gap: 3px; }\n.cf-msg-meta { display: flex; align-items: center; gap: 6px; }\n.cf-avatar { font-size: 12px; }\n.cf-msg-name {\n font: 700 11px -apple-system, system-ui; color: var(--cf-muted);\n}\n.cf-msg-body {\n font-size: 13px; line-height: 1.5; white-space: pre-wrap; word-break: break-word;\n background: rgba(255,255,255,0.04); border-radius: 9px; padding: 7px 10px;\n}\n.cf-overlay[data-theme=\"light\"] .cf-msg-body { background: rgba(0,0,0,0.03); }\n.cf-msg-agent .cf-msg-body { border-left: 2px solid var(--cf-accent); }\n.cf-thread-compose { display: flex; flex-direction: column; gap: 8px; }\n.cf-thread-actions { display: flex; gap: 8px; align-items: center; }\n.cf-thread-actions .cf-btn-primary { margin-left: auto; }\n.cf-resolved-tag { font: 600 11px -apple-system, system-ui; color: #3ecf8e; }\n.cf-tag {\n font: 700 9px -apple-system, system-ui; text-transform: uppercase;\n letter-spacing: 0.05em; padding: 2px 6px; border-radius: 5px;\n background: var(--cf-tag, var(--cf-accent)); color: #fff;\n}\n\n/* Respect reduced-motion: keep the UI usable, drop the motion. */\n@media (prefers-reduced-motion: reduce) {\n .cf-pin, .cf-popup, .cf-multi-box { animation: none !important; }\n .cf-pin.agent::after { display: none; }\n}\n`;function C(e,t,n){let o=()=>{e.replaceChildren();let r=n();r!==null&&(Array.isArray(r)?e.append(...r):e.append(r))};o();for(let r of t)r.subscribe(o)}function Re(e,t={}){let n=i(\"div\",{class:\"cf-overlay\"}),o=()=>{n.setAttribute(\"data-theme\",T.value===\"auto\"?\"dark\":T.value)};o(),T.subscribe(o);let r=document.createElement(\"style\");r.textContent=Be,n.appendChild(r);let s=i(\"div\",{class:\"cf-layer-hover\"});C(s,[h,f],()=>{let g=h.value;return!g||f.value?null:i(\"div\",{class:\"cf-hover\",style:j(g)})}),n.appendChild(s);let a=i(\"div\",{class:\"cf-layer-multi\"});C(a,[m,P],()=>m.value.map(g=>Le(g))),n.appendChild(a);let E=i(\"div\",{class:\"cf-layer-pins\"});C(E,[l,P],()=>l.value.map((g,c)=>Ce(g,c+1)).filter(g=>g!==null)),n.appendChild(E);let x=i(\"div\",{class:\"cf-layer-cursors\"});if(C(x,[v],()=>v.value.map(g=>Me(g))),n.appendChild(x),!t.headless){let g=i(\"div\",{class:\"cf-layer-toolbar\"});C(g,[u,p,l,m],()=>ke()),n.appendChild(g)}let k=i(\"div\",{class:\"cf-layer-composer\"});C(k,[f],()=>f.value?$e(f.value):null),n.appendChild(k);let M=i(\"div\",{class:\"cf-layer-thread\"});C(M,[b,l],()=>b.value?Ie(b.value):null),n.appendChild(M),e.appendChild(n)}var Oe=!1,_e=!1;function Ne(e={}){se(e),e.author&&(y.value=e.author),e.mode&&(u.value=e.mode),e.theme&&(T.value=e.theme),e.headless&&(_e=!0),je(),ae.value=!0,console.info(\"[annotations v1] ready on\",location.href)}function je(){if(Oe||typeof document>\"u\")return;if(!document.documentElement){document.addEventListener(\"DOMContentLoaded\",je,{once:!0});return}Oe=!0;let{host:e,appRoot:t}=be();pe(e),ge(),Re(t,{headless:_e});let n=()=>{P.value++};window.addEventListener(\"scroll\",n,{passive:!0,capture:!0}),window.addEventListener(\"resize\",n,{passive:!0})}var pt=Object.assign({__init:Ne},A);window.__annotations=pt;Ne();})();\n";
|
|
5
|
+
"\"use strict\";(()=>{var at=Object.defineProperty;var st=(e,t,n)=>t in e?at(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var ne=(e,t,n)=>st(e,typeof t!=\"symbol\"?t+\"\":t,n);var ve=\"0123456789ABCDEFGHJKMNPQRSTVWXYZ\";function K(e=Date.now()){let t=e,n=\"\";for(let r=0;r<10;r++)n=ve[t&31]+n,t=Math.floor(t/32);let o=\"\";for(let r=0;r<16;r++)o+=ve[Math.floor(Math.random()*32)];return n+o}var oe=class{constructor(t){ne(this,\"_value\");ne(this,\"_subs\",new Set);this._value=t}get value(){return this._value}set value(t){if(!Object.is(this._value,t)){this._value=t;for(let n of Array.from(this._subs))n(t)}}peek(){return this._value}subscribe(t){return this._subs.add(t),()=>{this._subs.delete(t)}}};function m(e){return new oe(e)}var d=m([]),g=m(\"view\"),B=m(\"dark\"),W=m(null),_=m(\"\"),ye=m(0),we=m(!1),T=m({kind:\"human\",id:\"human\",displayName:\"You\"}),w=m(\"element\"),k=m(null),b=m(null),y=m([]),x=m(null),z=m(0),E=m([]),Y=m(null),H=m(null),I=m(!0),V=m(null);function A(e,t){let n=W.value,o={type:e,timestamp:new Date().toISOString(),sessionId:_.value,sequence:++ye.value,payload:t};if(n)try{fetch(n,{method:\"POST\",headers:{\"Content-Type\":\"application/json\"},body:JSON.stringify(o),keepalive:!0}).catch(r=>console.warn(\"[annotations] webhook emit failed\",r))}catch(r){console.warn(\"[annotations] webhook emit threw synchronously\",r)}}var S={version:\"0.1.0\",schema:\"afs-1.1\",addAnnotation(e){let t=e.id??`ann_${K().slice(-12).toLowerCase()}`,n=Date.now(),o={id:t,comment:e.comment,elementPath:e.elementPath,element:e.element,x:e.x,y:e.y,timestamp:e.timestamp??n,url:e.url,boundingBox:e.boundingBox,reactComponents:e.reactComponents,cssClasses:e.cssClasses,computedStyles:e.computedStyles,accessibility:e.accessibility,nearbyText:e.nearbyText,selectedText:e.selectedText,isFixed:e.isFixed,isMultiSelect:e.isMultiSelect,fullPath:e.fullPath,nearbyElements:e.nearbyElements,elementBoundingBoxes:e.elementBoundingBoxes,intent:e.intent,severity:e.severity,kind:e.kind??\"feedback\",placement:e.placement,rearrange:e.rearrange,status:e.status??\"pending\",thread:[],author:e.author??{kind:\"agent\",id:\"agent\",displayName:\"Agent\"},createdAt:new Date(n).toISOString(),updatedAt:new Date(n).toISOString()};return d.value=[...d.value,o],A(\"annotation.created\",o),t},replyToAnnotation(e,t){if(!d.value.find(r=>r.id===e))return null;let o={id:`msg_${K().slice(-10).toLowerCase()}`,role:t.role,content:t.content,timestamp:Date.now()};return d.value=d.value.map(r=>r.id===e?{...r,thread:[...r.thread??[],o],updatedAt:new Date().toISOString()}:r),A(\"thread.message\",{annotationId:e,message:o}),o.id},updateAnnotation(e,t){let n=!1;return d.value=d.value.map(o=>o.id!==e?o:(n=!0,{...o,...t,updatedAt:new Date().toISOString()})),n&&A(\"annotation.updated\",{id:e,patch:t}),n},acknowledgeAnnotation(e){return this.updateAnnotation(e,{status:\"acknowledged\"})},resolveAnnotation(e,t=\"agent\"){return this.updateAnnotation(e,{status:\"resolved\",resolvedAt:new Date().toISOString(),resolvedBy:t})},dismissAnnotation(e){return this.updateAnnotation(e,{status:\"dismissed\"})},removeAnnotation(e){let t=d.value.length;d.value=d.value.filter(o=>o.id!==e);let n=d.value.length<t;return n&&A(\"annotation.deleted\",{id:e}),n},clearAnnotations(){let e=d.value.length;return d.value=[],x.value=null,A(\"session.updated\",{cleared:e}),e},focusAnnotation(e){let t=d.value.some(n=>n.id===e);return t&&(x.value=e),t},closeThread(){x.value=null},setCursors(e){E.value=e},setCursor(e){let t=E.value.filter(n=>n.id!==e.id);E.value=[...t,e]},removeCursor(e){let t=E.value.length;return E.value=E.value.filter(n=>n.id!==e),E.value.length<t},clearCursors(){E.value=[]},setMode(e){g.value!==e&&(g.value=e,A(\"session.updated\",{mode:e}))},getMode(){return g.value},subscribeMode(e){return g.subscribe(e)},setTheme(e){B.value=e},getAnnotations(){return d.value}};function ke(e){e.webhookUrl!==void 0&&(W.value=e.webhookUrl),_.value=e.sessionId??`cf_${K().slice(-12).toLowerCase()}`,A(\"session.created\",{sessionId:_.value,pageUrl:typeof location<\"u\"?location.href:\"\"})}var lt=[\"display\",\"position\",\"width\",\"height\",\"margin\",\"padding\",\"color\",\"background-color\",\"background\",\"font-family\",\"font-size\",\"font-weight\",\"line-height\",\"border\",\"border-radius\",\"box-shadow\",\"flex-direction\",\"justify-content\",\"align-items\",\"gap\",\"grid-template-columns\",\"z-index\",\"opacity\",\"text-align\"],ct=[\"role\",\"aria-label\",\"aria-labelledby\",\"aria-describedby\",\"aria-hidden\",\"aria-expanded\",\"aria-checked\",\"aria-selected\",\"alt\",\"title\",\"tabindex\",\"type\",\"name\",\"for\",\"href\"];function X(e){let t=e.getBoundingClientRect(),n=yt(e),o=window.scrollX,r=window.scrollY,a={x:t.left+(n?0:o),y:t.top+(n?0:r),width:t.width,height:t.height};return{element:e.tagName.toLowerCase(),elementPath:dt(e),fullPath:ut(e),x:wt((t.left+t.width/2)/window.innerWidth*100),y:n?t.top:t.top+r,url:location.href,boundingBox:a,isFixed:n,cssClasses:pt(e),computedStyles:ft(e),accessibility:mt(e),nearbyText:gt(e),nearbyElements:ht(e),reactComponents:xt(e)}}function dt(e){if(e.id&&Ee(e.id))return`#${ie(e.id)}`;let t=e.getAttribute(\"data-testid\");if(t)return`[data-testid=\"${t}\"]`;let n=e.tagName.toLowerCase(),o=Te(e);return o?`${n}.${ie(o)}`:n}function ut(e){let t=[],n=e;for(;n&&n.nodeType===1&&n.tagName.toLowerCase()!==\"html\";){if(n.id&&Ee(n.id)){t.unshift(`#${ie(n.id)}`);break}let o=n.tagName.toLowerCase(),r=n.parentElement;if(r){let a=Array.from(r.children).filter(l=>l.tagName===n.tagName);a.length>1&&(o+=`:nth-of-type(${a.indexOf(n)+1})`)}t.unshift(o),n=r}return t.join(\" > \")}function ae(e){if(!e)return null;try{return document.querySelector(e)}catch{return null}}function Ee(e){return!(e.length>40||/^[:]?r[a-z0-9]+[:]?$/i.test(e)||/^(radix|headlessui|mui|react-aria)[-:]/i.test(e))}function Te(e){for(let t of Array.from(e.classList))if(!(/^[a-z0-9_-]*[a-f0-9]{6,}$/i.test(t)&&/\\d/.test(t))&&!t.startsWith(\"cf-\"))return t;return e.classList[0]??null}function ie(e){return typeof CSS<\"u\"&&typeof CSS.escape==\"function\"?CSS.escape(e):e.replace(/([^\\w-])/g,\"\\\\$1\")}function pt(e){let t=Array.from(e.classList).filter(n=>!n.startsWith(\"cf-\"));return t.length?t.join(\" \"):void 0}function ft(e){let t=window.getComputedStyle(e),n=[];for(let o of lt){let r=t.getPropertyValue(o);r&&r!==\"none\"&&r!==\"normal\"&&r!==\"auto\"&&n.push(`${o}: ${r};`)}return n.length?n.join(`\n`):void 0}function mt(e){let t=[];for(let o of ct){let r=e.getAttribute(o);r!==null&&r!==\"\"&&t.push(`${o}=\"${r}\"`)}let n=bt(e);return n&&t.push(`text=\"${se(n,60)}\"`),t.length?t.join(\" \"):void 0}function gt(e){let t=(e.textContent??\"\").replace(/\\s+/g,\" \").trim();if(t)return se(t,200)}function ht(e){let t=[];e.parentElement&&t.push(`parent: ${re(e.parentElement)}`);let n=e.previousElementSibling;n&&t.push(`prev: ${re(n)}`);let o=e.nextElementSibling;return o&&t.push(`next: ${re(o)}`),t.length?t.join(\" | \"):void 0}function re(e){let t=e.tagName.toLowerCase(),n=Te(e),o=(e.textContent??\"\").replace(/\\s+/g,\" \").trim().slice(0,30);return`${t}${n?`.${n}`:\"\"}${o?` \"${o}\"`:\"\"}`}function bt(e){let t=\"\";for(let n of Array.from(e.childNodes))n.nodeType===3&&(t+=n.textContent??\"\");return t.replace(/\\s+/g,\" \").trim()}function xt(e){let t=Object.keys(e).find(s=>s.startsWith(\"__reactFiber$\")||s.startsWith(\"__reactInternalInstance$\"));if(!t)return;let n=e[t],o=[],r,a=0;for(;n&&a<30;){let s=n.type,h=typeof s==\"function\"?s.displayName??s.name:void 0;if(h&&/^[A-Z]/.test(h)&&!o.includes(h)&&(o.push(h),!r&&n.memoizedProps&&(r=n.memoizedProps),o.length>=3))break;n=n.return,a++}if(!o.length)return;let l=o[0],f=r?vt(r):\"\";return f?`${l} (${f})`:l}function vt(e){let t=[];for(let[n,o]of Object.entries(e))if(n!==\"children\"&&(typeof o==\"string\"?t.push(`${n}=${JSON.stringify(se(o,40))}`):(typeof o==\"number\"||typeof o==\"boolean\")&&t.push(`${n}=${o}`),t.length>=5))break;return t.join(\", \")}function yt(e){let t=e,n=0;for(;t&&n<20;){if(window.getComputedStyle(t).position===\"fixed\")return!0;t=t.parentElement,n++}return!1}function se(e,t){return e.length>t?`${e.slice(0,t-1)}\\u2026`:e}function wt(e){return Math.max(0,Math.min(100,Math.round(e*10)/10))}var le=null,Ae=!1,Se=0;function kt(e){let t=Date.now();t-Se<120||(Se=t,A(\"presence.cursor\",{id:T.value.id??\"human\",label:T.value.displayName??\"You\",kind:T.value.kind,x:e.clientX/window.innerWidth*100,y:e.clientY+window.scrollY}))}function Ce(e){le=e}function ce(e){return le?e.composedPath().includes(le):!1}function Le(e){return!(e instanceof Element)||e.tagName===\"HTML\"||e.tagName===\"BODY\"?null:e}function Et(e){if(ce(e)||kt(e),g.value!==\"feedback\"||b.value){k.value=null;return}if(ce(e)){k.value=null;return}let t=Le(e.target);if(!t){k.value=null;return}let n=t.getBoundingClientRect();k.value={top:n.top,left:n.left,width:n.width,height:n.height}}function Tt(e){if(g.value!==\"feedback\"||ce(e)||w.value===\"text\"||b.value)return;let t=Le(e.target);if(t){if(e.preventDefault(),e.stopPropagation(),w.value===\"multi\"){Ct(t);return}de(X(t))}}function At(){if(g.value!==\"feedback\"||w.value!==\"text\"||b.value)return;let e=window.getSelection(),t=e?.toString().trim();if(!t||!e||e.rangeCount===0)return;let n=e.getRangeAt(0),o=n.commonAncestorContainer.nodeType===1?n.commonAncestorContainer:n.commonAncestorContainer.parentElement;if(!o)return;let r=X(o),a=n.getBoundingClientRect(),l=a.width||a.height?{x:a.left+(r.isFixed?0:window.scrollX),y:a.top+(r.isFixed?0:window.scrollY),width:a.width,height:a.height}:r.boundingBox;de({...r,selectedText:t,boundingBox:l,x:(a.left+a.width/2)/window.innerWidth*100,y:r.isFixed?a.top:a.top+window.scrollY})}function St(e){if(e.key===\"Escape\"){if(b.value){b.value=null;return}if(y.value.length){y.value=[],k.value=null;return}g.value===\"feedback\"&&(g.value=\"view\",k.value=null)}}function Ct(e){let t=y.value;y.value=t.includes(e)?t.filter(n=>n!==e):[...t,e]}function Me(){let e=y.value;if(!e.length)return;let t=X(e[0]),n=e.map(r=>{let a=r.getBoundingClientRect();return{x:a.left+window.scrollX,y:a.top+window.scrollY,width:a.width,height:a.height}}),o=e.map(r=>r.tagName.toLowerCase()).join(\", \");de({...t,isMultiSelect:!0,elementBoundingBoxes:n,nearbyElements:`${e.length} elements: ${o}`})}function de(e){y.value=[],k.value=null,b.value=e}function $e(){Ae||(Ae=!0,document.addEventListener(\"mousemove\",Et,!0),document.addEventListener(\"click\",Tt,!0),document.addEventListener(\"mouseup\",At,!0),document.addEventListener(\"keydown\",St,!0))}var Be=\"annotations-v1-host\",Lt=`\n:host {\n all: initial;\n contain: layout style;\n font-family: -apple-system, \"SF Pro Text\", system-ui, sans-serif;\n font-size: 14px;\n line-height: 1.45;\n color: #f4f4f4;\n}\n*, *::before, *::after { box-sizing: border-box; }\nbutton { font: inherit; cursor: pointer; }\n[hidden] { display: none !important; }\n`,F=null;function Pe(){if(F&&document.contains(F.host))return F;let e=document.getElementById(Be);e&&e.remove();let t=document.createElement(\"div\");t.id=Be,t.style.cssText=[\"all: initial\",\"position: fixed\",\"inset: 0\",\"pointer-events: none\",\"z-index: 2147483647\"].join(\";\");let n=t.attachShadow({mode:\"closed\"}),o=document.createElement(\"style\");o.textContent=Lt,n.appendChild(o);let r=document.createElement(\"div\");return r.id=\"cf-app\",r.style.cssText=\"pointer-events: auto;\",n.appendChild(r),document.documentElement.appendChild(t),F={host:t,shadow:n,appRoot:r},F}function D(e){return`top:${e.top}px;left:${e.left}px;width:${e.width}px;height:${e.height}px;`}function G(e){let t=e.trim(),n=t.indexOf(`\n`);return n>0?t.slice(0,n):t}function Ie(e,t){return e.length>t?`${e.slice(0,t-1)}\\u2026`:e}function J(e,t){e.style.position=\"fixed\",e.style.left=`${t.left}px`,e.style.top=`${t.top+t.height+10}px`,e.style.right=\"auto\",e.style.bottom=\"auto\",requestAnimationFrame(()=>{let n=e.getBoundingClientRect();if(!n.width)return;let o=Math.max(8,Math.min(t.left,window.innerWidth-n.width-8)),r=t.top+t.height+10;r+n.height>window.innerHeight-8&&(r=t.top-n.height-10),r=Math.max(8,Math.min(r,window.innerHeight-n.height-8)),e.style.left=`${o}px`,e.style.top=`${r}px`,Mt(e)})}function Mt(e){if(typeof e.animate!=\"function\")return;let n=e.closest(\".cf-overlay\")?.querySelector(\".cf-fab, .cf-toolbar\");if(!n)return;let o=n.getBoundingClientRect(),r=e.getBoundingClientRect();if(!o.width||!r.width)return;let a=o.left+o.width/2-(r.left+r.width/2),l=o.top+o.height/2-(r.top+r.height/2),f=Math.max(.08,o.width/r.width),s=Math.max(.08,o.height/r.height);e.classList.add(\"cf-morph\"),e.style.transformOrigin=\"center center\",e.animate([{transform:`translate(${a}px, ${l}px) scale(${f}, ${s})`,opacity:.25},{transform:\"translate(0, 0) scale(1, 1)\",opacity:1}],{duration:360,easing:\"cubic-bezier(.34,1.3,.5,1)\",fill:\"both\"}).addEventListener(\"finish\",()=>{e.style.transform=\"\"})}function Z(e){let t=0;for(let n=0;n<e.length;n++)t=(t<<5)-t+e.charCodeAt(n);return t}function Q(e){let t=ae(e.fullPath??\"\")??ae(e.elementPath??\"\");if(t){let n=t.getBoundingClientRect();return{top:n.top,left:n.left,width:n.width,height:n.height}}return e.boundingBox?{top:e.boundingBox.y-(e.isFixed?0:window.scrollY),left:e.boundingBox.x-(e.isFixed?0:window.scrollX),width:e.boundingBox.width,height:e.boundingBox.height}:null}var $t=\"http://www.w3.org/2000/svg\";function Re(e,t){for(let[n,o]of Object.entries(t))if(o!=null){if(n===\"class\"){e.setAttribute(\"class\",String(o));continue}if(n===\"style\"){e.setAttribute(\"style\",String(o));continue}if(n===\"dataset\"){let r=o;if(e instanceof HTMLElement)for(let[a,l]of Object.entries(r))e.dataset[a]=l;continue}if(n.startsWith(\"on\")&&typeof o==\"function\"){let r=n.slice(2).toLowerCase();e.addEventListener(r,o);continue}if(typeof o==\"boolean\"){o&&e.setAttribute(n,\"\");continue}e.setAttribute(n,String(o))}}function ue(e,t){for(let n of t)if(!(n===null||n===!1||n===void 0)){if(Array.isArray(n)){ue(e,n);continue}if(n instanceof Node){e.appendChild(n);continue}e.appendChild(document.createTextNode(String(n)))}}function i(e,t,...n){let o=document.createElement(e);return t&&Re(o,t),ue(o,n),o}function pe(e,t,...n){let o=document.createElementNS($t,e);return t&&Re(o,t),ue(o,n),o}function R(e=\"\"){let t=i(\"span\",{class:`cf-brand${e?` ${e}`:\"\"}`}),n=V.value;return n?t.innerHTML=n:t.appendChild(i(\"span\",{class:\"cf-logo-dot\"})),t}var C=8,ee=(e,t,n)=>Math.max(t,Math.min(e,n));function Ne(e,t,n){t.addEventListener(\"pointerdown\",o=>{if(o.target?.closest(\"button\"))return;o.preventDefault();let r=e.getBoundingClientRect(),a=o.clientX-r.left,l=o.clientY-r.top,f=o.clientX,s=o.clientY,h=r.left,c=r.top,u=!1;t.setPointerCapture(o.pointerId),e.classList.add(\"cf-dragging\");let v=L=>{Math.abs(L.clientX-f)+Math.abs(L.clientY-s)>4&&(u=!0),h=ee(L.clientX-a,C,window.innerWidth-e.offsetWidth-C),c=ee(L.clientY-l,C,window.innerHeight-e.offsetHeight-C),e.style.left=`${h}px`,e.style.top=`${c}px`,e.style.right=\"auto\",e.style.bottom=\"auto\"},M=()=>{e.classList.remove(\"cf-dragging\"),t.removeEventListener(\"pointermove\",v),t.removeEventListener(\"pointerup\",M),u?Y.value={x:h,y:c}:n?.()};t.addEventListener(\"pointermove\",v),t.addEventListener(\"pointerup\",M)})}function He(e){let t=d.value;if(!t.length)return;let n=(e%t.length+t.length)%t.length,o=t[n],r=null;try{r=o.fullPath&&document.querySelector(o.fullPath)||(o.elementPath?document.querySelector(o.elementPath):null)}catch{}r?.scrollIntoView({behavior:\"smooth\",block:\"center\"}),x.value=o.id}function Bt(e){return e===\"text\"?\"Select text on the page to annotate it.\":e===\"multi\"?\"Click elements to group them, then commit.\":\"Click any element to comment. Esc to exit.\"}function U(e,t,n){return i(\"button\",{class:`cf-seg-btn${t?\" on\":\"\"}`,onClick:n},e)}function Oe(){let e=Y.value;return e?`left:${e.x}px;top:${e.y}px;right:auto;bottom:auto;`:void 0}function je(e){requestAnimationFrame(()=>{let t=e.getBoundingClientRect();if(!t.width||!t.height)return;let n=ee(t.left,C,Math.max(C,window.innerWidth-t.width-C)),o=ee(t.top,C,Math.max(C,window.innerHeight-t.height-C));(Math.abs(n-t.left)>.5||Math.abs(o-t.top)>.5)&&(e.style.left=`${n}px`,e.style.top=`${o}px`,e.style.right=\"auto\",e.style.bottom=\"auto\")})}function _e(){let e=d.value.length;if(I.value){let c=i(\"div\",{class:\"cf-fab\",role:\"button\",tabindex:\"0\",title:\"Open annotations \\xB7 drag to move\",style:Oe()});return c.appendChild(R()),e>0&&c.appendChild(i(\"span\",{class:\"cf-fab-badge\"},e>99?\"99+\":String(e))),c.addEventListener(\"keydown\",u=>{(u.key===\"Enter\"||u.key===\" \")&&(u.preventDefault(),I.value=!1)}),Ne(c,c,()=>{I.value=!1}),je(c),c}let t=g.value===\"feedback\",n=y.value.length,o=i(\"div\",{class:\"cf-toolbar-head\",title:\"Drag to move\"},i(\"span\",{class:\"cf-grip\",\"aria-hidden\":\"true\"},\"\\u283F\"),i(\"button\",{class:\"cf-collapse\",title:\"Collapse to icon\",onClick:()=>I.value=!0},R()),i(\"div\",{class:\"cf-seg\"},U(\"View\",g.value===\"view\",()=>{g.value=\"view\",k.value=null,y.value=[]}),U(\"Comment\",t,()=>{g.value=\"feedback\"})),i(\"span\",{class:\"cf-count\"},String(e)),e>0?i(\"button\",{class:\"cf-icon-btn cf-clear\",title:\"Clear all annotations\",onClick:()=>{S.clearAnnotations(),S.clearCursors()}},\"Clear\"):null),r=d.value,a=r.filter(c=>c.author?.kind===\"agent\").length,l=Math.max(0,r.findIndex(c=>c.id===x.value)),f=e>0?i(\"div\",{class:\"cf-nav\"},i(\"button\",{class:\"cf-nav-btn\",title:\"Previous\",onClick:()=>He(l-1)},\"\\u2039\"),i(\"span\",{class:\"cf-nav-label\"},a>0?`${a} agent${a===1?\"\":\"s\"} \\xB7 ${l+1}/${e}`:`${l+1} / ${e}`),i(\"button\",{class:\"cf-nav-btn\",title:\"Next\",onClick:()=>He(l+1)},\"\\u203A\")):null,s=i(\"div\",{class:\"cf-toolbar\",style:Oe()}),h=[o,f,t?i(\"div\",{class:\"cf-seg cf-tools\"},U(\"Element\",w.value===\"element\",()=>{w.value=\"element\",y.value=[]}),U(\"Text\",w.value===\"text\",()=>{w.value=\"text\",y.value=[]}),U(\"Multi\",w.value===\"multi\",()=>{w.value=\"multi\"})):null,t&&w.value===\"multi\"&&n?i(\"button\",{class:\"cf-btn cf-btn-primary cf-commit\",onClick:Me},`Comment ${n} element${n===1?\"\":\"s\"}`):null,t?i(\"div\",{class:\"cf-hint\"},Bt(w.value)):null,H.value?i(\"button\",{class:\"cf-btn cf-btn-primary cf-launch\",onClick:()=>H.value?.run()},H.value.label):null];for(let c of h)c&&s.appendChild(c);return Ne(s,o),je(s),s}var O={blocking:\"#ef4444\",important:\"#f59e0b\",suggestion:\"#3ecf8e\"},ze=\"#a78bfa\",Ye=[\"fix\",\"change\",\"question\",\"approve\"],Fe=[\"blocking\",\"important\",\"suggestion\"],De=\"#8b94a3\",j=[\"#a78bfa\",\"#3ecf8e\",\"#f59e0b\",\"#38bdf8\",\"#f472b6\"];function fe(e){let t=e.id||e.displayName||e.kind;return j[Math.abs(Z(t))%j.length]}function Pt(e){let t=(e.displayName??\"\").trim();if(!t)return e.kind===\"agent\"?\"AI\":\"?\";let n=t.split(/\\s+/).filter(Boolean);return n.length>=2?(n[0][0]+n[1][0]).toUpperCase():t.slice(0,2).toUpperCase()}function Ue(e){let t=fe(e);return e.avatarUrl?i(\"span\",{class:`cf-avatar cf-avatar-${e.kind}`,style:`--cf-av:${t};`},i(\"img\",{class:\"cf-avatar-img\",src:e.avatarUrl,alt:e.displayName??e.kind})):i(\"span\",{class:`cf-avatar cf-avatar-${e.kind}`,style:`--cf-av:${t};background:${t};`,title:e.displayName??e.kind},Pt(e))}var qe=new Set;function It(e,t){return{top:e.y-(t?0:window.scrollY),left:e.x-(t?0:window.scrollX),width:e.width,height:e.height}}function Rt(e,t){if(e.author?.kind===\"agent\"){let n=(e.author.displayName??\"\").trim();return n?n.charAt(0).toUpperCase():\"AI\"}return String(t)}function Ke(e,t){let n=e.author?.kind===\"agent\",o=n?fe(e.author):e.severity?O[e.severity]:ze,r=e.status===\"resolved\"||e.status===\"dismissed\",a=!qe.has(e.id);qe.add(e.id);let l=`${r?\" resolved\":\"\"}${n?\" agent\":\"\"}${a?\" cf-fresh\":\"\"}`,f=i(\"button\",{class:`cf-pin${l}`,title:e.author?.displayName?`${e.author.displayName}: ${G(e.comment)}`:G(e.comment),onClick:()=>{let v=x.value!==e.id;v&&(b.value=null),x.value=v?e.id:null}},Rt(e,t)),s=null;f.addEventListener(\"mouseenter\",()=>{s||x.value===e.id||(s=i(\"div\",{class:\"cf-pin-preview\"},G(e.comment)||e.comment),f.appendChild(s))}),f.addEventListener(\"mouseleave\",()=>{s?.remove(),s=null});let h=v=>i(\"div\",{class:`cf-anno${l}`,style:`${D(v)}--cf-pin:${o};`});if(e.isMultiSelect&&e.elementBoundingBoxes?.length){let v=i(\"div\",{class:\"cf-anno-group\"});return e.elementBoundingBoxes.forEach((M,L)=>{let $=h(It(M,e.isFixed));L===0&&$.appendChild(f),v.appendChild($)}),v}let c=Q(e);if(!c)return null;let u=h(c);return u.appendChild(f),u}function We(e){let t=e.getBoundingClientRect();return i(\"div\",{class:\"cf-multi-box\",style:D({top:t.top,left:t.left,width:t.width,height:t.height})})}function Ve(e){let t=e.x/100*window.innerWidth,n=e.y-window.scrollY,o=e.color??(e.kind===\"agent\"?j[Math.abs(Z(e.id))%j.length]:\"#ffffff\"),r=pe(\"svg\",{width:\"18\",height:\"18\",viewBox:\"0 0 18 18\",class:\"cf-cursor-arrow\"},pe(\"path\",{d:\"M2 2 L2 14 L6 10 L9 16 L11 15 L8 9 L14 9 Z\",fill:o,stroke:\"rgba(0,0,0,0.35)\",\"stroke-width\":\"0.75\"}));return i(\"div\",{class:`cf-cursor cf-cursor-${e.kind}`,style:`top:${n}px;left:${t}px;--cf-cursor:${o};`},r,i(\"span\",{class:\"cf-cursor-label\"},e.label))}function Xe(e){let t=\"change\",n=\"important\",o=i(\"textarea\",{class:\"cf-textarea\",placeholder:\"Describe the change\\u2026 (Markdown supported)\",autofocus:!0,onKeyDown:p=>{(p.metaKey||p.ctrlKey)&&p.key===\"Enter\"&&r()},onInput:()=>u()}),r=()=>{let p=o.value.trim();p&&(S.addAnnotation({...e,comment:p,intent:t,severity:n,kind:\"feedback\",author:T.value}),b.value=null)},a=()=>{b.value=null};function l(p,me,ge,rt){let he=p.map(P=>{let q=ge(P);return i(\"button\",{class:`cf-chip${q?\" on\":\"\"}`,style:q?`--cf-chip:${me(P)};`:void 0,onClick:()=>{rt(P),be()}},P)}),it=i(\"div\",{class:\"cf-chipset\"},...he);function be(){p.forEach((P,q)=>{let te=he[q],xe=ge(P);te.className=`cf-chip${xe?\" on\":\"\"}`,xe?te.setAttribute(\"style\",`--cf-chip:${me(P)};`):te.removeAttribute(\"style\")})}return{group:it,refresh:be}}let f=l(Ye,()=>De,p=>t===p,p=>{t=p}),s=l(Fe,p=>O[p],p=>n===p,p=>{n=p}),h=e.isMultiSelect?`${e.elementBoundingBoxes?.length??0} elements`:e.selectedText?`\"${Ie(e.selectedText,40)}\"`:`<${e.element}>`,c=i(\"button\",{class:\"cf-btn cf-btn-primary\",disabled:!0,onClick:r},\"Comment\");function u(){c.disabled=!o.value.trim()}let v=i(\"div\",{class:\"cf-details\",hidden:!0},i(\"div\",{class:\"cf-field\"},i(\"span\",{class:\"cf-field-label\"},\"Intent\"),f.group),i(\"div\",{class:\"cf-field\"},i(\"span\",{class:\"cf-field-label\"},\"Severity\"),s.group)),M=i(\"button\",{class:\"cf-disclose\",onClick:()=>{let p=v.hidden;v.hidden=!p,M.classList.toggle(\"on\",p),M.textContent=p?\"Details \\u25B4\":\"Details \\u25BE\"}},\"Details \\u25BE\"),L=i(\"div\",{class:\"cf-popup cf-composer\"},i(\"div\",{class:\"cf-popup-head\"},R(\"cf-popup-brand\"),i(\"span\",{class:\"cf-popup-target\"},h),i(\"button\",{class:\"cf-icon-btn\",onClick:a,title:\"Close (Esc)\"},\"\\u2715\")),o,v,i(\"div\",{class:\"cf-popup-actions\"},M,c)),$=e.boundingBox;return $&&J(L,{left:$.x-window.scrollX,top:$.y-window.scrollY,width:$.width,height:$.height}),requestAnimationFrame(()=>o.focus({preventScroll:!0})),L}function Ge(e,t){let n=e.displayName??(e.kind===\"agent\"?\"Agent\":\"You\");return i(\"div\",{class:`cf-msg cf-msg-${e.kind}`},i(\"div\",{class:\"cf-msg-meta\"},Ue(e),i(\"span\",{class:\"cf-msg-name\"},n)),i(\"div\",{class:\"cf-msg-body\"},t))}function Je(e){let t=d.value.find(s=>s.id===e);if(!t)return null;let n=i(\"textarea\",{class:\"cf-textarea cf-textarea-sm\",placeholder:\"Reply\\u2026\",onKeyDown:s=>{(s.metaKey||s.ctrlKey)&&s.key===\"Enter\"&&o()},onInput:()=>a()}),o=()=>{let s=n.value.trim();s&&S.replyToAnnotation(e,{role:T.value.kind,content:s})},r=i(\"button\",{class:\"cf-btn cf-btn-primary\",disabled:!0,onClick:o},\"Reply\");function a(){r.disabled=!n.value.trim()}let l=i(\"div\",{class:\"cf-popup cf-thread\"},i(\"div\",{class:\"cf-popup-head\"},R(\"cf-popup-brand\"),i(\"span\",{class:\"cf-popup-target\"},`<${t.element}>`),t.severity?i(\"span\",{class:\"cf-tag\",style:`--cf-tag:${O[t.severity]};`},t.severity):null,i(\"button\",{class:\"cf-icon-btn\",onClick:()=>{x.value=null},title:\"Close\"},\"\\u2715\")),i(\"div\",{class:\"cf-thread-body\"},Ge(t.author??{kind:\"human\",displayName:\"You\"},t.comment),...(t.thread??[]).map(s=>Ge({kind:s.role,displayName:s.role===\"agent\"?\"Agent\":\"You\"},s.content))),i(\"div\",{class:\"cf-thread-compose\"},n,i(\"div\",{class:\"cf-thread-actions\"},t.status!==\"resolved\"?i(\"button\",{class:\"cf-btn cf-btn-ghost\",onClick:()=>S.resolveAnnotation(e,T.value.kind)},\"Resolve\"):i(\"span\",{class:\"cf-resolved-tag\"},\"\\u2713 resolved\"),r))),f=Q(t);return f&&J(l,f),requestAnimationFrame(()=>n.focus({preventScroll:!0})),l}var Ze=`\n/* Box-sizing reset \\u2014 the host page's reset is scoped to its own tree and\n doesn't reach the overlay, so width:100% controls + padding would\n otherwise overflow their popups. Keep everything border-box. */\n.cf-overlay, .cf-overlay *, .cf-overlay *::before, .cf-overlay *::after {\n box-sizing: border-box;\n}\n.cf-overlay {\n position: fixed;\n inset: 0;\n pointer-events: none;\n font-family: -apple-system, \"SF Pro Text\", system-ui, sans-serif;\n font-size: 13px;\n color: #f4f4f5;\n --cf-bg: rgba(18, 18, 22, 0.96);\n --cf-border: rgba(255, 255, 255, 0.1);\n --cf-muted: rgba(255, 255, 255, 0.55);\n --cf-accent: #a78bfa;\n /* Liquid-glass fill for popups/panels \\u2014 low alpha so the backdrop blur\n actually reads as frosted glass instead of a solid black box. */\n --cf-glass: rgba(24, 24, 34, 0.55);\n --cf-glass-border: rgba(255, 255, 255, 0.14);\n}\n.cf-overlay[data-theme=\"light\"] {\n color: #18181b;\n --cf-bg: rgba(252, 252, 253, 0.97);\n --cf-border: rgba(0, 0, 0, 0.1);\n --cf-muted: rgba(0, 0, 0, 0.5);\n --cf-glass: rgba(252, 252, 253, 0.60);\n --cf-glass-border: rgba(0, 0, 0, 0.10);\n}\n\n/* Hover highlight + multi-select boxes */\n.cf-hover {\n position: fixed;\n pointer-events: none;\n border: 2px solid var(--cf-accent);\n background: rgba(167, 139, 250, 0.1);\n border-radius: 4px;\n box-shadow: 0 0 0 1px rgba(167, 139, 250, 0.4);\n transition: top .06s ease, left .06s ease, width .06s ease, height .06s ease;\n z-index: 1;\n}\n.cf-multi-box {\n position: fixed;\n pointer-events: none;\n border: 2px solid #3ecf8e;\n background: rgba(62, 207, 142, 0.12);\n border-radius: 4px;\n z-index: 1;\n animation: cf-multi-pulse 1.4s ease-in-out infinite;\n}\n@keyframes cf-multi-pulse {\n 0%, 100% { box-shadow: 0 0 0 0 rgba(62,207,142,0.0); }\n 50% { box-shadow: 0 0 0 3px rgba(62,207,142,0.25); }\n}\n\n/* Live presence cursors (multi-cursor collaboration) */\n.cf-cursor {\n position: fixed;\n z-index: 6;\n pointer-events: none;\n display: flex;\n align-items: flex-start;\n gap: 4px;\n /* Glide to new positions so multiple actors read as \"live\". */\n transition: top .12s ease, left .12s ease;\n will-change: top, left;\n}\n.cf-cursor-arrow { display: block; filter: drop-shadow(0 1px 2px rgba(0,0,0,0.4)); }\n.cf-cursor-label {\n transform: translateY(2px);\n background: var(--cf-cursor, #a78bfa);\n color: #0b0b0f;\n font: 700 11px -apple-system, system-ui;\n padding: 2px 7px;\n border-radius: 8px;\n white-space: nowrap;\n box-shadow: 0 2px 8px rgba(0,0,0,0.35);\n}\n.cf-cursor-human .cf-cursor-label { color: #18181b; }\n.cf-cursor-agent .cf-cursor-label { color: #0b0b0f; }\n\n/* Pins */\n/* Outline box hugging the annotated element (so it's obvious what the\n annotation refers to). Colour-coded per author via --cf-pin. */\n/* A multi-element annotation renders several .cf-anno outlines wrapped in\n one group \\u2014 display:contents so the wrapper itself adds no layout box and\n the fixed-positioned outlines anchor straight to the viewport. */\n.cf-anno-group { display: contents; }\n.cf-anno {\n position: fixed;\n pointer-events: none;\n border: 1.5px solid var(--cf-pin, #a78bfa);\n border-radius: 7px;\n background: color-mix(in srgb, var(--cf-pin, #a78bfa) 9%, transparent);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--cf-pin, #a78bfa) 15%, transparent);\n z-index: 2;\n}\n/* Entrance animation plays only on first render (.cf-fresh), never on the\n scroll/resize re-renders \\u2014 otherwise the box flickers while scrolling. */\n.cf-anno.cf-fresh { animation: cf-rise .18s cubic-bezier(.16,1,.3,1) both; }\n.cf-anno.agent { border-style: dashed; }\n.cf-anno.resolved { opacity: 0.4; }\n.cf-pin {\n position: absolute;\n top: -11px; left: -11px;\n width: 22px; height: 22px;\n border-radius: 50% 50% 50% 2px;\n background: var(--cf-pin, #a78bfa);\n color: #fff;\n border: 2px solid rgba(255,255,255,0.9);\n font: 700 11px -apple-system, system-ui;\n display: flex; align-items: center; justify-content: center;\n cursor: pointer;\n pointer-events: auto;\n box-shadow: 0 2px 8px rgba(0,0,0,0.35);\n z-index: 3;\n transition: transform .12s cubic-bezier(.34,1.56,.64,1);\n padding: 0;\n}\n/* Pop in only when a pin first renders (.cf-fresh) \\u2014 not on the scroll\n re-renders, which would otherwise replay the pop on every tick. */\n.cf-pin.cf-fresh { animation: cf-pin-pop .32s cubic-bezier(.34,1.56,.64,1) both; }\n.cf-pin:hover { transform: scale(1.2); }\n.cf-pin.resolved { opacity: 0.45; }\n/* Human vs agent reads from the box outline (solid vs dashed) + the pin\n glyph (number vs initial) + colour \\u2014 no extra per-pin chrome. */\n\n/* Hover-to-preview card \\u2014 shown next to a pin on mouseenter. */\n.cf-pin-preview {\n position: absolute;\n top: 26px; left: 0;\n max-width: 240px;\n width: max-content;\n pointer-events: none;\n background: var(--cf-glass);\n border: 1px solid var(--cf-glass-border);\n border-radius: 9px;\n padding: 7px 10px;\n font: 500 12px -apple-system, system-ui;\n line-height: 1.4;\n color: #f4f4f5;\n white-space: normal;\n overflow-wrap: anywhere;\n -webkit-backdrop-filter: blur(24px) saturate(180%);\n backdrop-filter: blur(24px) saturate(180%);\n box-shadow:\n 0 8px 28px rgba(0,0,0,0.4),\n inset 0 1px 0 rgba(255,255,255,0.18);\n z-index: 7;\n animation: cf-rise .14s cubic-bezier(.16,1,.3,1) both;\n}\n.cf-overlay[data-theme=\"light\"] .cf-pin-preview { color: #18181b; }\n\n@keyframes cf-pin-pop {\n 0% { transform: scale(0); opacity: 0; }\n 60% { transform: scale(1.25); opacity: 1; }\n 100% { transform: scale(1); opacity: 1; }\n}\n@keyframes cf-pin-ring {\n 0% { transform: scale(1); opacity: .7; }\n 100% { transform: scale(2.4); opacity: 0; }\n}\n\n/* Toolbar */\n.cf-toolbar {\n position: fixed;\n left: 16px; bottom: 16px;\n pointer-events: auto;\n /* Liquid glass \\u2014 translucent, frosted, with a bright top edge. */\n background: rgba(24,24,34,0.52);\n border: 1px solid rgba(255,255,255,0.14);\n border-radius: 17px;\n padding: 9px;\n display: flex; flex-direction: column; gap: 8px;\n -webkit-backdrop-filter: blur(30px) saturate(185%);\n backdrop-filter: blur(30px) saturate(185%);\n box-shadow:\n 0 18px 50px rgba(0,0,0,0.34),\n inset 0 1px 0 rgba(255,255,255,0.22),\n inset 0 -1px 0 rgba(0,0,0,0.22);\n z-index: 4;\n}\n.cf-toolbar-head {\n display: flex; align-items: center; gap: 7px;\n cursor: grab; user-select: none; touch-action: none;\n}\n.cf-toolbar-head:active { cursor: grabbing; }\n/* Visible drag affordance \\u2014 signals the panel can be moved. */\n.cf-grip {\n flex: 0 0 auto; color: var(--cf-muted);\n font-size: 13px; line-height: 1; letter-spacing: -2px; opacity: 0.5;\n}\n.cf-toolbar-head:hover .cf-grip { opacity: 0.85; }\n/* Logo button \\u2014 tap to collapse back to the puck. */\n.cf-collapse {\n display: inline-flex; align-items: center; justify-content: center;\n width: 26px; height: 26px; flex: 0 0 auto; padding: 0;\n border: 0; border-radius: 8px; cursor: pointer;\n /* Light tile so a dark brand logo reads on the dark panel. */\n background: #fff;\n box-shadow: 0 1px 3px rgba(0,0,0,0.25);\n}\n.cf-collapse:hover { filter: brightness(0.94); }\n.cf-brand {\n display: inline-flex; align-items: center; justify-content: center;\n width: 22px; height: 22px; flex: 0 0 auto; pointer-events: none;\n}\n.cf-brand svg { width: 18px; height: 18px; display: block; }\n.cf-toolbar.cf-dragging {\n box-shadow: 0 24px 64px rgba(0,0,0,0.6);\n transition: none;\n}\n.cf-logo-dot {\n width: 9px; height: 9px; border-radius: 50%;\n background: var(--cf-accent); box-shadow: 0 0 10px var(--cf-accent);\n}\n\n/* Collapsed FAB \\u2014 a draggable logo puck; tap to expand. */\n.cf-fab {\n position: fixed; left: 16px; bottom: 16px;\n width: 48px; height: 48px; border-radius: 16px;\n pointer-events: auto; cursor: grab; user-select: none; touch-action: none;\n display: flex; align-items: center; justify-content: center;\n /* Liquid glass puck \\u2014 frosted light so the dark mark reads. */\n background: rgba(255,255,255,0.66);\n -webkit-backdrop-filter: blur(22px) saturate(185%);\n backdrop-filter: blur(22px) saturate(185%);\n box-shadow:\n 0 12px 34px rgba(0,0,0,0.20),\n inset 0 1px 0 rgba(255,255,255,0.95),\n 0 0 0 0.5px rgba(0,0,0,0.06);\n z-index: 4;\n transition: transform .13s cubic-bezier(.34,1.56,.64,1), box-shadow .15s ease;\n}\n.cf-fab:hover { transform: scale(1.06); }\n.cf-fab.cf-dragging { cursor: grabbing; transition: none; box-shadow: 0 18px 44px rgba(0,0,0,0.42); }\n.cf-fab .cf-brand { width: 28px; height: 28px; cursor: inherit; }\n.cf-fab .cf-brand svg { width: 24px; height: 24px; }\n.cf-fab-badge {\n position: absolute; top: -5px; right: -5px;\n min-width: 18px; height: 18px; padding: 0 5px; box-sizing: border-box;\n border-radius: 9px; background: var(--cf-accent); color: #fff;\n font: 800 10px -apple-system, system-ui;\n display: flex; align-items: center; justify-content: center;\n box-shadow: 0 0 0 2px var(--cf-bg);\n}\n.cf-count {\n margin-left: auto;\n font: 700 11px -apple-system, system-ui;\n color: var(--cf-muted);\n background: rgba(255,255,255,0.08);\n padding: 1px 7px; border-radius: 8px;\n}\n.cf-overlay[data-theme=\"light\"] .cf-count { background: rgba(0,0,0,0.06); }\n\n/* View / Comment reads as a pill switch \\u2014 rounded track + sliding pill. */\n.cf-seg {\n display: flex; gap: 2px;\n background: rgba(255,255,255,0.07);\n border-radius: 999px; padding: 3px;\n}\n.cf-overlay[data-theme=\"light\"] .cf-seg { background: rgba(0,0,0,0.05); }\n.cf-seg-btn {\n flex: 1; border: none; background: transparent; color: var(--cf-muted);\n padding: 5px 14px; border-radius: 999px; cursor: pointer;\n font: 600 12px -apple-system, system-ui;\n transition: color .15s ease, background .2s ease;\n}\n.cf-seg-btn:hover { color: #fff; }\n.cf-seg-btn.on {\n background: var(--cf-accent); color: #fff;\n box-shadow: 0 1px 5px rgba(124,92,255,0.45);\n}\n/* Suggestions navigator \\u2014 count + jump to each annotation. */\n.cf-nav {\n display: flex; align-items: center; justify-content: space-between; gap: 6px;\n background: rgba(255,255,255,0.05); border-radius: 10px; padding: 2px 3px;\n}\n.cf-nav-btn {\n border: 0; background: transparent; color: var(--cf-muted); cursor: pointer;\n width: 24px; height: 22px; border-radius: 7px; font-size: 16px; line-height: 1; padding: 0;\n}\n.cf-nav-btn:hover { background: rgba(255,255,255,0.1); color: #fff; }\n.cf-nav-label { font: 700 11px -apple-system, system-ui; color: var(--cf-muted); }\n.cf-tools .cf-seg-btn.on { background: rgba(255,255,255,0.16); color: #fff; }\n.cf-overlay[data-theme=\"light\"] .cf-tools .cf-seg-btn.on { background: rgba(0,0,0,0.12); color: #111; }\n.cf-hint { font-size: 11px; color: var(--cf-muted); padding: 0 2px; }\n.cf-commit { width: 100%; }\n.cf-launch {\n width: 100%; border: 0; margin-top: 2px;\n background: linear-gradient(135deg, #7c5cff, #3ecf8e);\n box-shadow: 0 6px 18px rgba(124,92,255,0.35);\n}\n.cf-launch:hover { filter: brightness(1.07); }\n\n/* Buttons */\n.cf-btn {\n border: 1px solid var(--cf-border); background: transparent; color: inherit;\n padding: 6px 12px; border-radius: 8px; font: 600 12px -apple-system, system-ui;\n}\n.cf-btn:hover { background: rgba(255,255,255,0.06); }\n.cf-overlay[data-theme=\"light\"] .cf-btn:hover { background: rgba(0,0,0,0.04); }\n.cf-btn-primary {\n background: var(--cf-accent); border-color: var(--cf-accent); color: #fff;\n}\n.cf-btn-primary:hover { background: #9173f0; }\n.cf-btn-primary:disabled { opacity: 0.4; cursor: not-allowed; }\n.cf-btn-ghost { border-color: transparent; color: var(--cf-muted); }\n\n/* Popups (composer + thread) */\n.cf-popup {\n position: fixed;\n left: 16px; bottom: 92px;\n width: 320px;\n pointer-events: auto;\n /* Liquid glass \\u2014 translucent + heavily frosted with a bright top edge,\n matching the toolbar/FAB. The low-alpha fill is what lets the blur\n read as glass rather than a solid black box. */\n background: var(--cf-glass);\n border: 1px solid var(--cf-glass-border);\n border-radius: 16px;\n padding: 12px;\n display: flex; flex-direction: column; gap: 10px;\n -webkit-backdrop-filter: blur(30px) saturate(185%);\n backdrop-filter: blur(30px) saturate(185%);\n box-shadow:\n 0 18px 50px rgba(0,0,0,0.40),\n inset 0 1px 0 rgba(255,255,255,0.22),\n inset 0 -1px 0 rgba(0,0,0,0.22);\n z-index: 5;\n animation: cf-rise .2s cubic-bezier(.16,1,.3,1) both;\n}\n@keyframes cf-rise {\n from { transform: translateY(10px); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n}\n.cf-popup-head {\n display: flex; align-items: center; gap: 8px;\n}\n.cf-popup-target {\n font: 600 12px ui-monospace, \"SF Mono\", monospace;\n color: var(--cf-accent);\n overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n}\n.cf-icon-btn {\n margin-left: auto; border: none; background: transparent;\n color: var(--cf-muted); font-size: 13px; cursor: pointer;\n width: 22px; height: 22px; border-radius: 6px;\n}\n.cf-icon-btn:hover { background: rgba(255,255,255,0.08); }\n/* Clear / erase-board \\u2014 small text button after the count. */\n.cf-clear {\n margin-left: 4px; width: auto; height: auto; padding: 3px 8px;\n font: 700 10px -apple-system, system-ui; text-transform: uppercase;\n letter-spacing: 0.05em;\n}\n.cf-clear:hover { color: #fff; }\n\n.cf-textarea {\n width: 100%; min-height: 72px; resize: vertical;\n background: rgba(255,255,255,0.04);\n border: 1px solid var(--cf-border); border-radius: 9px;\n padding: 8px 10px; color: inherit; font: 400 13px -apple-system, system-ui;\n line-height: 1.45;\n}\n.cf-overlay[data-theme=\"light\"] .cf-textarea { background: rgba(0,0,0,0.03); }\n.cf-textarea:focus { outline: none; border-color: var(--cf-accent); }\n.cf-textarea-sm { min-height: 48px; }\n\n.cf-field { display: flex; flex-direction: column; gap: 5px; }\n.cf-field-label {\n font: 700 10px -apple-system, system-ui; letter-spacing: 0.06em;\n text-transform: uppercase; color: var(--cf-muted);\n}\n.cf-chipset { display: flex; flex-wrap: wrap; gap: 5px; }\n.cf-chip {\n border: 1px solid var(--cf-border); background: transparent; color: var(--cf-muted);\n padding: 4px 10px; border-radius: 20px; font: 600 11px -apple-system, system-ui;\n text-transform: capitalize;\n}\n.cf-chip.on {\n background: var(--cf-chip, var(--cf-accent));\n border-color: var(--cf-chip, var(--cf-accent));\n color: #fff;\n}\n.cf-popup-actions { display: flex; gap: 8px; align-items: center; justify-content: flex-end; }\n\n/* Composer grows out of the floater \\u2014 the WAAPI morph drives it, so drop\n the CSS keyframe to avoid a double animation. */\n.cf-popup.cf-morph { animation: none; }\n/* The logo in the popup head \\u2014 the floater mark that flew over + opened. */\n.cf-popup-brand {\n width: 20px; height: 20px; flex: 0 0 auto;\n display: inline-flex; align-items: center; justify-content: center;\n background: #fff; border-radius: 6px; box-shadow: 0 1px 2px rgba(0,0,0,0.25);\n}\n.cf-popup-brand svg { width: 15px; height: 15px; display: block; }\n/* Intent/severity disclosure \\u2014 minimal by default. */\n.cf-details { display: flex; flex-direction: column; gap: 10px; }\n.cf-details[hidden] { display: none; }\n.cf-disclose {\n margin-right: auto; border: 0; background: transparent; color: var(--cf-muted);\n font: 600 11px -apple-system, system-ui; cursor: pointer;\n padding: 6px 6px; border-radius: 7px;\n}\n.cf-disclose:hover, .cf-disclose.on { color: #fff; background: rgba(255,255,255,0.06); }\n.cf-overlay[data-theme=\"light\"] .cf-disclose:hover,\n.cf-overlay[data-theme=\"light\"] .cf-disclose.on { color: #111; background: rgba(0,0,0,0.05); }\n\n/* Thread */\n.cf-thread-body {\n display: flex; flex-direction: column; gap: 12px;\n max-height: 280px; overflow-y: auto; padding-right: 2px;\n}\n.cf-msg { display: flex; flex-direction: column; gap: 3px; }\n.cf-msg-meta { display: flex; align-items: center; gap: 6px; }\n.cf-avatar {\n width: 20px; height: 20px; flex: 0 0 auto;\n display: inline-flex; align-items: center; justify-content: center;\n border-radius: 50%; overflow: hidden;\n font: 800 9px -apple-system, system-ui; letter-spacing: 0.02em;\n color: #fff; text-shadow: 0 1px 1px rgba(0,0,0,0.25);\n box-shadow: 0 0 0 1.5px rgba(255,255,255,0.14), 0 1px 2px rgba(0,0,0,0.3);\n}\n/* Agents read as a distinct rounded \"logo\" tile; humans stay round. */\n.cf-avatar-agent { border-radius: 7px; }\n.cf-avatar-img { width: 100%; height: 100%; object-fit: cover; }\n.cf-msg-name {\n font: 700 11px -apple-system, system-ui; color: var(--cf-muted);\n}\n.cf-msg-body {\n font-size: 13px; line-height: 1.5; white-space: pre-wrap; word-break: break-word;\n background: rgba(255,255,255,0.04); border-radius: 9px; padding: 7px 10px;\n}\n.cf-overlay[data-theme=\"light\"] .cf-msg-body { background: rgba(0,0,0,0.03); }\n.cf-msg-agent .cf-msg-body { border-left: 2px solid var(--cf-accent); }\n.cf-thread-compose { display: flex; flex-direction: column; gap: 8px; }\n.cf-thread-actions { display: flex; gap: 8px; align-items: center; }\n.cf-thread-actions .cf-btn-primary { margin-left: auto; }\n.cf-resolved-tag { font: 600 11px -apple-system, system-ui; color: #3ecf8e; }\n.cf-tag {\n font: 700 9px -apple-system, system-ui; text-transform: uppercase;\n letter-spacing: 0.05em; padding: 2px 6px; border-radius: 5px;\n background: var(--cf-tag, var(--cf-accent)); color: #fff;\n}\n\n/* Respect reduced-motion: keep the UI usable, drop the motion. */\n@media (prefers-reduced-motion: reduce) {\n .cf-pin, .cf-popup, .cf-multi-box { animation: none !important; }\n .cf-pin.agent::after { display: none; }\n}\n`;function N(e,t,n){let o=()=>{e.replaceChildren();let r=n();r!==null&&(Array.isArray(r)?e.append(...r):e.append(r))};o();for(let r of t)r.subscribe(o)}function Qe(e,t={}){let n=i(\"div\",{class:\"cf-overlay\"}),o=()=>{n.setAttribute(\"data-theme\",B.value===\"auto\"?\"dark\":B.value)};o(),B.subscribe(o);let r=document.createElement(\"style\");r.textContent=Ze,n.appendChild(r);let a=i(\"div\",{class:\"cf-layer-hover\"});N(a,[k,b],()=>{let u=k.value;return!u||b.value?null:i(\"div\",{class:\"cf-hover\",style:D(u)})}),n.appendChild(a);let l=i(\"div\",{class:\"cf-layer-multi\"});N(l,[y,z],()=>y.value.map(u=>We(u))),n.appendChild(l);let f=i(\"div\",{class:\"cf-layer-pins\"});N(f,[d,z],()=>d.value.map((u,v)=>Ke(u,v+1)).filter(u=>u!==null)),n.appendChild(f);let s=i(\"div\",{class:\"cf-layer-cursors\"});if(N(s,[E],()=>E.value.map(u=>Ve(u))),n.appendChild(s),!t.headless){let u=i(\"div\",{class:\"cf-layer-toolbar\"});N(u,[g,w,d,y,Y,H,I,V],()=>_e()),n.appendChild(u)}let h=i(\"div\",{class:\"cf-layer-composer\"});N(h,[b],()=>b.value?Xe(b.value):null),n.appendChild(h);let c=i(\"div\",{class:\"cf-layer-thread\"});N(c,[x,d],()=>x.value?Je(x.value):null),n.appendChild(c),e.appendChild(n)}var et=!1,tt=!1;function nt(e={}){ke(e),e.author&&(T.value=e.author),e.mode&&(g.value=e.mode),e.theme&&(B.value=e.theme),e.headless&&(tt=!0),ot(),we.value=!0,console.info(\"[annotations v1] ready on\",location.href)}function ot(){if(et||typeof document>\"u\")return;if(!document.documentElement){document.addEventListener(\"DOMContentLoaded\",ot,{once:!0});return}et=!0;let{host:e,appRoot:t}=Pe();Ce(e),$e(),Qe(t,{headless:tt});let n=()=>{z.value++};window.addEventListener(\"scroll\",n,{passive:!0,capture:!0}),window.addEventListener(\"resize\",n,{passive:!0})}var Nt=Object.assign({__init:nt},S);window.__annotations=Nt;nt();})();\n";
|
package/src/output.ts
CHANGED
|
@@ -166,6 +166,6 @@ function quote(text: string): string {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
function formatThreadMessage(msg: ThreadMessage): string {
|
|
169
|
-
const author = msg.role === "agent" ? "
|
|
169
|
+
const author = msg.role === "agent" ? "agent" : "human";
|
|
170
170
|
return `- _${author}_: ${msg.content.trim()}`;
|
|
171
171
|
}
|
package/src/picker.ts
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
import { captureElement, type CapturedTarget } from "./capture.js";
|
|
20
20
|
import {
|
|
21
|
+
activeThreadId,
|
|
21
22
|
author,
|
|
22
23
|
draft,
|
|
23
24
|
feedbackTool,
|
|
@@ -74,10 +75,9 @@ function onMouseMove(event: MouseEvent): void {
|
|
|
74
75
|
hoverBox.value = null;
|
|
75
76
|
return;
|
|
76
77
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
78
|
+
// Text mode highlights the hovered text-bearing element too, so there's
|
|
79
|
+
// always a visible target before the user drags to select — same as
|
|
80
|
+
// element mode. The native selection highlight then shows the exact span.
|
|
81
81
|
const el = pickable(event.target);
|
|
82
82
|
if (!el) {
|
|
83
83
|
hoverBox.value = null;
|
|
@@ -127,7 +127,25 @@ function onMouseUp(): void {
|
|
|
127
127
|
: range.commonAncestorContainer.parentElement;
|
|
128
128
|
if (!anchor) return;
|
|
129
129
|
const captured = captureElement(anchor);
|
|
130
|
-
|
|
130
|
+
// Anchor the pin + composer to the SELECTED TEXT's rect — not the whole
|
|
131
|
+
// containing element — so the marker hugs exactly what was highlighted.
|
|
132
|
+
const rect = range.getBoundingClientRect();
|
|
133
|
+
const selectionBox =
|
|
134
|
+
rect.width || rect.height
|
|
135
|
+
? {
|
|
136
|
+
x: rect.left + (captured.isFixed ? 0 : window.scrollX),
|
|
137
|
+
y: rect.top + (captured.isFixed ? 0 : window.scrollY),
|
|
138
|
+
width: rect.width,
|
|
139
|
+
height: rect.height,
|
|
140
|
+
}
|
|
141
|
+
: captured.boundingBox;
|
|
142
|
+
openComposer({
|
|
143
|
+
...captured,
|
|
144
|
+
selectedText: text,
|
|
145
|
+
boundingBox: selectionBox,
|
|
146
|
+
x: ((rect.left + rect.width / 2) / window.innerWidth) * 100,
|
|
147
|
+
y: captured.isFixed ? rect.top : rect.top + window.scrollY,
|
|
148
|
+
});
|
|
131
149
|
}
|
|
132
150
|
|
|
133
151
|
function onKeyDown(event: KeyboardEvent): void {
|
package/src/store.ts
CHANGED
|
@@ -77,3 +77,37 @@ export interface PresenceCursor {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
export const cursors = signal<PresenceCursor[]>([]);
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* User-dragged toolbar position (viewport px from top-left). `null` =
|
|
83
|
+
* the default docked spot (bottom-right via CSS). Persisted here so it
|
|
84
|
+
* survives the toolbar layer's re-renders; the drag moves the element
|
|
85
|
+
* directly for smoothness and commits to this signal on release.
|
|
86
|
+
*/
|
|
87
|
+
export const toolbarPos = signal<{ x: number; y: number } | null>(null);
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Optional primary action shown in the toolbar (e.g. "Launch agents").
|
|
91
|
+
* Vendor-neutral: the host supplies the label + handler; the toolbar
|
|
92
|
+
* renders the button only when this is set, so the library hardcodes no
|
|
93
|
+
* product-specific behavior.
|
|
94
|
+
*/
|
|
95
|
+
export interface PrimaryAction {
|
|
96
|
+
label: string;
|
|
97
|
+
run: () => void;
|
|
98
|
+
}
|
|
99
|
+
export const primaryAction = signal<PrimaryAction | null>(null);
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Collapsed = the overlay shows just a draggable logo "FAB"; tapping it
|
|
103
|
+
* expands the control panel. Icon-first by default so the overlay stays
|
|
104
|
+
* out of the way until the user wants it.
|
|
105
|
+
*/
|
|
106
|
+
export const collapsed = signal<boolean>(true);
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Optional brand mark (inline SVG/HTML string) shown as the collapsed
|
|
110
|
+
* icon + in the expanded header. Host-supplied — the library ships a
|
|
111
|
+
* neutral default and hardcodes no brand asset.
|
|
112
|
+
*/
|
|
113
|
+
export const brandLogo = signal<string | null>(null);
|
package/src/ui/Avatar.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identity avatar — a small circular badge for a human or agent author.
|
|
3
|
+
*
|
|
4
|
+
* Vendor-neutral: the badge is derived entirely from the identity the
|
|
5
|
+
* host supplies (id / displayName / optional avatarUrl). No hardcoded
|
|
6
|
+
* names or brand assets live here — the host (e.g. its agent roster)
|
|
7
|
+
* decides who's who and may pass an `avatarUrl` for a real logo.
|
|
8
|
+
*
|
|
9
|
+
* - `avatarUrl` given → render the image.
|
|
10
|
+
* - otherwise → initials on a colour deterministically derived
|
|
11
|
+
* from the id, so each distinct author keeps a
|
|
12
|
+
* stable colour across pins, cursors, and threads.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { CURSOR_PALETTE } from "./constants.js";
|
|
16
|
+
import { hashId } from "./helpers.js";
|
|
17
|
+
import { el } from "./dom.js";
|
|
18
|
+
|
|
19
|
+
export interface AvatarIdentity {
|
|
20
|
+
kind: "human" | "agent";
|
|
21
|
+
id?: string;
|
|
22
|
+
displayName?: string;
|
|
23
|
+
avatarUrl?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Stable colour for an identity (shared with the presence-cursor palette). */
|
|
27
|
+
export function colorForIdentity(idt: AvatarIdentity): string {
|
|
28
|
+
const key = idt.id || idt.displayName || idt.kind;
|
|
29
|
+
return CURSOR_PALETTE[Math.abs(hashId(key)) % CURSOR_PALETTE.length]!;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function initials(idt: AvatarIdentity): string {
|
|
33
|
+
const name = (idt.displayName ?? "").trim();
|
|
34
|
+
if (!name) return idt.kind === "agent" ? "AI" : "?";
|
|
35
|
+
const parts = name.split(/\s+/).filter(Boolean);
|
|
36
|
+
if (parts.length >= 2) return (parts[0]![0]! + parts[1]![0]!).toUpperCase();
|
|
37
|
+
return name.slice(0, 2).toUpperCase();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function renderAvatar(idt: AvatarIdentity): HTMLElement {
|
|
41
|
+
const color = colorForIdentity(idt);
|
|
42
|
+
if (idt.avatarUrl) {
|
|
43
|
+
return el(
|
|
44
|
+
"span",
|
|
45
|
+
{ class: `cf-avatar cf-avatar-${idt.kind}`, style: `--cf-av:${color};` },
|
|
46
|
+
el("img", {
|
|
47
|
+
class: "cf-avatar-img",
|
|
48
|
+
src: idt.avatarUrl,
|
|
49
|
+
alt: idt.displayName ?? idt.kind,
|
|
50
|
+
}),
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
return el(
|
|
54
|
+
"span",
|
|
55
|
+
{
|
|
56
|
+
class: `cf-avatar cf-avatar-${idt.kind}`,
|
|
57
|
+
style: `--cf-av:${color};background:${color};`,
|
|
58
|
+
title: idt.displayName ?? idt.kind,
|
|
59
|
+
},
|
|
60
|
+
initials(idt),
|
|
61
|
+
);
|
|
62
|
+
}
|
package/src/ui/Composer.ts
CHANGED
|
@@ -23,7 +23,8 @@ import {
|
|
|
23
23
|
SEVERITY_COLOR,
|
|
24
24
|
SEVERITY_NEUTRAL,
|
|
25
25
|
} from "./constants.js";
|
|
26
|
-
import { truncate } from "./helpers.js";
|
|
26
|
+
import { placeNear, truncate } from "./helpers.js";
|
|
27
|
+
import { brandGlyph } from "./brand.js";
|
|
27
28
|
import { el } from "./dom.js";
|
|
28
29
|
|
|
29
30
|
export function renderComposer(target: CapturedTarget): HTMLElement {
|
|
@@ -134,16 +135,11 @@ export function renderComposer(target: CapturedTarget): HTMLElement {
|
|
|
134
135
|
submitBtn.disabled = !textarea.value.trim();
|
|
135
136
|
}
|
|
136
137
|
|
|
137
|
-
|
|
138
|
+
// Intent + severity are power-user controls — keep them out of the way
|
|
139
|
+
// behind a single disclosure so the default surface is just type + send.
|
|
140
|
+
const details = el(
|
|
138
141
|
"div",
|
|
139
|
-
{ class: "cf-
|
|
140
|
-
el(
|
|
141
|
-
"div",
|
|
142
|
-
{ class: "cf-popup-head" },
|
|
143
|
-
el("span", { class: "cf-popup-target" }, label),
|
|
144
|
-
el("button", { class: "cf-icon-btn", onClick: cancel, title: "Cancel" }, "✕"),
|
|
145
|
-
),
|
|
146
|
-
textarea,
|
|
142
|
+
{ class: "cf-details", hidden: true },
|
|
147
143
|
el(
|
|
148
144
|
"div",
|
|
149
145
|
{ class: "cf-field" },
|
|
@@ -156,11 +152,55 @@ export function renderComposer(target: CapturedTarget): HTMLElement {
|
|
|
156
152
|
el("span", { class: "cf-field-label" }, "Severity"),
|
|
157
153
|
severityGroup.group,
|
|
158
154
|
),
|
|
155
|
+
) as HTMLElement;
|
|
156
|
+
|
|
157
|
+
const detailsBtn = el(
|
|
158
|
+
"button",
|
|
159
|
+
{
|
|
160
|
+
class: "cf-disclose",
|
|
161
|
+
onClick: () => {
|
|
162
|
+
const open = details.hidden;
|
|
163
|
+
details.hidden = !open;
|
|
164
|
+
detailsBtn.classList.toggle("on", open);
|
|
165
|
+
detailsBtn.textContent = open ? "Details ▴" : "Details ▾";
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
"Details ▾",
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
const popup = el(
|
|
172
|
+
"div",
|
|
173
|
+
{ class: "cf-popup cf-composer" },
|
|
174
|
+
el(
|
|
175
|
+
"div",
|
|
176
|
+
{ class: "cf-popup-head" },
|
|
177
|
+
brandGlyph("cf-popup-brand"),
|
|
178
|
+
el("span", { class: "cf-popup-target" }, label),
|
|
179
|
+
el("button", { class: "cf-icon-btn", onClick: cancel, title: "Close (Esc)" }, "✕"),
|
|
180
|
+
),
|
|
181
|
+
textarea,
|
|
182
|
+
details,
|
|
159
183
|
el(
|
|
160
184
|
"div",
|
|
161
185
|
{ class: "cf-popup-actions" },
|
|
162
|
-
|
|
186
|
+
detailsBtn,
|
|
163
187
|
submitBtn,
|
|
164
188
|
),
|
|
165
189
|
);
|
|
190
|
+
|
|
191
|
+
// Anchor the composer next to the element being annotated.
|
|
192
|
+
const bb = target.boundingBox;
|
|
193
|
+
if (bb) {
|
|
194
|
+
placeNear(popup, {
|
|
195
|
+
left: bb.x - window.scrollX,
|
|
196
|
+
top: bb.y - window.scrollY,
|
|
197
|
+
width: bb.width,
|
|
198
|
+
height: bb.height,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
// Focus the comment field right away — the `autofocus` attribute doesn't
|
|
202
|
+
// fire on nodes created + inserted dynamically into the shadow root, so
|
|
203
|
+
// without this the user has to click into the box before typing.
|
|
204
|
+
requestAnimationFrame(() => textarea.focus({ preventScroll: true }));
|
|
205
|
+
return popup;
|
|
166
206
|
}
|
package/src/ui/Cursors.ts
CHANGED
|
@@ -43,10 +43,6 @@ export function renderCursor(c: PresenceCursor): HTMLElement {
|
|
|
43
43
|
style: `top:${top}px;left:${left}px;--cf-cursor:${color};`,
|
|
44
44
|
},
|
|
45
45
|
arrow,
|
|
46
|
-
el(
|
|
47
|
-
"span",
|
|
48
|
-
{ class: "cf-cursor-label" },
|
|
49
|
-
`${c.kind === "agent" ? "🤖 " : ""}${c.label}`,
|
|
50
|
-
),
|
|
46
|
+
el("span", { class: "cf-cursor-label" }, c.label),
|
|
51
47
|
);
|
|
52
48
|
}
|
package/src/ui/Pins.ts
CHANGED
|
@@ -8,42 +8,83 @@
|
|
|
8
8
|
* current multi-selection set.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { activeThreadId } from "../store.js";
|
|
12
|
-
import type { Annotation } from "../types.js";
|
|
11
|
+
import { activeThreadId, draft } from "../store.js";
|
|
12
|
+
import type { Annotation, BoundingBox } from "../types.js";
|
|
13
13
|
import { DEFAULT_DOT, SEVERITY_COLOR } from "./constants.js";
|
|
14
|
-
import {
|
|
14
|
+
import { colorForIdentity } from "./Avatar.js";
|
|
15
|
+
import { annotationBox, boxStyle, firstLine } from "./helpers.js";
|
|
15
16
|
import { el } from "./dom.js";
|
|
16
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Annotations whose entrance animation has already played. The pins layer
|
|
20
|
+
* re-renders on EVERY scroll/resize tick, so without this gate the
|
|
21
|
+
* `cf-rise`/`cf-pin-pop` keyframes would replay on each tick and the
|
|
22
|
+
* markers would flicker instead of tracking their element smoothly. We
|
|
23
|
+
* only tag a marker `.cf-fresh` (→ animated) the first time we see its id.
|
|
24
|
+
*/
|
|
25
|
+
const seenPins = new Set<string>();
|
|
26
|
+
|
|
27
|
+
/** A document-relative box → live screen coords (so it tracks scroll). */
|
|
28
|
+
function docBoxToScreen(
|
|
29
|
+
b: BoundingBox,
|
|
30
|
+
isFixed?: boolean,
|
|
31
|
+
): { top: number; left: number; width: number; height: number } {
|
|
32
|
+
return {
|
|
33
|
+
top: b.y - (isFixed ? 0 : window.scrollY),
|
|
34
|
+
left: b.x - (isFixed ? 0 : window.scrollX),
|
|
35
|
+
width: b.width,
|
|
36
|
+
height: b.height,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Short glyph shown inside the pin — agent initial, or the index. */
|
|
41
|
+
function pinLabel(a: Annotation, index: number): string {
|
|
42
|
+
if (a.author?.kind === "agent") {
|
|
43
|
+
const name = (a.author.displayName ?? "").trim();
|
|
44
|
+
return name ? name.charAt(0).toUpperCase() : "AI";
|
|
45
|
+
}
|
|
46
|
+
return String(index);
|
|
47
|
+
}
|
|
48
|
+
|
|
17
49
|
export function renderPin(a: Annotation, index: number): HTMLElement | null {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
const color =
|
|
50
|
+
const isAgent = a.author?.kind === "agent";
|
|
51
|
+
// Color-code by agent (matches their avatar + cursor); humans use severity.
|
|
52
|
+
const color = isAgent
|
|
53
|
+
? colorForIdentity(a.author!)
|
|
54
|
+
: a.severity
|
|
55
|
+
? SEVERITY_COLOR[a.severity]
|
|
56
|
+
: DEFAULT_DOT;
|
|
21
57
|
const resolved = a.status === "resolved" || a.status === "dismissed";
|
|
22
58
|
|
|
59
|
+
// Animate the entrance only the first render for this annotation — not on
|
|
60
|
+
// every scroll/resize re-render (which would otherwise flicker).
|
|
61
|
+
const fresh = !seenPins.has(a.id);
|
|
62
|
+
seenPins.add(a.id);
|
|
63
|
+
const flags = `${resolved ? " resolved" : ""}${isAgent ? " agent" : ""}${fresh ? " cf-fresh" : ""}`;
|
|
64
|
+
|
|
23
65
|
const pin = el(
|
|
24
66
|
"button",
|
|
25
67
|
{
|
|
26
|
-
class: `cf-pin${
|
|
27
|
-
|
|
28
|
-
|
|
68
|
+
class: `cf-pin${flags}`,
|
|
69
|
+
title: a.author?.displayName
|
|
70
|
+
? `${a.author.displayName}: ${firstLine(a.comment)}`
|
|
71
|
+
: firstLine(a.comment),
|
|
29
72
|
onClick: () => {
|
|
30
|
-
|
|
73
|
+
// One popup at a time: opening a thread closes any open composer,
|
|
74
|
+
// so a stray dark composer never stacks behind the thread panel.
|
|
75
|
+
const opening = activeThreadId.value !== a.id;
|
|
76
|
+
if (opening) draft.value = null;
|
|
77
|
+
activeThreadId.value = opening ? a.id : null;
|
|
31
78
|
},
|
|
32
79
|
},
|
|
33
|
-
a
|
|
80
|
+
pinLabel(a, index),
|
|
34
81
|
);
|
|
35
82
|
|
|
36
|
-
// Hover-to-preview: a small floating card with the comment
|
|
37
|
-
// mouseenter and removed on mouseleave. The card is appended to the
|
|
38
|
-
// pin so it lives/dies with it; it's positioned just below-right.
|
|
83
|
+
// Hover-to-preview: a small floating card with the comment.
|
|
39
84
|
let preview: HTMLElement | null = null;
|
|
40
85
|
pin.addEventListener("mouseenter", () => {
|
|
41
|
-
if (preview) return;
|
|
42
|
-
preview = el(
|
|
43
|
-
"div",
|
|
44
|
-
{ class: "cf-pin-preview" },
|
|
45
|
-
firstLine(a.comment) || a.comment,
|
|
46
|
-
);
|
|
86
|
+
if (preview || activeThreadId.value === a.id) return; // thread already open
|
|
87
|
+
preview = el("div", { class: "cf-pin-preview" }, firstLine(a.comment) || a.comment);
|
|
47
88
|
pin.appendChild(preview);
|
|
48
89
|
});
|
|
49
90
|
pin.addEventListener("mouseleave", () => {
|
|
@@ -51,7 +92,36 @@ export function renderPin(a: Annotation, index: number): HTMLElement | null {
|
|
|
51
92
|
preview = null;
|
|
52
93
|
});
|
|
53
94
|
|
|
54
|
-
|
|
95
|
+
// The outline box hugs each annotated element so it's obvious what the
|
|
96
|
+
// annotation refers to; the pin sits at the first element's top-left.
|
|
97
|
+
const makeWrap = (box: {
|
|
98
|
+
top: number;
|
|
99
|
+
left: number;
|
|
100
|
+
width: number;
|
|
101
|
+
height: number;
|
|
102
|
+
}): HTMLElement =>
|
|
103
|
+
el("div", {
|
|
104
|
+
class: `cf-anno${flags}`,
|
|
105
|
+
style: `${boxStyle(box)}--cf-pin:${color};`,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Multi-element annotation → outline EVERY selected element, not just the
|
|
109
|
+
// primary; the numbered pin sits on the first box.
|
|
110
|
+
if (a.isMultiSelect && a.elementBoundingBoxes?.length) {
|
|
111
|
+
const group = el("div", { class: "cf-anno-group" });
|
|
112
|
+
a.elementBoundingBoxes.forEach((b, i) => {
|
|
113
|
+
const wrap = makeWrap(docBoxToScreen(b, a.isFixed));
|
|
114
|
+
if (i === 0) wrap.appendChild(pin);
|
|
115
|
+
group.appendChild(wrap);
|
|
116
|
+
});
|
|
117
|
+
return group;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const box = annotationBox(a);
|
|
121
|
+
if (!box) return null;
|
|
122
|
+
const wrap = makeWrap(box);
|
|
123
|
+
wrap.appendChild(pin);
|
|
124
|
+
return wrap;
|
|
55
125
|
}
|
|
56
126
|
|
|
57
127
|
export function MultiBox(target: Element): HTMLElement {
|