@unhingged/vizu-core 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auto.cjs +337 -49
- package/dist/auto.cjs.map +1 -1
- package/dist/auto.js +337 -49
- package/dist/auto.js.map +1 -1
- package/dist/index.cjs +337 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +337 -49
- package/dist/index.js.map +1 -1
- package/dist/vizu.min.js +98 -63
- package/dist/vizu.min.js.map +1 -1
- package/package.json +1 -1
package/dist/vizu.min.js
CHANGED
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
"use strict";var VIZU=(()=>{var X=Object.defineProperty;var ze=Object.getOwnPropertyDescriptor;var xe=Object.getOwnPropertyNames;var ye=Object.prototype.hasOwnProperty;var
|
|
2
|
-
<a class="vz-attachment-display" href="${
|
|
3
|
-
<img src="${
|
|
1
|
+
"use strict";var VIZU=(()=>{var X=Object.defineProperty;var ze=Object.getOwnPropertyDescriptor;var xe=Object.getOwnPropertyNames;var ye=Object.prototype.hasOwnProperty;var ke=(o,e)=>{for(var t in e)X(o,t,{get:e[t],enumerable:!0})},we=(o,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of xe(e))!ye.call(o,i)&&i!==t&&X(o,i,{get:()=>e[i],enumerable:!(n=ze(e,i))||n.enumerable});return o};var Ce=o=>we(X({},"__esModule",{value:!0}),o);var We={};ke(We,{Vizu:()=>S});function U(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():Math.random().toString(36).slice(2)+Date.now().toString(36)}function Y(o,e){return!!o.closest(e)}function d(o){return o==null?"":String(o).replace(/[&<>"']/g,e=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[e])}function T(o){let e=new Date(o),n=Date.now()-o;return n<6e4?"just now":n<36e5?Math.floor(n/6e4)+"m ago":n<864e5?Math.floor(n/36e5)+"h ago":e.toLocaleDateString()+" "+e.toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}function Z(o){return o?String(o).split(/\s+/).filter(Boolean).slice(0,2).map(e=>e[0].toUpperCase()).join(""):""}function x(o,e="vz-comment-author-avatar"){if(!o)return`<span class="${e}">\xB7</span>`;let t=o.name||o.id||"User";return o.avatarUrl?`<span class="${e}"><img src="${d(o.avatarUrl)}" alt="${d(t)}" /></span>`:`<span class="${e}">${d(Z(t)||"?")}</span>`}function ie(){return Math.random().toString(36).slice(2,10)}var Ee=/\[@([^\]]+)\]\(vizu-mention:([a-zA-Z0-9_-]+)\)/g,Ae=/(\[#[^\]]+\]\(vizu-ref:[a-zA-Z0-9_-]+\)|\[@[^\]]+\]\(vizu-mention:[a-zA-Z0-9_-]+\))/g;function F(o,e){return o.split(Ae).map(n=>{let i=n.match(/^\[#([^\]]+)\]\(vizu-ref:([a-zA-Z0-9_-]+)\)$/);if(i)return`<a class="vz-ref" data-vz="ref" data-comment-id="${d(e)}" data-ref-id="${d(i[2])}" role="button" tabindex="0">${d(i[1])}</a>`;let r=n.match(/^\[@([^\]]+)\]\(vizu-mention:([a-zA-Z0-9_-]+)\)$/);return r?`<span class="vz-mention" data-vz="mention" data-mention-id="${d(r[2])}">@${d(r[1])}</span>`:d(n)}).join("")}function re(o){let e=[];for(let t of o.matchAll(Ee))e.includes(t[2])||e.push(t[2]);return e}function H(o){return!Array.isArray(o)||o.length===0?"":`<div class="vz-attachment-grid">${o.map(t=>{let n=t.mimeType?.startsWith("image/"),i=t.filename??t.url.split("/").pop()??"file";return n?`
|
|
2
|
+
<a class="vz-attachment-display" href="${d(t.url)}" target="_blank" rel="noreferrer" title="${d(i)}">
|
|
3
|
+
<img src="${d(t.url)}" alt="${d(i)}" loading="lazy" />
|
|
4
4
|
</a>
|
|
5
5
|
`:`
|
|
6
|
-
<a class="vz-attachment-display vz-attachment-file" href="${
|
|
6
|
+
<a class="vz-attachment-display vz-attachment-file" href="${d(t.url)}" target="_blank" rel="noreferrer">
|
|
7
7
|
<span class="vz-attachment-icon" aria-hidden="true">\u{1F4C4}</span>
|
|
8
|
-
<span class="vz-attachment-filename">${
|
|
8
|
+
<span class="vz-attachment-filename">${d(i)}</span>
|
|
9
9
|
</a>
|
|
10
|
-
`}).join("")}</div>`}function C(o){if(!o||typeof o!="object")return o;let e={...o};return(!Array.isArray(e.fingerprints)||e.fingerprints.length===0)&&e.fingerprint&&(e.fingerprints=[e.fingerprint]),delete e.fingerprint,e.url&&!e.pageUrl&&(e.pageUrl=e.url),(!("status"in e)||!Se(e.status))&&(e.status="open"),Array.isArray(e.replies)||(e.replies=[]),Array.isArray(e.attachments)||(e.attachments=[]),Array.isArray(e.mentions)||(e.mentions=[]),"fingerprintsRefreshedAt"in e||(e.fingerprintsRefreshedAt=null),e.schemaVersion=2,e}function Se(o){return o==="open"||o==="resolved"||o==="wontfix"}function A(o){if(!Array.isArray(o))return[];let e=[];for(let t of o)!t||typeof t!="object"||e.push(C(t));return e}function se(o){if(!Array.isArray(o))return!1;for(let e of o){if(!e||typeof e!="object")continue;let t=e;if(t.schemaVersion!==2||"fingerprint"in t||!Array.isArray(t.replies)||!Array.isArray(t.attachments)||!Array.isArray(t.mentions))return!0}return!1}var G=o=>`vizu:comments:${o}`,I=class{async load(e){return M(e)}async addComment(e,t){let n=M(e);n.push(t),P(e,n)}async updateComment(e,t,n){let i=M(e),r=i.findIndex(a=>a.id===t);if(r===-1)return null;let s={...i[r],...n,id:i[r].id};return i[r]=s,P(e,i),s}async removeComment(e,t){let n=M(e);P(e,n.filter(i=>i.id!==t))}async setAll(e,t){P(e,t)}async clear(e){typeof localStorage>"u"||localStorage.removeItem(G(e))}async addReply(e,t,n){let i=M(e),r=i.findIndex(a=>a.id===t);if(r===-1)return null;let s={...i[r],replies:[...i[r].replies??[],n]};return i[r]=s,P(e,i),s}async removeReply(e,t,n){let i=M(e),r=i.findIndex(a=>a.id===t);if(r===-1)return null;let s={...i[r],replies:(i[r].replies??[]).filter(a=>a.id!==n)};return i[r]=s,P(e,i),s}};function M(o){if(typeof localStorage>"u")return[];let e=localStorage.getItem(G(o));if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?A(t):[]}catch{return[]}}function P(o,e){typeof localStorage>"u"||localStorage.setItem(G(o),JSON.stringify(e))}var O=class{constructor(){this.store=new Map}async load(e){return[...this.store.get(e)||[]]}async addComment(e,t){let n=this.store.get(e)??[];this.store.set(e,[...n,t])}async updateComment(e,t,n){let i=this.store.get(e)??[],r=i.findIndex(l=>l.id===t);if(r===-1)return null;let s={...i[r],...n,id:i[r].id},a=[...i];return a[r]=s,this.store.set(e,a),s}async removeComment(e,t){let n=this.store.get(e)??[];this.store.set(e,n.filter(i=>i.id!==t))}async setAll(e,t){this.store.set(e,[...t])}async clear(e){this.store.delete(e)}async addReply(e,t,n){let i=this.store.get(e)??[],r=i.findIndex(l=>l.id===t);if(r===-1)return null;let s={...i[r],replies:[...i[r].replies??[],n]},a=[...i];return a[r]=s,this.store.set(e,a),s}async removeReply(e,t,n){let i=this.store.get(e)??[],r=i.findIndex(l=>l.id===t);if(r===-1)return null;let s={...i[r],replies:(i[r].replies??[]).filter(l=>l.id!==n)},a=[...i];return a[r]=s,this.store.set(e,a),s}},N=class{async load(){return[]}async addComment(){}async updateComment(){return null}async removeComment(){}async setAll(){}async clear(){}};function Q(o){if(!o||typeof o!="object")return!1;let e=o;return typeof e.load=="function"&&typeof e.save=="function"&&typeof e.clear=="function"&&typeof e.addComment!="function"}function oe(o){if(!Q(o))return o;let e=!1,t=()=>{e||(e=!0,typeof console<"u"&&console.warn("[vizu] StorageAdapter v1 detected. Per-comment ops are emulated via full-array rewrites \u2014 upgrade your adapter to the v2 interface (addComment / updateComment / removeComment / setAll / load / clear) when you can."))};return{async load(n){return t(),o.load(n)},async addComment(n,i){t();let r=await o.load(n);r.push(i),await o.save(n,r)},async updateComment(n,i,r){t();let s=await o.load(n),a=s.findIndex(d=>d.id===i);if(a===-1)return null;let l={...s[a],...r,id:s[a].id};return s[a]=l,await o.save(n,s),l},async removeComment(n,i){t();let r=await o.load(n);await o.save(n,r.filter(s=>s.id!==i))},async setAll(n,i){t(),await o.save(n,i)},async clear(n){await o.clear(n)}}}var Te="https://vizu.unhingged.com",ae=480,le=640,k=class{constructor(e){this.cachedToken=null;this.pendingAuth=null;this.lastNotifiedTokenId=null;this.accessDenied=!1;this.accessDeniedReason=null;this.workspace=e.workspace,this.apiUrl=(e.apiUrl??Te).replace(/\/$/,""),this.autoSignIn=e.autoSignIn!==!1,this.onAuthChanged=e.onAuthChanged;let t=this.readHashToken();t?(this.cachedToken=t,this.writeStoredToken(t)):this.cachedToken=this.readStoredToken(),this.cachedToken&&!this.isExpired(this.cachedToken)&&this.fireAuthChanged(this.cachedToken)}readHashToken(){if(typeof window>"u")return null;let e=window.location.hash;if(!e||e.length<2)return null;let t=new URLSearchParams(e.slice(1)),n=t.get("vizu_auth");if(!n)return null;try{let i=Uint8Array.from(atob(n),p=>p.charCodeAt(0)),r=new TextDecoder("utf-8").decode(i),s=JSON.parse(r);if(!s.t||!s.e||!s.u||!s.w||s.w!==this.workspace)return null;let a={token:s.t,expiresAt:s.e,userId:s.u,user:typeof s.n=="string"?{id:s.u,name:s.n,avatarUrl:typeof s.a=="string"?s.a:void 0}:null};t.delete("vizu_auth");let l=t.toString(),d=l?`#${l}`:"";try{let p=window.location.pathname+window.location.search+d;window.history.replaceState(null,"",p)}catch{}return a}catch{return null}}async preflightAuth(){await this.resolveToken()}isAccessDenied(){return this.accessDenied}accessDenialReason(){return this.accessDeniedReason}async searchMentionable(){if(!this.cachedToken||this.isExpired(this.cachedToken))return[];try{let e=await this.fetchAuthed(this.apiUrl+"/api/workspaces/"+encodeURIComponent(this.workspace)+"/mentionable",{method:"GET"});if(!e.ok)return[];let t=await this.parseJson(e);return Array.isArray(t?.users)?t.users:[]}catch{return[]}}async load(e){if(!this.cachedToken||this.isExpired(this.cachedToken)){let i=this.readStoredToken();if(!i||this.isExpired(i))return[];this.cachedToken=i}let t=[],n=null;do{let i=new URL(this.apiUrl+"/api/comments");i.searchParams.set("workspace",this.workspace),i.searchParams.set("limit","100"),n&&i.searchParams.set("cursor",n);let r=await this.fetchAuthed(i.toString(),{method:"GET"}),s=await this.parseJson(r);if(!r.ok)throw x(r.status,s);let a=Array.isArray(s.comments)?s.comments:[];t.push(...A(a)),n=typeof s.nextCursor=="string"?s.nextCursor:null}while(n);return t}async addComment(e,t){let n=await this.fetchAuthed(this.apiUrl+"/api/comments",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({workspace:this.workspace,comment:t})}),i=await this.parseJson(n);if(!n.ok)throw x(n.status,i)}async updateComment(e,t,n){let i=new URL(this.apiUrl+"/api/comments/"+encodeURIComponent(t));i.searchParams.set("workspace",this.workspace);let r=await this.fetchAuthed(i.toString(),{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(n)});if(r.status===404)return null;let s=await this.parseJson(r);if(!r.ok)throw x(r.status,s);return C(s.comment)}async removeComment(e,t){let n=new URL(this.apiUrl+"/api/comments/"+encodeURIComponent(t));n.searchParams.set("workspace",this.workspace);let i=await this.fetchAuthed(n.toString(),{method:"DELETE"});if(i.status!==404&&!i.ok){let r=await this.parseJson(i);throw x(i.status,r)}}async setAll(e,t){await this.clear(e);for(let n of t)await this.addComment(e,n)}async clear(e){let t=new URL(this.apiUrl+"/api/comments");t.searchParams.set("workspace",this.workspace),t.searchParams.set("all","true");let n=await this.fetchAuthed(t.toString(),{method:"DELETE"});if(n.status!==404&&!n.ok){let i=await this.parseJson(n);throw x(n.status,i)}}async addReply(e,t,n){let i=new URL(this.apiUrl+"/api/comments/"+encodeURIComponent(t)+"/replies");i.searchParams.set("workspace",this.workspace);let r=await this.fetchAuthed(i.toString(),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n)});if(r.status===404)return null;let s=await this.parseJson(r);if(!r.ok)throw x(r.status,s);return s?.comment?C(s.comment):null}async removeReply(e,t,n){let i=new URL(this.apiUrl+"/api/comments/"+encodeURIComponent(t)+"/replies/"+encodeURIComponent(n));i.searchParams.set("workspace",this.workspace);let r=await this.fetchAuthed(i.toString(),{method:"DELETE"});if(r.status===404)return null;if(!r.ok){let s=await this.parseJson(r);throw x(r.status,s)}return null}async uploadAttachment(e,t){let n=new FormData;n.append("file",t,t.name);let i=new URL(this.apiUrl+"/api/uploads");i.searchParams.set("workspace",this.workspace);let r=await this.fetchAuthed(i.toString(),{method:"POST",body:n}),s=await this.parseJson(r);if(!r.ok)throw x(r.status,s);if(!s?.url||typeof s.url!="string")throw x(500,{error:"upload_failed",message:"Backend did not return a URL."});return{id:H(),url:s.url,mimeType:t.type||"application/octet-stream",sizeBytes:t.size,uploadedAt:Date.now(),filename:t.name}}getAuthContext(){return this.cachedToken?{userId:this.cachedToken.userId,token:this.cachedToken.token,expiresAt:this.cachedToken.expiresAt}:null}isExpired(e){if(!e)return!0;let t=new Date(e.expiresAt).getTime();return!Number.isFinite(t)||t-Date.now()<3e4}readStoredToken(){if(typeof sessionStorage>"u")return null;let e=sessionStorage.getItem(this.sessionKey());if(!e)return null;try{let t=JSON.parse(e);return this.isExpired(t)?(sessionStorage.removeItem(this.sessionKey()),null):t}catch{return null}}writeStoredToken(e){this.cachedToken=e,typeof sessionStorage<"u"&&sessionStorage.setItem(this.sessionKey(),JSON.stringify(e))}clearStoredToken(){this.cachedToken=null,typeof sessionStorage<"u"&&sessionStorage.removeItem(this.sessionKey())}sessionKey(){return`vizu:cloud-token:v2:${this.workspace}`}async resolveToken(){if(this.cachedToken&&!this.isExpired(this.cachedToken))return this.cachedToken;let e=this.readStoredToken();if(e)return this.cachedToken=e,this.fireAuthChanged(e),e;if(this.accessDenied)throw V("access_denied",`No access to workspace "${this.workspace}" (${this.accessDeniedReason??"no_access"}).`);if(!this.autoSignIn)throw V("auth_required","Sign-in required; autoSignIn is disabled.");return this.pendingAuth||(this.pendingAuth=this.openSignInPopup().then(t=>(this.writeStoredToken(t),this.fireAuthChanged(t),t)).finally(()=>{this.pendingAuth=null})),this.pendingAuth}fireAuthChanged(e){if(this.lastNotifiedTokenId!==e.token){this.lastNotifiedTokenId=e.token;try{this.onAuthChanged?.({userId:e.userId,user:e.user})}catch(t){typeof console<"u"&&console.warn("[vizu/cloud] onAuthChanged callback threw",t)}}}openSignInPopup(){return new Promise((e,t)=>{if(typeof window>"u")return t(V("auth_unavailable","Sign-in requires a browser environment."));let n=new URL(this.apiUrl).origin,i=window.location.origin,r=this.apiUrl+"/connect?workspace="+encodeURIComponent(this.workspace)+"&origin="+encodeURIComponent(i),s=Math.max(0,Math.round((window.screen.width-ae)/2)),a=Math.max(0,Math.round((window.screen.height-le)/2)),l=window.open(r,"vizu-connect",`width=${ae},height=${le},left=${s},top=${a},noopener=no,popup=yes`);if(!l)try{let f=window.location.href,v=this.apiUrl+"/connect?workspace="+encodeURIComponent(this.workspace)+"&origin="+encodeURIComponent(i)+"&return_to="+encodeURIComponent(f);window.location.assign(v);return}catch(f){return t(V("popup_blocked","Sign-in popup was blocked and redirect fallback failed: "+(f instanceof Error?f.message:String(f))))}let d=!1,p=f=>{if(f.origin!==n)return;let v=f.data;if(!v)return;if(v.type==="vizu:auth-denied"&&v.workspace===this.workspace){d=!0,$(),this.accessDenied=!0,this.accessDeniedReason=typeof v.reason=="string"?v.reason:"no_access",t(V("access_denied",`No access to workspace "${this.workspace}" (${this.accessDeniedReason}).`));return}if(v.type!=="vizu:auth"||v.workspace!==this.workspace||typeof v.token!="string"||typeof v.expiresAt!="string"||typeof v.userId!="string")return;d=!0,$();let b=v.user,u=b&&typeof b.name=="string"?{id:typeof b.id=="string"?b.id:v.userId,name:b.name,avatarUrl:typeof b.avatarUrl=="string"?b.avatarUrl:void 0}:null;e({token:v.token,expiresAt:v.expiresAt,userId:v.userId,user:u})},m=window.setInterval(()=>{l.closed&&(d||($(),t(V("auth_canceled","Sign-in popup was closed before completing."))))},500);function $(){window.clearInterval(m),window.removeEventListener("message",p)}window.addEventListener("message",p)})}async fetchAuthed(e,t,n=!1){let i=await this.resolveToken(),r=new Headers(t.headers);r.set("authorization",`Bearer ${i.token}`);let s=await fetch(e,{...t,headers:r,credentials:"omit"});return s.status===401&&!n?(this.clearStoredToken(),this.fetchAuthed(e,t,!0)):s}async parseJson(e){try{let t=await e.text();return t?JSON.parse(t):null}catch{return null}}};function V(o,e){let t=new Error(e);return t.code=o,t}function x(o,e){let t=e?.error==="origin_not_allowed"?"origin_not_allowed":e?.error==="not_found"?"workspace_not_found":"http_error",n=new Error(e?.message??`HTTP ${o}`);return n.code=t,n.status=o,n.body=e,n}function ce(o){let e=[],t=o,n=0;for(;t&&t!==document.documentElement&&n<8;){let i=t.tagName.toLowerCase();if(t.id){e.unshift("#"+CSS.escape(t.id));break}let r=Array.from(t.classList).filter(a=>!a.startsWith("vz-")&&!a.startsWith("vizu-")).slice(0,2);r.length&&(i+="."+r.map(a=>CSS.escape(a)).join("."));let s=t.parentElement;if(s){let a=Array.from(s.children).filter(l=>l.tagName===t.tagName);if(a.length>1){let l=a.indexOf(t);i+=":nth-of-type("+(l+1)+")"}}e.unshift(i),t=s,n++}return e.join(" > ")}function D(o){let e=o.innerText||o.textContent||"",t=o.parentElement,n=0;return t&&(n=Array.from(t.children).indexOf(o)),{selector:ce(o),parentSelector:t?ce(t):"",tagName:o.tagName,textSnippet:e.trim().slice(0,80),siblingIndex:n,attributes:{id:o.id||void 0,classList:Array.from(o.classList).filter(i=>!i.startsWith("vz-")&&!i.startsWith("vizu-")),role:o.getAttribute("role")||void 0,ariaLabel:o.getAttribute("aria-label")||void 0,dataKey:o.getAttribute("data-vizu-key")||void 0},ancestorChain:Me(o,4),algorithmVersion:2}}function Me(o,e){let t=[],n=o.parentElement;for(;n&&t.length<e;){let i=n.parentElement,r=1;i&&(r=Array.from(i.children).filter(a=>a.tagName===n.tagName).indexOf(n)+1),t.push({tag:n.tagName,nthOfType:r}),n=i}return t.length>0?{steps:t}:void 0}function j(o,e=document){if(o.attributes.dataKey){try{let s=e.querySelector(`[data-vizu-key="${CSS.escape(o.attributes.dataKey)}"]`);if(s)return{element:s,confidence:"exact"}}catch{}return{element:null,confidence:"orphaned"}}if(o.attributes.id)try{let s=e.querySelector("#"+CSS.escape(o.attributes.id));if(s)return{element:s,confidence:"exact"}}catch{}let t=[],n=o.attributes.classList;if(n&&n.length>0){let s=Pe(e,o.tagName,n);s?.element&&t.push({el:s.element,rung:"class"})}try{let s=e.querySelector(o.selector);s&&t.push({el:s,rung:"selector"})}catch{}if(o.ancestorChain&&o.ancestorChain.steps.length>0){let s=Ve(e,o.tagName,o.ancestorChain.steps);s?.element&&t.push({el:s.element,rung:"chain"})}else try{let s=o.parentSelector?e.querySelector(o.parentSelector):e;if(s){let a=s.children[o.siblingIndex];a&&a.tagName===o.tagName&&t.push({el:a,rung:"chain"})}}catch{}if(o.textSnippet){let s=Re(e,o.tagName,o.textSnippet);s?.element&&t.push({el:s.element,rung:"text"})}if(t.length===0)return{element:null,confidence:"orphaned"};let i=new Map;for(let s of t){let a=i.get(s.el);a||(a=new Set,i.set(s.el,a)),a.add(s.rung)}let r=null;for(let[s,a]of i)(!r||a.size>r.rungs.size)&&(r={el:s,rungs:a});return r?r.rungs.size>=3?{element:r.el,confidence:"likely"}:r.rungs.size>=2?{element:r.el,confidence:"drifted"}:r.rungs.has("class")||r.rungs.has("chain")?{element:r.el,confidence:"drifted"}:{element:null,confidence:"orphaned"}:{element:null,confidence:"orphaned"}}function Pe(o,e,t){let n=new Set(t.filter(a=>!a.startsWith("vz-")&&!a.startsWith("vizu-")));if(n.size===0)return null;let i=o.querySelectorAll(e.toLowerCase()),r=null,s=0;for(let a of i){let l=new Set(Array.from(a.classList).filter(p=>!p.startsWith("vz-")&&!p.startsWith("vizu-")));if(l.size===0)continue;let d=Fe(n,l);d<.6||(!r||d>r.score?(s=r?.score??0,r={el:a,score:d}):d>s&&(s=d))}return!r||r.score-s<.1?null:{element:r.el,confidence:"likely"}}function Ve(o,e,t){let n=o.querySelectorAll(e.toLowerCase()),i=null;for(let r of n){let s=Le(r,t.length+3),a=Ue(s,t,3);a<2||(!i||a>i.score)&&(i={el:r,score:a})}return i?{element:i.el,confidence:i.score===t.length?"likely":"drifted"}:null}function Le(o,e){let t=[],n=o.parentElement;for(;n&&t.length<e;){let i=n.parentElement,r=1;i&&(r=Array.from(i.children).filter(a=>a.tagName===n.tagName).indexOf(n)+1),t.push({tag:n.tagName,nthOfType:r}),n=i}return t}function Re(o,e,t){let n=de(t);if(!n)return null;let i=o.querySelectorAll(e.toLowerCase()),r=null;for(let s of i){let a=s.innerText||s.textContent||"",l=de(a.slice(0,160));if(!l)continue;let d=$e(n,l);d<.75||(!r||d>r.score)&&(r={el:s,score:d})}return r?{element:r.el,confidence:"drifted"}:null}function de(o){return o.toLowerCase().replace(/\s+/g," ").trim()}function $e(o,e){if(o===e)return 1;if(o.length===0||e.length===0)return o.length===e.length?1:0;let t=Math.max(o.length,e.length);return 1-He(o,e)/t}function He(o,e){if(o.length>e.length){let i=o;o=e,e=i}let t=new Array(o.length+1),n=new Array(o.length+1);for(let i=0;i<=o.length;i++)t[i]=i;for(let i=1;i<=e.length;i++){n[0]=i;for(let r=1;r<=o.length;r++){let s=o.charCodeAt(r-1)===e.charCodeAt(i-1)?0:1;n[r]=Math.min(n[r-1]+1,t[r]+1,t[r-1]+s)}for(let r=0;r<=o.length;r++)t[r]=n[r]}return t[o.length]}function Ue(o,e,t){let n=0;for(let i=0;i<=t;i++){let r=0;for(let s=0;s<e.length;s++){let a=o[s+i];if(!a)break;a.tag===e[s].tag&&a.nthOfType===e[s].nthOfType&&r++}r>n&&(n=r)}return n}function Fe(o,e){if(o.size===0&&e.size===0)return 1;let t=0;for(let i of o)e.has(i)&&t++;let n=o.size+e.size-t;return n===0?0:t/n}function L(o,e){return j(o,e).element}function h(o){return o.selector+"|"+o.textSnippet}function R(o){let e=o.tagName.toLowerCase();if(o.textSnippet){let t=o.textSnippet.length>24?o.textSnippet.slice(0,24)+"\u2026":o.textSnippet;return e+": "+t}return o.attributes.id?e+"#"+o.attributes.id:o.attributes.classList&&o.attributes.classList.length?e+"."+o.attributes.classList[0]:e}var Ie="[data-vizu-root], [data-vizu-ignore], script, style, link, meta, head",B=class{constructor(e,t,n){this.current=null;this.active=!1;this.paused=!1;this.handleMove=e=>{if(this.paused){this.setCurrent(null),this.overlay.style.display="none";return}let t=document.elementFromPoint(e.clientX,e.clientY);if(!(!t||t===this.current)){if(this.shouldIgnore(t)){this.setCurrent(null),this.overlay.style.display="none";return}this.setCurrent(t),this.updateOverlay()}};this.handleClick=e=>{let t=document.elementFromPoint(e.clientX,e.clientY);t&&(this.shouldIgnore(t)||(e.preventDefault(),e.stopPropagation(),this.onClick(t,e)))};this.handleScroll=()=>{this.current&&this.updateOverlay()};this.root=e,this.extraIgnore=t,this.onClick=n,this.overlay=document.createElement("div"),this.overlay.className="vz-highlight",this.overlay.style.display="none",this.root.appendChild(this.overlay)}setCurrent(e){this.current!==e&&(this.current=e)}start(){this.active||(this.active=!0,document.addEventListener("mousemove",this.handleMove,!0),document.addEventListener("click",this.handleClick,!0),document.addEventListener("scroll",this.handleScroll,!0),window.addEventListener("resize",this.handleScroll))}stop(){this.active&&(this.active=!1,document.removeEventListener("mousemove",this.handleMove,!0),document.removeEventListener("click",this.handleClick,!0),document.removeEventListener("scroll",this.handleScroll,!0),window.removeEventListener("resize",this.handleScroll),this.overlay.style.display="none",this.setCurrent(null))}shouldIgnore(e){if(e===document.documentElement||e===document.body||Y(e,Ie))return!0;for(let t of this.extraIgnore)try{if(Y(e,t))return!0}catch{}return!1}setPaused(e){this.paused=e,e&&(this.setCurrent(null),this.overlay.style.display="none")}isPaused(){return this.paused}updateOverlay(){if(!this.current)return;let e=this.current.getBoundingClientRect();if(e.width===0||e.height===0){this.overlay.style.display="none";return}this.overlay.style.display="block",this.overlay.style.left=e.left+"px",this.overlay.style.top=e.top+"px",this.overlay.style.width=e.width+"px",this.overlay.style.height=e.height+"px"}destroy(){this.stop(),this.overlay.remove()}};function pe(o){return typeof CSS<"u"&&typeof CSS.escape=="function"?CSS.escape(o):o.replace(/["\\]/g,"\\$&")}function Oe(o){let t=(o.replies??[]).map(n=>`
|
|
11
|
-
<div class="vz-reply" data-reply-id="${
|
|
12
|
-
${n.author?`<div class="vz-comment-author">${
|
|
13
|
-
<div class="vz-reply-text">${
|
|
10
|
+
`}).join("")}</div>`}function E(o){if(!o||typeof o!="object")return o;let e={...o};return(!Array.isArray(e.fingerprints)||e.fingerprints.length===0)&&e.fingerprint&&(e.fingerprints=[e.fingerprint]),delete e.fingerprint,e.url&&!e.pageUrl&&(e.pageUrl=e.url),(!("status"in e)||!Se(e.status))&&(e.status="open"),Array.isArray(e.replies)||(e.replies=[]),Array.isArray(e.attachments)||(e.attachments=[]),Array.isArray(e.mentions)||(e.mentions=[]),"fingerprintsRefreshedAt"in e||(e.fingerprintsRefreshedAt=null),e.schemaVersion=2,e}function Se(o){return o==="open"||o==="resolved"||o==="wontfix"}function A(o){if(!Array.isArray(o))return[];let e=[];for(let t of o)!t||typeof t!="object"||e.push(E(t));return e}function se(o){if(!Array.isArray(o))return!1;for(let e of o){if(!e||typeof e!="object")continue;let t=e;if(t.schemaVersion!==2||"fingerprint"in t||!Array.isArray(t.replies)||!Array.isArray(t.attachments)||!Array.isArray(t.mentions))return!0}return!1}var G=o=>`vizu:comments:${o}`,I=class{async load(e){return P(e)}async addComment(e,t){let n=P(e);n.push(t),R(e,n)}async updateComment(e,t,n){let i=P(e),r=i.findIndex(a=>a.id===t);if(r===-1)return null;let s={...i[r],...n,id:i[r].id};return i[r]=s,R(e,i),s}async removeComment(e,t){let n=P(e);R(e,n.filter(i=>i.id!==t))}async setAll(e,t){R(e,t)}async clear(e){typeof localStorage>"u"||localStorage.removeItem(G(e))}async addReply(e,t,n){let i=P(e),r=i.findIndex(a=>a.id===t);if(r===-1)return null;let s={...i[r],replies:[...i[r].replies??[],n]};return i[r]=s,R(e,i),s}async removeReply(e,t,n){let i=P(e),r=i.findIndex(a=>a.id===t);if(r===-1)return null;let s={...i[r],replies:(i[r].replies??[]).filter(a=>a.id!==n)};return i[r]=s,R(e,i),s}};function P(o){if(typeof localStorage>"u")return[];let e=localStorage.getItem(G(o));if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?A(t):[]}catch{return[]}}function R(o,e){typeof localStorage>"u"||localStorage.setItem(G(o),JSON.stringify(e))}var O=class{constructor(){this.store=new Map}async load(e){return[...this.store.get(e)||[]]}async addComment(e,t){let n=this.store.get(e)??[];this.store.set(e,[...n,t])}async updateComment(e,t,n){let i=this.store.get(e)??[],r=i.findIndex(l=>l.id===t);if(r===-1)return null;let s={...i[r],...n,id:i[r].id},a=[...i];return a[r]=s,this.store.set(e,a),s}async removeComment(e,t){let n=this.store.get(e)??[];this.store.set(e,n.filter(i=>i.id!==t))}async setAll(e,t){this.store.set(e,[...t])}async clear(e){this.store.delete(e)}async addReply(e,t,n){let i=this.store.get(e)??[],r=i.findIndex(l=>l.id===t);if(r===-1)return null;let s={...i[r],replies:[...i[r].replies??[],n]},a=[...i];return a[r]=s,this.store.set(e,a),s}async removeReply(e,t,n){let i=this.store.get(e)??[],r=i.findIndex(l=>l.id===t);if(r===-1)return null;let s={...i[r],replies:(i[r].replies??[]).filter(l=>l.id!==n)},a=[...i];return a[r]=s,this.store.set(e,a),s}},_=class{async load(){return[]}async addComment(){}async updateComment(){return null}async removeComment(){}async setAll(){}async clear(){}};function Q(o){if(!o||typeof o!="object")return!1;let e=o;return typeof e.load=="function"&&typeof e.save=="function"&&typeof e.clear=="function"&&typeof e.addComment!="function"}function oe(o){if(!Q(o))return o;let e=!1,t=()=>{e||(e=!0,typeof console<"u"&&console.warn("[vizu] StorageAdapter v1 detected. Per-comment ops are emulated via full-array rewrites \u2014 upgrade your adapter to the v2 interface (addComment / updateComment / removeComment / setAll / load / clear) when you can."))};return{async load(n){return t(),o.load(n)},async addComment(n,i){t();let r=await o.load(n);r.push(i),await o.save(n,r)},async updateComment(n,i,r){t();let s=await o.load(n),a=s.findIndex(c=>c.id===i);if(a===-1)return null;let l={...s[a],...r,id:s[a].id};return s[a]=l,await o.save(n,s),l},async removeComment(n,i){t();let r=await o.load(n);await o.save(n,r.filter(s=>s.id!==i))},async setAll(n,i){t(),await o.save(n,i)},async clear(n){await o.clear(n)}}}var Me="https://vizu.unhingged.com",ae=480,le=640,C=class{constructor(e){this.cachedToken=null;this.pendingAuth=null;this.lastNotifiedTokenId=null;this.accessDenied=!1;this.accessDeniedReason=null;this.workspace=e.workspace,this.apiUrl=(e.apiUrl??Me).replace(/\/$/,""),this.autoSignIn=e.autoSignIn!==!1,this.onAuthChanged=e.onAuthChanged;let t=this.readHashToken();t?(this.cachedToken=t,this.writeStoredToken(t)):this.cachedToken=this.readStoredToken(),this.cachedToken&&!this.isExpired(this.cachedToken)&&this.fireAuthChanged(this.cachedToken)}readHashToken(){if(typeof window>"u")return null;let e=window.location.hash;if(!e||e.length<2)return null;let t=new URLSearchParams(e.slice(1)),n=t.get("vizu_auth");if(!n)return null;try{let i=Uint8Array.from(atob(n),p=>p.charCodeAt(0)),r=new TextDecoder("utf-8").decode(i),s=JSON.parse(r);if(!s.t||!s.e||!s.u||!s.w||s.w!==this.workspace)return null;let a={token:s.t,expiresAt:s.e,userId:s.u,user:typeof s.n=="string"?{id:s.u,name:s.n,avatarUrl:typeof s.a=="string"?s.a:void 0}:null};t.delete("vizu_auth");let l=t.toString(),c=l?`#${l}`:"";try{let p=window.location.pathname+window.location.search+c;window.history.replaceState(null,"",p)}catch{}return a}catch{return null}}async preflightAuth(){await this.resolveToken()}isAccessDenied(){return this.accessDenied}accessDenialReason(){return this.accessDeniedReason}async searchMentionable(){if(!this.cachedToken||this.isExpired(this.cachedToken))return[];try{let e=await this.fetchAuthed(this.apiUrl+"/api/workspaces/"+encodeURIComponent(this.workspace)+"/mentionable",{method:"GET"});if(!e.ok)return[];let t=await this.parseJson(e);return Array.isArray(t?.users)?t.users:[]}catch{return[]}}async load(e){if(!this.cachedToken||this.isExpired(this.cachedToken)){let i=this.readStoredToken();if(!i||this.isExpired(i))return[];this.cachedToken=i}let t=[],n=null;do{let i=new URL(this.apiUrl+"/api/comments");i.searchParams.set("workspace",this.workspace),i.searchParams.set("limit","100"),n&&i.searchParams.set("cursor",n);let r=await this.fetchAuthed(i.toString(),{method:"GET"}),s=await this.parseJson(r);if(!r.ok)throw y(r.status,s);let a=Array.isArray(s.comments)?s.comments:[];t.push(...A(a)),n=typeof s.nextCursor=="string"?s.nextCursor:null}while(n);return t}async addComment(e,t){let n=await this.fetchAuthed(this.apiUrl+"/api/comments",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({workspace:this.workspace,comment:t})}),i=await this.parseJson(n);if(!n.ok)throw y(n.status,i)}async updateComment(e,t,n){let i=new URL(this.apiUrl+"/api/comments/"+encodeURIComponent(t));i.searchParams.set("workspace",this.workspace);let r=await this.fetchAuthed(i.toString(),{method:"PATCH",headers:{"content-type":"application/json"},body:JSON.stringify(n)});if(r.status===404)return null;let s=await this.parseJson(r);if(!r.ok)throw y(r.status,s);return E(s.comment)}async removeComment(e,t){let n=new URL(this.apiUrl+"/api/comments/"+encodeURIComponent(t));n.searchParams.set("workspace",this.workspace);let i=await this.fetchAuthed(n.toString(),{method:"DELETE"});if(i.status!==404&&!i.ok){let r=await this.parseJson(i);throw y(i.status,r)}}async setAll(e,t){await this.clear(e);for(let n of t)await this.addComment(e,n)}async clear(e){let t=new URL(this.apiUrl+"/api/comments");t.searchParams.set("workspace",this.workspace),t.searchParams.set("all","true");let n=await this.fetchAuthed(t.toString(),{method:"DELETE"});if(n.status!==404&&!n.ok){let i=await this.parseJson(n);throw y(n.status,i)}}async addReply(e,t,n){let i=new URL(this.apiUrl+"/api/comments/"+encodeURIComponent(t)+"/replies");i.searchParams.set("workspace",this.workspace);let r=await this.fetchAuthed(i.toString(),{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify(n)});if(r.status===404)return null;let s=await this.parseJson(r);if(!r.ok)throw y(r.status,s);return s?.comment?E(s.comment):null}async removeReply(e,t,n){let i=new URL(this.apiUrl+"/api/comments/"+encodeURIComponent(t)+"/replies/"+encodeURIComponent(n));i.searchParams.set("workspace",this.workspace);let r=await this.fetchAuthed(i.toString(),{method:"DELETE"});if(r.status===404)return null;if(!r.ok){let s=await this.parseJson(r);throw y(r.status,s)}return null}async uploadAttachment(e,t){let n=new FormData;n.append("file",t,t.name);let i=new URL(this.apiUrl+"/api/uploads");i.searchParams.set("workspace",this.workspace);let r=await this.fetchAuthed(i.toString(),{method:"POST",body:n}),s=await this.parseJson(r);if(!r.ok)throw y(r.status,s);if(!s?.url||typeof s.url!="string")throw y(500,{error:"upload_failed",message:"Backend did not return a URL."});return{id:U(),url:s.url,mimeType:t.type||"application/octet-stream",sizeBytes:t.size,uploadedAt:Date.now(),filename:t.name}}getAuthContext(){return this.cachedToken?{userId:this.cachedToken.userId,token:this.cachedToken.token,expiresAt:this.cachedToken.expiresAt}:null}isExpired(e){if(!e)return!0;let t=new Date(e.expiresAt).getTime();return!Number.isFinite(t)||t-Date.now()<3e4}readStoredToken(){if(typeof sessionStorage>"u")return null;let e=sessionStorage.getItem(this.sessionKey());if(!e)return null;try{let t=JSON.parse(e);return this.isExpired(t)?(sessionStorage.removeItem(this.sessionKey()),null):t}catch{return null}}writeStoredToken(e){this.cachedToken=e,typeof sessionStorage<"u"&&sessionStorage.setItem(this.sessionKey(),JSON.stringify(e))}clearStoredToken(){this.cachedToken=null,typeof sessionStorage<"u"&&sessionStorage.removeItem(this.sessionKey())}sessionKey(){return`vizu:cloud-token:v2:${this.workspace}`}async resolveToken(){if(this.cachedToken&&!this.isExpired(this.cachedToken))return this.cachedToken;let e=this.readStoredToken();if(e)return this.cachedToken=e,this.fireAuthChanged(e),e;if(this.accessDenied)throw L("access_denied",`No access to workspace "${this.workspace}" (${this.accessDeniedReason??"no_access"}).`);if(!this.autoSignIn)throw L("auth_required","Sign-in required; autoSignIn is disabled.");return this.pendingAuth||(this.pendingAuth=this.openSignInPopup().then(t=>(this.writeStoredToken(t),this.fireAuthChanged(t),t)).finally(()=>{this.pendingAuth=null})),this.pendingAuth}fireAuthChanged(e){if(this.lastNotifiedTokenId!==e.token){this.lastNotifiedTokenId=e.token;try{this.onAuthChanged?.({userId:e.userId,user:e.user})}catch(t){typeof console<"u"&&console.warn("[vizu/cloud] onAuthChanged callback threw",t)}}}openSignInPopup(){return new Promise((e,t)=>{if(typeof window>"u")return t(L("auth_unavailable","Sign-in requires a browser environment."));let n=new URL(this.apiUrl).origin,i=window.location.origin,r=this.apiUrl+"/connect?workspace="+encodeURIComponent(this.workspace)+"&origin="+encodeURIComponent(i),s=Math.max(0,Math.round((window.screen.width-ae)/2)),a=Math.max(0,Math.round((window.screen.height-le)/2)),l=window.open(r,"vizu-connect",`width=${ae},height=${le},left=${s},top=${a},noopener=no,popup=yes`);if(!l)try{let b=window.location.href,f=this.apiUrl+"/connect?workspace="+encodeURIComponent(this.workspace)+"&origin="+encodeURIComponent(i)+"&return_to="+encodeURIComponent(b);window.location.assign(f);return}catch(b){return t(L("popup_blocked","Sign-in popup was blocked and redirect fallback failed: "+(b instanceof Error?b.message:String(b))))}let c=!1,p=b=>{if(b.origin!==n)return;let f=b.data;if(!f)return;if(f.type==="vizu:auth-denied"&&f.workspace===this.workspace){c=!0,v(),this.accessDenied=!0,this.accessDeniedReason=typeof f.reason=="string"?f.reason:"no_access",t(L("access_denied",`No access to workspace "${this.workspace}" (${this.accessDeniedReason}).`));return}if(f.type!=="vizu:auth"||f.workspace!==this.workspace||typeof f.token!="string"||typeof f.expiresAt!="string"||typeof f.userId!="string")return;c=!0,v();let z=f.user,u=z&&typeof z.name=="string"?{id:typeof z.id=="string"?z.id:f.userId,name:z.name,avatarUrl:typeof z.avatarUrl=="string"?z.avatarUrl:void 0}:null;e({token:f.token,expiresAt:f.expiresAt,userId:f.userId,user:u})},m=window.setInterval(()=>{l.closed&&(c||(v(),t(L("auth_canceled","Sign-in popup was closed before completing."))))},500);function v(){window.clearInterval(m),window.removeEventListener("message",p)}window.addEventListener("message",p)})}async fetchAuthed(e,t,n=!1){let i=await this.resolveToken(),r=new Headers(t.headers);r.set("authorization",`Bearer ${i.token}`);let s=await fetch(e,{...t,headers:r,credentials:"omit"});return s.status===401&&!n?(this.clearStoredToken(),this.fetchAuthed(e,t,!0)):s}async parseJson(e){try{let t=await e.text();return t?JSON.parse(t):null}catch{return null}}};function L(o,e){let t=new Error(e);return t.code=o,t}function y(o,e){let t=e?.error==="origin_not_allowed"?"origin_not_allowed":e?.error==="not_found"?"workspace_not_found":"http_error",n=new Error(e?.message??`HTTP ${o}`);return n.code=t,n.status=o,n.body=e,n}function ce(o){let e=[],t=o,n=0;for(;t&&t!==document.documentElement&&n<8;){let i=t.tagName.toLowerCase();if(t.id){e.unshift("#"+CSS.escape(t.id));break}let r=Array.from(t.classList).filter(a=>!a.startsWith("vz-")&&!a.startsWith("vizu-")).slice(0,2);r.length&&(i+="."+r.map(a=>CSS.escape(a)).join("."));let s=t.parentElement;if(s){let a=Array.from(s.children).filter(l=>l.tagName===t.tagName);if(a.length>1){let l=a.indexOf(t);i+=":nth-of-type("+(l+1)+")"}}e.unshift(i),t=s,n++}return e.join(" > ")}function D(o){let e=o.innerText||o.textContent||"",t=o.parentElement,n=0;return t&&(n=Array.from(t.children).indexOf(o)),{selector:ce(o),parentSelector:t?ce(t):"",tagName:o.tagName,textSnippet:e.trim().slice(0,80),siblingIndex:n,attributes:{id:o.id||void 0,classList:Array.from(o.classList).filter(i=>!i.startsWith("vz-")&&!i.startsWith("vizu-")),role:o.getAttribute("role")||void 0,ariaLabel:o.getAttribute("aria-label")||void 0,dataKey:o.getAttribute("data-vizu-key")||void 0},ancestorChain:Te(o,4),algorithmVersion:2}}function Te(o,e){let t=[],n=o.parentElement;for(;n&&t.length<e;){let i=n.parentElement,r=1;i&&(r=Array.from(i.children).filter(a=>a.tagName===n.tagName).indexOf(n)+1),t.push({tag:n.tagName,nthOfType:r}),n=i}return t.length>0?{steps:t}:void 0}function j(o,e=document){if(o.attributes.dataKey){try{let s=e.querySelector(`[data-vizu-key="${CSS.escape(o.attributes.dataKey)}"]`);if(s)return{element:s,confidence:"exact"}}catch{}return{element:null,confidence:"orphaned"}}if(o.attributes.id)try{let s=e.querySelector("#"+CSS.escape(o.attributes.id));if(s)return{element:s,confidence:"exact"}}catch{}let t=[],n=o.attributes.classList;if(n&&n.length>0){let s=Pe(e,o.tagName,n);s?.element&&t.push({el:s.element,rung:"class"})}try{let s=e.querySelector(o.selector);s&&t.push({el:s,rung:"selector"})}catch{}if(o.ancestorChain&&o.ancestorChain.steps.length>0){let s=Re(e,o.tagName,o.ancestorChain.steps);s?.element&&t.push({el:s.element,rung:"chain"})}else try{let s=o.parentSelector?e.querySelector(o.parentSelector):e;if(s){let a=s.children[o.siblingIndex];a&&a.tagName===o.tagName&&t.push({el:a,rung:"chain"})}}catch{}if(o.textSnippet){let s=Ve(e,o.tagName,o.textSnippet);s?.element&&t.push({el:s.element,rung:"text"})}if(t.length===0)return{element:null,confidence:"orphaned"};let i=new Map;for(let s of t){let a=i.get(s.el);a||(a=new Set,i.set(s.el,a)),a.add(s.rung)}let r=null;for(let[s,a]of i)(!r||a.size>r.rungs.size)&&(r={el:s,rungs:a});return r?r.rungs.size>=3?{element:r.el,confidence:"likely"}:r.rungs.size>=2?{element:r.el,confidence:"drifted"}:r.rungs.has("class")||r.rungs.has("chain")?{element:r.el,confidence:"drifted"}:{element:null,confidence:"orphaned"}:{element:null,confidence:"orphaned"}}function Pe(o,e,t){let n=new Set(t.filter(a=>!a.startsWith("vz-")&&!a.startsWith("vizu-")));if(n.size===0)return null;let i=o.querySelectorAll(e.toLowerCase()),r=null,s=0;for(let a of i){let l=new Set(Array.from(a.classList).filter(p=>!p.startsWith("vz-")&&!p.startsWith("vizu-")));if(l.size===0)continue;let c=He(n,l);c<.6||(!r||c>r.score?(s=r?.score??0,r={el:a,score:c}):c>s&&(s=c))}return!r||r.score-s<.1?null:{element:r.el,confidence:"likely"}}function Re(o,e,t){let n=o.querySelectorAll(e.toLowerCase()),i=null;for(let r of n){let s=Le(r,t.length+3),a=Fe(s,t,3);a<2||(!i||a>i.score)&&(i={el:r,score:a})}return i?{element:i.el,confidence:i.score===t.length?"likely":"drifted"}:null}function Le(o,e){let t=[],n=o.parentElement;for(;n&&t.length<e;){let i=n.parentElement,r=1;i&&(r=Array.from(i.children).filter(a=>a.tagName===n.tagName).indexOf(n)+1),t.push({tag:n.tagName,nthOfType:r}),n=i}return t}function Ve(o,e,t){let n=de(t);if(!n)return null;let i=o.querySelectorAll(e.toLowerCase()),r=null;for(let s of i){let a=s.innerText||s.textContent||"",l=de(a.slice(0,160));if(!l)continue;let c=$e(n,l);c<.75||(!r||c>r.score)&&(r={el:s,score:c})}return r?{element:r.el,confidence:"drifted"}:null}function de(o){return o.toLowerCase().replace(/\s+/g," ").trim()}function $e(o,e){if(o===e)return 1;if(o.length===0||e.length===0)return o.length===e.length?1:0;let t=Math.max(o.length,e.length);return 1-Ue(o,e)/t}function Ue(o,e){if(o.length>e.length){let i=o;o=e,e=i}let t=new Array(o.length+1),n=new Array(o.length+1);for(let i=0;i<=o.length;i++)t[i]=i;for(let i=1;i<=e.length;i++){n[0]=i;for(let r=1;r<=o.length;r++){let s=o.charCodeAt(r-1)===e.charCodeAt(i-1)?0:1;n[r]=Math.min(n[r-1]+1,t[r]+1,t[r-1]+s)}for(let r=0;r<=o.length;r++)t[r]=n[r]}return t[o.length]}function Fe(o,e,t){let n=0;for(let i=0;i<=t;i++){let r=0;for(let s=0;s<e.length;s++){let a=o[s+i];if(!a)break;a.tag===e[s].tag&&a.nthOfType===e[s].nthOfType&&r++}r>n&&(n=r)}return n}function He(o,e){if(o.size===0&&e.size===0)return 1;let t=0;for(let i of o)e.has(i)&&t++;let n=o.size+e.size-t;return n===0?0:t/n}function V(o,e){return j(o,e).element}function h(o){return o.selector+"|"+o.textSnippet}function $(o){let e=o.tagName.toLowerCase();if(o.textSnippet){let t=o.textSnippet.length>24?o.textSnippet.slice(0,24)+"\u2026":o.textSnippet;return e+": "+t}return o.attributes.id?e+"#"+o.attributes.id:o.attributes.classList&&o.attributes.classList.length?e+"."+o.attributes.classList[0]:e}var Ie="[data-vizu-root], [data-vizu-ignore], script, style, link, meta, head",B=class{constructor(e,t,n){this.current=null;this.active=!1;this.paused=!1;this.handleMove=e=>{if(this.paused){this.setCurrent(null),this.overlay.style.display="none";return}let t=document.elementFromPoint(e.clientX,e.clientY);if(!(!t||t===this.current)){if(this.shouldIgnore(t)){this.setCurrent(null),this.overlay.style.display="none";return}this.setCurrent(t),this.updateOverlay()}};this.handleClick=e=>{let t=document.elementFromPoint(e.clientX,e.clientY);t&&(this.shouldIgnore(t)||(e.preventDefault(),e.stopPropagation(),this.onClick(t,e)))};this.handleScroll=()=>{this.current&&this.updateOverlay()};this.root=e,this.extraIgnore=t,this.onClick=n,this.overlay=document.createElement("div"),this.overlay.className="vz-highlight",this.overlay.style.display="none",this.root.appendChild(this.overlay)}setCurrent(e){this.current!==e&&(this.current=e)}start(){this.active||(this.active=!0,document.addEventListener("mousemove",this.handleMove,!0),document.addEventListener("click",this.handleClick,!0),document.addEventListener("scroll",this.handleScroll,!0),window.addEventListener("resize",this.handleScroll))}stop(){this.active&&(this.active=!1,document.removeEventListener("mousemove",this.handleMove,!0),document.removeEventListener("click",this.handleClick,!0),document.removeEventListener("scroll",this.handleScroll,!0),window.removeEventListener("resize",this.handleScroll),this.overlay.style.display="none",this.setCurrent(null))}shouldIgnore(e){if(e===document.documentElement||e===document.body||Y(e,Ie))return!0;for(let t of this.extraIgnore)try{if(Y(e,t))return!0}catch{}return!1}setPaused(e){this.paused=e,e&&(this.setCurrent(null),this.overlay.style.display="none")}isPaused(){return this.paused}updateOverlay(){if(!this.current)return;let e=this.current.getBoundingClientRect();if(e.width===0||e.height===0){this.overlay.style.display="none";return}this.overlay.style.display="block",this.overlay.style.left=e.left+"px",this.overlay.style.top=e.top+"px",this.overlay.style.width=e.width+"px",this.overlay.style.height=e.height+"px"}destroy(){this.stop(),this.overlay.remove()}};function pe(o){return typeof CSS<"u"&&typeof CSS.escape=="function"?CSS.escape(o):o.replace(/["\\]/g,"\\$&")}function Oe(o){let t=(o.replies??[]).map(n=>`
|
|
11
|
+
<div class="vz-reply" data-reply-id="${d(n.id)}">
|
|
12
|
+
${n.author?`<div class="vz-comment-author">${x(n.author)} <span class="vz-comment-author-name">${d(n.author.name||n.author.id||"User")}</span></div>`:""}
|
|
13
|
+
<div class="vz-reply-text">${d(n.text)}</div>
|
|
14
14
|
<div class="vz-comment-meta">
|
|
15
|
-
<span>${
|
|
16
|
-
<button class="vz-comment-delete" data-vz="delete-reply" data-comment-id="${
|
|
15
|
+
<span>${d(T(typeof n.createdAt=="number"?n.createdAt:new Date(n.createdAt).getTime()))}</span>
|
|
16
|
+
<button class="vz-comment-delete" data-vz="delete-reply" data-comment-id="${d(o.id)}" data-reply-id="${d(n.id)}">delete</button>
|
|
17
17
|
</div>
|
|
18
18
|
</div>
|
|
19
19
|
`).join("");return`
|
|
20
|
-
<div class="vz-replies" data-comment-id="${
|
|
20
|
+
<div class="vz-replies" data-comment-id="${d(o.id)}" hidden>
|
|
21
21
|
<div class="vz-reply-list">${t}</div>
|
|
22
22
|
<div class="vz-reply-form">
|
|
23
|
-
<textarea class="vz-reply-input" placeholder="Reply\u2026" rows="2" data-comment-id="${
|
|
24
|
-
<button class="vz-btn vz-btn-primary vz-btn-reply" data-vz="send-reply" data-id="${
|
|
23
|
+
<textarea class="vz-reply-input" placeholder="Reply\u2026" rows="2" data-comment-id="${d(o.id)}"></textarea>
|
|
24
|
+
<button class="vz-btn vz-btn-primary vz-btn-reply" data-vz="send-reply" data-id="${d(o.id)}">Send</button>
|
|
25
25
|
</div>
|
|
26
26
|
</div>
|
|
27
|
-
`}var q=class{constructor(e,t){this.currentUser=null;this.anchorTargets=[];this.anchorFingerprints=[];this.pendingRefs={};this.pendingAttachments=[];this.mentionPicker=null;this.mentionPickerDismiss=null;this.handleEditorKey=e=>{let t=e.currentTarget;if(e.key==="#"){e.preventDefault();let n=this.saveRange();this.callbacks.onStartReferencePick(i=>this.insertChip(i,n),()=>{t.focus(),this.restoreRange(n)});return}if(e.key==="Enter"&&(e.metaKey||e.ctrlKey)){e.preventDefault(),this.save();return}e.key==="Escape"&&(e.preventDefault(),this.callbacks.onClose())};this.handleEditorPaste=e=>{if(this.callbacks.canUploadAttachments()){let i=e.clipboardData?.items??null;if(i){let r=[];for(let s=0;s<i.length;s++){let a=i[s];if(a.kind==="file"&&a.type.startsWith("image/")){let l=a.getAsFile();l&&r.push(l)}}if(r.length>0){e.preventDefault();for(let s of r)this.uploadAndAppend(s);return}}}e.preventDefault();let t=e.clipboardData?.getData("text/plain")??"",n=window.getSelection();if(n&&n.rangeCount>0){let i=n.getRangeAt(0);i.deleteContents(),i.insertNode(document.createTextNode(t)),i.collapse(!1),n.removeAllRanges(),n.addRange(i)}};this.handleDragOver=e=>{if(!e.dataTransfer||!Array.from(e.dataTransfer.types).includes("Files"))return;e.preventDefault(),this.el.querySelector('[data-vz="drop-hint"]')?.removeAttribute("hidden")};this.handleDragLeave=e=>{let t=e.currentTarget;if(e.relatedTarget instanceof Node&&t.contains(e.relatedTarget))return;this.el.querySelector('[data-vz="drop-hint"]')?.setAttribute("hidden","")};this.handleDrop=e=>{if(e.preventDefault(),this.el.querySelector('[data-vz="drop-hint"]')?.setAttribute("hidden",""),!e.dataTransfer)return;let n=Array.from(e.dataTransfer.files).filter(i=>i.type.startsWith("image/"));for(let i of n)this.uploadAndAppend(i)};this.handleFileChange=e=>{let t=e.currentTarget,n=t.files?Array.from(t.files):[];for(let i of n)this.uploadAndAppend(i);t.value=""};this.handleClick=e=>{let t=e.target,n=t.closest('[data-vz="remove-anchor"]');if(n){e.stopPropagation();let s=n.getAttribute("data-fp-key");s&&this.removeAnchor(s);return}let i=t.closest('[data-vz="ref"]');if(i){let s=i.getAttribute("data-comment-id"),a=i.getAttribute("data-ref-id");s&&a&&(e.stopPropagation(),this.callbacks.onJumpToReference(s,a));return}let r=t.getAttribute("data-vz");if(r==="close")this.callbacks.onClose();else if(r==="save")this.save();else if(r==="mention")e.preventDefault(),this.openMentionPicker(t);else if(r==="upload")e.preventDefault(),this.el.querySelector('[data-vz="file-input"]')?.click();else if(r==="remove-attachment"){e.preventDefault();let s=t.getAttribute("data-attachment-id");s&&(this.pendingAttachments=this.pendingAttachments.filter(a=>a.id!==s),this.renderAttachmentList())}else if(r==="delete"){let s=t.getAttribute("data-id");s&&this.callbacks.onDelete(s)}else if(r==="toggle-reply"){e.preventDefault();let s=t.getAttribute("data-id");if(!s)return;let a=this.el.querySelector(`.vz-replies[data-comment-id="${pe(s)}"]`);if(!a)return;a.hasAttribute("hidden")?(a.removeAttribute("hidden"),a.querySelector(".vz-reply-input")?.focus()):a.setAttribute("hidden","")}else if(r==="send-reply"){e.preventDefault();let s=t.getAttribute("data-id");if(!s)return;let a=this.el.querySelector(`.vz-reply-input[data-comment-id="${pe(s)}"]`),l=a?.value.trim()??"";if(!l)return;this.callbacks.onAddReply(s,l),a&&(a.value="")}else if(r==="delete-reply"){e.preventDefault();let s=t.getAttribute("data-comment-id"),a=t.getAttribute("data-reply-id");s&&a&&this.callbacks.onDeleteReply(s,a)}};this.root=e,this.callbacks=t,this.el=document.createElement("div"),this.el.className="vz-popover",this.el.style.display="none",this.el.addEventListener("click",this.handleClick),this.root.appendChild(this.el)}setUser(e){this.currentUser=e}open(e,t,n){this.anchorTargets=[...e],this.anchorFingerprints=[...t],this.pendingRefs={},this.render(n),this.position()}update(e){this.anchorTargets.length&&(this.render(e),this.position())}addAnchor(e,t){let n=h(e);return this.anchorFingerprints.some(i=>h(i)===n)?!1:(this.anchorFingerprints.push(e),t&&this.anchorTargets.push(t),this.callbacks.onAnchorsChanged([...this.anchorFingerprints]),this.renderAnchorList(),this.updateHeader(),!0)}removeAnchor(e){if(this.anchorFingerprints.length<=1)return!1;let t=this.anchorFingerprints.findIndex(n=>h(n)===e);return t<0?!1:(this.anchorFingerprints.splice(t,1),this.anchorTargets.splice(t,1),this.callbacks.onAnchorsChanged([...this.anchorFingerprints]),this.renderAnchorList(),this.updateHeader(),!0)}updateHeader(){let e=this.el.querySelector(".vz-popover-header span:first-child");e&&(e.textContent=this.anchorFingerprints.length>1?`Comment on ${this.anchorFingerprints.length} elements`:"Comment")}reposition(){this.anchorTargets.length&&this.position()}close(){this.closeMentionPicker(),this.el.style.display="none",this.anchorTargets=[],this.anchorFingerprints=[],this.pendingRefs={}}isOpen(){return this.anchorTargets.length>0}getAnchors(){return[...this.anchorFingerprints]}getAnchorTargets(){return[...this.anchorTargets]}render(e){if(!this.anchorTargets.length)return;let t=this.anchorTargets[0],n=Array.from(t.classList).filter(m=>!m.startsWith("vz-")&&!m.startsWith("vizu-")).slice(0,2),i=t.tagName.toLowerCase()+(t.id?"#"+t.id:"")+(n.length?"."+n.join("."):""),r=(t.innerText||"").trim(),s=r.slice(0,60),a=this.currentUser?`<div class="vz-comment-author">${
|
|
27
|
+
`}var q=class{constructor(e,t){this.currentUser=null;this.anchorTargets=[];this.anchorFingerprints=[];this.pendingRefs={};this.pendingAttachments=[];this.mentionPicker=null;this.mentionPickerDismiss=null;this.mentionPickerUsers=[];this.mentionPickerFiltered=[];this.mentionPickerSelected=0;this.mentionPickerMode="manual";this.mentionPickerEditorRange=null;this.handleEditorKey=e=>{let t=e.currentTarget;if(this.mentionPicker&&this.mentionPickerMode==="editor"){if(e.key==="ArrowDown"){e.preventDefault(),this.moveMentionPickerSelection(1);return}if(e.key==="ArrowUp"){e.preventDefault(),this.moveMentionPickerSelection(-1);return}if((e.key==="Enter"||e.key==="Tab")&&this.mentionPickerFiltered.length>0){e.preventDefault(),this.commitMentionPickerSelection();return}if(e.key==="Escape"){e.preventDefault(),this.closeMentionPicker();return}}if(e.key==="#"){e.preventDefault();let n=this.saveRange();this.callbacks.onStartReferencePick(i=>this.insertChip(i,n),()=>{t.focus(),this.restoreRange(n)});return}if(e.key==="Enter"&&(e.metaKey||e.ctrlKey)){e.preventDefault(),this.save();return}e.key==="Escape"&&(e.preventDefault(),this.callbacks.onClose())};this.handleEditorInput=()=>{let e=this.detectMentionTrigger();e?this.openOrUpdateEditorMentionPicker(e.range,e.query):this.mentionPicker&&this.mentionPickerMode==="editor"&&this.closeMentionPicker()};this.handleEditorPaste=e=>{if(this.callbacks.canUploadAttachments()){let i=e.clipboardData?.items??null;if(i){let r=[];for(let s=0;s<i.length;s++){let a=i[s];if(a.kind==="file"&&a.type.startsWith("image/")){let l=a.getAsFile();l&&r.push(l)}}if(r.length>0){e.preventDefault();for(let s of r)this.uploadAndAppend(s);return}}}e.preventDefault();let t=e.clipboardData?.getData("text/plain")??"",n=window.getSelection();if(n&&n.rangeCount>0){let i=n.getRangeAt(0);i.deleteContents(),i.insertNode(document.createTextNode(t)),i.collapse(!1),n.removeAllRanges(),n.addRange(i)}};this.handleDragOver=e=>{if(!e.dataTransfer||!Array.from(e.dataTransfer.types).includes("Files"))return;e.preventDefault(),this.el.querySelector('[data-vz="drop-hint"]')?.removeAttribute("hidden")};this.handleDragLeave=e=>{let t=e.currentTarget;if(e.relatedTarget instanceof Node&&t.contains(e.relatedTarget))return;this.el.querySelector('[data-vz="drop-hint"]')?.setAttribute("hidden","")};this.handleDrop=e=>{if(e.preventDefault(),this.el.querySelector('[data-vz="drop-hint"]')?.setAttribute("hidden",""),!e.dataTransfer)return;let n=Array.from(e.dataTransfer.files).filter(i=>i.type.startsWith("image/"));for(let i of n)this.uploadAndAppend(i)};this.handleFileChange=e=>{let t=e.currentTarget,n=t.files?Array.from(t.files):[];for(let i of n)this.uploadAndAppend(i);t.value=""};this.handleClick=e=>{let t=e.target,n=t.closest('[data-vz="remove-anchor"]');if(n){e.stopPropagation();let s=n.getAttribute("data-fp-key");s&&this.removeAnchor(s);return}let i=t.closest('[data-vz="ref"]');if(i){let s=i.getAttribute("data-comment-id"),a=i.getAttribute("data-ref-id");s&&a&&(e.stopPropagation(),this.callbacks.onJumpToReference(s,a));return}let r=t.getAttribute("data-vz");if(r==="close")this.callbacks.onClose();else if(r==="save")this.save();else if(r==="mention")e.preventDefault(),this.openMentionPicker(t);else if(r==="upload")e.preventDefault(),this.el.querySelector('[data-vz="file-input"]')?.click();else if(r==="remove-attachment"){e.preventDefault();let s=t.getAttribute("data-attachment-id");s&&(this.pendingAttachments=this.pendingAttachments.filter(a=>a.id!==s),this.renderAttachmentList())}else if(r==="delete"){let s=t.getAttribute("data-id");s&&this.callbacks.onDelete(s)}else if(r==="toggle-reply"){e.preventDefault();let s=t.getAttribute("data-id");if(!s)return;let a=this.el.querySelector(`.vz-replies[data-comment-id="${pe(s)}"]`);if(!a)return;a.hasAttribute("hidden")?(a.removeAttribute("hidden"),a.querySelector(".vz-reply-input")?.focus()):a.setAttribute("hidden","")}else if(r==="send-reply"){e.preventDefault();let s=t.getAttribute("data-id");if(!s)return;let a=this.el.querySelector(`.vz-reply-input[data-comment-id="${pe(s)}"]`),l=a?.value.trim()??"";if(!l)return;this.callbacks.onAddReply(s,l),a&&(a.value="")}else if(r==="delete-reply"){e.preventDefault();let s=t.getAttribute("data-comment-id"),a=t.getAttribute("data-reply-id");s&&a&&this.callbacks.onDeleteReply(s,a)}};this.root=e,this.callbacks=t,this.el=document.createElement("div"),this.el.className="vz-popover",this.el.style.display="none",this.el.addEventListener("click",this.handleClick),this.root.appendChild(this.el)}setUser(e){this.currentUser=e}open(e,t,n){this.anchorTargets=[...e],this.anchorFingerprints=[...t],this.pendingRefs={},this.render(n),this.position()}update(e){this.anchorTargets.length&&(this.render(e),this.position())}addAnchor(e,t){let n=h(e);return this.anchorFingerprints.some(i=>h(i)===n)?!1:(this.anchorFingerprints.push(e),t&&this.anchorTargets.push(t),this.callbacks.onAnchorsChanged([...this.anchorFingerprints]),this.renderAnchorList(),this.updateHeader(),!0)}removeAnchor(e){if(this.anchorFingerprints.length<=1)return!1;let t=this.anchorFingerprints.findIndex(n=>h(n)===e);return t<0?!1:(this.anchorFingerprints.splice(t,1),this.anchorTargets.splice(t,1),this.callbacks.onAnchorsChanged([...this.anchorFingerprints]),this.renderAnchorList(),this.updateHeader(),!0)}updateHeader(){let e=this.el.querySelector(".vz-popover-header span:first-child");e&&(e.textContent=this.anchorFingerprints.length>1?`Comment on ${this.anchorFingerprints.length} elements`:"Comment")}reposition(){this.anchorTargets.length&&this.position()}close(){this.closeMentionPicker(),this.el.style.display="none",this.anchorTargets=[],this.anchorFingerprints=[],this.pendingRefs={}}isOpen(){return this.anchorTargets.length>0}getAnchors(){return[...this.anchorFingerprints]}getAnchorTargets(){return[...this.anchorTargets]}render(e){if(!this.anchorTargets.length)return;let t=this.anchorTargets[0],n=Array.from(t.classList).filter(m=>!m.startsWith("vz-")&&!m.startsWith("vizu-")).slice(0,2),i=t.tagName.toLowerCase()+(t.id?"#"+t.id:"")+(n.length?"."+n.join("."):""),r=(t.innerText||"").trim(),s=r.slice(0,60),a=this.currentUser?`<div class="vz-comment-author">${x(this.currentUser)} <span class="vz-comment-author-name">${d(this.currentUser.name)}</span> <span>\xB7 you</span></div>`:"";this.el.innerHTML=`
|
|
28
28
|
<div class="vz-popover-header">
|
|
29
29
|
<span>${this.anchorFingerprints.length>1?`Comment on ${this.anchorFingerprints.length} elements`:"Comment"}</span>
|
|
30
30
|
<button class="vz-popover-close" data-vz="close" aria-label="Close">\xD7</button>
|
|
31
31
|
</div>
|
|
32
|
-
<div class="vz-target-label">${
|
|
32
|
+
<div class="vz-target-label">${d(i)}${s?" \xB7 “"+d(s)+(r.length>60?"\u2026":"")+"”":""}</div>
|
|
33
33
|
<div class="vz-anchor-list-wrap"></div>
|
|
34
34
|
<div class="vz-comment-list">
|
|
35
35
|
${e.length===0?'<div class="vz-empty">No comments yet.</div>':e.map(m=>`
|
|
36
|
-
<div class="vz-comment" data-comment-id="${
|
|
37
|
-
${m.author?`<div class="vz-comment-author">${
|
|
38
|
-
<div>${
|
|
39
|
-
${
|
|
36
|
+
<div class="vz-comment" data-comment-id="${d(m.id)}">
|
|
37
|
+
${m.author?`<div class="vz-comment-author">${x(m.author)} <span class="vz-comment-author-name">${d(m.author.name||m.author.id||"User")}</span></div>`:""}
|
|
38
|
+
<div>${F(m.text,m.id)}</div>
|
|
39
|
+
${H(m.attachments)}
|
|
40
40
|
<div class="vz-comment-meta">
|
|
41
|
-
<span>${
|
|
41
|
+
<span>${d(T(m.createdAt))}</span>
|
|
42
42
|
<span class="vz-comment-actions">
|
|
43
|
-
<button class="vz-comment-reply-toggle" data-vz="toggle-reply" data-id="${
|
|
44
|
-
<button class="vz-comment-delete" data-vz="delete" data-id="${
|
|
43
|
+
<button class="vz-comment-reply-toggle" data-vz="toggle-reply" data-id="${d(m.id)}">${(m.replies?.length??0)>0?`${m.replies.length} ${m.replies.length===1?"reply":"replies"}`:"reply"}</button>
|
|
44
|
+
<button class="vz-comment-delete" data-vz="delete" data-id="${d(m.id)}">delete</button>
|
|
45
45
|
</span>
|
|
46
46
|
</div>
|
|
47
47
|
${Oe(m)}
|
|
@@ -61,18 +61,23 @@
|
|
|
61
61
|
<button class="vz-btn vz-btn-primary" data-vz="save">Save</button>
|
|
62
62
|
</div>
|
|
63
63
|
<input type="file" class="vz-file-input" data-vz="file-input" accept="image/png,image/jpeg,image/webp,image/gif,image/svg+xml" hidden />
|
|
64
|
-
`,this.el.style.display="flex",this.renderAnchorList(),this.renderAttachmentList();let l=this.el.querySelector(".vz-textarea");l.focus(),l.addEventListener("keydown",this.handleEditorKey),l.addEventListener("paste",this.handleEditorPaste);let
|
|
64
|
+
`,this.el.style.display="flex",this.renderAnchorList(),this.renderAttachmentList();let l=this.el.querySelector(".vz-textarea");l.focus(),l.addEventListener("keydown",this.handleEditorKey),l.addEventListener("input",this.handleEditorInput),l.addEventListener("paste",this.handleEditorPaste);let c=this.el.querySelector(".vz-editor-wrap");c&&this.callbacks.canUploadAttachments()&&(c.addEventListener("dragover",this.handleDragOver),c.addEventListener("dragleave",this.handleDragLeave),c.addEventListener("drop",this.handleDrop));let p=this.el.querySelector('[data-vz="file-input"]');p&&p.addEventListener("change",this.handleFileChange)}renderAnchorList(){let e=this.el.querySelector(".vz-anchor-list-wrap");if(!e)return;if(this.anchorFingerprints.length<=1){e.innerHTML="";return}let t=this.anchorFingerprints.map(n=>{let i=h(n),r=$(n);return`<span class="vz-anchor-chip" data-fp-key="${d(i)}">${d(r)}<button class="vz-anchor-chip-remove" data-vz="remove-anchor" data-fp-key="${d(i)}" aria-label="Remove anchor">\xD7</button></span>`}).join("");e.innerHTML=`
|
|
65
65
|
<div class="vz-anchor-hint">Anchored to ${this.anchorFingerprints.length} elements:</div>
|
|
66
66
|
<div class="vz-anchor-list">${t}</div>
|
|
67
|
-
`}async uploadAndAppend(e){if(!this.callbacks.canUploadAttachments())return;let t="upload-"+Math.random().toString(36).slice(2,9),n={id:t,url:"",mimeType:e.type||"application/octet-stream",sizeBytes:e.size,uploadedAt:Date.now(),filename:e.name};this.pendingAttachments.push(n),this.renderAttachmentList(t);try{let i=await this.callbacks.onUploadAttachment(e),r=this.pendingAttachments.findIndex(s=>s.id===t);r>=0?this.pendingAttachments[r]=i:this.pendingAttachments.push(i),this.renderAttachmentList()}catch(i){this.pendingAttachments=this.pendingAttachments.filter(r=>r.id!==t),this.renderAttachmentList(),typeof console<"u"&&console.warn("[vizu] attachment upload failed",i)}}renderAttachmentList(e){let t=this.el.querySelector('[data-vz="attachment-list"]');if(t){if(this.pendingAttachments.length===0){t.innerHTML="";return}t.innerHTML=this.pendingAttachments.map(n=>{let r=n.id===e||n.url===""?'<div class="vz-attachment-thumb vz-attachment-thumb-loading">\u2026</div>':n.mimeType.startsWith("image/")?`<img class="vz-attachment-thumb" src="${
|
|
68
|
-
<div class="vz-attachment-item" data-attachment-id="${
|
|
67
|
+
`}detectMentionTrigger(){let e=window.getSelection();if(!e||e.rangeCount===0||!e.isCollapsed)return null;let t=e.getRangeAt(0),n=t.startContainer;if(n.nodeType!==Node.TEXT_NODE)return null;let i=n.textContent??"",r=t.startOffset;for(let s=r-1;s>=0;s--){let a=i[s];if(a==="@"){let l=s>0?i[s-1]:"";if(l!==""&&!/\s/.test(l))return null;let c=i.slice(s+1,r);if(/\s/.test(c))return null;let p=document.createRange();return p.setStart(n,s),p.setEnd(n,r),{range:p,query:c}}if(/\s/.test(a))return null}return null}async uploadAndAppend(e){if(!this.callbacks.canUploadAttachments())return;let t="upload-"+Math.random().toString(36).slice(2,9),n={id:t,url:"",mimeType:e.type||"application/octet-stream",sizeBytes:e.size,uploadedAt:Date.now(),filename:e.name};this.pendingAttachments.push(n),this.renderAttachmentList(t);try{let i=await this.callbacks.onUploadAttachment(e),r=this.pendingAttachments.findIndex(s=>s.id===t);r>=0?this.pendingAttachments[r]=i:this.pendingAttachments.push(i),this.renderAttachmentList()}catch(i){this.pendingAttachments=this.pendingAttachments.filter(r=>r.id!==t),this.renderAttachmentList(),typeof console<"u"&&console.warn("[vizu] attachment upload failed",i)}}renderAttachmentList(e){let t=this.el.querySelector('[data-vz="attachment-list"]');if(t){if(this.pendingAttachments.length===0){t.innerHTML="";return}t.innerHTML=this.pendingAttachments.map(n=>{let r=n.id===e||n.url===""?'<div class="vz-attachment-thumb vz-attachment-thumb-loading">\u2026</div>':n.mimeType.startsWith("image/")?`<img class="vz-attachment-thumb" src="${d(n.url)}" alt="${d(n.filename??"")}" />`:'<div class="vz-attachment-thumb">\u{1F4C4}</div>';return`
|
|
68
|
+
<div class="vz-attachment-item" data-attachment-id="${d(n.id)}">
|
|
69
69
|
${r}
|
|
70
|
-
<button class="vz-attachment-remove" data-vz="remove-attachment" data-attachment-id="${
|
|
70
|
+
<button class="vz-attachment-remove" data-vz="remove-attachment" data-attachment-id="${d(n.id)}" aria-label="Remove attachment">\xD7</button>
|
|
71
71
|
</div>
|
|
72
|
-
`}).join("")}}saveRange(){let e=window.getSelection();return!e||e.rangeCount===0?null:e.getRangeAt(0).cloneRange()}restoreRange(e){if(!e)return;let t=window.getSelection();t?.removeAllRanges(),t?.addRange(e)}insertChip(e,t){let n=ie();this.pendingRefs[n]=e;let i
|
|
72
|
+
`}).join("")}}saveRange(){let e=window.getSelection();return!e||e.rangeCount===0?null:e.getRangeAt(0).cloneRange()}restoreRange(e){if(!e)return;let t=window.getSelection();t?.removeAllRanges(),t?.addRange(e)}insertChip(e,t){let n=ie();this.pendingRefs[n]=e;let i=$(e),r=document.createElement("span");r.className="vz-chip",r.contentEditable="false",r.setAttribute("data-ref-id",n),r.textContent=i;let s=this.el.querySelector(".vz-textarea");s.focus(),this.restoreRange(t);let a=window.getSelection();if(!a||a.rangeCount===0){s.appendChild(r),s.appendChild(document.createTextNode(" ")),this.moveCursorToEnd(s);return}let l=a.getRangeAt(0);l.deleteContents(),l.insertNode(r);let c=document.createTextNode(" ");r.after(c);let p=document.createRange();p.setStartAfter(c),p.collapse(!0),a.removeAllRanges(),a.addRange(p)}moveCursorToEnd(e){let t=document.createRange();t.selectNodeContents(e),t.collapse(!1);let n=window.getSelection();n?.removeAllRanges(),n?.addRange(t)}serializeEditor(e){let t=[],n=i=>{if(i.nodeType===Node.TEXT_NODE){t.push(i.textContent||"");return}if(i.nodeType!==Node.ELEMENT_NODE)return;let r=i;if(r.classList.contains("vz-chip")){let s=r.getAttribute("data-mention-id");if(s){let c=r.textContent||"",p=c.startsWith("@")?c.slice(1):c;t.push(`[@${p}](vizu-mention:${s})`);return}let a=r.getAttribute("data-ref-id"),l=r.textContent||"";a&&t.push(`[#${l}](vizu-ref:${a})`);return}if(r.tagName==="BR"){t.push(`
|
|
73
73
|
`);return}r.tagName==="DIV"&&t.length&&!t[t.length-1].endsWith(`
|
|
74
74
|
`)&&t.push(`
|
|
75
|
-
`);for(let s of r.childNodes)n(s)};for(let i of e.childNodes)n(i);return t.join("").trim()}async openMentionPicker(e){if(this.closeMentionPicker(),!this.callbacks.onMentionSearch)return;
|
|
75
|
+
`);for(let s of r.childNodes)n(s)};for(let i of e.childNodes)n(i);return t.join("").trim()}async openMentionPicker(e){if(this.closeMentionPicker(),!this.callbacks.onMentionSearch)return;this.mentionPickerMode="manual",this.mentionPickerEditorRange=null;let t=this.createMentionPickerShell();this.positionMentionPickerByElement(t,e);let n=await this.fetchMentionables();if(this.mentionPicker!==t)return;this.mentionPickerUsers=n,this.renderMentionPickerBody(""),this.positionMentionPickerByElement(t,e),t.querySelector(".vz-mention-search")?.focus(),this.wireMentionPickerDismiss()}async openOrUpdateEditorMentionPicker(e,t){if(this.mentionPickerEditorRange=e,!this.mentionPicker||this.mentionPickerMode!=="editor"){this.closeMentionPicker(),this.mentionPickerMode="editor";let n=this.createMentionPickerShell({showSearch:!1});this.positionMentionPickerByRange(n,e);let i=await this.fetchMentionables();if(this.mentionPicker!==n)return;this.mentionPickerUsers=i,this.renderMentionPickerBody(t),this.positionMentionPickerByRange(n,e),this.wireMentionPickerDismiss();return}this.renderMentionPickerBody(t),this.positionMentionPickerByRange(this.mentionPicker,e)}createMentionPickerShell({showSearch:e=!0}={}){let t=document.createElement("div");if(t.className="vz-mention-picker",t.setAttribute("data-vz-ignore",""),t.innerHTML=`
|
|
76
|
+
${e?'<input class="vz-mention-search" type="text" placeholder="Search teammates\u2026" autocomplete="off" spellcheck="false" />':""}
|
|
77
|
+
<div class="vz-mention-list" data-vz="mention-list">
|
|
78
|
+
<div class="vz-mention-loading">Loading\u2026</div>
|
|
79
|
+
</div>
|
|
80
|
+
`,this.el.appendChild(t),this.mentionPicker=t,this.mentionPickerFiltered=[],this.mentionPickerSelected=0,t.addEventListener("mousemove",n=>{let i=n.target?.closest(".vz-mention-item");if(!i)return;let r=Number(i.getAttribute("data-mention-index")??-1);r>=0&&r!==this.mentionPickerSelected&&(this.mentionPickerSelected=r,this.refreshMentionPickerHighlight())}),t.addEventListener("click",n=>{let i=n.target?.closest(".vz-mention-item");if(!i)return;n.preventDefault(),n.stopPropagation();let r=Number(i.getAttribute("data-mention-index")??-1);r>=0&&(this.mentionPickerSelected=r,this.commitMentionPickerSelection())}),e){let n=t.querySelector(".vz-mention-search");n?.addEventListener("input",()=>this.renderMentionPickerBody(n.value)),n?.addEventListener("keydown",i=>{i.key==="ArrowDown"?(i.preventDefault(),this.moveMentionPickerSelection(1)):i.key==="ArrowUp"?(i.preventDefault(),this.moveMentionPickerSelection(-1)):i.key==="Enter"||i.key==="Tab"?this.mentionPickerFiltered.length>0&&(i.preventDefault(),this.commitMentionPickerSelection()):i.key==="Escape"&&(i.preventDefault(),this.closeMentionPicker())})}return t}async fetchMentionables(){if(!this.callbacks.onMentionSearch)return[];try{return await this.callbacks.onMentionSearch()}catch{return[]}}renderMentionPickerBody(e){let t=this.mentionPicker;if(!t)return;let n=t.querySelector('[data-vz="mention-list"]');if(!n)return;let i=e.toLowerCase().trim();if(this.mentionPickerFiltered=i?this.mentionPickerUsers.filter(r=>r.name.toLowerCase().includes(i)):this.mentionPickerUsers.slice(),this.mentionPickerSelected=0,this.mentionPickerFiltered.length===0){n.innerHTML=`<div class="vz-mention-empty">${this.mentionPickerUsers.length===0?"No teammates to mention yet.":"No teammates match."}</div>`;return}n.innerHTML=this.mentionPickerFiltered.map((r,s)=>{let a=r.avatarUrl?`<img class="vz-mention-avatar" src="${d(r.avatarUrl)}" alt="" />`:`<span class="vz-mention-avatar vz-mention-avatar-initials">${d(Z(r.name)||"?")}</span>`;return`<button class="vz-mention-item${s===0?" is-selected":""}" type="button" data-mention-index="${s}">${a}<span class="vz-mention-item-name">${d(r.name)}</span></button>`}).join("")}moveMentionPickerSelection(e){if(this.mentionPickerFiltered.length===0)return;let t=this.mentionPickerFiltered.length;this.mentionPickerSelected=(this.mentionPickerSelected+e+t)%t,this.refreshMentionPickerHighlight()}refreshMentionPickerHighlight(){let e=this.mentionPicker;if(!e)return;let t=e.querySelectorAll(".vz-mention-item");t.forEach((n,i)=>n.classList.toggle("is-selected",i===this.mentionPickerSelected)),t[this.mentionPickerSelected]?.scrollIntoView({block:"nearest"})}commitMentionPickerSelection(){let e=this.mentionPickerFiltered[this.mentionPickerSelected];e&&(this.mentionPickerMode==="editor"&&this.mentionPickerEditorRange?this.replaceRangeWithMentionChip(this.mentionPickerEditorRange,e):this.insertMentionChip(e),this.closeMentionPicker())}replaceRangeWithMentionChip(e,t){let n=this.el.querySelector(".vz-textarea");if(!n)return;let i=document.createElement("span");i.className="vz-chip vz-chip-mention",i.contentEditable="false",i.setAttribute("data-mention-id",t.id),i.textContent="@"+t.name,e.deleteContents(),e.insertNode(i);let r=document.createTextNode(" ");i.after(r);let s=window.getSelection();if(s){let a=document.createRange();a.setStartAfter(r),a.collapse(!0),s.removeAllRanges(),s.addRange(a)}n.focus()}wireMentionPickerDismiss(){if(this.mentionPickerDismiss)return;let e=n=>{n.key==="Escape"&&(n.preventDefault(),this.closeMentionPicker())},t=n=>{if(this.mentionPicker&&!this.mentionPicker.contains(n.target)){if(this.mentionPickerMode==="editor"){let i=this.el.querySelector(".vz-textarea");if(i&&i.contains(n.target))return}this.closeMentionPicker()}};document.addEventListener("keydown",e,!0),document.addEventListener("mousedown",t,!0),this.mentionPickerDismiss=()=>{document.removeEventListener("keydown",e,!0),document.removeEventListener("mousedown",t,!0)}}closeMentionPicker(){this.mentionPickerDismiss&&(this.mentionPickerDismiss(),this.mentionPickerDismiss=null),this.mentionPicker&&(this.mentionPicker.remove(),this.mentionPicker=null),this.mentionPickerFiltered=[],this.mentionPickerSelected=0,this.mentionPickerEditorRange=null}positionMentionPickerByElement(e,t){let n=this.el.getBoundingClientRect(),i=t.getBoundingClientRect();e.style.left=`${i.left-n.left}px`,e.style.top=`${i.bottom-n.top+4}px`}positionMentionPickerByRange(e,t){let n=this.el.getBoundingClientRect(),i=t.getBoundingClientRect();e.style.left=`${i.left-n.left}px`,e.style.top=`${i.bottom-n.top+4}px`}insertMentionChip(e){if(!e||!e.id)return;let t=this.el.querySelector(".vz-textarea");if(!t)return;let n=document.createElement("span");n.className="vz-chip vz-chip-mention",n.contentEditable="false",n.setAttribute("data-mention-id",e.id),n.textContent="@"+e.name,t.focus();let i=window.getSelection();if(!i||i.rangeCount===0){t.appendChild(n),t.appendChild(document.createTextNode(" ")),this.moveCursorToEnd(t);return}let r=i.getRangeAt(0);r.deleteContents(),r.insertNode(n);let s=document.createTextNode(" ");n.after(s);let a=document.createRange();a.setStartAfter(s),a.collapse(!0),i.removeAllRanges(),i.addRange(a)}save(){let e=this.el.querySelector(".vz-textarea");if(!e)return;let t=this.serializeEditor(e),n=this.pendingAttachments.filter(c=>c.url).length;if(!t&&n===0)return;let i={},r=/\[#[^\]]+\]\(vizu-ref:([a-zA-Z0-9_-]+)\)/g,s;for(;(s=r.exec(t))!==null;){let c=s[1];this.pendingRefs[c]&&(i[c]=this.pendingRefs[c])}let a=re(t),l=this.pendingAttachments.filter(c=>c.url);this.callbacks.onAdd(t,[...this.anchorFingerprints],Object.keys(i).length?i:void 0,a.length?a:void 0,l.length?l:void 0),e.innerHTML="",this.pendingRefs={},this.pendingAttachments=[],this.renderAttachmentList()}position(){let e=this.anchorTargets[0];if(!e)return;let t=e.getBoundingClientRect(),n=this.el.getBoundingClientRect(),i=8,r=t.bottom+i,s=t.left;r+n.height>window.innerHeight-i&&(r=t.top-n.height-i,r<i&&(r=i)),s+n.width>window.innerWidth-i&&(s=window.innerWidth-n.width-i),s<i&&(s=i),this.el.style.top=r+"px",this.el.style.left=s+"px"}destroy(){this.closeMentionPicker(),this.el.removeEventListener("click",this.handleClick),this.el.remove()}};var K=class{constructor(e,t){this.sidebarOpen=!1;this.actions=[];this.commentsCount=0;this.user=null;this.multiSelectMode=!1;this.selectionCount=0;this.handleClick=e=>{let t=e.target.closest("[data-vz]");if(!t)return;let n=t.getAttribute("data-vz");if(n==="sidebar")this.callbacks.onToggleSidebar();else if(n==="disable")this.callbacks.onDisable();else if(n==="multi")this.callbacks.onToggleMultiSelect();else if(n==="commit-selection")this.callbacks.onCommitSelection();else if(n==="clear-selection")this.callbacks.onClearSelection();else if(n==="action"){let i=t.getAttribute("data-id");i&&this.callbacks.onAction(i)}};this.root=e,this.callbacks=t,this.el=document.createElement("div"),this.el.className="vz-pill",this.el.style.display="none",this.el.addEventListener("click",this.handleClick),this.root.appendChild(this.el)}setActions(e){this.actions=e,this.render()}setComments(e){this.commentsCount=e.length,this.render()}setUser(e){this.user=e,this.render()}setSidebarOpen(e){this.sidebarOpen=e;let t=this.el.querySelector('[data-vz="sidebar"]');t&&t.classList.toggle("is-active",e)}setMultiSelectState(e,t){this.multiSelectMode=e,this.selectionCount=t,this.el.classList.toggle("is-selecting",e&&t>0),this.render()}show(){this.el.style.display="flex",this.render()}hide(){this.el.style.display="none"}render(){if(this.el.style.display==="none")return;let e=this.actions.filter(s=>s.visibleWhen?s.visibleWhen({commentsCount:this.commentsCount}):!0),t=this.user?`<div class="vz-pill-user" title="${d(this.user.name)}">${x(this.user,"vz-pill-user-avatar")} ${d(this.user.name.split(" ")[0])}</div><span class="vz-pill-divider"></span>`:"",n=e.length?e.map(s=>`<button class="vz-pill-btn${s.variant==="primary"?" primary":""}" data-vz="action" data-id="${d(s.id)}"${s.title?` title="${d(s.title)}"`:""}>${d(s.label)}</button>`).join("")+'<span class="vz-pill-divider"></span>':"",i="";if(this.multiSelectMode&&this.selectionCount>0)i=`
|
|
76
81
|
<span class="vz-pill-count-btn is-active" title="Selected ${this.selectionCount} element${this.selectionCount===1?"":"s"}">${this.selectionCount} selected</span>
|
|
77
82
|
<button class="vz-pill-btn primary" data-vz="commit-selection">Comment on ${this.selectionCount} \u2192</button>
|
|
78
83
|
<button class="vz-pill-btn" data-vz="clear-selection">Cancel</button>
|
|
@@ -86,48 +91,48 @@
|
|
|
86
91
|
<span class="vz-pill-dot" aria-hidden="true"></span>
|
|
87
92
|
<span class="vz-pill-label">Vizu</span>
|
|
88
93
|
${i}
|
|
89
|
-
<button class="vz-pill-toggle ${this.multiSelectMode?"is-active":""}" data-vz="multi" title="${
|
|
94
|
+
<button class="vz-pill-toggle ${this.multiSelectMode?"is-active":""}" data-vz="multi" title="${d(r)}" aria-label="${d(r)}">
|
|
90
95
|
<svg viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.6"><rect x="1" y="1" width="5" height="5"/><rect x="8" y="1" width="5" height="5"/><rect x="1" y="8" width="5" height="5"/><rect x="8" y="8" width="5" height="5"/></svg>
|
|
91
96
|
</button>
|
|
92
97
|
<button class="vz-pill-icon-btn" data-vz="disable" title="Disable Vizu (toggle with shortcut)" aria-label="Disable">\xD7</button>
|
|
93
|
-
`}toast(e){let t=document.createElement("div");t.className="vz-toast",t.textContent=e,this.root.appendChild(t),setTimeout(()=>t.remove(),1800)}destroy(){this.el.removeEventListener("click",this.handleClick),this.el.remove()}};var J=class{constructor(e,t){this.open=!1;this.filter="open";this.lastComments=[];this.lastConfidence=new Map;this.handleClick=e=>{let t=e.target,n=t.closest('[data-vz="ref"]');if(n){e.stopPropagation();let
|
|
98
|
+
`}toast(e){let t=document.createElement("div");t.className="vz-toast",t.textContent=e,this.root.appendChild(t),setTimeout(()=>t.remove(),1800)}destroy(){this.el.removeEventListener("click",this.handleClick),this.el.remove()}};var J=class{constructor(e,t){this.open=!1;this.filter="open";this.lastComments=[];this.lastConfidence=new Map;this.handleClick=e=>{let t=e.target,n=t.closest('[data-vz="ref"]');if(n){e.stopPropagation();let c=n.getAttribute("data-comment-id"),p=n.getAttribute("data-ref-id");c&&p&&this.callbacks.onJumpToReference(c,p);return}let i=t.closest('[data-vz="filter"]');if(i){e.stopPropagation();let c=i.getAttribute("data-filter");c&&c!==this.filter&&(this.filter=c,this.render(this.lastComments));return}let r=t.closest('[data-vz="status"]');if(r){e.stopPropagation();let c=r.getAttribute("data-id"),p=r.getAttribute("data-status");c&&p&&this.callbacks.onUpdateStatus(c,p);return}let s=t.closest('[data-vz="delete"]');if(s){e.stopPropagation();let c=s.getAttribute("data-id");c&&this.callbacks.onDelete(c);return}if(t.closest('[data-vz="close"]')){this.callbacks.onClose();return}let a=t.closest('[data-vz="jump-fp"]');if(a&&a.__fp){e.stopPropagation(),this.callbacks.onJumpTo(a.__fp);return}let l=t.closest('[data-vz="jump"]');l&&l.__group&&this.callbacks.onJumpTo(l.__group.fp)};this.root=e,this.callbacks=t,this.el=document.createElement("div"),this.el.className="vz-sidebar",this.el.addEventListener("click",this.handleClick),this.root.appendChild(this.el)}toggle(e,t){this.open?this.close():this.show(e,t)}show(e,t){this.render(e,t),requestAnimationFrame(()=>this.el.classList.add("is-open")),this.open=!0}update(e,t){this.open&&this.render(e,t)}close(){this.el.classList.remove("is-open"),this.open=!1}isOpen(){return this.open}render(e,t){this.lastComments=e,t&&(this.lastConfidence=new Map(t));let n=this.lastConfidence,i=e.filter(u=>n.get(u.id)==="orphaned"),r=e.filter(u=>n.get(u.id)!=="orphaned"),s=this.filter==="open"?r.filter(u=>(u.status??"open")==="open"):r,a=this.filter==="open"?[]:i,l=De(e);if(s.length===0&&a.length===0){let u=this.filter==="open"&&e.length>0?`<strong>Nothing open.</strong>${l.resolved+l.wontfix} closed comment${l.resolved+l.wontfix===1?"":"s"} hidden. Switch to <em>All</em> to see them.`:"<strong>No comments yet</strong>Click any element on the page to leave one.";this.el.innerHTML=`
|
|
94
99
|
${this.headerHtml(e.length,l)}
|
|
95
100
|
<div class="vz-sidebar-body">
|
|
96
101
|
<div class="vz-sidebar-empty">${u}</div>
|
|
97
102
|
</div>
|
|
98
|
-
`;return}let
|
|
103
|
+
`;return}let c=new Map;for(let u of s){let k=(u.fingerprints||[])[0];if(!k)continue;let g=h(k),w=c.get(g);w?w.comments.push(u):c.set(g,{fp:k,comments:[u]})}let p=[],m=0;for(let u of c.values()){let M=u.fp.tagName.toLowerCase()+(u.fp.attributes.id?"#"+u.fp.attributes.id:""),k=u.fp.textSnippet.slice(0,60);p.push(`
|
|
99
104
|
<div class="vz-sidebar-group" data-group="${m}">
|
|
100
105
|
<div class="vz-sidebar-group-head">
|
|
101
|
-
${
|
|
102
|
-
${
|
|
106
|
+
${d(M)}
|
|
107
|
+
${k?`<div class="vz-sidebar-group-text">“${d(k)}${u.fp.textSnippet.length>60?"\u2026":""}”</div>`:""}
|
|
103
108
|
</div>
|
|
104
|
-
${u.comments.map(g=>{let w=g.status??"open",te=(g.fingerprints||[]).slice(1),
|
|
105
|
-
<div class="vz-sidebar-item ${w!=="open"?"vz-sidebar-item-dim":""}" data-comment-id="${
|
|
109
|
+
${u.comments.map(g=>{let w=g.status??"open",te=(g.fingerprints||[]).slice(1),fe=te.length?`<div class="vz-anchor-list" style="margin-top:6px">${te.map((ne,be)=>`<span class="vz-anchor-chip" data-vz="jump-fp" data-fp-key="${d(h(ne))}" title="Jump to this element">+${be+2}/${(g.fingerprints||[]).length} ${d($(ne))}</span>`).join("")}</div>`:"",ve=n.get(g.id)==="drifted"?'<span class="vz-status-pill vz-status-drifted" title="Anchored via a fallback match \u2014 the original element may have moved or changed.">drifted</span>':"";return`
|
|
110
|
+
<div class="vz-sidebar-item ${w!=="open"?"vz-sidebar-item-dim":""}" data-comment-id="${d(g.id)}" data-vz="jump" data-group="${m}" role="button" tabindex="0">
|
|
106
111
|
<div class="vz-sidebar-item-row">
|
|
107
|
-
${g.author?`<div class="vz-sidebar-item-author">${
|
|
108
|
-
${
|
|
112
|
+
${g.author?`<div class="vz-sidebar-item-author">${x(g.author)} <span class="vz-sidebar-item-author-name">${d(g.author.name)}</span></div>`:""}
|
|
113
|
+
${ve}
|
|
109
114
|
${w!=="open"?`<span class="vz-status-pill vz-status-${w}">${w}</span>`:""}
|
|
110
115
|
</div>
|
|
111
|
-
<span class="vz-sidebar-item-text">${
|
|
112
|
-
${
|
|
113
|
-
${
|
|
116
|
+
<span class="vz-sidebar-item-text">${F(g.text,g.id)}</span>
|
|
117
|
+
${H(g.attachments)}
|
|
118
|
+
${fe}
|
|
114
119
|
<div class="vz-sidebar-item-meta">
|
|
115
|
-
<span>${
|
|
120
|
+
<span>${d(T(g.createdAt))}${(g.fingerprints||[]).length>1?` \xB7 grouped (${(g.fingerprints||[]).length})`:""}${(g.replies?.length??0)>0?` \xB7 ${g.replies.length} ${g.replies.length===1?"reply":"replies"}`:""}</span>
|
|
116
121
|
<span class="vz-sidebar-item-actions">
|
|
117
|
-
${
|
|
118
|
-
<button class="vz-sidebar-item-delete" data-vz="delete" data-id="${
|
|
122
|
+
${Ne(g.id,w)}
|
|
123
|
+
<button class="vz-sidebar-item-delete" data-vz="delete" data-id="${d(g.id)}" title="Delete comment">delete</button>
|
|
119
124
|
</span>
|
|
120
125
|
</div>
|
|
121
126
|
</div>
|
|
122
127
|
`}).join("")}
|
|
123
128
|
</div>
|
|
124
|
-
`),m++}let
|
|
129
|
+
`),m++}let v=a.length>0?_e(a):"";this.el.innerHTML=`
|
|
125
130
|
${this.headerHtml(e.length,l)}
|
|
126
131
|
<div class="vz-sidebar-body">
|
|
127
132
|
${p.join("")}
|
|
128
|
-
${
|
|
133
|
+
${v}
|
|
129
134
|
</div>
|
|
130
|
-
`;let
|
|
135
|
+
`;let b=this.el.querySelectorAll('[data-vz="jump"]'),f=[...c.values()];for(let u of b){let M=Number(u.dataset.group);u.__group=f[M]}let z=this.el.querySelectorAll('[data-vz="jump-fp"]');for(let u of z){let M=u.getAttribute("data-fp-key"),k=je(s,M||"");u.__fp=k}}headerHtml(e,t){return`
|
|
131
136
|
<div class="vz-sidebar-header">
|
|
132
137
|
<div class="vz-sidebar-title">
|
|
133
138
|
Comments
|
|
@@ -143,24 +148,24 @@
|
|
|
143
148
|
All <span class="vz-sidebar-filter-count">${e}</span>
|
|
144
149
|
</button>
|
|
145
150
|
</div>
|
|
146
|
-
`}destroy(){this.el.removeEventListener("click",this.handleClick),this.el.remove()}};function De(o){let e={open:0,resolved:0,wontfix:0};for(let t of o){let n=t.status??"open";e[n]=(e[n]??0)+1}return e}function
|
|
151
|
+
`}destroy(){this.el.removeEventListener("click",this.handleClick),this.el.remove()}};function De(o){let e={open:0,resolved:0,wontfix:0};for(let t of o){let n=t.status??"open";e[n]=(e[n]??0)+1}return e}function Ne(o,e){let t=d(o);return e==="open"?`
|
|
147
152
|
<button class="vz-sidebar-item-status" data-vz="status" data-id="${t}" data-status="resolved" title="Mark resolved">resolve</button>
|
|
148
153
|
<button class="vz-sidebar-item-status" data-vz="status" data-id="${t}" data-status="wontfix" title="Won't fix">won't fix</button>
|
|
149
|
-
`:`<button class="vz-sidebar-item-status" data-vz="status" data-id="${t}" data-status="open" title="Reopen">reopen</button>`}function
|
|
150
|
-
<div class="vz-sidebar-item vz-sidebar-item-orphan" data-comment-id="${
|
|
154
|
+
`:`<button class="vz-sidebar-item-status" data-vz="status" data-id="${t}" data-status="open" title="Reopen">reopen</button>`}function _e(o){let e=o.map(t=>{let n=t.status??"open",i=(t.fingerprints||[])[0],r=i?i.tagName.toLowerCase():"comment",s=i?.textSnippet?.slice(0,60)??"";return`
|
|
155
|
+
<div class="vz-sidebar-item vz-sidebar-item-orphan" data-comment-id="${d(t.id)}">
|
|
151
156
|
<div class="vz-sidebar-item-row">
|
|
152
|
-
${t.author?`<div class="vz-sidebar-item-author">${
|
|
157
|
+
${t.author?`<div class="vz-sidebar-item-author">${x(t.author)} <span class="vz-sidebar-item-author-name">${d(t.author.name)}</span></div>`:""}
|
|
153
158
|
<span class="vz-status-pill vz-status-orphan">orphaned</span>
|
|
154
159
|
</div>
|
|
155
160
|
<div class="vz-sidebar-orphan-anchor">
|
|
156
|
-
was on <code>${
|
|
161
|
+
was on <code>${d(r)}</code>${s?` “${d(s)}${(i?.textSnippet?.length??0)>60?"\u2026":""}”`:""}
|
|
157
162
|
</div>
|
|
158
|
-
<span class="vz-sidebar-item-text">${
|
|
159
|
-
${
|
|
163
|
+
<span class="vz-sidebar-item-text">${F(t.text,t.id)}</span>
|
|
164
|
+
${H(t.attachments)}
|
|
160
165
|
<div class="vz-sidebar-item-meta">
|
|
161
|
-
<span>${
|
|
166
|
+
<span>${d(T(t.createdAt))}${n!=="open"?` \xB7 ${n}`:""}</span>
|
|
162
167
|
<span class="vz-sidebar-item-actions">
|
|
163
|
-
<button class="vz-sidebar-item-delete" data-vz="delete" data-id="${
|
|
168
|
+
<button class="vz-sidebar-item-delete" data-vz="delete" data-id="${d(t.id)}" title="Delete comment">delete</button>
|
|
164
169
|
</span>
|
|
165
170
|
</div>
|
|
166
171
|
</div>
|
|
@@ -589,6 +594,26 @@
|
|
|
589
594
|
flex-direction: column;
|
|
590
595
|
gap: 1px;
|
|
591
596
|
}
|
|
597
|
+
.vz-mention-search {
|
|
598
|
+
width: 100%;
|
|
599
|
+
padding: 7px 9px;
|
|
600
|
+
margin-bottom: 4px;
|
|
601
|
+
background: rgba(255,255,255,0.04);
|
|
602
|
+
border: 1px solid var(--vz-border, rgba(255,255,255,0.08));
|
|
603
|
+
border-radius: 6px;
|
|
604
|
+
color: #fafafa;
|
|
605
|
+
font-size: 13px;
|
|
606
|
+
outline: none;
|
|
607
|
+
box-sizing: border-box;
|
|
608
|
+
}
|
|
609
|
+
.vz-mention-search:focus { border-color: var(--vz-accent, #ff8b6e); }
|
|
610
|
+
.vz-mention-search::placeholder { color: var(--vz-text-muted); }
|
|
611
|
+
.vz-mention-list {
|
|
612
|
+
display: flex;
|
|
613
|
+
flex-direction: column;
|
|
614
|
+
gap: 1px;
|
|
615
|
+
min-height: 0;
|
|
616
|
+
}
|
|
592
617
|
.vz-mention-loading,
|
|
593
618
|
.vz-mention-empty {
|
|
594
619
|
padding: 10px 12px;
|
|
@@ -610,9 +635,9 @@
|
|
|
610
635
|
cursor: pointer;
|
|
611
636
|
transition: background 80ms;
|
|
612
637
|
}
|
|
613
|
-
.vz-mention-item
|
|
638
|
+
.vz-mention-item.is-selected,
|
|
614
639
|
.vz-mention-item:focus-visible {
|
|
615
|
-
background: var(--vz-bg-hover, rgba(255,255,255,0.
|
|
640
|
+
background: var(--vz-bg-hover, rgba(255,255,255,0.08));
|
|
616
641
|
outline: none;
|
|
617
642
|
}
|
|
618
643
|
.vz-mention-avatar {
|
|
@@ -915,7 +940,9 @@
|
|
|
915
940
|
.vz-ref:hover { background: color-mix(in srgb, var(--vz-accent) 30%, var(--vz-bg-hover)); transform: translateY(-1px); }
|
|
916
941
|
.vz-ref::before { content: ''; width: 5px; height: 5px; border-radius: 50%; background: var(--vz-accent); flex-shrink: 0; }
|
|
917
942
|
|
|
918
|
-
/* Inline @mention chip (rendered inside displayed comments)
|
|
943
|
+
/* Inline @mention chip (rendered inside displayed comments). user-select
|
|
944
|
+
* prevents the user from selecting the chip's text and editing it; the
|
|
945
|
+
* displayed comment is intentionally read-only after save. */
|
|
919
946
|
.vz-mention {
|
|
920
947
|
display: inline-flex; align-items: center;
|
|
921
948
|
padding: 1px 8px 1px 8px;
|
|
@@ -925,13 +952,21 @@
|
|
|
925
952
|
font-size: 12px; font-weight: 600;
|
|
926
953
|
vertical-align: baseline;
|
|
927
954
|
margin: 0 1px;
|
|
955
|
+
user-select: none;
|
|
956
|
+
-webkit-user-select: none;
|
|
957
|
+
cursor: default;
|
|
928
958
|
}
|
|
929
959
|
|
|
930
|
-
/* Mention chip inside the editor (contenteditable=false token)
|
|
960
|
+
/* Mention chip inside the editor (contenteditable=false token). Same
|
|
961
|
+
* non-selection rules as .vz-mention so the chip stays atomic even
|
|
962
|
+
* inside the editable surface \u2014 caret can land before/after but not
|
|
963
|
+
* inside, no partial-text edit. */
|
|
931
964
|
.vz-chip-mention {
|
|
932
965
|
background: color-mix(in srgb, var(--vz-accent) 16%, var(--vz-bg-hover));
|
|
933
966
|
color: var(--vz-accent);
|
|
934
967
|
font-weight: 600;
|
|
968
|
+
user-select: none;
|
|
969
|
+
-webkit-user-select: none;
|
|
935
970
|
}
|
|
936
971
|
.vz-chip-mention::before { display: none; }
|
|
937
972
|
|
|
@@ -1154,12 +1189,12 @@
|
|
|
1154
1189
|
from { opacity: 0; transform: translateX(-50%) translateY(8px); }
|
|
1155
1190
|
to { opacity: 1; transform: translateX(-50%) translateY(0); }
|
|
1156
1191
|
}
|
|
1157
|
-
`;function me(){if(typeof document>"u"||document.getElementById("vizu-styles"))return;let o=document.createElement("style");o.id="vizu-styles",o.textContent=Be,(document.head||document.documentElement).appendChild(o)}function ue(o){let e=o.toLowerCase().split("+").map(n=>n.trim());return{key:e.pop()||"",mod:e.includes("mod")||e.includes("cmdorctrl"),shift:e.includes("shift"),alt:e.includes("alt")||e.includes("option"),ctrl:e.includes("ctrl")||e.includes("control")}}function he(o,e){let t=typeof navigator<"u"&&/Mac|iPhone|iPad/.test(navigator.platform);if(o.key.toLowerCase()!==e.key)return!1;if(e.mod){if(!(t?o.metaKey:o.ctrlKey))return!1}else if(e.ctrl&&!o.ctrlKey)return!1;return!(e.shift!==o.shiftKey||e.alt!==o.altKey)}var W=class{constructor(){this.listeners=new Map}on(e,t){let n=this.listeners.get(e);return n||(n=new Set,this.listeners.set(e,n)),n.add(t),()=>this.off(e,t)}off(e,t){this.listeners.get(e)?.delete(t)}emit(e,t){let n=this.listeners.get(e);if(n)for(let i of[...n])try{i(t)}catch(r){console.error("[vizu] event handler error for",e,r)}}clear(){this.listeners.clear()}};var ee={shortcut:"mod+shift+e",namespace:"default",accent:"#FF6647"};function qe(o,e,t,n){return e?(o&&typeof console<"u"&&console.warn("[vizu] Both `cloud` and `storage` set; `cloud` wins. Drop `storage` to silence this."),new k({workspace:e.workspace,apiUrl:e.apiUrl,autoSignIn:e.autoSignIn,onAuthChanged:n})):o?o==="local"?new I:o==="memory"?new O:o==="none"?new N:Q(o)?oe(o):o:t==="local"?new I:new O}var E=class{constructor(e={},t="memory"){this.root=null;this.highlighter=null;this.popover=null;this.pill=null;this.sidebar=null;this.markers=new Map;this.outlines=new Map;this.activeAnchorOutlines=new Map;this.spotlights=new Set;this.comments=[];this.resolvedConfidence=new Map;this.selfHealedThisSession=new Set;this.actions=[];this.user=null;this.hasLoadedComments=!1;this.enabled=!1;this.rafScheduled=!1;this.bus=new W;this.picking=null;this.pickingBanner=null;this.multiSelectMode=!1;this.pendingFingerprints=[];this.pendingTargets=[];this.unsubscribeStorage=null;this.onKeyDown=e=>{if(this.picking&&e.key==="Escape"){e.preventDefault(),this.cancelPicking();return}if(this.pendingFingerprints.length>0&&e.key==="Escape"&&!this.popover?.isOpen()){e.preventDefault(),this.clearSelection();return}if(he(e,this.parsedShortcut)){e.preventDefault();let t=this.enabled;this.toggle(),!t&&this.enabled&&this.storage instanceof k&&this.storage.preflightAuth().catch(n=>{n?.code==="auth_canceled"||n?.code==="popup_blocked"||typeof console<"u"&&console.warn("[vizu] preflight auth failed:",n)})}};this.scheduleReposition=()=>{this.rafScheduled||(this.rafScheduled=!0,requestAnimationFrame(()=>{this.rafScheduled=!1;for(let[,e]of this.outlines)e.target&&this.positionOutline(e.outline,e.target);for(let[,e]of this.markers)e.target&&this.positionMarker(e.marker,e.target);for(let[,e]of this.activeAnchorOutlines){let t=L(e.fp);t&&this.positionOutline(e.el,t)}for(let e of this.spotlights)e.target&&this.positionOutline(e.el,e.target);this.popover?.reposition()}))};this.opts={...e},this.opts.shortcut=e.shortcut??ee.shortcut,this.opts.namespace=e.namespace??ee.namespace,this.opts.accent=e.accent??ee.accent,this.parsedShortcut=ue(this.opts.shortcut),this.user=e.user??null,this.storage=qe(e.storage,e.cloud,t,n=>{n.user&&this.setUser(n.user),this.hasLoadedComments&&this.loadComments()}),e.actions&&(this.actions=[...e.actions]),e.onCommentAdded&&this.bus.on("comment:added",n=>e.onCommentAdded(n.comment)),e.onCommentRemoved&&this.bus.on("comment:removed",n=>e.onCommentRemoved(n.id)),typeof window<"u"&&(window.addEventListener("keydown",this.onKeyDown),this.loadComments().catch(()=>{}).finally(()=>{this.hasLoadedComments=!0,this.subscribeStorage()}),e.startEnabled&&this.deferred(()=>this.enable()))}on(e,t){return this.bus.on(e,t)}off(e,t){this.bus.off(e,t)}setUser(e){this.user=e,this.popover?.setUser(e),this.pill?.setUser(e),this.bus.emit("user:changed",{user:e}),this.enabled&&!this.root&&this.shouldMount()&&this.deferred(()=>this.mount())}getUser(){return this.user}async searchMentionable(){return this.storage instanceof k?this.storage.searchMentionable():[]}shouldMount(){return this.storage instanceof k?this.user!==null:!0}requireUserOrAuth(){return!(this.storage instanceof k)||this.user?!0:(this.storage.isAccessDenied()||this.storage.preflightAuth().catch(()=>{}),!1)}enable(){this.enabled||(this.enabled=!0,this.bus.emit("enabled",{}),this.shouldMount()&&this.deferred(()=>this.mount()))}disable(){this.enabled&&(this.enabled=!1,this.unmount(),this.bus.emit("disabled",{}))}toggle(){this.enabled?this.disable():this.enable()}isEnabled(){return this.enabled}destroy(){this.disable(),typeof window<"u"&&window.removeEventListener("keydown",this.onKeyDown),this.unsubscribeStorage?.(),this.unsubscribeStorage=null,this.selfHealedThisSession.clear(),this.bus.clear()}addAction(e){let t=this.actions.findIndex(n=>n.id===e.id);t>=0?this.actions[t]=e:this.actions.push(e),this.pill?.setActions(this.actions)}removeAction(e){this.actions=this.actions.filter(t=>t.id!==e),this.pill?.setActions(this.actions)}getActions(){return[...this.actions]}invokeAction(e){let t=this.actions.find(i=>i.id===e);if(!t)return;let n=this.actionContext();this.bus.emit("action:invoked",{id:e}),t.onClick(n)}getComments(){return[...this.comments]}async setComments(e,t){this.comments=A(e),t?.persist!==!1&&await this.storage.setAll(this.opts.namespace,this.comments),this.bus.emit("comments:set",{comments:this.getComments()}),this.refreshUi()}async updateComment(e,t){let n=this.comments.findIndex(d=>d.id===e);if(n===-1)return null;let{id:i,schemaVersion:r,...s}=t,a={...this.comments[n],...s};this.comments[n]=a;let l=await this.storage.updateComment(this.opts.namespace,e,s);return this.bus.emit("comment:updated",{comment:l??a}),this.refreshUi(),a}getAuthContext(){return this.storage.getAuthContext?.()??null}async selfHealComment(e,t){this.selfHealedThisSession.add(e.id);let n=[],i=!1;for(let a of e.fingerprints||[]){let l=t.get(h(a));if(l?.target){let d=D(l.target);n.push(d),!i&&h(d)!==h(a)&&(i=!0)}else n.push(a)}if(!i)return;let r={fingerprints:n,fingerprintsRefreshedAt:Date.now()},s=this.comments.findIndex(a=>a.id===e.id);s!==-1&&(this.comments[s]={...this.comments[s],...r});try{await this.storage.updateComment(this.opts.namespace,e.id,r)}catch(a){typeof console<"u"&&console.warn("[vizu] self-heal write failed for",e.id,a)}}getCommentConfidence(e){return this.resolvedConfidence.get(e)}getResolvedConfidence(){return new Map(this.resolvedConfidence)}canUploadAttachments(){return typeof this.storage.uploadAttachment=="function"}async uploadAttachment(e){if(!this.storage.uploadAttachment)throw new Error("[vizu] active storage adapter does not support uploadAttachment; only the CloudStorageAdapter does in v1");return this.storage.uploadAttachment(this.opts.namespace,e)}async addReply(e,t,n){if(!this.requireUserOrAuth())return null;let i=this.comments.findIndex(a=>a.id===e);if(i===-1)return null;let r={id:H(),text:t.trim(),createdAt:Date.now(),author:this.user??void 0,authorId:this.user?.id,mentions:n&&n.length?n:void 0};if(!r.text)return null;let s={...this.comments[i],replies:[...this.comments[i].replies??[],r]};if(this.comments[i]=s,this.storage.addReply?await this.storage.addReply(this.opts.namespace,e,r):await this.storage.updateComment(this.opts.namespace,e,{replies:s.replies}),this.bus.emit("comment:updated",{comment:s}),this.refreshUi(),this.popover?.isOpen()){let a=this.popover.getAnchors();a[0]&&this.popover.update(this.commentsForElement(a[0]))}return s}async removeReply(e,t){let n=this.comments.findIndex(s=>s.id===e);if(n===-1)return null;let i=(this.comments[n].replies??[]).filter(s=>s.id!==t);if(i.length===(this.comments[n].replies??[]).length)return null;let r={...this.comments[n],replies:i};if(this.comments[n]=r,this.storage.removeReply?await this.storage.removeReply(this.opts.namespace,e,t):await this.storage.updateComment(this.opts.namespace,e,{replies:i}),this.bus.emit("comment:updated",{comment:r}),this.refreshUi(),this.popover?.isOpen()){let s=this.popover.getAnchors();s[0]&&this.popover.update(this.commentsForElement(s[0]))}return r}async clearAll(){let e=this.comments;this.comments=[],await this.storage.clear(this.opts.namespace),this.bus.emit("comments:cleared",{previous:e}),this.refreshUi(),this.popover?.close(),this.clearActiveAnchorOutlines(),this.highlighter?.setPaused(!1)}toggleMultiSelectMode(){this.requireUserOrAuth()&&(this.multiSelectMode=!this.multiSelectMode,this.multiSelectMode||this.clearSelection(),this.pill?.setMultiSelectState(this.multiSelectMode,this.pendingFingerprints.length))}clearSelection(){this.pendingFingerprints=[],this.pendingTargets=[],this.multiSelectMode=!1,this.clearActiveAnchorOutlines(),this.pill?.setMultiSelectState(!1,0)}commitSelection(){if(this.pendingFingerprints.length===0)return;let e=[...this.pendingTargets],t=[...this.pendingFingerprints];this.pendingFingerprints=[],this.pendingTargets=[],this.multiSelectMode=!1,this.pill?.setMultiSelectState(!1,0),(e.find(i=>!!i)??L(t[0]))&&this.openPopoverFor(e.filter(Boolean),t)}getSelection(){return[...this.pendingFingerprints]}async loadComments(){let e=await this.storage.load(this.opts.namespace);this.comments=A(e),se(e)&&this.storage.setAll(this.opts.namespace,this.comments).catch(()=>{}),this.bus.emit("comments:loaded",{comments:this.getComments()}),this.enabled&&this.refreshUi()}subscribeStorage(){this.unsubscribeStorage||this.storage.subscribe&&(this.unsubscribeStorage=this.storage.subscribe(this.opts.namespace,e=>{if(e.type==="added"){let t=C(e.comment);this.comments.some(n=>n.id===t.id)||(this.comments.push(t),this.bus.emit("comment:added",{comment:t}),this.refreshUi())}else if(e.type==="updated"){let t=this.comments.findIndex(i=>i.id===e.comment.id),n=C(e.comment);t>=0&&(this.comments[t]=n,this.bus.emit("comment:updated",{comment:n}),this.refreshUi())}else if(e.type==="removed"){let t=this.comments.findIndex(n=>n.id===e.id);if(t>=0){let[n]=this.comments.splice(t,1);this.bus.emit("comment:removed",{id:e.id,comment:n}),this.refreshUi()}}else if(e.type==="cleared"){let t=this.comments;this.comments=[],this.bus.emit("comments:cleared",{previous:t}),this.refreshUi()}}))}deferred(e){typeof document>"u"||(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e,{once:!0}):e())}mount(){this.root||(me(),this.root=document.createElement("div"),this.root.setAttribute("data-vizu-root",""),this.root.style.setProperty("--vz-accent",this.opts.accent),document.body.appendChild(this.root),this.popover=new q(this.root,{onAdd:(e,t,n,i,r)=>{this.addCommentFromPopover(e,t,n,i,r)},onUploadAttachment:e=>this.uploadAttachment(e),canUploadAttachments:()=>this.canUploadAttachments(),onDelete:e=>{this.removeComment(e)},onAddReply:(e,t)=>{this.addReply(e,t)},onDeleteReply:(e,t)=>{this.removeReply(e,t)},onClose:()=>this.closePopover(),onStartReferencePick:(e,t)=>this.startPicking(e,t),onJumpToReference:(e,t)=>this.jumpToReference(e,t),onAnchorsChanged:e=>this.syncActiveAnchorOutlines(e),onMentionSearch:()=>this.searchMentionable()}),this.popover.setUser(this.user),this.sidebar=new J(this.root,{onJumpTo:e=>this.jumpToFingerprint(e),onJumpToReference:(e,t)=>this.jumpToReference(e,t),onDelete:e=>{this.removeComment(e)},onUpdateStatus:(e,t)=>{this.updateComment(e,{status:t})},onClose:()=>this.closeSidebar()}),this.pill=new K(this.root,{onToggleSidebar:()=>this.toggleSidebar(),onDisable:()=>this.disable(),onAction:e=>this.invokeAction(e),onToggleMultiSelect:()=>this.toggleMultiSelectMode(),onCommitSelection:()=>this.commitSelection(),onClearSelection:()=>this.clearSelection()}),this.pill.setActions(this.actions),this.pill.setUser(this.user),this.pill.setComments(this.comments),this.pill.setMultiSelectState(this.multiSelectMode,this.pendingFingerprints.length),this.pill.show(),this.highlighter=new B(this.root,this.opts.ignoreSelectors??[],(e,t)=>this.onElementClick(e,t)),this.highlighter.start(),this.renderAllMarkers(),window.addEventListener("scroll",this.scheduleReposition,!0),window.addEventListener("resize",this.scheduleReposition),this.bus.emit("mounted",{}))}unmount(){window.removeEventListener("scroll",this.scheduleReposition,!0),window.removeEventListener("resize",this.scheduleReposition),this.highlighter?.destroy(),this.popover?.destroy(),this.pill?.destroy(),this.sidebar?.destroy(),this.root?.remove(),this.root=null,this.highlighter=null,this.popover=null,this.pill=null,this.sidebar=null,this.markers.clear(),this.outlines.clear(),this.activeAnchorOutlines.clear();for(let e of this.spotlights)e.el.remove();this.spotlights.clear(),this.pendingFingerprints=[],this.pendingTargets=[],this.bus.emit("unmounted",{})}onElementClick(e,t){if(this.picking){let r=D(e),s=this.picking;this.picking=null,this.hidePickingBanner(),this.highlighter?.setPaused(this.popover?.isOpen()??!1),s.onSelect(r);return}if(!this.requireUserOrAuth())return;let n=D(e);if(this.popover?.isOpen()&&t.shiftKey){this.popover.addAnchor(n,e);return}if(!this.popover?.isOpen()&&(this.multiSelectMode||t.shiftKey)){this.addToSelection(n,e);return}this.bus.emit("element:selected",{target:e,fingerprint:n});let i=this.commentsForElement(n);this.openPopoverFor([e],[n],i)}openPopoverFor(e,t,n){if(!this.popover)return;let i=n??this.commentsForElement(t[0]);this.popover.open(e,t,i),this.syncActiveAnchorOutlines(t),this.highlighter?.setPaused(!0)}closePopover(){this.popover?.close(),this.clearActiveAnchorOutlines(),this.highlighter?.setPaused(!1),this.bus.emit("element:deselected",{})}addToSelection(e,t){let n=h(e);this.pendingFingerprints.some(i=>h(i)===n)?(this.pendingFingerprints=this.pendingFingerprints.filter(i=>h(i)!==n),this.pendingTargets=this.pendingTargets.filter(i=>i!==t)):(this.pendingFingerprints.push(e),this.pendingTargets.push(t)),this.syncActiveAnchorOutlines(this.pendingFingerprints),this.pill?.setMultiSelectState(!0,this.pendingFingerprints.length),this.pendingFingerprints.length>0?this.multiSelectMode=!0:this.multiSelectMode=!1}commentsForElement(e){let t=h(e);return this.comments.filter(n=>(n.fingerprints||[]).some(i=>h(i)===t))}async addCommentFromPopover(e,t,n,i,r){if(!this.requireUserOrAuth()||t.length===0)return;let s=typeof location<"u"?location.href:void 0,a={id:H(),schemaVersion:2,fingerprints:t,text:e,createdAt:Date.now(),pageVersion:this.opts.pageVersion,pageUrl:s,author:this.user??void 0,authorId:this.user?.id,references:n&&Object.keys(n).length?n:void 0,status:"open",replies:[],attachments:r??[],mentions:i??[]};this.comments.push(a),await this.storage.addComment(this.opts.namespace,a),this.bus.emit("comment:added",{comment:a}),this.popover?.update(this.commentsForElement(t[0])),this.refreshUi()}async removeComment(e){let t=this.comments.find(n=>n.id===e);if(t){if(this.comments=this.comments.filter(n=>n.id!==e),await this.storage.removeComment(this.opts.namespace,e),this.bus.emit("comment:removed",{id:e,comment:t}),this.popover?.isOpen()){let n=this.popover.getAnchors();n[0]&&this.popover.update(this.commentsForElement(n[0]))}this.refreshUi()}}refreshUi(){this.pill?.setComments(this.comments),this.renderAllMarkers(),this.sidebar?.update(this.comments,this.resolvedConfidence)}toggleSidebar(){if(!this.sidebar)return;this.sidebar.isOpen()||this.renderAllMarkers(),this.sidebar.toggle(this.comments,this.resolvedConfidence);let e=this.sidebar.isOpen();this.pill?.setSidebarOpen(e),this.bus.emit(e?"sidebar:opened":"sidebar:closed",{})}closeSidebar(){this.sidebar?.close(),this.pill?.setSidebarOpen(!1),this.bus.emit("sidebar:closed",{})}startPicking(e,t){this.picking&&this.picking.onCancel(),this.picking={onSelect:e,onCancel:t},this.highlighter?.setPaused(!1),this.showPickingBanner()}cancelPicking(){if(!this.picking)return;let e=this.picking;this.picking=null,this.hidePickingBanner(),this.highlighter?.setPaused(this.popover?.isOpen()??!1),e.onCancel()}showPickingBanner(){this.root&&(this.pickingBanner=document.createElement("div"),this.pickingBanner.className="vz-picking-banner",this.pickingBanner.innerHTML=`
|
|
1192
|
+
`;function me(){if(typeof document>"u"||document.getElementById("vizu-styles"))return;let o=document.createElement("style");o.id="vizu-styles",o.textContent=Be,(document.head||document.documentElement).appendChild(o)}function ue(o){let e=o.toLowerCase().split("+").map(n=>n.trim());return{key:e.pop()||"",mod:e.includes("mod")||e.includes("cmdorctrl"),shift:e.includes("shift"),alt:e.includes("alt")||e.includes("option"),ctrl:e.includes("ctrl")||e.includes("control")}}function he(o,e){let t=typeof navigator<"u"&&/Mac|iPhone|iPad/.test(navigator.platform);if(o.key.toLowerCase()!==e.key)return!1;if(e.mod){if(!(t?o.metaKey:o.ctrlKey))return!1}else if(e.ctrl&&!o.ctrlKey)return!1;return!(e.shift!==o.shiftKey||e.alt!==o.altKey)}var W=class{constructor(){this.listeners=new Map}on(e,t){let n=this.listeners.get(e);return n||(n=new Set,this.listeners.set(e,n)),n.add(t),()=>this.off(e,t)}off(e,t){this.listeners.get(e)?.delete(t)}emit(e,t){let n=this.listeners.get(e);if(n)for(let i of[...n])try{i(t)}catch(r){console.error("[vizu] event handler error for",e,r)}}clear(){this.listeners.clear()}};var ee={shortcut:"mod+shift+e",namespace:"default",accent:"#FF6647"};function qe(o,e,t,n){return e?(o&&typeof console<"u"&&console.warn("[vizu] Both `cloud` and `storage` set; `cloud` wins. Drop `storage` to silence this."),new C({workspace:e.workspace,apiUrl:e.apiUrl,autoSignIn:e.autoSignIn,onAuthChanged:n})):o?o==="local"?new I:o==="memory"?new O:o==="none"?new _:Q(o)?oe(o):o:t==="local"?new I:new O}var S=class{constructor(e={},t="memory"){this.root=null;this.highlighter=null;this.popover=null;this.pill=null;this.sidebar=null;this.markers=new Map;this.outlines=new Map;this.activeAnchorOutlines=new Map;this.spotlights=new Set;this.comments=[];this.resolvedConfidence=new Map;this.selfHealedThisSession=new Set;this.actions=[];this.user=null;this.hasLoadedComments=!1;this.enabled=!1;this.rafScheduled=!1;this.bus=new W;this.picking=null;this.pickingBanner=null;this.multiSelectMode=!1;this.pendingFingerprints=[];this.pendingTargets=[];this.unsubscribeStorage=null;this.onKeyDown=e=>{if(this.picking&&e.key==="Escape"){e.preventDefault(),this.cancelPicking();return}if(this.pendingFingerprints.length>0&&e.key==="Escape"&&!this.popover?.isOpen()){e.preventDefault(),this.clearSelection();return}if(he(e,this.parsedShortcut)){e.preventDefault();let t=this.enabled;this.toggle(),!t&&this.enabled&&this.storage instanceof C&&this.storage.preflightAuth().catch(n=>{n?.code==="auth_canceled"||n?.code==="popup_blocked"||typeof console<"u"&&console.warn("[vizu] preflight auth failed:",n)})}};this.scheduleReposition=()=>{this.rafScheduled||(this.rafScheduled=!0,requestAnimationFrame(()=>{this.rafScheduled=!1;for(let[,e]of this.outlines)e.target&&this.positionOutline(e.outline,e.target);for(let[,e]of this.markers)e.target&&this.positionMarker(e.marker,e.target);for(let[,e]of this.activeAnchorOutlines){let t=V(e.fp);t&&this.positionOutline(e.el,t)}for(let e of this.spotlights)e.target&&this.positionOutline(e.el,e.target);this.popover?.reposition()}))};this.opts={...e},this.opts.shortcut=e.shortcut??ee.shortcut,this.opts.namespace=e.namespace??ee.namespace,this.opts.accent=e.accent??ee.accent,this.parsedShortcut=ue(this.opts.shortcut),this.user=e.user??null,this.storage=qe(e.storage,e.cloud,t,n=>{n.user&&this.setUser(n.user),this.hasLoadedComments&&this.loadComments()}),e.actions&&(this.actions=[...e.actions]),e.onCommentAdded&&this.bus.on("comment:added",n=>e.onCommentAdded(n.comment)),e.onCommentRemoved&&this.bus.on("comment:removed",n=>e.onCommentRemoved(n.id)),typeof window<"u"&&(window.addEventListener("keydown",this.onKeyDown),this.loadComments().catch(()=>{}).finally(()=>{this.hasLoadedComments=!0,this.subscribeStorage()}),e.startEnabled&&this.deferred(()=>this.enable()))}on(e,t){return this.bus.on(e,t)}off(e,t){this.bus.off(e,t)}setUser(e){this.user=e,this.popover?.setUser(e),this.pill?.setUser(e),this.bus.emit("user:changed",{user:e}),this.enabled&&!this.root&&this.shouldMount()&&this.deferred(()=>this.mount())}getUser(){return this.user}async searchMentionable(){return this.storage instanceof C?this.storage.searchMentionable():[]}shouldMount(){return this.storage instanceof C?this.user!==null:!0}requireUserOrAuth(){return!(this.storage instanceof C)||this.user?!0:(this.storage.isAccessDenied()||this.storage.preflightAuth().catch(()=>{}),!1)}enable(){this.enabled||(this.enabled=!0,this.bus.emit("enabled",{}),this.shouldMount()&&this.deferred(()=>this.mount()))}disable(){this.enabled&&(this.enabled=!1,this.unmount(),this.bus.emit("disabled",{}))}toggle(){this.enabled?this.disable():this.enable()}isEnabled(){return this.enabled}destroy(){this.disable(),typeof window<"u"&&window.removeEventListener("keydown",this.onKeyDown),this.unsubscribeStorage?.(),this.unsubscribeStorage=null,this.selfHealedThisSession.clear(),this.bus.clear()}addAction(e){let t=this.actions.findIndex(n=>n.id===e.id);t>=0?this.actions[t]=e:this.actions.push(e),this.pill?.setActions(this.actions)}removeAction(e){this.actions=this.actions.filter(t=>t.id!==e),this.pill?.setActions(this.actions)}getActions(){return[...this.actions]}invokeAction(e){let t=this.actions.find(i=>i.id===e);if(!t)return;let n=this.actionContext();this.bus.emit("action:invoked",{id:e}),t.onClick(n)}getComments(){return[...this.comments]}async setComments(e,t){this.comments=A(e),t?.persist!==!1&&await this.storage.setAll(this.opts.namespace,this.comments),this.bus.emit("comments:set",{comments:this.getComments()}),this.refreshUi()}async updateComment(e,t){let n=this.comments.findIndex(c=>c.id===e);if(n===-1)return null;let{id:i,schemaVersion:r,...s}=t,a={...this.comments[n],...s};this.comments[n]=a;let l=await this.storage.updateComment(this.opts.namespace,e,s);return this.bus.emit("comment:updated",{comment:l??a}),this.refreshUi(),a}getAuthContext(){return this.storage.getAuthContext?.()??null}async selfHealComment(e,t){this.selfHealedThisSession.add(e.id);let n=[],i=!1;for(let a of e.fingerprints||[]){let l=t.get(h(a));if(l?.target){let c=D(l.target);n.push(c),!i&&h(c)!==h(a)&&(i=!0)}else n.push(a)}if(!i)return;let r={fingerprints:n,fingerprintsRefreshedAt:Date.now()},s=this.comments.findIndex(a=>a.id===e.id);s!==-1&&(this.comments[s]={...this.comments[s],...r});try{await this.storage.updateComment(this.opts.namespace,e.id,r)}catch(a){typeof console<"u"&&console.warn("[vizu] self-heal write failed for",e.id,a)}}getCommentConfidence(e){return this.resolvedConfidence.get(e)}getResolvedConfidence(){return new Map(this.resolvedConfidence)}canUploadAttachments(){return typeof this.storage.uploadAttachment=="function"}async uploadAttachment(e){if(!this.storage.uploadAttachment)throw new Error("[vizu] active storage adapter does not support uploadAttachment; only the CloudStorageAdapter does in v1");return this.storage.uploadAttachment(this.opts.namespace,e)}async addReply(e,t,n){if(!this.requireUserOrAuth())return null;let i=this.comments.findIndex(a=>a.id===e);if(i===-1)return null;let r={id:U(),text:t.trim(),createdAt:Date.now(),author:this.user??void 0,authorId:this.user?.id,mentions:n&&n.length?n:void 0};if(!r.text)return null;let s={...this.comments[i],replies:[...this.comments[i].replies??[],r]};this.comments[i]=s;try{this.storage.addReply?await this.storage.addReply(this.opts.namespace,e,r):await this.storage.updateComment(this.opts.namespace,e,{replies:s.replies})}catch(a){let l=this.comments[i];this.comments[i]={...l,replies:(l.replies??[]).filter(v=>v.id!==r.id)};let c=a?.code??"",p=a?.status,m=p===403||c==="forbidden"?"Reply failed \u2014 you don't have permission.":p===401||c==="auth_required"?"Reply failed \u2014 please sign in again.":"Reply failed. Please try again.";if(this.pill?.toast(m),this.refreshUi(),this.popover?.isOpen()){let v=this.popover.getAnchors();v[0]&&this.popover.update(this.commentsForElement(v[0]))}return typeof console<"u"&&console.warn("[vizu] addReply failed",a),null}if(this.bus.emit("comment:updated",{comment:s}),this.refreshUi(),this.popover?.isOpen()){let a=this.popover.getAnchors();a[0]&&this.popover.update(this.commentsForElement(a[0]))}return s}async removeReply(e,t){let n=this.comments.findIndex(s=>s.id===e);if(n===-1)return null;let i=(this.comments[n].replies??[]).filter(s=>s.id!==t);if(i.length===(this.comments[n].replies??[]).length)return null;let r={...this.comments[n],replies:i};if(this.comments[n]=r,this.storage.removeReply?await this.storage.removeReply(this.opts.namespace,e,t):await this.storage.updateComment(this.opts.namespace,e,{replies:i}),this.bus.emit("comment:updated",{comment:r}),this.refreshUi(),this.popover?.isOpen()){let s=this.popover.getAnchors();s[0]&&this.popover.update(this.commentsForElement(s[0]))}return r}async clearAll(){let e=this.comments;this.comments=[],await this.storage.clear(this.opts.namespace),this.bus.emit("comments:cleared",{previous:e}),this.refreshUi(),this.popover?.close(),this.clearActiveAnchorOutlines(),this.highlighter?.setPaused(!1)}toggleMultiSelectMode(){this.requireUserOrAuth()&&(this.multiSelectMode=!this.multiSelectMode,this.multiSelectMode||this.clearSelection(),this.pill?.setMultiSelectState(this.multiSelectMode,this.pendingFingerprints.length))}clearSelection(){this.pendingFingerprints=[],this.pendingTargets=[],this.multiSelectMode=!1,this.clearActiveAnchorOutlines(),this.pill?.setMultiSelectState(!1,0)}commitSelection(){if(this.pendingFingerprints.length===0)return;let e=[...this.pendingTargets],t=[...this.pendingFingerprints];this.pendingFingerprints=[],this.pendingTargets=[],this.multiSelectMode=!1,this.pill?.setMultiSelectState(!1,0),(e.find(i=>!!i)??V(t[0]))&&this.openPopoverFor(e.filter(Boolean),t)}getSelection(){return[...this.pendingFingerprints]}async loadComments(){let e=await this.storage.load(this.opts.namespace);this.comments=A(e),se(e)&&this.storage.setAll(this.opts.namespace,this.comments).catch(()=>{}),this.bus.emit("comments:loaded",{comments:this.getComments()}),this.enabled&&this.refreshUi()}subscribeStorage(){this.unsubscribeStorage||this.storage.subscribe&&(this.unsubscribeStorage=this.storage.subscribe(this.opts.namespace,e=>{if(e.type==="added"){let t=E(e.comment);this.comments.some(n=>n.id===t.id)||(this.comments.push(t),this.bus.emit("comment:added",{comment:t}),this.refreshUi())}else if(e.type==="updated"){let t=this.comments.findIndex(i=>i.id===e.comment.id),n=E(e.comment);t>=0&&(this.comments[t]=n,this.bus.emit("comment:updated",{comment:n}),this.refreshUi())}else if(e.type==="removed"){let t=this.comments.findIndex(n=>n.id===e.id);if(t>=0){let[n]=this.comments.splice(t,1);this.bus.emit("comment:removed",{id:e.id,comment:n}),this.refreshUi()}}else if(e.type==="cleared"){let t=this.comments;this.comments=[],this.bus.emit("comments:cleared",{previous:t}),this.refreshUi()}}))}deferred(e){typeof document>"u"||(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e,{once:!0}):e())}mount(){this.root||(me(),this.root=document.createElement("div"),this.root.setAttribute("data-vizu-root",""),this.root.style.setProperty("--vz-accent",this.opts.accent),document.body.appendChild(this.root),this.popover=new q(this.root,{onAdd:(e,t,n,i,r)=>{this.addCommentFromPopover(e,t,n,i,r)},onUploadAttachment:e=>this.uploadAttachment(e),canUploadAttachments:()=>this.canUploadAttachments(),onDelete:e=>{this.removeComment(e)},onAddReply:(e,t)=>{this.addReply(e,t)},onDeleteReply:(e,t)=>{this.removeReply(e,t)},onClose:()=>this.closePopover(),onStartReferencePick:(e,t)=>this.startPicking(e,t),onJumpToReference:(e,t)=>this.jumpToReference(e,t),onAnchorsChanged:e=>this.syncActiveAnchorOutlines(e),onMentionSearch:()=>this.searchMentionable()}),this.popover.setUser(this.user),this.sidebar=new J(this.root,{onJumpTo:e=>this.jumpToFingerprint(e),onJumpToReference:(e,t)=>this.jumpToReference(e,t),onDelete:e=>{this.removeComment(e)},onUpdateStatus:(e,t)=>{this.updateComment(e,{status:t})},onClose:()=>this.closeSidebar()}),this.pill=new K(this.root,{onToggleSidebar:()=>this.toggleSidebar(),onDisable:()=>this.disable(),onAction:e=>this.invokeAction(e),onToggleMultiSelect:()=>this.toggleMultiSelectMode(),onCommitSelection:()=>this.commitSelection(),onClearSelection:()=>this.clearSelection()}),this.pill.setActions(this.actions),this.pill.setUser(this.user),this.pill.setComments(this.comments),this.pill.setMultiSelectState(this.multiSelectMode,this.pendingFingerprints.length),this.pill.show(),this.highlighter=new B(this.root,this.opts.ignoreSelectors??[],(e,t)=>this.onElementClick(e,t)),this.highlighter.start(),this.renderAllMarkers(),window.addEventListener("scroll",this.scheduleReposition,!0),window.addEventListener("resize",this.scheduleReposition),this.bus.emit("mounted",{}))}unmount(){window.removeEventListener("scroll",this.scheduleReposition,!0),window.removeEventListener("resize",this.scheduleReposition),this.highlighter?.destroy(),this.popover?.destroy(),this.pill?.destroy(),this.sidebar?.destroy(),this.root?.remove(),this.root=null,this.highlighter=null,this.popover=null,this.pill=null,this.sidebar=null,this.markers.clear(),this.outlines.clear(),this.activeAnchorOutlines.clear();for(let e of this.spotlights)e.el.remove();this.spotlights.clear(),this.pendingFingerprints=[],this.pendingTargets=[],this.bus.emit("unmounted",{})}onElementClick(e,t){if(this.picking){let r=D(e),s=this.picking;this.picking=null,this.hidePickingBanner(),this.highlighter?.setPaused(this.popover?.isOpen()??!1),s.onSelect(r);return}if(!this.requireUserOrAuth())return;let n=D(e);if(this.popover?.isOpen()&&t.shiftKey){this.popover.addAnchor(n,e);return}if(!this.popover?.isOpen()&&(this.multiSelectMode||t.shiftKey)){this.addToSelection(n,e);return}this.bus.emit("element:selected",{target:e,fingerprint:n});let i=this.commentsForElement(n);this.openPopoverFor([e],[n],i)}openPopoverFor(e,t,n){if(!this.popover)return;let i=n??this.commentsForElement(t[0]);this.popover.open(e,t,i),this.syncActiveAnchorOutlines(t),this.highlighter?.setPaused(!0)}closePopover(){this.popover?.close(),this.clearActiveAnchorOutlines(),this.highlighter?.setPaused(!1),this.bus.emit("element:deselected",{})}addToSelection(e,t){let n=h(e);this.pendingFingerprints.some(i=>h(i)===n)?(this.pendingFingerprints=this.pendingFingerprints.filter(i=>h(i)!==n),this.pendingTargets=this.pendingTargets.filter(i=>i!==t)):(this.pendingFingerprints.push(e),this.pendingTargets.push(t)),this.syncActiveAnchorOutlines(this.pendingFingerprints),this.pill?.setMultiSelectState(!0,this.pendingFingerprints.length),this.pendingFingerprints.length>0?this.multiSelectMode=!0:this.multiSelectMode=!1}commentsForElement(e){let t=h(e);return this.comments.filter(n=>(n.fingerprints||[]).some(i=>h(i)===t))}async addCommentFromPopover(e,t,n,i,r){if(!this.requireUserOrAuth()||t.length===0)return;let s=typeof location<"u"?location.href:void 0,a={id:U(),schemaVersion:2,fingerprints:t,text:e,createdAt:Date.now(),pageVersion:this.opts.pageVersion,pageUrl:s,author:this.user??void 0,authorId:this.user?.id,references:n&&Object.keys(n).length?n:void 0,status:"open",replies:[],attachments:r??[],mentions:i??[]};this.comments.push(a),await this.storage.addComment(this.opts.namespace,a),this.bus.emit("comment:added",{comment:a}),this.popover?.update(this.commentsForElement(t[0])),this.refreshUi()}async removeComment(e){let t=this.comments.find(n=>n.id===e);if(t){if(this.comments=this.comments.filter(n=>n.id!==e),await this.storage.removeComment(this.opts.namespace,e),this.bus.emit("comment:removed",{id:e,comment:t}),this.popover?.isOpen()){let n=this.popover.getAnchors();n[0]&&this.popover.update(this.commentsForElement(n[0]))}this.refreshUi()}}refreshUi(){this.pill?.setComments(this.comments),this.renderAllMarkers(),this.sidebar?.update(this.comments,this.resolvedConfidence)}toggleSidebar(){if(!this.sidebar)return;this.sidebar.isOpen()||this.renderAllMarkers(),this.sidebar.toggle(this.comments,this.resolvedConfidence);let e=this.sidebar.isOpen();this.pill?.setSidebarOpen(e),this.bus.emit(e?"sidebar:opened":"sidebar:closed",{})}closeSidebar(){this.sidebar?.close(),this.pill?.setSidebarOpen(!1),this.bus.emit("sidebar:closed",{})}startPicking(e,t){this.picking&&this.picking.onCancel(),this.picking={onSelect:e,onCancel:t},this.highlighter?.setPaused(!1),this.showPickingBanner()}cancelPicking(){if(!this.picking)return;let e=this.picking;this.picking=null,this.hidePickingBanner(),this.highlighter?.setPaused(this.popover?.isOpen()??!1),e.onCancel()}showPickingBanner(){this.root&&(this.pickingBanner=document.createElement("div"),this.pickingBanner.className="vz-picking-banner",this.pickingBanner.innerHTML=`
|
|
1158
1193
|
<span>Click any element to reference</span>
|
|
1159
1194
|
<span class="vz-picking-banner-hint">or press <span class="vz-picking-banner-kbd">Esc</span></span>
|
|
1160
1195
|
<button class="vz-picking-banner-cancel" data-vz="cancel-pick">Cancel</button>
|
|
1161
|
-
`,this.pickingBanner.addEventListener("click",e=>{e.target.closest('[data-vz="cancel-pick"]')&&this.cancelPicking()}),this.root.appendChild(this.pickingBanner))}hidePickingBanner(){this.pickingBanner?.remove(),this.pickingBanner=null}jumpToReference(e,t){let i=this.comments.find(r=>r.id===e)?.references?.[t];i&&this.jumpToFingerprint(i)}jumpToFingerprint(e){let t=
|
|
1196
|
+
`,this.pickingBanner.addEventListener("click",e=>{e.target.closest('[data-vz="cancel-pick"]')&&this.cancelPicking()}),this.root.appendChild(this.pickingBanner))}hidePickingBanner(){this.pickingBanner?.remove(),this.pickingBanner=null}jumpToReference(e,t){let i=this.comments.find(r=>r.id===e)?.references?.[t];i&&this.jumpToFingerprint(i)}jumpToFingerprint(e){let t=V(e);if(!t){this.pill?.toast("Element not found on this page");return}if(t.scrollIntoView({behavior:"smooth",block:"center"}),this.root){let r=document.createElement("div");r.className="vz-jump-spotlight",this.root.appendChild(r),this.positionOutline(r,t);let s={el:r,target:t};this.spotlights.add(s),setTimeout(()=>{r.remove(),this.spotlights.delete(s)},1800)}let n=h(e),i=this.outlines.get(n)?.outline;i&&setTimeout(()=>{i.classList.remove("is-pulsing"),i.offsetWidth,i.classList.add("is-pulsing"),setTimeout(()=>i.classList.remove("is-pulsing"),1500)},250)}renderAllMarkers(){if(!this.root)return;for(let{marker:r}of this.markers.values())r.remove();for(let{outline:r}of this.outlines.values())r.remove();this.markers.clear(),this.outlines.clear();let e=new Map,t=new Map,n=r=>r==="exact"?3:r==="likely"?2:r==="drifted"?1:0,i=(r,s)=>r===void 0||n(s)>n(r)?s:r;for(let r of this.comments)for(let s of r.fingerprints||[]){let a=h(s),l=e.get(a);if(!l){let c=j(s);l={fp:s,target:c.element,confidence:c.confidence,commentIds:[]},e.set(a,l)}l.commentIds.push(r.id),t.set(r.id,i(t.get(r.id),l.confidence))}this.resolvedConfidence.clear();for(let r of this.comments){let s=t.get(r.id)??"orphaned";this.resolvedConfidence.set(r.id,s);let a=(r.fingerprints||[])[0],l=a?e.get(h(a))?.target??null:null;this.bus.emit("anchor:resolved",{comment:r,confidence:s,element:l})}for(let r of this.comments)this.selfHealedThisSession.has(r.id)||this.resolvedConfidence.get(r.id)==="drifted"&&this.selfHealComment(r,e);for(let[r,s]of e){if(s.confidence==="orphaned"||!s.target)continue;let a=document.createElement("div");a.className="vz-comment-outline",s.confidence==="drifted"&&a.classList.add("vz-comment-outline-drifted"),this.root.appendChild(a),this.outlines.set(r,{outline:a,target:s.target,fp:s.fp});let l=document.createElement("button");l.className="vz-marker",s.confidence==="drifted"&&l.classList.add("vz-marker-drifted"),l.setAttribute("data-element-key",r),l.setAttribute("data-confidence",s.confidence);let c=s.commentIds.length;if(c>1)l.textContent=String(c),l.title=c+" comments";else{l.textContent="";let p=this.comments.find(m=>m.id===s.commentIds[0]);l.title=p?.author?"Comment by "+p.author.name:"Comment"}l.addEventListener("click",p=>{if(p.preventDefault(),p.stopPropagation(),s.target){let m=this.commentsForElement(s.fp);this.openPopoverFor([s.target],[s.fp],m)}}),l.addEventListener("mouseenter",()=>this.pulseRelatedElements(s.commentIds,r)),this.root.appendChild(l),this.markers.set(r,{marker:l,target:s.target,fp:s.fp,commentIds:s.commentIds}),s.target?(this.positionMarker(l,s.target),this.positionOutline(a,s.target)):(l.style.display="none",a.style.display="none")}}pulseRelatedElements(e,t){let n=new Set;for(let i of e){let r=this.comments.find(a=>a.id===i);if(!r)continue;let s=r.fingerprints||[];if(!(s.length<=1))for(let a of s){let l=h(a);l!==t&&n.add(l)}}for(let i of n){let r=this.outlines.get(i)?.outline;r&&(r.classList.remove("is-pulsing"),r.offsetWidth,r.classList.add("is-pulsing"),setTimeout(()=>r.classList.remove("is-pulsing"),900))}}positionOutline(e,t){let n=t.getBoundingClientRect();if(n.width===0&&n.height===0){e.style.display="none";return}e.style.display="",e.style.left=n.left+"px",e.style.top=n.top+"px",e.style.width=n.width+"px",e.style.height=n.height+"px"}positionMarker(e,t){let n=t.getBoundingClientRect();if(n.width===0&&n.height===0){e.style.display="none";return}e.style.display="",e.style.left=n.right+"px",e.style.top=n.top+"px"}syncActiveAnchorOutlines(e){if(!this.root)return;let t=new Set(e.map(n=>h(n)));for(let[n,i]of this.activeAnchorOutlines)t.has(n)||(i.el.remove(),this.activeAnchorOutlines.delete(n));for(let n of e){let i=h(n);if(this.activeAnchorOutlines.has(i))continue;let r=document.createElement("div");r.className="vz-comment-outline is-selected-pending",this.root.appendChild(r);let s=V(n);s?this.positionOutline(r,s):r.style.display="none",this.activeAnchorOutlines.set(i,{el:r,fp:n})}}clearActiveAnchorOutlines(){for(let e of this.activeAnchorOutlines.values())e.el.remove();this.activeAnchorOutlines.clear()}actionContext(){return{comments:this.getComments(),pageHtml:this.snapshotHtml(),pageUrl:typeof location<"u"?location.href:void 0,pageVersion:this.opts.pageVersion,user:this.user??void 0,timestamp:Date.now(),copyToClipboard:e=>this.copyToClipboard(e),toast:e=>this.pill?.toast(e)}}snapshotHtml(){let e=document.documentElement.cloneNode(!0);for(let n of e.querySelectorAll("[data-vizu-root], #vizu-styles"))n.remove();return`<!DOCTYPE html>
|
|
1162
1197
|
<html`+document.documentElement.getAttributeNames().map(n=>` ${n}="${(document.documentElement.getAttribute(n)||"").replace(/"/g,""")}"`).join("")+`>
|
|
1163
1198
|
`+e.innerHTML+`
|
|
1164
|
-
</html>`}copyToClipboard(e){navigator.clipboard?.writeText?navigator.clipboard.writeText(e).catch(()=>ge(e)):ge(e)}};function ge(o){let e=document.createElement("textarea");e.value=o,e.style.position="fixed",e.style.opacity="0",document.body.appendChild(e),e.select();try{document.execCommand("copy")}catch{}e.remove()}function Ke(){if(typeof document>"u")return null;let o=document.currentScript;if(o)return o;let e=Array.from(document.scripts);return e.find(t=>t.dataset&&t.dataset.vizuAuto!==void 0)||e.find(t=>t.src&&/vizu(\.min)?\.js/.test(t.src))||null}function Je(){if(typeof window>"u"||window.__vizu)return;let e=Ke()?.dataset||{},t=e.storage&&["local","memory","none"].includes(e.storage)?e.storage:"local",n=e.userName?{id:e.userId,name:e.userName,avatarUrl:e.userAvatar,email:e.userEmail}:void 0,i={shortcut:e.shortcut||"mod+shift+e",namespace:e.namespace||"default",pageVersion:e.version,accent:e.accent,startEnabled:e.startEnabled==="true"||e.startEnabled===""||e.startEnabled==="1",ignoreSelectors:e.ignore?e.ignore.split(",").map(s=>s.trim()).filter(Boolean):[],storage:t,user:n},r=new
|
|
1199
|
+
</html>`}copyToClipboard(e){navigator.clipboard?.writeText?navigator.clipboard.writeText(e).catch(()=>ge(e)):ge(e)}};function ge(o){let e=document.createElement("textarea");e.value=o,e.style.position="fixed",e.style.opacity="0",document.body.appendChild(e),e.select();try{document.execCommand("copy")}catch{}e.remove()}function Ke(){if(typeof document>"u")return null;let o=document.currentScript;if(o)return o;let e=Array.from(document.scripts);return e.find(t=>t.dataset&&t.dataset.vizuAuto!==void 0)||e.find(t=>t.src&&/vizu(\.min)?\.js/.test(t.src))||null}function Je(){if(typeof window>"u"||window.__vizu)return;let e=Ke()?.dataset||{},t=e.storage&&["local","memory","none"].includes(e.storage)?e.storage:"local",n=e.userName?{id:e.userId,name:e.userName,avatarUrl:e.userAvatar,email:e.userEmail}:void 0,i={shortcut:e.shortcut||"mod+shift+e",namespace:e.namespace||"default",pageVersion:e.version,accent:e.accent,startEnabled:e.startEnabled==="true"||e.startEnabled===""||e.startEnabled==="1",ignoreSelectors:e.ignore?e.ignore.split(",").map(s=>s.trim()).filter(Boolean):[],storage:t,user:n},r=new S(i,"local");window.__vizu=r,window.Vizu=S}Je();return Ce(We);})();
|
|
1165
1200
|
//# sourceMappingURL=vizu.min.js.map
|