backpack-viewer 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,35 @@
1
+ var An=Object.defineProperty;var Bn=(t,e,a)=>e in t?An(t,e,{enumerable:!0,configurable:!0,writable:!0,value:a}):t[e]=a;var Ct=(t,e,a)=>Bn(t,typeof e!="symbol"?e+"":e,a);(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const d of document.querySelectorAll('link[rel="modulepreload"]'))r(d);new MutationObserver(d=>{for(const x of d)if(x.type==="childList")for(const b of x.addedNodes)b.tagName==="LINK"&&b.rel==="modulepreload"&&r(b)}).observe(document,{childList:!0,subtree:!0});function a(d){const x={};return d.integrity&&(x.integrity=d.integrity),d.referrerPolicy&&(x.referrerPolicy=d.referrerPolicy),d.crossOrigin==="use-credentials"?x.credentials="include":d.crossOrigin==="anonymous"?x.credentials="omit":x.credentials="same-origin",x}function r(d){if(d.ep)return;d.ep=!0;const x=a(d);fetch(d.href,x)}})();async function rt(){const t=await fetch("/api/ontologies");return t.ok?t.json():[]}async function vt(t){const e=await fetch(`/api/ontologies/${encodeURIComponent(t)}`);if(!e.ok)throw new Error(`Failed to load ontology: ${t}`);return e.json()}async function Pn(){const t=await fetch("/api/remotes");return t.ok?t.json():[]}async function Rn(t){const e=await fetch(`/api/remotes/${encodeURIComponent(t)}`);if(!e.ok)throw new Error(`Failed to load remote graph: ${t}`);return e.json()}async function Lt(t,e){if(!(await fetch(`/api/ontologies/${encodeURIComponent(t)}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)})).ok)throw new Error(`Failed to save ontology: ${t}`)}async function Dn(t,e){if(!(await fetch(`/api/ontologies/${encodeURIComponent(t)}/rename`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e})})).ok)throw new Error(`Failed to rename ontology: ${t}`)}async function Fn(t){const e=await fetch(`/api/graphs/${encodeURIComponent(t)}/branches`);return e.ok?e.json():[]}async function en(t,e,a){const r=await fetch(`/api/graphs/${encodeURIComponent(t)}/branches`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e,from:a})});if(!r.ok){const d=await r.json().catch(()=>({}));throw new Error(d.error||"Failed to create branch")}}async function tn(t,e){const a=await fetch(`/api/graphs/${encodeURIComponent(t)}/branches/switch`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e})});if(!a.ok){const r=await a.json().catch(()=>({}));throw new Error(r.error||"Failed to switch branch")}}async function $n(t,e){const a=await fetch(`/api/graphs/${encodeURIComponent(t)}/branches/${encodeURIComponent(e)}`,{method:"DELETE"});if(!a.ok){const r=await a.json().catch(()=>({}));throw new Error(r.error||"Failed to delete branch")}}async function On(t){const e=await fetch(`/api/graphs/${encodeURIComponent(t)}/snapshots`);return e.ok?e.json():[]}async function Wn(t,e){const a=await fetch(`/api/graphs/${encodeURIComponent(t)}/snapshots`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({label:e})});if(!a.ok){const r=await a.json().catch(()=>({}));throw new Error(r.error||"Failed to create snapshot")}}async function Hn(t,e){const a=await fetch(`/api/graphs/${encodeURIComponent(t)}/rollback`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({version:e})});if(!a.ok){const r=await a.json().catch(()=>({}));throw new Error(r.error||"Failed to rollback")}}async function zn(t){const e=await fetch(`/api/graphs/${encodeURIComponent(t)}/snippets`);return e.ok?e.json():[]}async function nn(t,e,a,r,d){const x=await fetch(`/api/graphs/${encodeURIComponent(t)}/snippets`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({label:e,description:d,nodeIds:a,edgeIds:r})});if(!x.ok)throw new Error("Failed to save snippet");return(await x.json()).id}async function jn(t,e){const a=await fetch(`/api/graphs/${encodeURIComponent(t)}/snippets/${encodeURIComponent(e)}`);if(!a.ok)throw new Error("Snippet not found");return a.json()}async function Yn(t,e){if(!(await fetch(`/api/graphs/${encodeURIComponent(t)}/snippets/${encodeURIComponent(e)}`,{method:"DELETE"})).ok)throw new Error("Failed to delete snippet")}const on="bp-dialog-overlay";function Ft(){var e;(e=document.querySelector(`.${on}`))==null||e.remove();const t=document.createElement("div");return t.className=on,document.body.appendChild(t),t}function $t(t,e){const a=document.createElement("div");a.className="bp-dialog";const r=document.createElement("h4");return r.className="bp-dialog-title",r.textContent=e,a.appendChild(r),t.appendChild(a),t.addEventListener("click",d=>{d.target===t&&t.remove()}),a}function Ot(t,e){const a=document.createElement("div");a.className="bp-dialog-buttons";for(const r of e){const d=document.createElement("button");d.className="bp-dialog-btn",r.accent&&d.classList.add("bp-dialog-btn-accent"),r.danger&&d.classList.add("bp-dialog-btn-danger"),d.textContent=r.label,d.addEventListener("click",r.onClick),a.appendChild(d)}t.appendChild(a)}function bn(t,e){return new Promise(a=>{var b;const r=Ft(),d=$t(r,t),x=document.createElement("p");x.className="bp-dialog-message",x.textContent=e,d.appendChild(x),Ot(d,[{label:"Cancel",onClick:()=>{r.remove(),a(!1)}},{label:"Confirm",accent:!0,onClick:()=>{r.remove(),a(!0)}}]),(b=d.querySelector(".bp-dialog-btn-accent"))==null||b.focus()})}function bt(t,e,a){return new Promise(r=>{const d=Ft(),x=$t(d,t),b=document.createElement("input");b.type="text",b.className="bp-dialog-input",b.placeholder=e??"",b.value="",x.appendChild(b);const c=()=>{const n=b.value.trim();d.remove(),r(n||null)};b.addEventListener("keydown",n=>{n.key==="Enter"&&c(),n.key==="Escape"&&(d.remove(),r(null))}),Ot(x,[{label:"Cancel",onClick:()=>{d.remove(),r(null)}},{label:"OK",accent:!0,onClick:c}]),b.focus(),b.select()})}function Un(){return new Promise(t=>{const e=Ft(),a=$t(e,"Add Backpack"),r=document.createElement("p");r.className="bp-dialog-message",r.textContent="Enter the absolute path to a directory that should become a backpack. It will be shown in the sidebar using the last segment of the path as its display name.",a.appendChild(r);const d=document.createElement("label");d.className="bp-dialog-label",d.textContent="Path",a.appendChild(d);const x=document.createElement("div");x.className="bp-dialog-path-row",a.appendChild(x);const b=document.createElement("input");b.type="text",b.className="bp-dialog-input bp-dialog-path-input",b.placeholder="/Users/you/OneDrive/work",x.appendChild(b);const c=document.createElement("button");c.type="button",c.className="bp-dialog-btn bp-dialog-browse-btn",c.textContent="Browse...",x.appendChild(c),typeof window.showDirectoryPicker=="function"||(c.disabled=!0,c.title="Browser doesn't support native folder picker — paste the path manually"),c.addEventListener("click",async g=>{g.preventDefault();try{const U=await window.showDirectoryPicker({mode:"read"});D.textContent=`Picked "${U.name}" — paste the absolute path to it below.`,b.focus()}catch{}});const D=document.createElement("div");D.className="bp-dialog-picker-hint",a.appendChild(D);const i=document.createElement("div");i.className="bp-dialog-activate-row";const s=document.createElement("input");s.type="checkbox",s.id="bp-dialog-activate",s.checked=!0;const L=document.createElement("label");L.htmlFor="bp-dialog-activate",L.textContent="Switch to this backpack after registering",i.appendChild(s),i.appendChild(L),a.appendChild(i),b.addEventListener("dragover",g=>{g.preventDefault(),b.classList.add("bp-dialog-drag-over")}),b.addEventListener("dragleave",()=>{b.classList.remove("bp-dialog-drag-over")}),b.addEventListener("drop",g=>{var oe,W,ee;g.preventDefault(),b.classList.remove("bp-dialog-drag-over");const U=(oe=g.dataTransfer)==null?void 0:oe.items;if(!U||U.length===0)return;const q=(ee=(W=U[0]).webkitGetAsEntry)==null?void 0:ee.call(W);q!=null&&q.isDirectory&&(D.textContent=`Dropped "${q.name}" — paste the absolute path to it below.`)});const $=()=>{const g=b.value.trim();g&&(e.remove(),t({path:g,activate:s.checked}))};b.addEventListener("keydown",g=>{g.key==="Enter"&&$(),g.key==="Escape"&&(e.remove(),t(null))}),Ot(a,[{label:"Cancel",onClick:()=>{e.remove(),t(null)}},{label:"Register",accent:!0,onClick:$}]),b.focus()})}function Xn(t,e=3e3){const a=document.querySelector(".bp-toast");a&&a.remove();const r=document.createElement("div");r.className="bp-toast",r.textContent=t,document.body.appendChild(r),setTimeout(()=>r.classList.add("bp-toast-visible"),10),setTimeout(()=>{r.classList.remove("bp-toast-visible"),setTimeout(()=>r.remove(),300)},e)}function sn(t){return t>=1e3?`${(t/1e3).toFixed(1)}k tokens`:`${t} tokens`}function an(t,e){return t*50+e*25+50}function qn(t,e){const a=typeof e=="function"?{onSelect:e}:e,r=document.createElement("h2");r.textContent="Backpack Viewer";const d=document.createElement("input");d.type="text",d.placeholder="Filter...",d.id="filter";const x=document.createElement("ul");x.id="ontology-list";const b=document.createElement("h3");b.className="sidebar-section-heading",b.textContent="REMOTE GRAPHS",b.hidden=!0;const c=document.createElement("ul");c.id="remote-list",c.className="remote-list",c.hidden=!0;const n=document.createElement("div");n.className="sidebar-footer",n.innerHTML='<a href="mailto:support@backpackontology.com">support@backpackontology.com</a><span>Feedback & support</span><span class="sidebar-version">v0.6.0</span>';const D=document.createElement("button");D.className="sidebar-collapse-btn",D.title="Toggle sidebar (Tab)",D.innerHTML='<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="11 17 6 12 11 7"/><polyline points="18 17 13 12 18 7"/></svg>';let i=!1;function s(){i=!i,t.classList.toggle("sidebar-collapsed",i),de.classList.toggle("hidden",!i)}D.addEventListener("click",s);const L=document.createElement("div");L.className="sidebar-heading-row",L.appendChild(r),L.appendChild(D),t.appendChild(L);const $=document.createElement("div");$.className="sidebar-stale-banner",$.hidden=!0,t.appendChild($);const g=document.createElement("button");g.className="backpack-picker-pill",g.type="button",g.setAttribute("aria-haspopup","listbox"),g.setAttribute("aria-expanded","false");const U=document.createElement("span");U.className="backpack-picker-dot";const q=document.createElement("span");q.className="backpack-picker-name",q.textContent="...";const oe=document.createElement("span");oe.className="backpack-picker-caret",oe.textContent="▾",g.appendChild(U),g.appendChild(q),g.appendChild(oe);const W=document.createElement("div");W.className="backpack-picker-dropdown",W.hidden=!0,W.setAttribute("role","listbox");const ee=document.createElement("div");ee.className="backpack-picker-container",ee.appendChild(g),ee.appendChild(W),t.appendChild(ee);let _=!1;function B(){_=!1,W.hidden=!0,g.setAttribute("aria-expanded","false")}function se(){_=!0,W.hidden=!1,g.setAttribute("aria-expanded","true")}g.addEventListener("click",j=>{j.stopPropagation(),_?B():se()}),document.addEventListener("click",j=>{ee.contains(j.target)||B()});let J=[],z=null;function te(){W.replaceChildren();for(const S of J){const l=document.createElement("button");l.className="backpack-picker-item",l.type="button",l.setAttribute("role","option"),S.active&&l.classList.add("active");const f=document.createElement("span");f.className="backpack-picker-item-dot",f.style.setProperty("--backpack-color",S.color);const k=document.createElement("span");k.className="backpack-picker-item-name",k.textContent=S.name;const C=document.createElement("span");C.className="backpack-picker-item-path",C.textContent=S.path,l.appendChild(f),l.appendChild(k),l.appendChild(C),l.addEventListener("click",E=>{E.stopPropagation(),B(),!S.active&&a.onBackpackSwitch&&a.onBackpackSwitch(S.name)}),W.appendChild(l)}const j=document.createElement("div");j.className="backpack-picker-divider",W.appendChild(j);const G=document.createElement("button");G.className="backpack-picker-item backpack-picker-add",G.type="button",G.textContent="+ Add new backpack…",G.addEventListener("click",async S=>{if(S.stopPropagation(),B(),!a.onBackpackRegister)return;const l=await Un();l&&a.onBackpackRegister(l.path,l.activate)}),W.appendChild(G)}const de=document.createElement("button");de.className="tools-pane-toggle hidden",de.title="Show sidebar (Tab)",de.innerHTML='<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="13 7 18 12 13 17"/><polyline points="6 7 11 12 6 17"/></svg>',de.addEventListener("click",s),t.appendChild(d),t.appendChild(x),t.appendChild(b),t.appendChild(c),t.appendChild(n);let ce=[],ue=[],ye="";return d.addEventListener("input",()=>{const j=d.value.toLowerCase();for(const G of ce){const S=G.dataset.name??"";G.style.display=S.includes(j)?"":"none"}for(const G of ue){const S=G.dataset.name??"";G.style.display=S.includes(j)?"":"none"}}),{setStaleVersionBanner(j,G){$.replaceChildren();const S=document.createElement("div");S.className="sidebar-stale-banner-title",S.textContent=`Viewer ${j} is out of date`;const l=document.createElement("div");l.className="sidebar-stale-banner-subtitle",l.textContent=`Latest is ${G}. Your version is stuck because of an npx cache.`;const f=document.createElement("pre");f.className="sidebar-stale-banner-hint",f.textContent=`npm cache clean --force
2
+ npx backpack-viewer@latest`,$.appendChild(S),$.appendChild(l),$.appendChild(f),$.hidden=!1},setBackpacks(j){J=j.slice();const G=j.find(S=>S.active)??null;z=G,G&&(q.textContent=G.name,U.style.setProperty("--backpack-color",G.color),t.style.setProperty("--backpack-color",G.color)),te()},setActiveBackpack(j){z=j,J=J.map(G=>({...G,active:G.name===j.name})),J.some(G=>G.name===j.name)||J.push({...j,active:!0}),q.textContent=j.name,U.style.setProperty("--backpack-color",j.color),t.style.setProperty("--backpack-color",j.color),te()},getActiveBackpack(){return z},setSummaries(j){x.innerHTML="";const G=fetch("/api/locks").then(S=>S.json()).catch(()=>({}));ce=j.map(S=>{const l=document.createElement("li");l.className="ontology-item",l.dataset.name=S.name;const f=document.createElement("span");f.className="name",f.textContent=S.name;const k=document.createElement("span");k.className="stats";const C=an(S.nodeCount,S.edgeCount);k.textContent=`${S.nodeCount} nodes, ${S.edgeCount} edges · ~${sn(C)}`;const E=document.createElement("span");E.className="sidebar-branch",E.dataset.graph=S.name;const y=document.createElement("span");if(y.className="sidebar-lock-badge",y.dataset.graph=S.name,G.then(m=>{if(!y.isConnected)return;const T=m[S.name];T&&typeof T=="object"&&T.author&&(y.textContent=`editing: ${T.author}`,y.title=`Last activity: ${T.lastActivity??""}`,y.classList.add("active"))}),l.appendChild(f),l.appendChild(k),l.appendChild(y),l.appendChild(E),a.onRename){const m=document.createElement("button");m.className="sidebar-edit-btn",m.textContent="✎",m.title="Rename";const T=a.onRename;m.addEventListener("click",w=>{w.stopPropagation();const P=document.createElement("input");P.type="text",P.className="sidebar-rename-input",P.value=S.name,f.textContent="",f.appendChild(P),m.style.display="none",P.focus(),P.select();const A=()=>{const h=P.value.trim();h&&h!==S.name?T(S.name,h):(f.textContent=S.name,m.style.display="")};P.addEventListener("blur",A),P.addEventListener("keydown",h=>{h.key==="Enter"&&P.blur(),h.key==="Escape"&&(P.value=S.name,P.blur())})}),l.appendChild(m)}return l.addEventListener("click",()=>a.onSelect(S.name)),x.appendChild(l),l}),ye&&this.setActive(ye)},setActive(j){ye=j;for(const G of ce)G.classList.toggle("active",G.dataset.name===j);for(const G of ue)G.classList.toggle("active",G.dataset.name===j)},setRemotes(j){c.replaceChildren(),ue=j.map(S=>{const l=document.createElement("li");l.className="ontology-item ontology-item-remote",l.dataset.name=S.name;const f=document.createElement("div");f.className="remote-name-row";const k=document.createElement("span");k.className="name",k.textContent=S.name;const C=document.createElement("span");C.className="remote-badge",C.textContent=S.pinned?"remote · pinned":"remote",C.title=`Source: ${S.source??S.url}`,f.appendChild(k),f.appendChild(C);const E=document.createElement("span");E.className="stats";const y=an(S.nodeCount,S.edgeCount);E.textContent=`${S.nodeCount} nodes, ${S.edgeCount} edges · ~${sn(y)}`;const m=document.createElement("span");return m.className="remote-source",m.textContent=S.source??new URL(S.url).hostname,m.title=S.url,l.appendChild(f),l.appendChild(E),l.appendChild(m),l.addEventListener("click",()=>a.onSelect(S.name)),c.appendChild(l),l});const G=j.length>0;b.hidden=!G,c.hidden=!G,ye&&this.setActive(ye)},setActiveBranch(j,G,S){const l=x.querySelectorAll(`.sidebar-branch[data-graph="${j}"]`);for(const f of l){f.textContent=`/ ${G}`,f.title="Click to switch branch",f.style.cursor="pointer";const k=f.cloneNode(!0);f.replaceWith(k),k.addEventListener("click",C=>{C.stopPropagation(),ve(j,k,S??[])})}},setSnippets(j,G){var f;const S=ce.find(k=>k.dataset.name===j);if(!S||((f=S.querySelector(".sidebar-snippets"))==null||f.remove(),G.length===0))return;const l=document.createElement("div");l.className="sidebar-snippets";for(const k of G){const C=document.createElement("div");C.className="sidebar-snippet";const E=document.createElement("span");E.className="sidebar-snippet-label",E.textContent=`◆ ${k.label}`,E.title=`${k.nodeCount} nodes — click to load`;const y=document.createElement("button");y.className="sidebar-snippet-delete",y.textContent="×",y.title="Delete snippet",y.addEventListener("click",m=>{var T;m.stopPropagation(),(T=a.onSnippetDelete)==null||T.call(a,j,k.id)}),C.appendChild(E),C.appendChild(y),C.addEventListener("click",m=>{var T;m.stopPropagation(),(T=a.onSnippetLoad)==null||T.call(a,j,k.id)}),l.appendChild(C)}S.appendChild(l)},toggle:s,expandBtn:de};function ve(j,G,S){const l=t.querySelector(".branch-picker");l&&l.remove();const f=document.createElement("div");f.className="branch-picker";for(const C of S){const E=document.createElement("div");E.className="branch-picker-item",C.active&&E.classList.add("branch-picker-active");const y=document.createElement("span");if(y.textContent=C.name,E.appendChild(y),!C.active&&a.onBranchDelete){const m=document.createElement("button");m.className="branch-picker-delete",m.textContent="×",m.title=`Delete ${C.name}`,m.addEventListener("click",T=>{T.stopPropagation(),bn("Delete branch",`Delete branch "${C.name}"?`).then(w=>{w&&(a.onBranchDelete(j,C.name),f.remove())})}),E.appendChild(m)}C.active||E.addEventListener("click",()=>{var m;(m=a.onBranchSwitch)==null||m.call(a,j,C.name),f.remove()}),f.appendChild(E)}if(a.onBranchCreate){const C=document.createElement("div");C.className="branch-picker-item branch-picker-create",C.textContent="+ New branch",C.addEventListener("click",()=>{bt("New branch","Branch name").then(E=>{E&&(a.onBranchCreate(j,E),f.remove())})}),f.appendChild(C)}G.after(f);const k=C=>{f.contains(C.target)||(f.remove(),document.removeEventListener("click",k))};setTimeout(()=>document.addEventListener("click",k),0)}}function Rt(t,e,a,r){return{x0:t,y0:e,x1:a,y1:r,cx:0,cy:0,mass:0,children:[null,null,null,null],body:null}}function cn(t,e,a){const r=(t.x0+t.x1)/2,d=(t.y0+t.y1)/2;return(e<r?0:1)+(a<d?0:2)}function ln(t,e){const a=(t.x0+t.x1)/2,r=(t.y0+t.y1)/2;switch(e){case 0:return[t.x0,t.y0,a,r];case 1:return[a,t.y0,t.x1,r];case 2:return[t.x0,r,a,t.y1];default:return[a,r,t.x1,t.y1]}}function Dt(t,e){if(t.mass===0&&t.body===null){t.body=e,t.cx=e.x,t.cy=e.y,t.mass=1;return}if(t.body!==null){const d=t.body;t.body=null,d.x===e.x&&d.y===e.y&&(e.x+=(Math.random()-.5)*.1,e.y+=(Math.random()-.5)*.1);const x=cn(t,d.x,d.y);if(t.children[x]===null){const[b,c,n,D]=ln(t,x);t.children[x]=Rt(b,c,n,D)}Dt(t.children[x],d)}const a=cn(t,e.x,e.y);if(t.children[a]===null){const[d,x,b,c]=ln(t,a);t.children[a]=Rt(d,x,b,c)}Dt(t.children[a],e);const r=t.mass+1;t.cx=(t.cx*t.mass+e.x)/r,t.cy=(t.cy*t.mass+e.y)/r,t.mass=r}function _n(t){if(t.length===0)return null;let e=1/0,a=1/0,r=-1/0,d=-1/0;for(const i of t)i.x<e&&(e=i.x),i.y<a&&(a=i.y),i.x>r&&(r=i.x),i.y>d&&(d=i.y);const x=Math.max(r-e,d-a)*.1+50,b=(e+r)/2,c=(a+d)/2,n=Math.max(r-e,d-a)/2+x,D=Rt(b-n,c-n,b+n,c+n);for(const i of t)Dt(D,i);return D}function Gn(t,e,a,r,d,x){En(t,e,a,r,d,x)}function En(t,e,a,r,d,x){if(t.mass===0)return;const b=t.cx-e.x,c=t.cy-e.y,n=b*b+c*c;if(t.body!==null){if(t.body!==e){let i=Math.sqrt(n);i<x&&(i=x);const s=r*d/(i*i),L=b/i*s,$=c/i*s;e.vx-=L,e.vy-=$}return}const D=t.x1-t.x0;if(D*D/n<a*a){let i=Math.sqrt(n);i<x&&(i=x);const s=r*t.mass*d/(i*i),L=b/i*s,$=c/i*s;e.vx-=L,e.vy-=$;return}for(let i=0;i<4;i++)t.children[i]!==null&&En(t.children[i],e,a,r,d,x)}const wn={clusterStrength:.08,spacing:1.5},rn=6e3,Vn=12e3,Kn=.004,kn=140,Sn=350,dn=.9,pn=.01,dt=30,It=50;let Ge={...wn};function ot(t){t.clusterStrength!==void 0&&(Ge.clusterStrength=t.clusterStrength),t.spacing!==void 0&&(Ge.spacing=t.spacing)}function mt(){return{...Ge}}function Jn(t){if(t<=30)return{...wn};const e=Math.log2(t/30);return{clusterStrength:Math.min(.5,.08+.06*e),spacing:Math.min(15,1.5+1.2*e)}}function Zn(t,e){for(const a of Object.values(t))if(typeof a=="string")return a;return e}function un(t,e,a){const r=new Set(e);let d=new Set(e);for(let x=0;x<a;x++){const b=new Set;for(const c of t.edges)d.has(c.sourceId)&&!r.has(c.targetId)&&b.add(c.targetId),d.has(c.targetId)&&!r.has(c.sourceId)&&b.add(c.sourceId);for(const c of b)r.add(c);if(d=b,b.size===0)break}return{nodes:t.nodes.filter(x=>r.has(x.id)),edges:t.edges.filter(x=>r.has(x.sourceId)&&r.has(x.targetId)),metadata:t.metadata}}function Mt(t){const e=new Map,a=[...new Set(t.nodes.map(n=>n.type))],r=Math.sqrt(a.length)*Sn*.6*Math.max(1,Ge.spacing),d=new Map,x=new Map;for(const n of t.nodes)x.set(n.type,(x.get(n.type)??0)+1);const b=t.nodes.map(n=>{const D=a.indexOf(n.type),i=2*Math.PI*D/Math.max(a.length,1),s=Math.cos(i)*r,L=Math.sin(i)*r,$=d.get(n.type)??0;d.set(n.type,$+1);const g=x.get(n.type)??1,U=2*Math.PI*$/g,q=kn*.6,oe={id:n.id,x:s+Math.cos(U)*q,y:L+Math.sin(U)*q,vx:0,vy:0,label:Zn(n.properties,n.id),type:n.type};return e.set(n.id,oe),oe}),c=t.edges.map(n=>({sourceId:n.sourceId,targetId:n.targetId,type:n.type}));return{nodes:b,edges:c,nodeMap:e}}const Qn=.7,eo=80;function to(t,e){const{nodes:a,edges:r,nodeMap:d}=t,x=Vn*Ge.spacing;if(a.length>=eo){const c=_n(a);if(c)for(const D of a)Gn(c,D,Qn,x,e,dt);const n=x-rn;if(n>0){const D=new Map;for(const i of a){let s=D.get(i.type);s||(s=[],D.set(i.type,s)),s.push(i)}for(const i of D.values())for(let s=0;s<i.length;s++)for(let L=s+1;L<i.length;L++){const $=i[s],g=i[L];let U=g.x-$.x,q=g.y-$.y,oe=Math.sqrt(U*U+q*q);oe<dt&&(oe=dt);const W=n*e/(oe*oe),ee=U/oe*W,_=q/oe*W;$.vx+=ee,$.vy+=_,g.vx-=ee,g.vy-=_}}}else for(let c=0;c<a.length;c++)for(let n=c+1;n<a.length;n++){const D=a[c],i=a[n];let s=i.x-D.x,L=i.y-D.y,$=Math.sqrt(s*s+L*L);$<dt&&($=dt);const U=(D.type===i.type?rn:x)*e/($*$),q=s/$*U,oe=L/$*U;D.vx-=q,D.vy-=oe,i.vx+=q,i.vy+=oe}for(const c of r){const n=d.get(c.sourceId),D=d.get(c.targetId);if(!n||!D)continue;const i=D.x-n.x,s=D.y-n.y,L=Math.sqrt(i*i+s*s);if(L===0)continue;const $=n.type===D.type?kn*Ge.spacing:Sn*Ge.spacing,g=Kn*(L-$)*e,U=i/L*g,q=s/L*g;n.vx+=U,n.vy+=q,D.vx-=U,D.vy-=q}for(const c of a)c.vx-=c.x*pn*e,c.vy-=c.y*pn*e;const b=new Map;for(const c of a){const n=b.get(c.type)??{x:0,y:0,count:0};n.x+=c.x,n.y+=c.y,n.count++,b.set(c.type,n)}for(const c of b.values())c.x/=c.count,c.y/=c.count;for(const c of a){const n=b.get(c.type);c.vx+=(n.x-c.x)*Ge.clusterStrength*e,c.vy+=(n.y-c.y)*Ge.clusterStrength*e}for(const c of a){if(c.pinned){c.vx=0,c.vy=0;continue}c.vx*=dn,c.vy*=dn;const n=Math.sqrt(c.vx*c.vx+c.vy*c.vy);n>It&&(c.vx=c.vx/n*It,c.vy=c.vy/n*It),c.x+=c.vx,c.y+=c.vy}return e*.995}const mn=["#d4a27f","#c17856","#b07a5e","#d4956b","#a67c5a","#cc9e7c","#c4866a","#cb8e6c","#b8956e","#a88a70","#d9b08c","#c4a882","#e8b898","#b5927a","#a8886e","#d1a990"],hn=new Map;function Le(t){const e=hn.get(t);if(e)return e;let a=0;for(let d=0;d<t.length;d++)a=(a<<5)-a+t.charCodeAt(d)|0;const r=mn[Math.abs(a)%mn.length];return hn.set(t,r),r}class no{constructor(e){Ct(this,"cells",new Map);Ct(this,"cellSize");Ct(this,"invCell");this.cellSize=e,this.invCell=1/e}key(e,a){const r=e+32768|0,d=a+32768|0;return r*73856093^d*19349663}clear(){this.cells.clear()}insert(e){const a=Math.floor(e.x*this.invCell),r=Math.floor(e.y*this.invCell),d=this.key(a,r),x=this.cells.get(d);x?x.push(e):this.cells.set(d,[e])}rebuild(e){this.cells.clear();for(const a of e)this.insert(a)}query(e,a,r){const d=r*r,x=Math.floor((e-r)*this.invCell),b=Math.floor((e+r)*this.invCell),c=Math.floor((a-r)*this.invCell),n=Math.floor((a+r)*this.invCell);let D=null,i=d;for(let s=x;s<=b;s++)for(let L=c;L<=n;L++){const $=this.cells.get(this.key(s,L));if($)for(const g of $){const U=g.x-e,q=g.y-a,oe=U*U+q*q;oe<=i&&(i=oe,D=g)}}return D}}const st=new Map,oo=2e3;function so(t,e,a){return`${t}|${e}|${a}`}const ao=new OffscreenCanvas(1,1),fn=ao.getContext("2d");function co(t,e,a){fn.font=e;const r=fn.measureText(t),d=Math.ceil(r.width)+2,x=Math.ceil(r.actualBoundingBoxAscent+r.actualBoundingBoxDescent)+4,b=new OffscreenCanvas(d,x),c=b.getContext("2d");return c.font=e,c.fillStyle=a,c.textAlign="left",c.textBaseline="top",c.fillText(t,1,1),{canvas:b,width:d,height:x}}function gn(t,e,a,r,d,x,b){const c=so(e,d,x);let n=st.get(c);if(!n){if(st.size>=oo){const s=st.keys().next().value;s!==void 0&&st.delete(s)}n=co(e,d,x),st.set(c,n)}const D=a-n.width/2,i=b==="top"?r:r-n.height;t.drawImage(n.canvas,D,i)}function io(){st.clear()}function ge(t){return getComputedStyle(document.documentElement).getPropertyValue(t).trim()}const Fe=20,Tt=.001,lo={hideBadges:.4,hideLabels:.25,hideEdgeLabels:.35,smallNodes:.2,hideArrows:.15,dotNodes:.1,hullsOnly:.05},ro={zoomFactor:1.3,zoomMin:.05,zoomMax:10,panAnimationMs:300};function xt(t,e,a,r,d,x=100){const b=(t-a.x)*a.scale,c=(e-a.y)*a.scale;return b>=-x&&b<=r+x&&c>=-x&&c<=d+x}function po(t,e,a,r){const d={...lo,...(r==null?void 0:r.lod)??{}},x={...ro,...(r==null?void 0:r.navigation)??{}},b={pulseSpeed:.02,...(r==null?void 0:r.walk)??{}},c=t.querySelector("canvas"),n=c.getContext("2d"),D=window.devicePixelRatio||1;let i={x:0,y:0,scale:1},s=null,L=1,$=0,g=new Set,U=null,q=!0,oe=!0,W=!0,ee=!0,_="idle",B=[],se=0,J=0,z=null,te=new Set;const de=5;let ce=null,ue=null,ye=1,ve=null,j=null,G=null,S=!1,l=[],f=0,k=0;const C=400,E=new no(Fe*2);let y=0;function m(){he(),y||(y=requestAnimationFrame(()=>{y=0,we()}))}const T=150;let w=null,P=!1;function A(){if(!w)try{w=new Worker(new URL("/assets/layout-worker-4xak23M6.js",import.meta.url),{type:"module"}),w.onmessage=h,w.onerror=()=>{P=!1,w=null,Ue()}}catch{return P=!1,null}return w}function h(p){const v=p.data;if(v.type==="tick"&&s){const M=v.positions,F=s.nodes;for(let Y=0;Y<F.length;Y++)F[Y].x=M[Y*4],F[Y].y=M[Y*4+1],F[Y].vx=M[Y*4+2],F[Y].vy=M[Y*4+3];L=v.alpha,E.rebuild(F),we()}v.type==="settled"&&(L=0,S&&l.length>0&&!Be&&(Be=requestAnimationFrame(ht)))}let N=null,X=null,ae=!0;function he(){ae=!0}let o=null,u=null;const R=x.panAnimationMs;function V(){c.width=c.clientWidth*D,c.height=c.clientHeight*D,he(),m()}const ie=new ResizeObserver(V);ie.observe(t),V();function le(p,v){return[p/i.scale+i.x,v/i.scale+i.y]}function me(p,v){if(!s)return null;const[M,F]=le(p,v);return E.query(M,F,Fe)}function Ce(){if(!z)return;n.save();const p=Math.min(z.x1,z.x2),v=Math.max(z.x1,z.x2),M=Math.min(z.y1,z.y2),F=Math.max(z.y1,z.y2);n.strokeStyle=ge("--accent")||"#d4a27f",n.fillStyle=ge("--accent")||"#d4a27f",n.globalAlpha=.12,n.fillRect(p,M,v-p,F-M),n.globalAlpha=.8,n.lineWidth=1/Math.max(i.scale,.5),n.setLineDash([6/Math.max(i.scale,.5),4/Math.max(i.scale,.5)]),n.strokeRect(p,M,v-p,F-M),n.setLineDash([]),n.restore()}function Me(){if(!s)return;f+=b.pulseSpeed;const p=new Set(l);n.save(),n.setTransform(D,0,0,D,0,0),N&&(n.clearRect(0,0,c.clientWidth,c.clientHeight),n.drawImage(N,0,0,c.clientWidth,c.clientHeight)),n.save(),n.translate(-i.x*i.scale,-i.y*i.scale),n.scale(i.scale,i.scale);const v=ge("--canvas-walk-edge")||"#1a1a1a",M=[];for(const H of s.edges){if(!p.has(H.sourceId)||!p.has(H.targetId)||H.sourceId===H.targetId)continue;const O=s.nodeMap.get(H.sourceId),K=s.nodeMap.get(H.targetId);!O||!K||M.push(O.x,O.y,K.x,K.y)}if(M.length>0){n.beginPath();for(let H=0;H<M.length;H+=4)n.moveTo(M[H],M[H+1]),n.lineTo(M[H+2],M[H+3]);n.strokeStyle=v,n.lineWidth=3,n.globalAlpha=.5+.5*Math.sin(f),n.stroke(),n.globalAlpha=1}const F=i.scale<d.smallNodes?Fe*.5:Fe,Y=ge("--accent")||"#d4a27f";for(const H of l){const O=s.nodeMap.get(H);if(!O||!xt(O.x,O.y,i,c.clientWidth,c.clientHeight))continue;const K=H===l[l.length-1],Z=.5+.5*Math.sin(f);n.strokeStyle=Y,n.lineWidth=K?3:2,n.globalAlpha=K?.5+.5*Z:.3+.4*Z,n.beginPath(),n.arc(O.x,O.y,F+(K?6:4),0,Math.PI*2),n.stroke()}n.globalAlpha=1,Ce(),n.restore(),ee&&s.nodes.length>1&&Je(),n.restore()}function we(){var Gt;if(!s){n.clearRect(0,0,c.width,c.height);return}if(!ae&&N&&S&&l.length>0&&L<Tt){Me();return}const p=L<Tt&&S&&l.length>0;S&&l.length>0&&(f+=b.pulseSpeed);const v=S&&!p?new Set(l):null,M=ge("--canvas-edge"),F=ge("--canvas-edge-highlight"),Y=ge("--canvas-edge-dim"),H=ge("--canvas-edge-label"),O=ge("--canvas-edge-label-highlight"),K=ge("--canvas-edge-label-dim"),Z=ge("--canvas-arrow"),re=ge("--canvas-arrow-highlight"),fe=ge("--canvas-node-label"),xe=ge("--canvas-node-label-dim"),be=ge("--canvas-type-badge"),Ne=ge("--canvas-type-badge-dim"),Te=ge("--canvas-selection-border"),$e=ge("--canvas-node-border");if(n.save(),n.setTransform(D,0,0,D,0,0),n.clearRect(0,0,c.clientWidth,c.clientHeight),n.save(),n.translate(-i.x*i.scale,-i.y*i.scale),n.scale(i.scale,i.scale),W&&i.scale>=d.smallNodes){const Q=new Map;for(const Ie of s.nodes){if(U!==null&&!U.has(Ie.id))continue;const Re=Q.get(Ie.type)??[];Re.push(Ie),Q.set(Ie.type,Re)}for(const[Ie,Re]of Q){if(Re.length<2)continue;const Qe=Le(Ie),Ke=Fe*2.5;let ze=1/0,Ye=1/0,Ae=-1/0,_e=-1/0;for(const De of Re)De.x<ze&&(ze=De.x),De.y<Ye&&(Ye=De.y),De.x>Ae&&(Ae=De.x),De.y>_e&&(_e=De.y);n.beginPath();const ne=(Ae-ze)/2+Ke,Se=(_e-Ye)/2+Ke,Ee=(ze+Ae)/2,Oe=(Ye+_e)/2;n.ellipse(Ee,Oe,ne,Se,0,0,Math.PI*2),n.fillStyle=Qe,n.globalAlpha=.05,n.fill(),n.strokeStyle=Qe,n.globalAlpha=.12,n.lineWidth=1,n.setLineDash([4,4]),n.stroke(),n.setLineDash([]),n.globalAlpha=1}}let je=null;if(g.size>0){je=new Set;for(const Q of s.edges)g.has(Q.sourceId)&&je.add(Q.targetId),g.has(Q.targetId)&&je.add(Q.sourceId)}const Ve=ge("--accent")||"#d4a27f",He=ge("--canvas-walk-edge")||"#1a1a1a",nt=i.scale>=d.hideArrows,gt=oe&&i.scale>=d.hideEdgeLabels;if(q){const Q=[],Ie=[],Re=[],Qe=[],Ke=[],ze=[];for(const ne of s.edges){const Se=s.nodeMap.get(ne.sourceId),Ee=s.nodeMap.get(ne.targetId);if(!Se||!Ee||!xt(Se.x,Se.y,i,c.clientWidth,c.clientHeight,200)&&!xt(Ee.x,Ee.y,i,c.clientWidth,c.clientHeight,200))continue;const Oe=U===null||U.has(ne.sourceId),De=U===null||U.has(ne.targetId),Vt=Oe&&De;if(U!==null&&!Oe&&!De)continue;const Nt=g.size>0&&(g.has(ne.sourceId)||g.has(ne.targetId))||U!==null&&Vt,Kt=U!==null&&!Vt,Jt=v!==null&&v.has(ne.sourceId)&&v.has(ne.targetId),Zt=G?ce==null?void 0:ce.edges.find(yt=>yt.sourceId===ne.sourceId&&yt.targetId===ne.targetId||yt.targetId===ne.sourceId&&yt.sourceId===ne.targetId):null,Qt=!!(G&&Zt&&G.edgeIds.has(Zt.id));if(ne.sourceId===ne.targetId){In(Se,ne.type,Nt,M,F,H,O);continue}(Qt?Ke:Jt?Qe:Nt?Ie:Kt?Re:Q).push(Se.x,Se.y,Ee.x,Ee.y),(nt||gt)&&ze.push({sx:Se.x,sy:Se.y,tx:Ee.x,ty:Ee.y,type:ne.type,highlighted:Nt,edgeDimmed:Kt,isPathEdge:Qt,isWalkEdge:Jt})}const Ye=nt?1.5:1,_e=[{lines:Q,color:M,width:Ye,alpha:1},{lines:Re,color:Y,width:Ye,alpha:1},{lines:Ie,color:F,width:nt?2.5:1,alpha:1},{lines:Ke,color:Ve,width:3,alpha:1},{lines:Qe,color:He,width:3,alpha:.5+.5*Math.sin(f)}];for(const ne of _e)if(ne.lines.length!==0){n.beginPath();for(let Se=0;Se<ne.lines.length;Se+=4)n.moveTo(ne.lines[Se],ne.lines[Se+1]),n.lineTo(ne.lines[Se+2],ne.lines[Se+3]);n.strokeStyle=ne.color,n.lineWidth=ne.width,n.globalAlpha=ne.alpha,n.stroke()}n.globalAlpha=1;for(const ne of ze)if(nt&&Ln(ne.sx,ne.sy,ne.tx,ne.ty,ne.highlighted||ne.isPathEdge,Z,re),gt){const Se=(ne.sx+ne.tx)/2,Ee=(ne.sy+ne.ty)/2;n.fillStyle=ne.highlighted?O:ne.edgeDimmed?K:H,n.font="9px system-ui, sans-serif",n.textAlign="center",n.textBaseline="bottom",n.fillText(ne.type,Se,Ee-4)}}const St=performance.now()-k,ke=Math.min(1,St/C),Xe=1-(1-ke)*(1-ke),qe=ke<1,_t=i.scale<d.hullsOnly,Tn=!_t&&i.scale<d.dotNodes;if(!_t)for(const Q of s.nodes){if(!xt(Q.x,Q.y,i,c.clientWidth,c.clientHeight))continue;const Ie=Le(Q.type);if(Tn){const Ee=U!==null&&!U.has(Q.id);n.fillStyle=Ie;const Oe=Ee?.1:.8;n.globalAlpha=qe?Oe*Xe:Oe,n.fillRect(Q.x-2,Q.y-2,4,4);continue}const Re=g.has(Q.id),Qe=je!==null&&je.has(Q.id),Ke=U!==null&&!U.has(Q.id),ze=Ke||g.size>0&&!Re&&!Qe,Ye=i.scale<d.smallNodes?Fe*.5:Fe,Ae=qe?Ye*Xe:Ye;if(v!=null&&v.has(Q.id)){const Ee=l[l.length-1]===Q.id,Oe=.5+.5*Math.sin(f),De=ge("--accent")||"#d4a27f";n.save(),n.strokeStyle=De,n.lineWidth=Ee?3:2,n.globalAlpha=Ee?.5+.5*Oe:.3+.4*Oe,n.beginPath(),n.arc(Q.x,Q.y,Ae+(Ee?6:4),0,Math.PI*2),n.stroke(),n.restore()}Re&&(n.save(),n.shadowColor=Ie,n.shadowBlur=20,n.beginPath(),n.arc(Q.x,Q.y,Ae+3,0,Math.PI*2),n.fillStyle=Ie,n.globalAlpha=.3,n.fill(),n.restore()),n.beginPath(),n.arc(Q.x,Q.y,Ae,0,Math.PI*2),n.fillStyle=Ie;const _e=Ke?.1:ze?.3:1;n.globalAlpha=qe?_e*Xe:_e,n.fill(),n.strokeStyle=Re?Te:$e,n.lineWidth=Re?3:1.5,n.stroke(),n.globalAlpha=1,Q.pinned&&(n.save(),n.strokeStyle=$e,n.globalAlpha=.55,n.lineWidth=1,n.setLineDash([3,3]),n.beginPath(),n.arc(Q.x,Q.y,Ae+4,0,Math.PI*2),n.stroke(),n.setLineDash([]),n.restore()),G&&G.nodeIds.has(Q.id)&&!Re&&(n.save(),n.shadowColor=ge("--accent")||"#d4a27f",n.shadowBlur=15,n.beginPath(),n.arc(Q.x,Q.y,Ae+2,0,Math.PI*2),n.strokeStyle=ge("--accent")||"#d4a27f",n.globalAlpha=.5,n.lineWidth=2,n.stroke(),n.restore());const ne=ce==null?void 0:ce.nodes.find(Ee=>Ee.id===Q.id);if(((Gt=ne==null?void 0:ne.properties)==null?void 0:Gt._starred)===!0&&(n.fillStyle="#ffd700",n.font="10px system-ui, sans-serif",n.textAlign="left",n.textBaseline="bottom",n.fillText("★",Q.x+Ae-2,Q.y-Ae+2)),i.scale>=d.hideLabels){const Ee=Q.label.length>24?Q.label.slice(0,22)+"...":Q.label,Oe=ze?xe:fe;gn(n,Ee,Q.x,Q.y+Ae+4,"11px system-ui, sans-serif",Oe,"top")}if(i.scale>=d.hideBadges){const Ee=ze?Ne:be;gn(n,Q.type,Q.x,Q.y-Ae-3,"9px system-ui, sans-serif",Ee,"bottom")}n.globalAlpha=1}if(n.restore(),n.restore(),p){const Q=c.width,Ie=c.height;(!N||N.width!==Q||N.height!==Ie)&&(N=new OffscreenCanvas(Q,Ie),X=N.getContext("2d")),X&&(X.clearRect(0,0,Q,Ie),X.drawImage(c,0,0),ae=!1),Me();return}ee&&s.nodes.length>1&&Je(),z&&(n.save(),n.setTransform(D,0,0,D,0,0),n.translate(-i.x*i.scale,-i.y*i.scale),n.scale(i.scale,i.scale),Ce(),n.restore())}function Je(){if(!s)return;const p=140,v=100,M=8,F=c.clientWidth-p-16,Y=c.clientHeight-v-16;let H=1/0,O=1/0,K=-1/0,Z=-1/0;for(const ke of s.nodes)ke.x<H&&(H=ke.x),ke.y<O&&(O=ke.y),ke.x>K&&(K=ke.x),ke.y>Z&&(Z=ke.y);const re=K-H||1,fe=Z-O||1,xe=Math.min((p-M*2)/re,(v-M*2)/fe),be=F+M+(p-M*2-re*xe)/2,Ne=Y+M+(v-M*2-fe*xe)/2;n.save(),n.setTransform(D,0,0,D,0,0),n.fillStyle=ge("--bg-surface")||"#1a1a1a",n.globalAlpha=.85,n.beginPath(),n.roundRect(F,Y,p,v,8),n.fill(),n.strokeStyle=ge("--border")||"#2a2a2a",n.globalAlpha=1,n.lineWidth=1,n.stroke(),n.globalAlpha=.15,n.strokeStyle=ge("--canvas-edge")||"#555",n.lineWidth=.5;for(const ke of s.edges){const Xe=s.nodeMap.get(ke.sourceId),qe=s.nodeMap.get(ke.targetId);!Xe||!qe||ke.sourceId===ke.targetId||(n.beginPath(),n.moveTo(be+(Xe.x-H)*xe,Ne+(Xe.y-O)*xe),n.lineTo(be+(qe.x-H)*xe,Ne+(qe.y-O)*xe),n.stroke())}n.globalAlpha=.8;for(const ke of s.nodes){const Xe=be+(ke.x-H)*xe,qe=Ne+(ke.y-O)*xe;n.beginPath(),n.arc(Xe,qe,2,0,Math.PI*2),n.fillStyle=Le(ke.type),n.fill()}const Te=i.x,$e=i.y,je=i.x+c.clientWidth/i.scale,Ve=i.y+c.clientHeight/i.scale,He=be+(Te-H)*xe,nt=Ne+($e-O)*xe,gt=(je-Te)*xe,St=(Ve-$e)*xe;n.globalAlpha=.3,n.strokeStyle=ge("--accent")||"#d4a27f",n.lineWidth=1.5,n.strokeRect(Math.max(F,Math.min(He,F+p)),Math.max(Y,Math.min(nt,Y+v)),Math.min(gt,p),Math.min(St,v)),n.globalAlpha=1,n.restore()}function Ln(p,v,M,F,Y,H,O){const K=Math.atan2(F-v,M-p),Z=M-Math.cos(K)*Fe,re=F-Math.sin(K)*Fe,fe=8;n.beginPath(),n.moveTo(Z,re),n.lineTo(Z-fe*Math.cos(K-.4),re-fe*Math.sin(K-.4)),n.lineTo(Z-fe*Math.cos(K+.4),re-fe*Math.sin(K+.4)),n.closePath(),n.fillStyle=Y?O:H,n.fill()}function In(p,v,M,F,Y,H,O){const K=p.x+Fe+15,Z=p.y-Fe-15;n.beginPath(),n.arc(K,Z,15,0,Math.PI*2),n.strokeStyle=M?Y:F,n.lineWidth=M?2.5:1.5,n.stroke(),oe&&(n.fillStyle=M?O:H,n.font="9px system-ui, sans-serif",n.textAlign="center",n.fillText(v,K,Z-18))}function Wt(){if(!o||!u)return;const p=performance.now()-u.time,v=Math.min(p/R,1),M=1-Math.pow(1-v,3);i.x=u.x+(o.x-u.x)*M,i.y=u.y+(o.y-u.y)*M,he(),we(),v<1?requestAnimationFrame(Wt):(o=null,u=null)}let Be=0;function ht(){if(!S||l.length===0){Be=0;return}we(),Be=requestAnimationFrame(ht)}function Et(){if(!s||s.nodes.length===0)return;let p=1/0,v=1/0,M=-1/0,F=-1/0;for(const re of s.nodes)re.x<p&&(p=re.x),re.y<v&&(v=re.y),re.x>M&&(M=re.x),re.y>F&&(F=re.y);const Y=Fe*4,H=M-p+Y*2,O=F-v+Y*2,K=c.clientWidth/Math.max(H,1),Z=c.clientHeight/Math.max(O,1);i.scale=Math.min(K,Z,2),i.x=(p+M)/2-c.clientWidth/(2*i.scale),i.y=(v+F)/2-c.clientHeight/(2*i.scale),m()}function Ue(){if(!s||L<Tt){$=0,S&&l.length>0&&!Be&&(Be=requestAnimationFrame(ht));return}L=to(s,L),E.rebuild(s.nodes),we(),$=requestAnimationFrame(Ue)}let wt=!1,et=0,tt=0,Ht=0,zt=0;c.addEventListener("mousedown",p=>{_="pending",wt=!1,et=p.clientX,tt=p.clientY,Ht=p.clientX,zt=p.clientY;const v=c.getBoundingClientRect(),M=p.clientX-v.left,F=p.clientY-v.top,Y=me(M,F);if(Y&&!S){if(B=[],g.has(Y.id)&&g.size>1)for(const K of g){const Z=s==null?void 0:s.nodeMap.get(K);Z&&B.push({node:Z,startX:Z.x,startY:Z.y})}else B.push({node:Y,startX:Y.x,startY:Y.y});const[H,O]=le(M,F);se=H,J=O}else if(!Y&&p.shiftKey){const[H,O]=le(M,F);z={x1:H,y1:O,x2:H,y2:O}}}),c.addEventListener("mousemove",p=>{if(_==="idle")return;const v=p.clientX-et,M=p.clientY-tt,F=Math.abs(p.clientX-Ht),Y=Math.abs(p.clientY-zt);if(_==="pending"&&(F>de||Y>de)&&(wt=!0,B.length>0?(_="nodeDrag",P&&w&&w.postMessage({type:"stop"})):z?_="rubberBand":_="pan"),_==="nodeDrag"){const H=c.getBoundingClientRect(),O=p.clientX-H.left,K=p.clientY-H.top,[Z,re]=le(O,K),fe=Z-se,xe=re-J;for(const be of B)be.node.x=be.startX+fe,be.node.y=be.startY+xe,be.node.vx=0,be.node.vy=0,be.node.pinned=!0,te.add(be.node.id);E.rebuild((s==null?void 0:s.nodes)??[]),m()}else if(_==="rubberBand"&&z){const H=c.getBoundingClientRect(),O=p.clientX-H.left,K=p.clientY-H.top,[Z,re]=le(O,K);z.x2=Z,z.y2=re,m()}else _==="pan"&&(i.x-=v/i.scale,i.y-=M/i.scale,m());et=p.clientX,tt=p.clientY}),c.addEventListener("mouseup",p=>{const v=_==="nodeDrag",M=_==="rubberBand";if(B.map(Z=>Z.node.id),v){if(P&&w&&s){const Z=B.map(re=>({id:re.node.id,x:re.node.x,y:re.node.y}));w.postMessage({type:"pin",updates:Z}),w.postMessage({type:"resume",alpha:.5})}else L=Math.max(L,.5),$||Ue();_="idle",B=[],m();return}if(M&&z&&s){const Z=Math.min(z.x1,z.x2),re=Math.max(z.x1,z.x2),fe=Math.min(z.y1,z.y2),xe=Math.max(z.y1,z.y2);p.shiftKey||g.clear();for(const Ne of s.nodes)Ne.x>=Z&&Ne.x<=re&&Ne.y>=fe&&Ne.y<=xe&&g.add(Ne.id);const be=[...g];e==null||e(be.length>0?be:null),z=null,_="idle",m();return}if(_==="pan"){_="idle";return}if(_="idle",z=null,wt)return;const F=c.getBoundingClientRect(),Y=p.clientX-F.left,H=p.clientY-F.top,O=me(Y,H),K=p.ctrlKey||p.metaKey||p.shiftKey;if(S&&ue&&O&&s){const Z=l.length>0?l[l.length-1]:ue[0],re=new Set([Z]),fe=[{id:Z,path:[Z]}];let xe=null;for(;fe.length>0;){const{id:$e,path:je}=fe.shift();if($e===O.id){xe=je;break}for(const Ve of s.edges){let He=null;Ve.sourceId===$e?He=Ve.targetId:Ve.targetId===$e&&(He=Ve.sourceId),He&&!re.has(He)&&(re.add(He),fe.push({id:He,path:[...je,He]}))}}if(!xe)return;for(const $e of xe.slice(1))l.includes($e)||l.push($e);ue=[O.id];const be=Math.max(1,ye);ye=be;const Ne=un(ce,[O.id],be);cancelAnimationFrame($),w&&w.postMessage({type:"stop"}),s=Mt(Ne),E.rebuild(s.nodes),L=1,g=new Set([O.id]),U=null,i={x:0,y:0,scale:1},P=Ne.nodes.length>=T;const Te=P?A():null;Te?Te.postMessage({type:"start",data:Ne}):(P=!1,Ue()),setTimeout(()=>{s&&Et()},300),a==null||a({seedNodeIds:[O.id],hops:be,totalNodes:Ne.nodes.length}),e==null||e([O.id]);return}if(O){K?g.has(O.id)?g.delete(O.id):g.add(O.id):g.size===1&&g.has(O.id)?g.clear():(g.clear(),g.add(O.id));const Z=[...g];e==null||e(Z.length>0?Z:null)}else g.clear(),e==null||e(null);m()}),c.addEventListener("mouseleave",()=>{if(_==="nodeDrag")if(P&&w&&B.length>0){const p=B.map(v=>({id:v.node.id,x:v.node.x,y:v.node.y}));w.postMessage({type:"pin",updates:p}),w.postMessage({type:"resume",alpha:.5})}else L=Math.max(L,.5),$||Ue();_==="rubberBand"&&(z=null,m()),_="idle",B=[]}),c.addEventListener("wheel",p=>{p.preventDefault();const v=c.getBoundingClientRect(),M=p.clientX-v.left,F=p.clientY-v.top,[Y,H]=le(M,F),O=p.ctrlKey?1-p.deltaY*.01:p.deltaY>0?.9:1.1;i.scale=Math.max(x.zoomMin,Math.min(x.zoomMax,i.scale*O)),i.x=Y-M/i.scale,i.y=H-F/i.scale,m()},{passive:!1});let We=[],jt=0,Yt=1,Ut=0,Xt=0,kt=!1;c.addEventListener("touchstart",p=>{p.preventDefault(),We=Array.from(p.touches),We.length===2?(jt=qt(We[0],We[1]),Yt=i.scale):We.length===1&&(et=We[0].clientX,tt=We[0].clientY,Ut=We[0].clientX,Xt=We[0].clientY,kt=!1)},{passive:!1}),c.addEventListener("touchmove",p=>{p.preventDefault();const v=Array.from(p.touches);if(v.length===2&&We.length===2){const F=qt(v[0],v[1])/jt;i.scale=Math.max(x.zoomMin,Math.min(x.zoomMax,Yt*F)),m()}else if(v.length===1){const M=v[0].clientX-et,F=v[0].clientY-tt;(Math.abs(v[0].clientX-Ut)>10||Math.abs(v[0].clientY-Xt)>10)&&(kt=!0),i.x-=M/i.scale,i.y-=F/i.scale,et=v[0].clientX,tt=v[0].clientY,m()}We=v},{passive:!1}),c.addEventListener("touchend",p=>{if(p.preventDefault(),kt||p.changedTouches.length!==1)return;const v=p.changedTouches[0],M=c.getBoundingClientRect(),F=v.clientX-M.left,Y=v.clientY-M.top,H=me(F,Y);if(H){g.size===1&&g.has(H.id)?g.clear():(g.clear(),g.add(H.id));const O=[...g];e==null||e(O.length>0?O:null)}else g.clear(),e==null||e(null);m()},{passive:!1}),c.addEventListener("gesturestart",p=>p.preventDefault()),c.addEventListener("gesturechange",p=>p.preventDefault());function qt(p,v){const M=p.clientX-v.clientX,F=p.clientY-v.clientY;return Math.sqrt(M*M+F*F)}const at=document.createElement("div");at.className="zoom-controls";const ct=document.createElement("button");ct.className="zoom-btn",ct.textContent="+",ct.title="Zoom in",ct.addEventListener("click",()=>{const p=c.clientWidth/2,v=c.clientHeight/2,[M,F]=le(p,v);i.scale=Math.min(x.zoomMax,i.scale*x.zoomFactor),i.x=M-p/i.scale,i.y=F-v/i.scale,m()});const it=document.createElement("button");it.className="zoom-btn",it.textContent="−",it.title="Zoom out",it.addEventListener("click",()=>{const p=c.clientWidth/2,v=c.clientHeight/2,[M,F]=le(p,v);i.scale=Math.max(x.zoomMin,i.scale/x.zoomFactor),i.x=M-p/i.scale,i.y=F-v/i.scale,m()});const lt=document.createElement("button");lt.className="zoom-btn",lt.textContent="○",lt.title="Reset zoom",lt.addEventListener("click",()=>{if(s){if(i={x:0,y:0,scale:1},s.nodes.length>0){let p=1/0,v=1/0,M=-1/0,F=-1/0;for(const O of s.nodes)O.x<p&&(p=O.x),O.y<v&&(v=O.y),O.x>M&&(M=O.x),O.y>F&&(F=O.y);const Y=(p+M)/2,H=(v+F)/2;i.x=Y-c.clientWidth/2,i.y=H-c.clientHeight/2}m()}}),at.appendChild(ct),at.appendChild(lt),at.appendChild(it),t.appendChild(at);const Pe=document.createElement("div");Pe.className="node-tooltip",Pe.style.display="none",t.appendChild(Pe);let ft=null,Ze=null;return c.addEventListener("mousemove",p=>{if(_!=="idle"&&_!=="pending"){Pe.style.display!=="none"&&(Pe.style.display="none",ft=null);return}const v=c.getBoundingClientRect(),M=p.clientX-v.left,F=p.clientY-v.top,Y=me(M,F),H=(Y==null?void 0:Y.id)??null;H!==ft?(ft=H,Pe.style.display="none",Ze&&clearTimeout(Ze),Ze=null,H&&Y&&(Ze=setTimeout(()=>{if(!s||!ce)return;const O=s.edges.filter(K=>K.sourceId===H||K.targetId===H).length;Pe.textContent=`${Y.label} · ${Y.type} · ${O} edge${O!==1?"s":""}`,Pe.style.left=`${p.clientX-v.left+12}px`,Pe.style.top=`${p.clientY-v.top-8}px`,Pe.style.display="block"},200))):H&&Pe.style.display==="block"&&(Pe.style.left=`${p.clientX-v.left+12}px`,Pe.style.top=`${p.clientY-v.top-8}px`)}),c.addEventListener("mouseleave",()=>{Pe.style.display="none",ft=null,Ze&&clearTimeout(Ze),Ze=null}),{loadGraph(p){if(cancelAnimationFrame($),w&&w.postMessage({type:"stop"}),io(),ce=p,ue=null,ve=null,j=null,k=performance.now(),s=Mt(p),E.rebuild(s.nodes),L=1,g=new Set,U=null,te.clear(),_="idle",B=[],z=null,i={x:0,y:0,scale:1},s.nodes.length>0){let M=1/0,F=1/0,Y=-1/0,H=-1/0;for(const fe of s.nodes)fe.x<M&&(M=fe.x),fe.y<F&&(F=fe.y),fe.x>Y&&(Y=fe.x),fe.y>H&&(H=fe.y);const O=(M+Y)/2,K=(F+H)/2,Z=c.clientWidth,re=c.clientHeight;i.x=O-Z/2,i.y=K-re/2}P=p.nodes.length>=T;const v=P?A():null;v?v.postMessage({type:"start",data:p}):(P=!1,Ue())},setFilteredNodeIds(p){U=p,m()},releaseAllPins(){if(!s)return!1;let p=!1;for(const v of s.nodes)v.pinned&&(p=!0,v.pinned=!1);return te.clear(),_="idle",B=[],z=null,p&&(P&&w?w.postMessage({type:"unpin",ids:"all"}):(L=Math.max(L,.5),$||Ue()),m()),p},hasPinnedNodes(){if(!s)return!1;for(const p of s.nodes)if(p.pinned)return!0;return!1},clearSelection(){g.size!==0&&(g.clear(),e==null||e(null),m())},getSelectedNodeIds(){return[...g]},panToNode(p){this.panToNodes([p])},panToNodes(p){if(!s||p.length===0)return;const v=p.map(Y=>s.nodeMap.get(Y)).filter(Boolean);if(v.length===0)return;g=new Set(p),e==null||e(p);const M=c.clientWidth,F=c.clientHeight;if(v.length===1)u={x:i.x,y:i.y,time:performance.now()},o={x:v[0].x-M/(2*i.scale),y:v[0].y-F/(2*i.scale)};else{let Y=1/0,H=1/0,O=-1/0,K=-1/0;for(const Te of v)Te.x<Y&&(Y=Te.x),Te.y<H&&(H=Te.y),Te.x>O&&(O=Te.x),Te.y>K&&(K=Te.y);const Z=Fe*4,re=O-Y+Z*2,fe=K-H+Z*2,xe=Math.min(M/re,F/fe,i.scale);i.scale=xe;const be=(Y+O)/2,Ne=(H+K)/2;u={x:i.x,y:i.y,time:performance.now()},o={x:be-M/(2*i.scale),y:Ne-F/(2*i.scale)}}Wt()},setEdges(p){q=p,m()},setEdgeLabels(p){oe=p,m()},setTypeHulls(p){W=p,m()},setMinimap(p){ee=p,m()},centerView(){Et()},panBy(p,v){i.x+=p/i.scale,i.y+=v/i.scale,m()},zoomBy(p){const v=c.clientWidth/2,M=c.clientHeight/2,[F,Y]=le(v,M);i.scale=Math.max(x.zoomMin,Math.min(x.zoomMax,i.scale*p)),i.x=F-v/i.scale,i.y=Y-M/i.scale,m()},reheat(){P&&w?w.postMessage({type:"params",params:mt()}):(L=.5,cancelAnimationFrame($),Ue())},exportImage(p){if(!s)return"";const v=c.width,M=c.height;if(p==="png"){const O=document.createElement("canvas");O.width=v,O.height=M;const K=O.getContext("2d");return K.fillStyle=ge("--bg")||"#141414",K.fillRect(0,0,v,M),K.drawImage(c,0,0),Mn(K,v,M),O.toDataURL("image/png")}const F=c.toDataURL("image/png"),Y=Math.max(16,Math.round(v/80)),H=`<svg xmlns="http://www.w3.org/2000/svg" width="${v}" height="${M}">
3
+ <image href="${F}" width="${v}" height="${M}"/>
4
+ <text x="${v-20}" y="${M-16}" text-anchor="end" font-family="system-ui, sans-serif" font-size="${Y}" fill="#ffffff" opacity="0.4">backpackontology.com</text>
5
+ </svg>`;return"data:image/svg+xml;charset=utf-8,"+encodeURIComponent(H)},enterFocus(p,v){if(!ce||!s)return;for(const Y of s.nodes)Y.pinned=!1;te.clear(),_="idle",B=[],z=null,ue||(ve=s,j={...i}),ue=p,ye=v;const M=un(ce,p,v);cancelAnimationFrame($),w&&w.postMessage({type:"stop"}),s=Mt(M),E.rebuild(s.nodes),L=1,g=new Set(p),U=null,i={x:0,y:0,scale:1},P=M.nodes.length>=T;const F=P?A():null;F?F.postMessage({type:"start",data:M}):(P=!1,Ue()),setTimeout(()=>{!s||!ue||Et()},300),a==null||a({seedNodeIds:p,hops:v,totalNodes:M.nodes.length})},exitFocus(){if(!(!ue||!ve)){cancelAnimationFrame($),w&&w.postMessage({type:"stop"}),s=ve;for(const p of s.nodes)p.pinned=!1;te.clear(),_="idle",B=[],z=null,E.rebuild(s.nodes),i=j??{x:0,y:0,scale:1},ue=null,ve=null,j=null,g=new Set,U=null,m(),a==null||a(null)}},isFocused(){return ue!==null},getFocusInfo(){return!ue||!s?null:{seedNodeIds:ue,hops:ye,totalNodes:s.nodes.length}},findPath(p,v){if(!s)return null;const M=new Set([p]),F=[{nodeId:p,path:[p],edges:[]}];for(;F.length>0;){const{nodeId:Y,path:H,edges:O}=F.shift();if(Y===v)return{nodeIds:H,edgeIds:O};for(const K of s.edges){let Z=null;if(K.sourceId===Y?Z=K.targetId:K.targetId===Y&&(Z=K.sourceId),Z&&!M.has(Z)){M.add(Z);const re=ce==null?void 0:ce.edges.find(fe=>fe.sourceId===K.sourceId&&fe.targetId===K.targetId||fe.targetId===K.sourceId&&fe.sourceId===K.targetId);F.push({nodeId:Z,path:[...H,Z],edges:[...O,(re==null?void 0:re.id)??""]})}}}return null},setHighlightedPath(p,v){p&&v?G={nodeIds:new Set(p),edgeIds:new Set(v)}:G=null,m()},clearHighlightedPath(){G=null,m()},setWalkMode(p){S=p,this.releaseAllPins(),p?(l=ue?[...ue]:[...g],Be||(Be=requestAnimationFrame(ht))):(l=[],Be&&(cancelAnimationFrame(Be),Be=0)),m()},getWalkMode(){return S},getWalkTrail(){return[...l]},getFilteredNodeIds(){return U},removeFromWalkTrail(p){l=l.filter(v=>v!==p),m()},nodeAtScreen(p,v){return me(p,v)},getNodeIds(){if(!s)return[];if(ue){const p=new Set(ue),v=s.nodes.filter(F=>p.has(F.id)).map(F=>F.id),M=s.nodes.filter(F=>!p.has(F.id)).map(F=>F.id);return[...v,...M]}return s.nodes.map(p=>p.id)},destroy(){cancelAnimationFrame($),y&&(cancelAnimationFrame(y),y=0),Be&&(cancelAnimationFrame(Be),Be=0),w&&(w.terminate(),w=null),N=null,X=null,ie.disconnect()}};function Mn(p,v,M){const F=Math.max(16,Math.round(v/80));p.save(),p.font=`${F}px system-ui, sans-serif`,p.fillStyle="rgba(255, 255, 255, 0.4)",p.textAlign="right",p.textBaseline="bottom",p.fillText("backpackontology.com",v-20,M-16),p.restore()}}function pt(t){for(const e of Object.values(t.properties))if(typeof e=="string")return e;return t.id}const uo="✎";function mo(t,e,a,r){const d=document.createElement("div");d.id="info-panel",d.className="info-panel hidden",t.appendChild(d);let x=!1,b=[],c=-1,n=!1,D=null,i=[],s=!1,L=[],$=-1;function g(){d.classList.add("hidden"),d.classList.remove("info-panel-maximized"),d.innerHTML="",x=!1,b=[],c=-1}function U(B){!D||!a||(c<b.length-1&&(b=b.slice(0,c+1)),b.push(B),c=b.length-1,n=!0,a(B),n=!1)}function q(){if(c<=0||!D)return;c--,n=!0;const B=b[c];a==null||a(B),ee(B,D),n=!1}function oe(){if(c>=b.length-1||!D)return;c++,n=!0;const B=b[c];a==null||a(B),ee(B,D),n=!1}function W(){const B=document.createElement("div");B.className="info-panel-toolbar";const se=document.createElement("button");se.className="info-toolbar-btn",se.textContent="←",se.title="Back",se.disabled=c<=0,se.addEventListener("click",q),B.appendChild(se);const J=document.createElement("button");if(J.className="info-toolbar-btn",J.textContent="→",J.title="Forward",J.disabled=c>=b.length-1,J.addEventListener("click",oe),B.appendChild(J),r&&i.length>0){const de=document.createElement("button");de.className="info-toolbar-btn info-focus-btn",de.textContent="◎",de.title="Focus on neighborhood (F)",de.disabled=s,s&&(de.style.opacity="0.3"),de.addEventListener("click",()=>{s||r(i)}),B.appendChild(de)}const z=document.createElement("button");z.className="info-toolbar-btn",z.textContent=x?"⎘":"⛶",z.title=x?"Restore":"Maximize",z.addEventListener("click",()=>{x=!x,d.classList.toggle("info-panel-maximized",x),z.textContent=x?"⎘":"⛶",z.title=x?"Restore":"Maximize"}),B.appendChild(z);const te=document.createElement("button");return te.className="info-toolbar-btn info-close-btn",te.textContent="×",te.title="Close",te.addEventListener("click",g),B.appendChild(te),B}function ee(B,se){const J=se.nodes.find(y=>y.id===B);if(!J)return;const z=se.edges.filter(y=>y.sourceId===B||y.targetId===B);L=z.map(y=>y.sourceId===B?y.targetId:y.sourceId),$=-1,d.innerHTML="",d.classList.remove("hidden"),x&&d.classList.add("info-panel-maximized");const te=document.createElement("div");te.className="info-panel-header",te.appendChild(W());const de=document.createElement("div");de.className="info-header";const ce=document.createElement("span");if(ce.className="info-type-badge",ce.textContent=J.type,ce.style.backgroundColor=Le(J.type),e){ce.classList.add("info-editable");const y=document.createElement("button");y.className="info-inline-edit",y.textContent=uo,y.addEventListener("click",m=>{m.stopPropagation();const T=document.createElement("input");T.type="text",T.className="info-edit-inline-input",T.value=J.type,ce.textContent="",ce.appendChild(T),T.focus(),T.select();const w=()=>{const P=T.value.trim();P&&P!==J.type?e.onChangeNodeType(B,P):(ce.textContent=J.type,ce.appendChild(y))};T.addEventListener("blur",w),T.addEventListener("keydown",P=>{P.key==="Enter"&&T.blur(),P.key==="Escape"&&(T.value=J.type,T.blur())})}),ce.appendChild(y)}const ue=document.createElement("h3");ue.className="info-label",ue.textContent=pt(J);const ye=document.createElement("span");ye.className="info-id",ye.textContent=J.id,de.appendChild(ce),de.appendChild(ue),de.appendChild(ye),te.appendChild(de),d.appendChild(te);const ve=document.createElement("div");ve.className="info-panel-body";const j=Object.keys(J.properties),G=ut("Properties");if(j.length>0){const y=document.createElement("dl");y.className="info-props";for(const m of j){const T=document.createElement("dt");T.textContent=m;const w=document.createElement("dd");if(e){const P=At(J.properties[m]),A=document.createElement("textarea");A.className="info-edit-input",A.value=P,A.rows=1,A.addEventListener("input",()=>yn(A)),A.addEventListener("keydown",N=>{N.key==="Enter"&&!N.shiftKey&&(N.preventDefault(),A.blur())}),A.addEventListener("blur",()=>{const N=A.value;N!==P&&e.onUpdateNode(B,{[m]:fo(N)})}),w.appendChild(A),requestAnimationFrame(()=>yn(A));const h=document.createElement("button");h.className="info-delete-prop",h.textContent="×",h.title=`Remove ${m}`,h.addEventListener("click",()=>{const N={...J.properties};delete N[m],e.onUpdateNode(B,N)}),w.appendChild(h)}else w.appendChild(ho(J.properties[m]));y.appendChild(T),y.appendChild(w)}G.appendChild(y)}if(e){const y=document.createElement("button");y.className="info-add-btn",y.textContent="+ Add property",y.addEventListener("click",()=>{const m=document.createElement("div");m.className="info-add-row";const T=document.createElement("input");T.type="text",T.className="info-edit-input",T.placeholder="key";const w=document.createElement("input");w.type="text",w.className="info-edit-input",w.placeholder="value";const P=document.createElement("button");P.className="info-add-save",P.textContent="Add",P.addEventListener("click",()=>{T.value&&e.onAddProperty(B,T.value,w.value)}),m.appendChild(T),m.appendChild(w),m.appendChild(P),G.appendChild(m),T.focus()}),G.appendChild(y)}if(ve.appendChild(G),z.length>0){const y=ut(`Connections (${z.length})`),m=document.createElement("ul");m.className="info-connections";for(const T of z){const w=T.sourceId===B,P=w?T.targetId:T.sourceId,A=se.nodes.find(u=>u.id===P),h=A?pt(A):P,N=document.createElement("li");if(N.className="info-connection",a&&A&&(N.classList.add("info-connection-link"),N.addEventListener("click",u=>{u.target.closest(".info-delete-edge")||U(P)})),A){const u=document.createElement("span");u.className="info-target-dot",u.style.backgroundColor=Le(A.type),N.appendChild(u)}const X=document.createElement("span");X.className="info-arrow",X.textContent=w?"→":"←";const ae=document.createElement("span");ae.className="info-edge-type",ae.textContent=T.type;const he=document.createElement("span");he.className="info-target",he.textContent=h,N.appendChild(X),N.appendChild(ae),N.appendChild(he);const o=Object.keys(T.properties);if(o.length>0){const u=document.createElement("div");u.className="info-edge-props";for(const R of o){const V=document.createElement("span");V.className="info-edge-prop",V.textContent=`${R}: ${At(T.properties[R])}`,u.appendChild(V)}N.appendChild(u)}if(e){const u=document.createElement("button");u.className="info-delete-edge",u.textContent="×",u.title="Remove connection",u.addEventListener("click",R=>{R.stopPropagation(),e.onDeleteEdge(T.id)}),N.appendChild(u)}m.appendChild(N)}y.appendChild(m),ve.appendChild(y)}const S=ut("Timestamps"),l=document.createElement("dl");l.className="info-props";const f=document.createElement("dt");f.textContent="created";const k=document.createElement("dd");k.textContent=Cn(J.createdAt);const C=document.createElement("dt");C.textContent="updated";const E=document.createElement("dd");if(E.textContent=Cn(J.updatedAt),l.appendChild(f),l.appendChild(k),l.appendChild(C),l.appendChild(E),S.appendChild(l),ve.appendChild(S),e){const y=document.createElement("div");y.className="info-section info-danger";const m=document.createElement("button");m.className="info-delete-node",m.textContent="Delete node",m.addEventListener("click",()=>{e.onDeleteNode(B),g()}),y.appendChild(m),ve.appendChild(y)}d.appendChild(ve)}function _(B,se){const J=new Set(B),z=se.nodes.filter(S=>J.has(S.id));if(z.length===0)return;const te=se.edges.filter(S=>J.has(S.sourceId)&&J.has(S.targetId));d.innerHTML="",d.classList.remove("hidden"),x&&d.classList.add("info-panel-maximized"),d.appendChild(W());const de=document.createElement("div");de.className="info-header";const ce=document.createElement("h3");ce.className="info-label",ce.textContent=`${z.length} nodes selected`,de.appendChild(ce);const ue=document.createElement("div");ue.className="info-badge-row";const ye=new Map;for(const S of z)ye.set(S.type,(ye.get(S.type)??0)+1);for(const[S,l]of ye){const f=document.createElement("span");f.className="info-type-badge",f.style.backgroundColor=Le(S),f.textContent=l>1?`${S} (${l})`:S,ue.appendChild(f)}de.appendChild(ue),d.appendChild(de);const ve=ut("Selected Nodes"),j=document.createElement("ul");j.className="info-connections";for(const S of z){const l=document.createElement("li");l.className="info-connection",a&&(l.classList.add("info-connection-link"),l.addEventListener("click",()=>{U(S.id)}));const f=document.createElement("span");f.className="info-target-dot",f.style.backgroundColor=Le(S.type);const k=document.createElement("span");k.className="info-target",k.textContent=pt(S);const C=document.createElement("span");C.className="info-edge-type",C.textContent=S.type,l.appendChild(f),l.appendChild(k),l.appendChild(C),j.appendChild(l)}ve.appendChild(j),d.appendChild(ve);const G=ut(te.length>0?`Connections Between Selected (${te.length})`:"Connections Between Selected");if(te.length===0){const S=document.createElement("p");S.className="info-empty-message",S.textContent="No direct connections between selected nodes",G.appendChild(S)}else{const S=document.createElement("ul");S.className="info-connections";for(const l of te){const f=se.nodes.find(N=>N.id===l.sourceId),k=se.nodes.find(N=>N.id===l.targetId),C=f?pt(f):l.sourceId,E=k?pt(k):l.targetId,y=document.createElement("li");if(y.className="info-connection",f){const N=document.createElement("span");N.className="info-target-dot",N.style.backgroundColor=Le(f.type),y.appendChild(N)}const m=document.createElement("span");m.className="info-target",m.textContent=C;const T=document.createElement("span");T.className="info-arrow",T.textContent="→";const w=document.createElement("span");w.className="info-edge-type",w.textContent=l.type;const P=document.createElement("span");if(P.className="info-arrow",P.textContent="→",y.appendChild(m),y.appendChild(T),y.appendChild(w),y.appendChild(P),k){const N=document.createElement("span");N.className="info-target-dot",N.style.backgroundColor=Le(k.type),y.appendChild(N)}const A=document.createElement("span");A.className="info-target",A.textContent=E,y.appendChild(A);const h=Object.keys(l.properties);if(h.length>0){const N=document.createElement("div");N.className="info-edge-props";for(const X of h){const ae=document.createElement("span");ae.className="info-edge-prop",ae.textContent=`${X}: ${At(l.properties[X])}`,N.appendChild(ae)}y.appendChild(N)}S.appendChild(y)}G.appendChild(S)}d.appendChild(G)}return{show(B,se){if(D=se,i=B,B.length===1&&!n){const J=B[0];b[c]!==J&&(c<b.length-1&&(b=b.slice(0,c+1)),b.push(J),c=b.length-1)}B.length===1?ee(B[0],se):B.length>1&&_(B,se)},hide:g,goBack:q,goForward:oe,cycleConnection(B){if(L.length===0)return null;$===-1?$=B===1?0:L.length-1:($+=B,$>=L.length&&($=0),$<0&&($=L.length-1));const se=d.querySelectorAll(".info-connection");return se.forEach((J,z)=>{J.classList.toggle("info-connection-active",z===$)}),$>=0&&se[$]&&se[$].scrollIntoView({block:"nearest"}),L[$]??null},setFocusDisabled(B){s=B;const se=d.querySelector(".info-focus-btn");se&&(se.disabled=B,se.style.opacity=B?"0.3":"")},get visible(){return!d.classList.contains("hidden")}}}function ut(t){const e=document.createElement("div");e.className="info-section";const a=document.createElement("h4");return a.className="info-section-title",a.textContent=t,e.appendChild(a),e}function ho(t){if(Array.isArray(t)){const a=document.createElement("div");a.className="info-array";for(const r of t){const d=document.createElement("span");d.className="info-tag",d.textContent=String(r),a.appendChild(d)}return a}if(t!==null&&typeof t=="object"){const a=document.createElement("pre");return a.className="info-json",a.textContent=JSON.stringify(t,null,2),a}const e=document.createElement("span");return e.className="info-value",e.textContent=String(t??""),e}function At(t){return Array.isArray(t)?t.map(String).join(", "):t!==null&&typeof t=="object"?JSON.stringify(t):String(t??"")}function fo(t){const e=t.trim();if(e==="true")return!0;if(e==="false")return!1;if(e!==""&&!isNaN(Number(e)))return Number(e);if(e.startsWith("[")&&e.endsWith("]")||e.startsWith("{")&&e.endsWith("}"))try{return JSON.parse(e)}catch{return t}return t}function yn(t){t.style.height="auto",t.style.height=t.scrollHeight+"px"}function Cn(t){try{return new Date(t).toLocaleString()}catch{return t}}function Nn(t){for(const e of Object.values(t.properties))if(typeof e=="string")return e;return t.id}function vn(t,e){const a=e.toLowerCase();if(Nn(t).toLowerCase().includes(a)||t.type.toLowerCase().includes(a))return!0;for(const r of Object.values(t.properties))if(typeof r=="string"&&r.toLowerCase().includes(a))return!0;return!1}function go(t,e){const a=(e==null?void 0:e.maxResults)??8,r=(e==null?void 0:e.debounceMs)??150;let d=null,x=null,b=null,c=null;const n=document.createElement("div");n.className="search-overlay hidden";const D=document.createElement("div");D.className="search-input-wrap";const i=document.createElement("input");i.className="search-input",i.type="text",i.placeholder="Search nodes...",i.setAttribute("autocomplete","off"),i.setAttribute("spellcheck","false");const s=document.createElement("kbd");s.className="search-kbd",s.textContent="/",D.appendChild(i),D.appendChild(s);const L=document.createElement("ul");L.className="search-results hidden",n.appendChild(D),n.appendChild(L),t.appendChild(n);function $(){if(!d)return null;const W=i.value.trim();if(W.length===0)return null;const ee=new Set;for(const _ of d.nodes)vn(_,W)&&ee.add(_.id);return ee}function g(){const W=$();x==null||x(W),U()}function U(){L.innerHTML="",q=-1;const W=i.value.trim();if(!d||W.length===0){L.classList.add("hidden");return}const ee=[];for(const _ of d.nodes)if(vn(_,W)&&(ee.push(_),ee.length>=a))break;if(ee.length===0){L.classList.add("hidden");return}for(const _ of ee){const B=document.createElement("li");B.className="search-result-item";const se=document.createElement("span");se.className="search-result-dot",se.style.backgroundColor=Le(_.type);const J=document.createElement("span");J.className="search-result-label";const z=Nn(_);J.textContent=z.length>36?z.slice(0,34)+"...":z;const te=document.createElement("span");te.className="search-result-type",te.textContent=_.type,B.appendChild(se),B.appendChild(J),B.appendChild(te),B.addEventListener("click",()=>{b==null||b(_.id),i.value="",L.classList.add("hidden"),g()}),L.appendChild(B)}L.classList.remove("hidden")}i.addEventListener("input",()=>{c&&clearTimeout(c),c=setTimeout(g,r)});let q=-1;function oe(){const W=L.querySelectorAll(".search-result-item");W.forEach((ee,_)=>{ee.classList.toggle("search-result-active",_===q)}),q>=0&&W[q]&&W[q].scrollIntoView({block:"nearest"})}return i.addEventListener("keydown",W=>{const ee=L.querySelectorAll(".search-result-item");W.key==="ArrowDown"?(W.preventDefault(),ee.length>0&&(q=Math.min(q+1,ee.length-1),oe())):W.key==="ArrowUp"?(W.preventDefault(),ee.length>0&&(q=Math.max(q-1,0),oe())):W.key==="Enter"?(W.preventDefault(),q>=0&&ee[q]?ee[q].click():ee.length>0&&ee[0].click(),i.blur()):W.key==="Escape"&&(i.value="",i.blur(),L.classList.add("hidden"),q=-1,g())}),document.addEventListener("click",W=>{n.contains(W.target)||L.classList.add("hidden")}),i.addEventListener("focus",()=>s.classList.add("hidden")),i.addEventListener("blur",()=>{i.value.length===0&&s.classList.remove("hidden")}),{setLearningGraphData(W){d=W,i.value="",L.classList.add("hidden"),d&&d.nodes.length>0?n.classList.remove("hidden"):n.classList.add("hidden")},onFilterChange(W){x=W},onNodeSelect(W){b=W},clear(){i.value="",L.classList.add("hidden"),x==null||x(null)},focus(){i.focus()}}}function yo(t,e){let a=null,r=null,d=!0,x=null,b=!0,c=!0,n=!0,D="types",i="",s="",L=[],$=[];const g={types:new Set,nodeIds:new Set};function U(){if(!a)return[];const l=new Set;for(const f of a.nodes)g.types.has(f.type)&&l.add(f.id);for(const f of g.nodeIds)l.add(f);return[...l]}function q(l){if(g.nodeIds.has(l))return!0;const f=a==null?void 0:a.nodes.find(k=>k.id===l);return f?g.types.has(f.type):!1}function oe(){return g.types.size===0&&g.nodeIds.size===0}function W(){const l=U();e.onFocusChange(l.length>0?l:null)}const ee=document.createElement("button");ee.className="tools-pane-toggle hidden",ee.title="Graph Inspector",ee.innerHTML='<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7h16"/><path d="M4 12h16"/><path d="M4 17h10"/></svg>';const _=document.createElement("div");_.className="tools-pane-content hidden",t.appendChild(ee),t.appendChild(_),ee.addEventListener("click",()=>{var l;d=!d,_.classList.toggle("hidden",d),ee.classList.toggle("active",!d),d||(l=e.onOpen)==null||l.call(e)});function B(){if(_.innerHTML="",!r)return;const l=document.createElement("div");if(l.className="tools-pane-summary",l.innerHTML=`<span>${r.nodeCount} nodes</span><span class="tools-pane-sep">&middot;</span><span>${r.edgeCount} edges</span><span class="tools-pane-sep">&middot;</span><span>${r.types.length} types</span>`,_.appendChild(l),a&&r.nodeCount>0){const E=Math.ceil(JSON.stringify(a).length/4),y=Math.round(E/r.nodeCount),m=Math.max(10,Math.round(y*.3)*Math.min(5,r.nodeCount)),T=E>m?Math.round((1-m/E)*100):0,w=document.createElement("div");w.className="tools-pane-token-card";const P=Math.min(100,T),A=document.createElement("div");A.className="token-card-label",A.textContent="Token Efficiency",w.appendChild(A);const h=document.createElement("div");h.className="token-card-stat",h.textContent=`~${E.toLocaleString()} tokens stored`,w.appendChild(h);const N=document.createElement("div");N.className="token-card-bar";const X=document.createElement("div");X.className="token-card-bar-fill",X.style.width=`${P}%`,N.appendChild(X),w.appendChild(N);const ae=document.createElement("div");ae.className="token-card-stat",ae.textContent=`A search returns ~${m} tokens instead of ~${E.toLocaleString()} (${T}% reduction)`,w.appendChild(ae),_.appendChild(w)}const f=document.createElement("div");f.className="tools-pane-tabs";const k=[{id:"types",label:"Types"},{id:"insights",label:"Insights"},{id:"controls",label:"Controls"}];for(const E of k){const y=document.createElement("button");y.className="tools-pane-tab",D===E.id&&y.classList.add("tools-pane-tab-active"),y.textContent=E.label,y.addEventListener("click",()=>{D=E.id,B()}),f.appendChild(y)}_.appendChild(f),oe()||z(),$.length>0&&J(),D==="types"&&r.types.length>5?_.appendChild(de("Filter types...",i,E=>{i=E,se()})):D==="insights"&&r.orphans.length+r.singletons.length+r.emptyNodes.length>5&&_.appendChild(de("Filter issues...",s,y=>{s=y,se()}));const C=document.createElement("div");C.className="tools-pane-tab-content",_.appendChild(C),se()}function se(){const l=_.querySelector(".tools-pane-tab-content");l&&(l.innerHTML="",D==="types"?ce(l):D==="insights"?ue(l):D==="controls"&&ye(l))}function J(){_.appendChild(j(`Walk Trail (${$.length})`,l=>{for(let k=0;k<$.length;k++){const C=$[k],E=k===$.length-1;if(C.edgeType){const h=document.createElement("div");h.className="walk-trail-edge",h.textContent=`↓ ${C.edgeType}`,l.appendChild(h)}const y=document.createElement("div");y.className="tools-pane-row tools-pane-clickable",E&&(y.style.fontWeight="600");const m=document.createElement("span");m.className="tools-pane-count",m.style.minWidth="18px",m.textContent=`${k+1}`;const T=document.createElement("span");T.className="tools-pane-dot",T.style.backgroundColor=Le(C.type);const w=document.createElement("span");w.className="tools-pane-name",w.textContent=C.label;const P=document.createElement("span");P.className="tools-pane-count",P.textContent=C.type;const A=document.createElement("button");A.className="tools-pane-edit",A.style.opacity="1",A.textContent="×",A.title="Remove from trail",A.addEventListener("click",h=>{var N;h.stopPropagation(),(N=e.onWalkTrailRemove)==null||N.call(e,C.id)}),y.appendChild(m),y.appendChild(T),y.appendChild(w),y.appendChild(P),y.appendChild(A),y.addEventListener("click",()=>{e.onNavigateToNode(C.id)}),l.appendChild(y)}const f=document.createElement("div");if(f.className="tools-pane-export-row",e.onWalkIsolate){const k=document.createElement("button");k.className="tools-pane-export-btn",k.textContent="Isolate (I)",k.addEventListener("click",()=>e.onWalkIsolate()),f.appendChild(k)}if(e.onWalkSaveSnippet&&$.length>=2){const k=document.createElement("button");k.className="tools-pane-export-btn",k.textContent="Save snippet",k.addEventListener("click",()=>{bt("Save snippet","Name for this snippet").then(C=>{C&&e.onWalkSaveSnippet(C)})}),f.appendChild(k)}l.appendChild(f)}))}function z(){if(!r||!a)return;const l=U();_.appendChild(j("Focused",f=>{for(const y of g.types){const m=r.types.find(N=>N.name===y);if(!m)continue;const T=document.createElement("div");T.className="tools-pane-row tools-pane-clickable";const w=document.createElement("span");w.className="tools-pane-dot",w.style.backgroundColor=Le(m.name);const P=document.createElement("span");P.className="tools-pane-name",P.textContent=m.name;const A=document.createElement("span");A.className="tools-pane-count",A.textContent=`${m.count} nodes`;const h=document.createElement("button");h.className="tools-pane-edit tools-pane-focus-active",h.style.opacity="1",h.textContent="×",h.title=`Remove ${m.name} from focus`,T.appendChild(w),T.appendChild(P),T.appendChild(A),T.appendChild(h),h.addEventListener("click",N=>{N.stopPropagation(),g.types.delete(m.name),W(),B()}),f.appendChild(T)}for(const y of g.nodeIds){const m=a.nodes.find(X=>X.id===y);if(!m)continue;const T=xn(m.properties)??m.id,w=document.createElement("div");w.className="tools-pane-row tools-pane-clickable";const P=document.createElement("span");P.className="tools-pane-dot",P.style.backgroundColor=Le(m.type);const A=document.createElement("span");A.className="tools-pane-name",A.textContent=T;const h=document.createElement("span");h.className="tools-pane-count",h.textContent=m.type;const N=document.createElement("button");N.className="tools-pane-edit tools-pane-focus-active",N.style.opacity="1",N.textContent="×",N.title=`Remove ${T} from focus`,w.appendChild(P),w.appendChild(A),w.appendChild(h),w.appendChild(N),w.addEventListener("click",X=>{X.target.closest(".tools-pane-edit")||e.onNavigateToNode(y)}),N.addEventListener("click",X=>{X.stopPropagation(),g.nodeIds.delete(y),W(),B()}),f.appendChild(w)}const k=document.createElement("div");k.className="tools-pane-row tools-pane-clickable tools-pane-focus-clear";const C=document.createElement("span");C.className="tools-pane-name",C.style.color="var(--accent)",C.textContent=`${l.length} total`;const E=document.createElement("span");E.className="tools-pane-badge",E.textContent="clear all",k.appendChild(C),k.appendChild(E),k.addEventListener("click",()=>{g.types.clear(),g.nodeIds.clear(),W(),B()}),f.appendChild(k)}))}function te(l){const f=document.createElement("div");f.className="tools-pane-row tools-pane-clickable",x===l.name&&f.classList.add("active");const k=document.createElement("span");k.className="tools-pane-dot",k.style.backgroundColor=Le(l.name);const C=document.createElement("span");C.className="tools-pane-name",C.textContent=l.name;const E=document.createElement("span");E.className="tools-pane-count",E.textContent=String(l.count);const y=document.createElement("button");y.className="tools-pane-edit tools-pane-focus-toggle",g.types.has(l.name)&&y.classList.add("tools-pane-focus-active"),y.textContent="◎",y.title=g.types.has(l.name)?`Remove ${l.name} from focus`:`Add ${l.name} to focus`;const m=document.createElement("button");return m.className="tools-pane-edit",m.textContent="✎",m.title=`Rename all ${l.name} nodes`,f.appendChild(k),f.appendChild(C),f.appendChild(E),f.appendChild(y),f.appendChild(m),f.addEventListener("click",T=>{T.target.closest(".tools-pane-edit")||(x===l.name?(x=null,e.onFilterByType(null)):(x=l.name,e.onFilterByType(l.name)),B())}),y.addEventListener("click",T=>{T.stopPropagation(),g.types.has(l.name)?g.types.delete(l.name):g.types.add(l.name),W(),B()}),m.addEventListener("click",T=>{T.stopPropagation(),G(f,l.name,w=>{w&&w!==l.name&&e.onRenameNodeType(l.name,w)})}),f}function de(l,f,k){const C=document.createElement("input");return C.type="text",C.className="tools-pane-search",C.placeholder=l,C.value=f,C.addEventListener("input",()=>k(C.value)),C}function ce(l){if(!r)return;const f=i.toLowerCase();if(r.types.length){const C=r.types.filter(E=>!g.types.has(E.name)).filter(E=>!f||E.name.toLowerCase().includes(f));C.length>0&&l.appendChild(j("Node Types",E=>{for(const y of C)E.appendChild(te(y))}))}const k=r.edgeTypes.filter(C=>!f||C.name.toLowerCase().includes(f));k.length&&l.appendChild(j("Edge Types",C=>{for(const E of k){const y=document.createElement("div");y.className="tools-pane-row tools-pane-clickable";const m=document.createElement("span");m.className="tools-pane-name",m.textContent=E.name;const T=document.createElement("span");T.className="tools-pane-count",T.textContent=String(E.count);const w=document.createElement("button");w.className="tools-pane-edit",w.textContent="✎",w.title=`Rename all ${E.name} edges`,y.appendChild(m),y.appendChild(T),y.appendChild(w),w.addEventListener("click",P=>{P.stopPropagation(),G(y,E.name,A=>{A&&A!==E.name&&e.onRenameEdgeType(E.name,A)})}),C.appendChild(y)}}))}function ue(l){if(!r)return;const f=s.toLowerCase(),k=r.starred.filter(A=>!f||A.label.toLowerCase().includes(f)||A.type.toLowerCase().includes(f));k.length&&l.appendChild(j("★ Starred",A=>{for(const X of k){const ae=document.createElement("div");ae.className="tools-pane-row tools-pane-clickable";const he=document.createElement("span");he.className="tools-pane-dot",he.style.backgroundColor=Le(X.type);const o=document.createElement("span");o.className="tools-pane-name",o.textContent=X.label;const u=document.createElement("button");u.className="tools-pane-edit tools-pane-focus-toggle",q(X.id)&&u.classList.add("tools-pane-focus-active"),u.textContent="◎",u.title=q(X.id)?`Remove ${X.label} from focus`:`Add ${X.label} to focus`,ae.appendChild(he),ae.appendChild(o),ae.appendChild(u),ae.addEventListener("click",R=>{R.target.closest(".tools-pane-edit")||e.onNavigateToNode(X.id)}),u.addEventListener("click",R=>{R.stopPropagation(),g.nodeIds.has(X.id)?g.nodeIds.delete(X.id):g.nodeIds.add(X.id),W(),B()}),A.appendChild(ae)}const h=document.createElement("div");h.className="tools-pane-row tools-pane-actions";const N=document.createElement("button");if(N.className="tools-pane-action-btn",N.textContent="Focus all",N.title="Enter focus mode with all starred nodes",N.addEventListener("click",()=>{g.nodeIds.clear(),g.types.clear();for(const X of r.starred)g.nodeIds.add(X.id);W(),B()}),h.appendChild(N),e.onStarredSaveSnippet){const X=document.createElement("button");X.className="tools-pane-action-btn",X.textContent="Save as snippet",X.title="Save starred nodes as a reusable snippet",X.addEventListener("click",async()=>{const ae=await bt("Snippet name","starred");ae&&e.onStarredSaveSnippet(ae,r.starred.map(he=>he.id))}),h.appendChild(X)}A.appendChild(h)}));const C=r.mostConnected.filter(A=>!f||A.label.toLowerCase().includes(f)||A.type.toLowerCase().includes(f));C.length&&l.appendChild(j("Most Connected",A=>{for(const h of C){const N=document.createElement("div");N.className="tools-pane-row tools-pane-clickable";const X=document.createElement("span");X.className="tools-pane-dot",X.style.backgroundColor=Le(h.type);const ae=document.createElement("span");ae.className="tools-pane-name",ae.textContent=h.label;const he=document.createElement("span");he.className="tools-pane-count",he.textContent=`${h.connections}`;const o=document.createElement("button");o.className="tools-pane-edit tools-pane-focus-toggle",q(h.id)&&o.classList.add("tools-pane-focus-active"),o.textContent="◎",o.title=q(h.id)?`Remove ${h.label} from focus`:`Add ${h.label} to focus`,N.appendChild(X),N.appendChild(ae),N.appendChild(he),N.appendChild(o),N.addEventListener("click",u=>{u.target.closest(".tools-pane-edit")||e.onNavigateToNode(h.id)}),o.addEventListener("click",u=>{u.stopPropagation(),g.nodeIds.has(h.id)?g.nodeIds.delete(h.id):g.nodeIds.add(h.id),W(),B()}),A.appendChild(N)}}));const E=r.orphans.filter(A=>!f||A.label.toLowerCase().includes(f)||A.type.toLowerCase().includes(f)),y=r.singletons.filter(A=>!f||A.name.toLowerCase().includes(f)),m=r.emptyNodes.filter(A=>!f||A.label.toLowerCase().includes(f)||A.type.toLowerCase().includes(f)),T=E.length>0,w=y.length>0,P=m.length>0;if(!T&&!w&&!P){const A=document.createElement("div");A.className="tools-pane-empty-msg",A.textContent="No issues found",l.appendChild(A);return}T&&l.appendChild(j("Orphans",A=>{for(const h of E.slice(0,5)){const N=document.createElement("div");N.className="tools-pane-row tools-pane-clickable tools-pane-issue";const X=document.createElement("span");X.className="tools-pane-dot",X.style.backgroundColor=Le(h.type);const ae=document.createElement("span");ae.className="tools-pane-name",ae.textContent=h.label;const he=document.createElement("span");he.className="tools-pane-badge",he.textContent="orphan";const o=document.createElement("button");o.className="tools-pane-edit tools-pane-focus-toggle",q(h.id)&&o.classList.add("tools-pane-focus-active"),o.textContent="◎",o.title=q(h.id)?`Remove ${h.label} from focus`:`Add ${h.label} to focus`,N.appendChild(X),N.appendChild(ae),N.appendChild(he),N.appendChild(o),N.addEventListener("click",u=>{u.target.closest(".tools-pane-edit")||e.onNavigateToNode(h.id)}),o.addEventListener("click",u=>{u.stopPropagation(),g.nodeIds.has(h.id)?g.nodeIds.delete(h.id):g.nodeIds.add(h.id),W(),B()}),A.appendChild(N)}if(E.length>5){const h=document.createElement("div");h.className="tools-pane-more",h.textContent=`+ ${E.length-5} more orphans`,A.appendChild(h)}})),w&&l.appendChild(j("Singletons",A=>{for(const h of y.slice(0,5)){const N=document.createElement("div");N.className="tools-pane-row tools-pane-issue";const X=document.createElement("span");X.className="tools-pane-dot",X.style.backgroundColor=Le(h.name);const ae=document.createElement("span");ae.className="tools-pane-name",ae.textContent=h.name;const he=document.createElement("span");he.className="tools-pane-badge",he.textContent="1 node",N.appendChild(X),N.appendChild(ae),N.appendChild(he),A.appendChild(N)}})),P&&l.appendChild(j("Empty Nodes",A=>{for(const h of m.slice(0,5)){const N=document.createElement("div");N.className="tools-pane-row tools-pane-issue";const X=document.createElement("span");X.className="tools-pane-dot",X.style.backgroundColor=Le(h.type);const ae=document.createElement("span");ae.className="tools-pane-name",ae.textContent=h.label;const he=document.createElement("span");he.className="tools-pane-badge",he.textContent="empty",N.appendChild(X),N.appendChild(ae),N.appendChild(he),A.appendChild(N)}if(r.emptyNodes.length>5){const h=document.createElement("div");h.className="tools-pane-more",h.textContent=`+ ${r.emptyNodes.length-5} more empty nodes`,A.appendChild(h)}}))}function ye(l){l.appendChild(j("Display",f=>{const k=document.createElement("div");k.className="tools-pane-row tools-pane-clickable";const C=document.createElement("input");C.type="checkbox",C.checked=b,C.className="tools-pane-checkbox";const E=document.createElement("span");E.className="tools-pane-name",E.textContent="Edge labels",k.appendChild(C),k.appendChild(E),k.addEventListener("click",h=>{h.target!==C&&(C.checked=!C.checked),b=C.checked,e.onToggleEdgeLabels(b)}),f.appendChild(k);const y=document.createElement("div");y.className="tools-pane-row tools-pane-clickable";const m=document.createElement("input");m.type="checkbox",m.checked=c,m.className="tools-pane-checkbox";const T=document.createElement("span");T.className="tools-pane-name",T.textContent="Type regions",y.appendChild(m),y.appendChild(T),y.addEventListener("click",h=>{h.target!==m&&(m.checked=!m.checked),c=m.checked,e.onToggleTypeHulls(c)}),f.appendChild(y);const w=document.createElement("div");w.className="tools-pane-row tools-pane-clickable";const P=document.createElement("input");P.type="checkbox",P.checked=n,P.className="tools-pane-checkbox";const A=document.createElement("span");A.className="tools-pane-name",A.textContent="Minimap",w.appendChild(P),w.appendChild(A),w.addEventListener("click",h=>{h.target!==P&&(P.checked=!P.checked),n=P.checked,e.onToggleMinimap(n)}),f.appendChild(w)})),l.appendChild(j("Layout",f=>{f.appendChild(ve("Clustering",0,1,.02,.08,k=>{e.onLayoutChange("clusterStrength",k)})),f.appendChild(ve("Spacing",.5,20,.5,1.5,k=>{e.onLayoutChange("spacing",k)})),f.appendChild(ve("Pan speed",20,200,10,60,k=>{e.onPanSpeedChange(k)}))})),l.appendChild(j("Export",f=>{const k=document.createElement("div");k.className="tools-pane-export-row";const C=document.createElement("button");C.className="tools-pane-export-btn",C.textContent="Export PNG",C.addEventListener("click",()=>e.onExport("png"));const E=document.createElement("button");E.className="tools-pane-export-btn",E.textContent="Export SVG",E.addEventListener("click",()=>e.onExport("svg")),k.appendChild(C),k.appendChild(E),f.appendChild(k)})),(e.onSnapshot||e.onRollback)&&l.appendChild(j("Versions",f=>{const k=document.createElement("div");k.className="tools-pane-export-row";const C=document.createElement("button");if(C.className="tools-pane-export-btn",C.textContent="Save snapshot",C.addEventListener("click",()=>{bt("Save snapshot","Label (optional)").then(E=>{var y;(y=e.onSnapshot)==null||y.call(e,E||void 0)})}),k.appendChild(C),f.appendChild(k),L.length>0)for(const E of L){const y=document.createElement("div");y.className="tools-pane-row";const m=document.createElement("span");m.className="tools-pane-name";const T=Co(E.timestamp);m.textContent=E.label?`#${E.version} ${E.label}`:`#${E.version}`,m.title=`${T} — ${E.nodeCount} nodes, ${E.edgeCount} edges`;const w=document.createElement("span");w.className="tools-pane-count",w.textContent=T;const P=document.createElement("button");P.className="tools-pane-edit",P.style.opacity="1",P.textContent="↩",P.title="Restore this snapshot",P.addEventListener("click",()=>{bn("Restore snapshot",`Restore snapshot #${E.version}? Current state will be lost unless you save a snapshot first.`).then(A=>{var h;A&&((h=e.onRollback)==null||h.call(e,E.version))})}),y.appendChild(m),y.appendChild(w),y.appendChild(P),f.appendChild(y)}else{const E=document.createElement("div");E.className="tools-pane-empty-msg",E.textContent="No snapshots yet",f.appendChild(E)}}))}function ve(l,f,k,C,E,y){const m=document.createElement("div");m.className="tools-pane-slider-row";const T=document.createElement("span");T.className="tools-pane-slider-label",T.textContent=l;const w=document.createElement("input");w.type="range",w.className="tools-pane-slider",w.min=String(f),w.max=String(k),w.step=String(C),w.value=String(E);const P=document.createElement("span");return P.className="tools-pane-slider-value",P.textContent=String(E),w.addEventListener("input",()=>{const A=parseFloat(w.value);P.textContent=A%1===0?String(A):A.toFixed(2),y(A)}),m.appendChild(T),m.appendChild(w),m.appendChild(P),m}function j(l,f){const k=document.createElement("div");k.className="tools-pane-section";const C=document.createElement("div");return C.className="tools-pane-heading",C.textContent=l,k.appendChild(C),f(k),k}function G(l,f,k){const C=document.createElement("input");C.className="tools-pane-inline-input",C.value=f,C.type="text";const E=l.innerHTML;l.innerHTML="",l.classList.add("tools-pane-editing"),l.appendChild(C),C.focus(),C.select();function y(){const m=C.value.trim();l.classList.remove("tools-pane-editing"),m&&m!==f?k(m):l.innerHTML=E}C.addEventListener("keydown",m=>{m.key==="Enter"&&(m.preventDefault(),y()),m.key==="Escape"&&(l.innerHTML=E,l.classList.remove("tools-pane-editing"))}),C.addEventListener("blur",y)}function S(l){const f=new Map,k=new Map,C=new Map,E=new Set;for(const h of l.nodes)f.set(h.type,(f.get(h.type)??0)+1);for(const h of l.edges)k.set(h.type,(k.get(h.type)??0)+1),C.set(h.sourceId,(C.get(h.sourceId)??0)+1),C.set(h.targetId,(C.get(h.targetId)??0)+1),E.add(h.sourceId),E.add(h.targetId);const y=h=>xn(h.properties)??h.id,m=l.nodes.filter(h=>h.properties._starred===!0).map(h=>({id:h.id,label:y(h),type:h.type})),T=l.nodes.filter(h=>!E.has(h.id)).map(h=>({id:h.id,label:y(h),type:h.type})),w=[...f.entries()].filter(([,h])=>h===1).map(([h])=>({name:h})),P=l.nodes.filter(h=>Object.keys(h.properties).length===0).map(h=>({id:h.id,label:h.id,type:h.type})),A=l.nodes.map(h=>({id:h.id,label:y(h),type:h.type,connections:C.get(h.id)??0})).filter(h=>h.connections>0).sort((h,N)=>N.connections-h.connections).slice(0,5);return{nodeCount:l.nodes.length,edgeCount:l.edges.length,types:[...f.entries()].sort((h,N)=>N[1]-h[1]).map(([h,N])=>({name:h,count:N})),edgeTypes:[...k.entries()].sort((h,N)=>N[1]-h[1]).map(([h,N])=>({name:h,count:N})),starred:m,orphans:T,singletons:w,emptyNodes:P,mostConnected:A}}return{collapse(){d=!0,_.classList.add("hidden"),ee.classList.remove("active")},addToFocusSet(l){for(const f of l)g.nodeIds.add(f);W(),B()},clearFocusSet(){g.types.clear(),g.nodeIds.clear(),W(),B()},setData(l){a=l,x=null,g.types.clear(),g.nodeIds.clear(),a&&a.nodes.length>0?(r=S(a),ee.classList.remove("hidden"),B()):(r=null,ee.classList.add("hidden"),_.classList.add("hidden"))},setSnapshots(l){L=l,D==="controls"&&se()},setWalkTrail(l){$=l,B()}}}function Co(t){const e=Date.now()-new Date(t).getTime(),a=Math.floor(e/6e4);if(a<1)return"just now";if(a<60)return`${a}m ago`;const r=Math.floor(a/60);return r<24?`${r}h ago`:`${Math.floor(r/24)}d ago`}function xn(t){for(const e of Object.values(t))if(typeof e=="string")return e;return null}function vo(t,e){const a=e.split("+"),r=a.pop(),d=a.map(n=>n.toLowerCase()),x=d.includes("ctrl")||d.includes("cmd")||d.includes("meta"),b=d.includes("shift"),c=d.includes("alt");return x!==(t.ctrlKey||t.metaKey)||!x&&(t.ctrlKey||t.metaKey)||b&&!t.shiftKey||c!==t.altKey?!1:r.toLowerCase()==="escape"?t.key==="Escape":r.toLowerCase()==="tab"?t.key==="Tab":d.length>0?t.key.toLowerCase()===r.toLowerCase():t.key===r}function xo(){return{search:"Focus search",searchAlt:"Focus search (alt)",undo:"Undo",redo:"Redo",help:"Toggle help",escape:"Exit focus / close panel",focus:"Focus on selected / exit focus",toggleEdges:"Toggle edges on/off",center:"Center view on graph",nextNode:"Next node in view",prevNode:"Previous node in view",nextConnection:"Next connection",prevConnection:"Previous connection",historyBack:"Node history back",historyForward:"Node history forward",hopsIncrease:"Increase hops",hopsDecrease:"Decrease hops",panLeft:"Pan left",panDown:"Pan down",panUp:"Pan up",panRight:"Pan right",panFastLeft:"Pan fast left",zoomOut:"Zoom out",zoomIn:"Zoom in",panFastRight:"Pan fast right",spacingDecrease:"Decrease spacing",spacingIncrease:"Increase spacing",clusteringDecrease:"Decrease clustering",clusteringIncrease:"Increase clustering",toggleSidebar:"Toggle sidebar",walkMode:"Toggle walk mode (in focus)",walkIsolate:"Isolate walk trail nodes",resetPins:"Release all pinned nodes (reset manual layout)"}}const bo=[{key:"Click",description:"Select node"},{key:"Shift+Click",description:"Multi-select nodes"},{key:"Drag node",description:"Move & pin node (temporary)"},{key:"Drag selection",description:"Move selected group (temporary)"},{key:"Shift+drag empty",description:"Rubber-band select"},{key:"Drag empty",description:"Pan canvas"},{key:"Scroll",description:"Zoom in/out"}],Eo=["search","searchAlt","undo","redo","help","focus","toggleEdges","center","nextNode","prevNode","nextConnection","prevConnection","historyBack","historyForward","hopsIncrease","hopsDecrease","panLeft","panDown","panUp","panRight","panFastLeft","panFastRight","zoomIn","zoomOut","spacingDecrease","spacingIncrease","clusteringDecrease","clusteringIncrease","toggleSidebar","walkMode","walkIsolate","resetPins","escape"];function wo(t){return t.split("+").map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join("+")}function ko(t,e){const a=xo(),r=document.createElement("div");r.className="shortcuts-overlay hidden";const d=document.createElement("div");d.className="shortcuts-modal";const x=document.createElement("h3");x.className="shortcuts-title",x.textContent="Keyboard Shortcuts";const b=document.createElement("div");b.className="shortcuts-list";for(const s of Eo){const L=e[s];if(!L)continue;const $=document.createElement("div");$.className="shortcuts-row";const g=document.createElement("div");g.className="shortcuts-keys";const U=document.createElement("kbd");U.textContent=wo(L),g.appendChild(U);const q=document.createElement("span");q.className="shortcuts-desc",q.textContent=a[s],$.appendChild(g),$.appendChild(q),b.appendChild($)}for(const s of bo){const L=document.createElement("div");L.className="shortcuts-row";const $=document.createElement("div");$.className="shortcuts-keys";const g=document.createElement("kbd");g.textContent=s.key,$.appendChild(g);const U=document.createElement("span");U.className="shortcuts-desc",U.textContent=s.description,L.appendChild($),L.appendChild(U),b.appendChild(L)}const c=document.createElement("button");c.className="shortcuts-close",c.textContent="×",d.appendChild(c),d.appendChild(x),d.appendChild(b),r.appendChild(d),t.appendChild(r);function n(){r.classList.remove("hidden")}function D(){r.classList.add("hidden")}function i(){r.classList.toggle("hidden")}return c.addEventListener("click",D),r.addEventListener("click",s=>{s.target===r&&D()}),{show:n,hide:D,toggle:i}}function So(t){const e=document.createElement("div");return e.className="empty-state",e.innerHTML=`
6
+ <div class="empty-state-bg">
7
+ <div class="empty-state-circle c1"></div>
8
+ <div class="empty-state-circle c2"></div>
9
+ <div class="empty-state-circle c3"></div>
10
+ <div class="empty-state-circle c4"></div>
11
+ <div class="empty-state-circle c5"></div>
12
+ <svg class="empty-state-lines" viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice">
13
+ <line x1="80" y1="60" x2="220" y2="140" stroke="currentColor" stroke-width="0.5" opacity="0.15"/>
14
+ <line x1="220" y1="140" x2="320" y2="80" stroke="currentColor" stroke-width="0.5" opacity="0.15"/>
15
+ <line x1="220" y1="140" x2="160" y2="240" stroke="currentColor" stroke-width="0.5" opacity="0.15"/>
16
+ <line x1="160" y1="240" x2="300" y2="220" stroke="currentColor" stroke-width="0.5" opacity="0.15"/>
17
+ </svg>
18
+ </div>
19
+ <div class="empty-state-content">
20
+ <div class="empty-state-icon">
21
+ <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
22
+ <path d="M21 16V8a2 2 0 00-1-1.73l-7-4a2 2 0 00-2 0l-7 4A2 2 0 002 8v8a2 2 0 001 1.73l7 4a2 2 0 002 0l7-4A2 2 0 0022 16z"/>
23
+ <polyline points="3.27 6.96 12 12.01 20.73 6.96"/>
24
+ <line x1="12" y1="22.08" x2="12" y2="12"/>
25
+ </svg>
26
+ </div>
27
+ <h2 class="empty-state-title">No learning graphs yet</h2>
28
+ <p class="empty-state-desc">Connect Backpack to Claude, then start a conversation. Claude will build your first learning graph automatically.</p>
29
+ <div class="empty-state-setup">
30
+ <div class="empty-state-label">Add Backpack to Claude Code:</div>
31
+ <code class="empty-state-code">claude mcp add backpack-local -s user -- npx backpack-ontology@latest</code>
32
+ </div>
33
+ <p class="empty-state-hint">Press <kbd>?</kbd> for keyboard shortcuts</p>
34
+ </div>
35
+ `,t.appendChild(e),{show(){e.classList.remove("hidden")},hide(){e.classList.add("hidden")}}}const No=30;function Lo(){let t=[],e=[];return{push(a){t.push(JSON.stringify(a)),t.length>No&&t.shift(),e=[]},undo(a){return t.length===0?null:(e.push(JSON.stringify(a)),JSON.parse(t.pop()))},redo(a){return e.length===0?null:(t.push(JSON.stringify(a)),JSON.parse(e.pop()))},canUndo(){return t.length>0},canRedo(){return e.length>0},clear(){t=[],e=[]}}}function Io(t,e){let a=null;function r(b,c,n,D,i){d(),a=document.createElement("div"),a.className="context-menu",a.style.left=`${D}px`,a.style.top=`${i}px`;const s=[{label:n?"★ Unstar":"☆ Star",action:()=>e.onStar(b),premium:!1},{label:"◎ Focus on node",action:()=>e.onFocusNode(b),premium:!1},{label:"⑂ Explore in branch",action:()=>e.onExploreInBranch(b),premium:!1},{label:"⎘ Copy ID",action:()=>e.onCopyId(b),premium:!1}];e.onExpand&&s.push({label:"⊕ Expand node",action:()=>e.onExpand(b),premium:!0}),e.onExplainPath&&s.push({label:"↔ Explain path to…",action:()=>e.onExplainPath(b),premium:!0}),e.onEnrich&&s.push({label:"≡ Enrich from web",action:()=>e.onEnrich(b),premium:!0});let L=!1;for(const g of s){if(!L&&g.premium){const q=document.createElement("div");q.className="context-menu-separator",a.appendChild(q),L=!0}const U=document.createElement("div");U.className="context-menu-item",U.textContent=g.label,U.addEventListener("click",()=>{g.action(),d()}),a.appendChild(U)}t.appendChild(a);const $=a.getBoundingClientRect();$.right>window.innerWidth&&(a.style.left=`${D-$.width}px`),$.bottom>window.innerHeight&&(a.style.top=`${i-$.height}px`),setTimeout(()=>document.addEventListener("click",d),0),document.addEventListener("keydown",x)}function d(){a&&(a.remove(),a=null),document.removeEventListener("click",d),document.removeEventListener("keydown",x)}function x(b){b.key==="Escape"&&d()}return{show:r,hide:d}}const Mo={search:"/",searchAlt:"ctrl+k",undo:"ctrl+z",redo:"ctrl+shift+z",help:"?",escape:"Escape",focus:"f",toggleEdges:"e",center:"c",nextNode:".",prevNode:",",nextConnection:">",prevConnection:"<",historyBack:"(",historyForward:")",hopsIncrease:"=",hopsDecrease:"-",panLeft:"h",panDown:"j",panUp:"k",panRight:"l",panFastLeft:"H",zoomOut:"J",zoomIn:"K",panFastRight:"L",spacingDecrease:"[",spacingIncrease:"]",clusteringDecrease:"{",clusteringIncrease:"}",toggleSidebar:"Tab",walkMode:"w",walkIsolate:"i",resetPins:"r"},To={host:"127.0.0.1",port:5173},Ao={edges:!0,edgeLabels:!0,typeHulls:!0,minimap:!0,theme:"system"},Bo={spacing:1.5,clustering:.08},Po={panSpeed:60,panFastMultiplier:3,zoomFactor:1.3,zoomMin:.05,zoomMax:10,panAnimationMs:300},Ro={hideBadges:.4,hideLabels:.25,hideEdgeLabels:.35,smallNodes:.2,hideArrows:.15},Do={pulseSpeed:.02},Fo={maxSearchResults:8,maxQualityItems:5,maxMostConnected:5,searchDebounceMs:150},$o={keybindings:Mo,server:To,display:Ao,layout:Bo,navigation:Po,lod:Ro,walk:Do,limits:Fo};let pe="",I=null,Bt=new Set,Pt=!1;async function Oo(){const t=document.getElementById("canvas-container"),e={...$o};try{const o=await fetch("/api/config");if(o.ok){const u=await o.json();Object.assign(e.keybindings,u.keybindings??{}),Object.assign(e.display,u.display??{}),Object.assign(e.layout,u.layout??{}),Object.assign(e.navigation,u.navigation??{}),Object.assign(e.lod,u.lod??{}),Object.assign(e.limits,u.limits??{})}}catch{}const a=e.keybindings,r=window.matchMedia("(prefers-color-scheme: dark)"),d=e.display.theme==="system"?r.matches?"dark":"light":e.display.theme,b=localStorage.getItem("backpack-theme")??d;document.documentElement.setAttribute("data-theme",b);const c=document.createElement("button");c.className="theme-toggle",c.textContent=b==="light"?"☾":"☼",c.title="Toggle light/dark mode",c.addEventListener("click",()=>{const u=document.documentElement.getAttribute("data-theme")==="light"?"dark":"light";document.documentElement.setAttribute("data-theme",u),localStorage.setItem("backpack-theme",u),c.textContent=u==="light"?"☾":"☼"}),t.appendChild(c);const n=Lo();async function D(){if(!pe||!I)return;I.metadata.updatedAt=new Date().toISOString(),await Lt(pe,I),s.loadGraph(I),z.setLearningGraphData(I),te.setData(I);const o=await rt();S.setSummaries(o)}async function i(o){I=o,await Lt(pe,I),s.loadGraph(I),z.setLearningGraphData(I),te.setData(I);const u=await rt();S.setSummaries(u)}let s;const L=mo(t,{onUpdateNode(o,u){if(!I)return;n.push(I);const R=I.nodes.find(V=>V.id===o);R&&(R.properties={...R.properties,...u},R.updatedAt=new Date().toISOString(),D().then(()=>L.show([o],I)))},onChangeNodeType(o,u){if(!I)return;n.push(I);const R=I.nodes.find(V=>V.id===o);R&&(R.type=u,R.updatedAt=new Date().toISOString(),D().then(()=>L.show([o],I)))},onDeleteNode(o){I&&(n.push(I),I.nodes=I.nodes.filter(u=>u.id!==o),I.edges=I.edges.filter(u=>u.sourceId!==o&&u.targetId!==o),D())},onDeleteEdge(o){var R;if(!I)return;n.push(I);const u=(R=I.edges.find(V=>V.id===o))==null?void 0:R.sourceId;I.edges=I.edges.filter(V=>V.id!==o),D().then(()=>{u&&I&&L.show([u],I)})},onAddProperty(o,u,R){if(!I)return;n.push(I);const V=I.nodes.find(ie=>ie.id===o);V&&(V.properties[u]=R,V.updatedAt=new Date().toISOString(),D().then(()=>L.show([o],I)))}},o=>{s.panToNode(o)},o=>{te.addToFocusSet(o)}),$=window.matchMedia("(max-width: 768px)");let g=[],U=e.display.edges,q=e.navigation.panSpeed,oe=-1,W=null;function ee(o){W&&W.remove(),W=document.createElement("div"),W.className="focus-indicator";const u=document.createElement("span");u.className="focus-indicator-label",u.textContent=`Focused: ${o.totalNodes} nodes`;const R=document.createElement("span");R.className="focus-indicator-hops",R.textContent=`${o.hops}`;const V=document.createElement("button");V.className="focus-indicator-btn",V.textContent="−",V.title="Fewer hops",V.disabled=o.hops===0,V.addEventListener("click",()=>{s.enterFocus(o.seedNodeIds,Math.max(0,o.hops-1))});const ie=document.createElement("button");ie.className="focus-indicator-btn",ie.textContent="+",ie.title="More hops",ie.disabled=!1,ie.addEventListener("click",()=>{s.enterFocus(o.seedNodeIds,o.hops+1)});const le=document.createElement("button");le.className="focus-indicator-btn focus-indicator-exit",le.textContent="×",le.title="Exit focus (Esc)",le.addEventListener("click",()=>te.clearFocusSet());const me=document.createElement("button");me.className="walk-indicator",s.getWalkMode()&&me.classList.add("active"),me.textContent="Walk",me.title="Toggle walk mode (W) — click nodes to traverse",me.addEventListener("click",()=>{s.setWalkMode(!s.getWalkMode()),me.classList.toggle("active",s.getWalkMode())}),W.appendChild(u),W.appendChild(V),W.appendChild(R),W.appendChild(ie),W.appendChild(me),W.appendChild(le)}function _(){W&&(W.remove(),W=null)}const B=document.createElement("div");B.className="path-bar hidden",t.appendChild(B);function se(o){if(B.innerHTML="",!I)return;for(let R=0;R<o.nodeIds.length;R++){const V=o.nodeIds[R],ie=I.nodes.find(Ce=>Ce.id===V);if(!ie)continue;const le=Object.values(ie.properties).find(Ce=>typeof Ce=="string")??ie.id;if(R>0){const Ce=o.edgeIds[R-1],Me=I.edges.find(Je=>Je.id===Ce),we=document.createElement("span");we.className="path-bar-edge",we.textContent=Me?`→ ${Me.type} →`:"→",B.appendChild(we)}const me=document.createElement("span");me.className="path-bar-node",me.textContent=le,me.addEventListener("click",()=>s.panToNode(V)),B.appendChild(me)}const u=document.createElement("button");u.className="path-bar-close",u.textContent="×",u.addEventListener("click",J),B.appendChild(u),B.classList.remove("hidden")}function J(){B.classList.add("hidden"),B.innerHTML="",s.clearHighlightedPath()}s=po(t,o=>{if(g=o??[],!s.getWalkMode())if(o&&o.length===2){const u=s.findPath(o[0],o[1]);u&&u.nodeIds.length>0?(s.setHighlightedPath(u.nodeIds,u.edgeIds),se(u)):J()}else J();o&&o.length>0&&I?(L.show(o,I),$.matches&&te.collapse(),w(pe,o)):(L.hide(),pe&&w(pe))},o=>{if(o){ee(o);const u=t.querySelector(".canvas-top-left");u&&W&&u.appendChild(W),w(pe,o.seedNodeIds),L.setFocusDisabled(o.hops===0),f()}else _(),L.setFocusDisabled(!1),pe&&w(pe),f()},{lod:e.lod,navigation:e.navigation,walk:e.walk});const z=go(t,{maxResults:e.limits.maxSearchResults,debounceMs:e.limits.searchDebounceMs}),te=yo(t,{onFilterByType(o){if(I)if(o===null)s.setFilteredNodeIds(null);else{const u=new Set(((I==null?void 0:I.nodes)??[]).filter(R=>R.type===o).map(R=>R.id));s.setFilteredNodeIds(u)}},onNavigateToNode(o){s.panToNode(o),I&&L.show([o],I)},onWalkTrailRemove(o){s.removeFromWalkTrail(o),f()},onWalkIsolate(){if(!I)return;const o=s.getWalkTrail();o.length!==0&&s.enterFocus(o,0)},async onWalkSaveSnippet(o){if(!pe||!I)return;const u=s.getWalkTrail();if(u.length<2)return;const R=new Set(u),V=I.edges.filter(ie=>R.has(ie.sourceId)&&R.has(ie.targetId)).map(ie=>ie.id);await nn(pe,o,u,V),await E(pe)},async onStarredSaveSnippet(o,u){if(!pe||!I)return;const R=new Set(u),V=I.edges.filter(ie=>R.has(ie.sourceId)&&R.has(ie.targetId)).map(ie=>ie.id);await nn(pe,o,u,V),await E(pe)},onFocusChange(o){o&&o.length>0?s.enterFocus(o,0):s.isFocused()&&s.exitFocus()},onRenameNodeType(o,u){if(I){n.push(I);for(const R of I.nodes)R.type===o&&(R.type=u,R.updatedAt=new Date().toISOString());D()}},onRenameEdgeType(o,u){if(I){n.push(I);for(const R of I.edges)R.type===o&&(R.type=u);D()}},onToggleEdgeLabels(o){s.setEdgeLabels(o)},onToggleTypeHulls(o){s.setTypeHulls(o)},onToggleMinimap(o){s.setMinimap(o)},onLayoutChange(o,u){ot({[o]:u}),s.reheat()},onPanSpeedChange(o){q=o},onExport(o){const u=s.exportImage(o);if(!u)return;const R=document.createElement("a");R.download=`${pe||"graph"}.${o}`,R.href=u,R.click()},onSnapshot:async o=>{pe&&(await Wn(pe,o),await C(pe))},onRollback:async o=>{pe&&(await Hn(pe,o),I=await vt(pe),s.loadGraph(I),z.setLearningGraphData(I),te.setData(I),await C(pe))},onOpen(){$.matches&&L.hide()}}),de=document.createElement("div");de.className="canvas-top-bar";const ce=document.createElement("div");ce.className="canvas-top-left";const ue=document.createElement("div");ue.className="canvas-top-center";const ye=document.createElement("div");ye.className="canvas-top-right";const ve=t.querySelector(".tools-pane-toggle");ve&&ce.appendChild(ve);const j=t.querySelector(".search-overlay");j&&ue.appendChild(j);const G=t.querySelector(".zoom-controls");G&&ye.appendChild(G),ye.appendChild(c),de.appendChild(ce),de.appendChild(ue),de.appendChild(ye),t.appendChild(de),z.onFilterChange(o=>{s.setFilteredNodeIds(o)}),z.onNodeSelect(o=>{s.isFocused()&&te.clearFocusSet(),s.panToNode(o),I&&L.show([o],I)});const S=qn(document.getElementById("sidebar"),{onSelect:o=>A(o),onRename:async(o,u)=>{await Dn(o,u),pe===o&&(pe=u);const R=await rt();S.setSummaries(R),S.setActive(pe),pe===u&&(I=await vt(u),s.loadGraph(I),z.setLearningGraphData(I),te.setData(I))},onBranchSwitch:async(o,u)=>{await tn(o,u),await k(o),I=await vt(o),s.loadGraph(I),z.setLearningGraphData(I),te.setData(I),await C(o)},onBranchCreate:async(o,u)=>{await en(o,u),await k(o)},onBranchDelete:async(o,u)=>{await $n(o,u),await k(o)},onSnippetLoad:async(o,u)=>{var V;const R=await jn(o,u);((V=R==null?void 0:R.nodeIds)==null?void 0:V.length)>0&&s.enterFocus(R.nodeIds,0)},onSnippetDelete:async(o,u)=>{await Yn(o,u),await E(o)},onBackpackSwitch:async o=>{await fetch("/api/backpacks/switch",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:o})}),await l()},onBackpackRegister:async(o,u)=>{await fetch("/api/backpacks",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:o,activate:u})}),await l()}});async function l(){try{const u=await(await fetch("/api/backpacks")).json();S.setBackpacks(u)}catch{}try{const o=await rt();S.setSummaries(o),pe&&!o.some(u=>u.name===pe)&&(pe="",I=null,s.loadGraph({metadata:{name:"",description:"",createdAt:"",updatedAt:""},nodes:[],edges:[]}))}catch{}}function f(){const o=s.getWalkTrail();if(!I||o.length===0){te.setWalkTrail([]),J();return}const u=[],R=o.map((V,ie)=>{const le=I.nodes.find(Ce=>Ce.id===V);let me;if(ie>0){const Ce=o[ie-1],Me=I.edges.find(we=>we.sourceId===Ce&&we.targetId===V||we.targetId===Ce&&we.sourceId===V);me=Me==null?void 0:Me.type,Me&&u.push(Me.id)}return{id:V,label:le?Object.values(le.properties).find(Ce=>typeof Ce=="string")??le.id:V,type:(le==null?void 0:le.type)??"?",edgeType:me}});te.setWalkTrail(R),o.length>=2?(s.setHighlightedPath(o,u),se({nodeIds:o,edgeIds:u})):J()}async function k(o){const u=await Fn(o),R=u.find(V=>V.active);R&&S.setActiveBranch(o,R.name,u)}async function C(o){const u=await On(o);te.setSnapshots(u)}async function E(o){const u=await zn(o);S.setSnippets(o,u)}ce.insertBefore(S.expandBtn,ce.firstChild);const y=ko(t,a),m=So(t),T=Io(t,{onStar(o){if(!I)return;const u=I.nodes.find(V=>V.id===o);if(!u)return;const R=u.properties._starred===!0;u.properties._starred=!R,Lt(pe,I),s.loadGraph(I)},onFocusNode(o){te.addToFocusSet([o])},onExploreInBranch(o){if(pe){const u=`explore-${o.slice(0,8)}`;en(pe,u).then(()=>{tn(pe,u).then(()=>{s.enterFocus([o],1)})})}},onCopyId(o){navigator.clipboard.writeText(o)}});t.addEventListener("contextmenu",o=>{o.preventDefault();const u=t.querySelector("canvas");if(!u||!I)return;const R=u.getBoundingClientRect(),V=o.clientX-R.left,ie=o.clientY-R.top,le=s.nodeAtScreen(V,ie);if(!le)return;const me=I.nodes.find(we=>we.id===le.id);if(!me)return;const Ce=Object.values(me.properties).find(we=>typeof we=="string")??me.id,Me=me.properties._starred===!0;T.show(me.id,Ce,Me,o.clientX-R.left,o.clientY-R.top)}),e.display.edges||s.setEdges(!1),e.display.edgeLabels||s.setEdgeLabels(!1),e.display.typeHulls||s.setTypeHulls(!1),e.display.minimap||s.setMinimap(!1);function w(o,u){const R=[];u!=null&&u.length&&R.push("node="+u.map(encodeURIComponent).join(","));const V=s.getFocusInfo();V&&(R.push("focus="+V.seedNodeIds.map(encodeURIComponent).join(",")),R.push("hops="+V.hops));const ie="#"+encodeURIComponent(o)+(R.length?"?"+R.join("&"):"");history.replaceState(null,"",ie)}function P(){const o=window.location.hash.slice(1);if(!o)return{graph:null,nodes:[],focus:[],hops:1};const[u,R]=o.split("?"),V=u?decodeURIComponent(u):null;let ie=[],le=[],me=1;if(R){const Ce=new URLSearchParams(R),Me=Ce.get("node");Me&&(ie=Me.split(",").map(decodeURIComponent));const we=Ce.get("focus");we&&(le=we.split(",").map(decodeURIComponent));const Je=Ce.get("hops");Je&&(me=Math.max(0,parseInt(Je,10)||1))}return{graph:V,nodes:ie,focus:le,hops:me}}async function A(o,u,R,V){pe=o,Pt=Bt.has(o),S.setActive(o),L.hide(),_(),z.clear(),n.clear(),I=Pt?await Rn(o):await vt(o);const ie=Jn(I.nodes.length);if(ot({spacing:Math.max(e.layout.spacing,ie.spacing),clusterStrength:Math.max(e.layout.clustering,ie.clusterStrength)}),s.loadGraph(I),z.setLearningGraphData(I),te.setData(I),m.hide(),w(o),Pt||(await k(o),await C(o),await E(o)),R!=null&&R.length&&I){const le=R.filter(me=>I.nodes.some(Ce=>Ce.id===me));if(le.length){setTimeout(()=>{s.enterFocus(le,V??1)},500);return}}if(u!=null&&u.length&&I){const le=u.filter(me=>I.nodes.some(Ce=>Ce.id===me));le.length&&setTimeout(()=>{s.panToNodes(le),I&&L.show(le,I),w(o,le)},500)}}try{const u=await(await fetch("/api/backpacks")).json();S.setBackpacks(u)}catch{}fetch("/api/version-check").then(o=>o.json()).then(o=>{o.stale&&o.latest&&S.setStaleVersionBanner(o.current,o.latest)}).catch(()=>{});const[h,N]=await Promise.all([rt(),Pn().catch(()=>[])]);S.setSummaries(h),S.setRemotes(N),Bt=new Set(N.map(o=>o.name));const X=P(),ae=X.graph&&h.some(o=>o.name===X.graph)||X.graph&&Bt.has(X.graph)?X.graph:h.length>0?h[0].name:N.length>0?N[0].name:null;ae?await A(ae,X.nodes.length?X.nodes:void 0,X.focus.length?X.focus:void 0,X.hops):m.show();const he={search(){z.focus()},searchAlt(){z.focus()},undo(){if(I){const o=n.undo(I);o&&i(o)}},redo(){if(I){const o=n.redo(I);o&&i(o)}},focus(){s.isFocused()?te.clearFocusSet():g.length>0&&te.addToFocusSet(g)},hopsDecrease(){const o=s.getFocusInfo();o&&o.hops>0&&s.enterFocus(o.seedNodeIds,o.hops-1)},hopsIncrease(){const o=s.getFocusInfo();o&&s.enterFocus(o.seedNodeIds,o.hops+1)},nextNode(){const o=s.getNodeIds();o.length>0&&(oe=(oe+1)%o.length,s.panToNode(o[oe]),I&&L.show([o[oe]],I))},prevNode(){const o=s.getNodeIds();o.length>0&&(oe=oe<=0?o.length-1:oe-1,s.panToNode(o[oe]),I&&L.show([o[oe]],I))},nextConnection(){const o=L.cycleConnection(1);o&&s.panToNode(o)},prevConnection(){const o=L.cycleConnection(-1);o&&s.panToNode(o)},historyBack(){L.goBack()},historyForward(){L.goForward()},center(){s.centerView()},toggleEdges(){U=!U,s.setEdges(U)},panLeft(){s.panBy(-q,0)},panDown(){s.panBy(0,q)},panUp(){s.panBy(0,-q)},panRight(){s.panBy(q,0)},panFastLeft(){s.panBy(-q*e.navigation.panFastMultiplier,0)},zoomOut(){s.zoomBy(1/e.navigation.zoomFactor)},zoomIn(){s.zoomBy(e.navigation.zoomFactor)},panFastRight(){s.panBy(q*e.navigation.panFastMultiplier,0)},spacingDecrease(){const o=mt();ot({spacing:Math.max(.5,o.spacing-.5)}),s.reheat()},spacingIncrease(){const o=mt();ot({spacing:Math.min(20,o.spacing+.5)}),s.reheat()},clusteringDecrease(){const o=mt();ot({clusterStrength:Math.max(0,o.clusterStrength-.03)}),s.reheat()},clusteringIncrease(){const o=mt();ot({clusterStrength:Math.min(1,o.clusterStrength+.03)}),s.reheat()},help(){y.toggle()},toggleSidebar(){S.toggle()},resetPins(){s.releaseAllPins()&&Xn("Manual layout reset — pins released")},walkIsolate(){if(!I)return;const o=s.getWalkTrail();o.length!==0&&s.enterFocus(o,0)},walkMode(){!s.isFocused()&&g.length>0&&te.addToFocusSet(g),s.setWalkMode(!s.getWalkMode());const o=t.querySelector(".walk-indicator");o&&o.classList.toggle("active",s.getWalkMode()),f()},escape(){s.getSelectedNodeIds().length>0?s.clearSelection():s.isFocused()?te.clearFocusSet():y.hide()}};document.addEventListener("keydown",o=>{var u;if(!(o.target instanceof HTMLInputElement||o.target instanceof HTMLTextAreaElement)){for(const[R,V]of Object.entries(a))if(vo(o,V)){(R==="search"||R==="searchAlt"||R==="undo"||R==="redo"||R==="toggleSidebar")&&o.preventDefault(),(u=he[R])==null||u.call(he);return}}}),window.addEventListener("hashchange",()=>{const o=P();if(o.graph&&o.graph!==pe)A(o.graph,o.nodes.length?o.nodes:void 0,o.focus.length?o.focus:void 0,o.hops);else if(o.graph&&o.focus.length&&I)s.enterFocus(o.focus,o.hops);else if(o.graph&&o.nodes.length&&I){s.isFocused()&&s.exitFocus();const u=o.nodes.filter(R=>I.nodes.some(V=>V.id===R));u.length&&(s.panToNodes(u),L.show(u,I))}})}Oo();
@@ -0,0 +1 @@
1
+ (function(){"use strict";function T(t,s,n,r){return{x0:t,y0:s,x1:n,y1:r,cx:0,cy:0,mass:0,children:[null,null,null,null],body:null}}function L(t,s,n){const r=(t.x0+t.x1)/2,i=(t.y0+t.y1)/2;return(s<r?0:1)+(n<i?0:2)}function R(t,s){const n=(t.x0+t.x1)/2,r=(t.y0+t.y1)/2;switch(s){case 0:return[t.x0,t.y0,n,r];case 1:return[n,t.y0,t.x1,r];case 2:return[t.x0,r,n,t.y1];default:return[n,r,t.x1,t.y1]}}function _(t,s){if(t.mass===0&&t.body===null){t.body=s,t.cx=s.x,t.cy=s.y,t.mass=1;return}if(t.body!==null){const i=t.body;t.body=null,i.x===s.x&&i.y===s.y&&(s.x+=(Math.random()-.5)*.1,s.y+=(Math.random()-.5)*.1);const y=L(t,i.x,i.y);if(t.children[y]===null){const[l,e,c,f]=R(t,y);t.children[y]=T(l,e,c,f)}_(t.children[y],i)}const n=L(t,s.x,s.y);if(t.children[n]===null){const[i,y,l,e]=R(t,n);t.children[n]=T(i,y,l,e)}_(t.children[n],s);const r=t.mass+1;t.cx=(t.cx*t.mass+s.x)/r,t.cy=(t.cy*t.mass+s.y)/r,t.mass=r}function j(t){if(t.length===0)return null;let s=1/0,n=1/0,r=-1/0,i=-1/0;for(const o of t)o.x<s&&(s=o.x),o.y<n&&(n=o.y),o.x>r&&(r=o.x),o.y>i&&(i=o.y);const y=Math.max(r-s,i-n)*.1+50,l=(s+r)/2,e=(n+i)/2,c=Math.max(r-s,i-n)/2+y,f=T(l-c,e-c,l+c,e+c);for(const o of t)_(f,o);return f}function z(t,s,n,r,i,y){q(t,s,n,r,i,y)}function q(t,s,n,r,i,y){if(t.mass===0)return;const l=t.cx-s.x,e=t.cy-s.y,c=l*l+e*e;if(t.body!==null){if(t.body!==s){let o=Math.sqrt(c);o<y&&(o=y);const a=r*i/(o*o),x=l/o*a,u=e/o*a;s.vx-=x,s.vy-=u}return}const f=t.x1-t.x0;if(f*f/c<n*n){let o=Math.sqrt(c);o<y&&(o=y);const a=r*t.mass*i/(o*o),x=l/o*a,u=e/o*a;s.vx-=x,s.vy-=u;return}for(let o=0;o<4;o++)t.children[o]!==null&&q(t.children[o],s,n,r,i,y)}const N={clusterStrength:.08,spacing:1.5},P=6e3,X=12e3,F=.004,O=140,w=350,H=.9,C=.01,I=30,E=50;let S={...N};function B(t){t.clusterStrength!==void 0&&(S.clusterStrength=t.clusterStrength),t.spacing!==void 0&&(S.spacing=t.spacing)}function V(t){if(t<=30)return{...N};const s=Math.log2(t/30);return{clusterStrength:Math.min(.5,.08+.06*s),spacing:Math.min(15,1.5+1.2*s)}}function D(t,s){for(const n of Object.values(t))if(typeof n=="string")return n;return s}function K(t){const s=new Map,n=[...new Set(t.nodes.map(c=>c.type))],r=Math.sqrt(n.length)*w*.6*Math.max(1,S.spacing),i=new Map,y=new Map;for(const c of t.nodes)y.set(c.type,(y.get(c.type)??0)+1);const l=t.nodes.map(c=>{const f=n.indexOf(c.type),o=2*Math.PI*f/Math.max(n.length,1),a=Math.cos(o)*r,x=Math.sin(o)*r,u=i.get(c.type)??0;i.set(c.type,u+1);const d=y.get(c.type)??1,v=2*Math.PI*u/d,M=O*.6,m={id:c.id,x:a+Math.cos(v)*M,y:x+Math.sin(v)*M,vx:0,vy:0,label:D(c.properties,c.id),type:c.type};return s.set(c.id,m),m}),e=t.edges.map(c=>({sourceId:c.sourceId,targetId:c.targetId,type:c.type}));return{nodes:l,edges:e,nodeMap:s}}const Q=.7,b=80;function J(t,s){const{nodes:n,edges:r,nodeMap:i}=t,y=X*S.spacing;if(n.length>=b){const e=j(n);if(e)for(const f of n)z(e,f,Q,y,s,I);const c=y-P;if(c>0){const f=new Map;for(const o of n){let a=f.get(o.type);a||(a=[],f.set(o.type,a)),a.push(o)}for(const o of f.values())for(let a=0;a<o.length;a++)for(let x=a+1;x<o.length;x++){const u=o[a],d=o[x];let v=d.x-u.x,M=d.y-u.y,m=Math.sqrt(v*v+M*M);m<I&&(m=I);const k=c*s/(m*m),G=v/m*k,U=M/m*k;u.vx+=G,u.vy+=U,d.vx-=G,d.vy-=U}}}else for(let e=0;e<n.length;e++)for(let c=e+1;c<n.length;c++){const f=n[e],o=n[c];let a=o.x-f.x,x=o.y-f.y,u=Math.sqrt(a*a+x*x);u<I&&(u=I);const v=(f.type===o.type?P:y)*s/(u*u),M=a/u*v,m=x/u*v;f.vx-=M,f.vy-=m,o.vx+=M,o.vy+=m}for(const e of r){const c=i.get(e.sourceId),f=i.get(e.targetId);if(!c||!f)continue;const o=f.x-c.x,a=f.y-c.y,x=Math.sqrt(o*o+a*a);if(x===0)continue;const u=c.type===f.type?O*S.spacing:w*S.spacing,d=F*(x-u)*s,v=o/x*d,M=a/x*d;c.vx+=v,c.vy+=M,f.vx-=v,f.vy-=M}for(const e of n)e.vx-=e.x*C*s,e.vy-=e.y*C*s;const l=new Map;for(const e of n){const c=l.get(e.type)??{x:0,y:0,count:0};c.x+=e.x,c.y+=e.y,c.count++,l.set(e.type,c)}for(const e of l.values())e.x/=e.count,e.y/=e.count;for(const e of n){const c=l.get(e.type);e.vx+=(c.x-e.x)*S.clusterStrength*s,e.vy+=(c.y-e.y)*S.clusterStrength*s}for(const e of n){if(e.pinned){e.vx=0,e.vy=0;continue}e.vx*=H,e.vy*=H;const c=Math.sqrt(e.vx*e.vx+e.vy*e.vy);c>E&&(e.vx=e.vx/c*E,e.vy=e.vy/c*E),e.x+=e.vx,e.y+=e.vy}return s*.995}const W=.001,Z=3;let g=!1,h=null,p=1;function Y(t){const s=new Float64Array(t.length*4);for(let n=0;n<t.length;n++){const r=t[n];s[n*4]=r.x,s[n*4+1]=r.y,s[n*4+2]=r.vx,s[n*4+3]=r.vy}return s}function A(){if(!g||!h)return;for(let s=0;s<Z;s++){if(p<W){g=!1;const n=Y(h.nodes);self.postMessage({type:"tick",positions:n,alpha:p},{transfer:[n.buffer]}),self.postMessage({type:"settled"});return}p=J(h,p)}const t=Y(h.nodes);self.postMessage({type:"tick",positions:t,alpha:p},{transfer:[t.buffer]}),setTimeout(A,0)}self.onmessage=t=>{const s=t.data;if(s.type==="start"){g=!1;const n=s.data,r=s.params,i=V(n.nodes.length);B({...i,...r}),h=K(n),p=1,g=!0,A()}if(s.type==="stop"&&(g=!1),s.type==="resume"&&!g&&h&&(p=Math.max(p,typeof s.alpha=="number"?s.alpha:.5),g=!0,A()),s.type==="pin"&&h){const n=s.updates;for(const r of n){const i=h.nodeMap.get(r.id);i&&(i.x=r.x,i.y=r.y,i.vx=0,i.vy=0,i.pinned=!0)}p=Math.max(p,.3),g||(g=!0,A())}if(s.type==="unpin"&&h){const n=s.ids;if(n==="all")for(const r of h.nodes)r.pinned=!1;else if(Array.isArray(n))for(const r of n){const i=h.nodeMap.get(r);i&&(i.pinned=!1)}p=Math.max(p,.5),g||(g=!0,A())}s.type==="params"&&(B(s.params),p=Math.max(p,.3),!g&&h&&(g=!0,A()))}})();
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Backpack Viewer</title>
7
- <script type="module" crossorigin src="/assets/index-CKYlU1zT.js"></script>
7
+ <script type="module" crossorigin src="/assets/index-BROJmzot.js"></script>
8
8
  <link rel="stylesheet" crossorigin href="/assets/index-B3z5bBGl.css">
9
9
  </head>
10
10
  <body>
package/dist/canvas.d.ts CHANGED
@@ -28,6 +28,21 @@ export interface FocusInfo {
28
28
  export declare function initCanvas(container: HTMLElement, onNodeClick?: (nodeIds: string[] | null) => void, onFocusChange?: (focus: FocusInfo | null) => void, config?: CanvasConfig): {
29
29
  loadGraph(data: LearningGraphData): void;
30
30
  setFilteredNodeIds(ids: Set<string> | null): void;
31
+ /**
32
+ * Release every manually-pinned node so the force simulation
33
+ * reclaims them. Called by main.ts when a data event (node/edge
34
+ * add/remove, graph switch, backpack switch, focus/walk mode
35
+ * enter/exit, live reload) invalidates the user's temporary
36
+ * layout tweaks.
37
+ *
38
+ * Returns `true` if any pins were released, so the caller can
39
+ * show a toast only when the user actually loses work.
40
+ */
41
+ releaseAllPins(): boolean;
42
+ hasPinnedNodes(): boolean;
43
+ /** Clear the multi-selection (used by ESC keyboard shortcut). */
44
+ clearSelection(): void;
45
+ getSelectedNodeIds(): string[];
31
46
  panToNode(nodeId: string): void;
32
47
  panToNodes(nodeIds: string[]): void;
33
48
  setEdges(visible: boolean): void;