backpack-viewer 0.7.3 → 0.7.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -221,10 +221,9 @@ async function main() {
221
221
  focusIndicator = null;
222
222
  }
223
223
  }
224
- // --- Path bar ---
224
+ // --- Path bar (appended to bottom-center column later) ---
225
225
  const pathBar = document.createElement("div");
226
226
  pathBar.className = "path-bar hidden";
227
- canvasContainer.appendChild(pathBar);
228
227
  function showPathBar(path) {
229
228
  pathBar.replaceChildren();
230
229
  if (!currentData)
@@ -510,16 +509,33 @@ async function main() {
510
509
  focus: canvas.getFocusInfo(),
511
510
  }));
512
511
  topCenter.appendChild(copyPromptBtn);
513
- const extSlotBottomLeft = document.createElement("div");
514
- extSlotBottomLeft.className = "ext-slot ext-slot-bottom-left";
515
- canvasContainer.appendChild(extSlotBottomLeft);
516
- const extSlotBottomRight = document.createElement("div");
517
- extSlotBottomRight.className = "ext-slot ext-slot-bottom-right";
518
- canvasContainer.appendChild(extSlotBottomRight);
519
512
  topBar.appendChild(topLeft);
520
513
  topBar.appendChild(topCenter);
521
514
  topBar.appendChild(topRight);
522
515
  canvasContainer.appendChild(topBar);
516
+ // --- Bottom bar: mirrors top bar structure at the bottom of the canvas ---
517
+ const bottomBar = document.createElement("div");
518
+ bottomBar.className = "canvas-bottom-bar";
519
+ const bottomLeft = document.createElement("div");
520
+ bottomLeft.className = "canvas-bottom-left";
521
+ const bottomCenter = document.createElement("div");
522
+ bottomCenter.className = "canvas-bottom-center";
523
+ const bottomRight = document.createElement("div");
524
+ bottomRight.className = "canvas-bottom-right";
525
+ const extSlotBottomLeft = document.createElement("div");
526
+ extSlotBottomLeft.className = "ext-slot ext-slot-bottom-left";
527
+ bottomLeft.appendChild(extSlotBottomLeft);
528
+ const extSlotBottomCenter = document.createElement("div");
529
+ extSlotBottomCenter.className = "ext-slot ext-slot-bottom-center";
530
+ bottomCenter.appendChild(extSlotBottomCenter);
531
+ bottomCenter.appendChild(pathBar);
532
+ const extSlotBottomRight = document.createElement("div");
533
+ extSlotBottomRight.className = "ext-slot ext-slot-bottom-right";
534
+ bottomRight.appendChild(extSlotBottomRight);
535
+ bottomBar.appendChild(bottomLeft);
536
+ bottomBar.appendChild(bottomCenter);
537
+ bottomBar.appendChild(bottomRight);
538
+ canvasContainer.appendChild(bottomBar);
523
539
  search.onFilterChange((ids) => {
524
540
  canvas.setFilteredNodeIds(ids);
525
541
  });
@@ -960,6 +976,7 @@ async function main() {
960
976
  topLeft: extSlotTopLeft,
961
977
  topRight: extSlotTopRight,
962
978
  bottomLeft: extSlotBottomLeft,
979
+ bottomCenter: extSlotBottomCenter,
963
980
  bottomRight: extSlotBottomRight,
964
981
  },
965
982
  subscribe: (event, cb) => eventBus.subscribe(event, cb),
package/dist/style.css CHANGED
@@ -687,6 +687,36 @@ body {
687
687
  justify-content: center;
688
688
  }
689
689
 
690
+ /* --- Bottom Bar --- */
691
+
692
+ .canvas-bottom-bar {
693
+ position: absolute;
694
+ bottom: 16px;
695
+ left: 16px;
696
+ right: 16px;
697
+ display: flex;
698
+ justify-content: space-between;
699
+ align-items: flex-end;
700
+ pointer-events: none;
701
+ gap: 8px;
702
+ }
703
+
704
+ .canvas-bottom-left,
705
+ .canvas-bottom-center,
706
+ .canvas-bottom-right {
707
+ display: flex;
708
+ align-items: center;
709
+ gap: 4px;
710
+ pointer-events: auto;
711
+ position: relative;
712
+ z-index: var(--z-floating);
713
+ }
714
+
715
+ .canvas-bottom-center {
716
+ flex: 1;
717
+ justify-content: center;
718
+ }
719
+
690
720
  /* --- Focus Indicator --- */
691
721
 
692
722
  .focus-indicator {
@@ -2071,6 +2101,12 @@ body {
2071
2101
  right: 8px;
2072
2102
  }
2073
2103
 
2104
+ .canvas-bottom-bar {
2105
+ bottom: 8px;
2106
+ left: 8px;
2107
+ right: 8px;
2108
+ }
2109
+
2074
2110
  .tools-pane-content {
2075
2111
  top: 48px;
2076
2112
  left: 8px;
@@ -2302,11 +2338,6 @@ body {
2302
2338
  /* --- Path Bar --- */
2303
2339
 
2304
2340
  .path-bar {
2305
- position: absolute;
2306
- bottom: 16px;
2307
- left: 50%;
2308
- transform: translateX(-50%);
2309
- z-index: var(--z-canvas-overlay);
2310
2341
  display: flex;
2311
2342
  align-items: center;
2312
2343
  gap: 4px;
@@ -2429,14 +2460,11 @@ body {
2429
2460
  display: inline-flex;
2430
2461
  }
2431
2462
 
2432
- /* Bottom slots float in the canvas corners. Hidden until populated so
2433
- empty corners don't take any space. The bottom-center area is left
2434
- clear for the path bar — see .path-bar above. */
2463
+ /* Bottom slots live inside bottom bar columns. Hidden until populated
2464
+ so empty slots take no space. */
2435
2465
  .ext-slot-bottom-left,
2466
+ .ext-slot-bottom-center,
2436
2467
  .ext-slot-bottom-right {
2437
- position: absolute;
2438
- bottom: 16px;
2439
- z-index: var(--z-floating);
2440
2468
  display: none;
2441
2469
  flex-direction: row;
2442
2470
  align-items: center;
@@ -2446,18 +2474,10 @@ body {
2446
2474
  border: 1px solid var(--border);
2447
2475
  border-radius: 12px;
2448
2476
  box-shadow: 0 4px 16px var(--shadow);
2449
- pointer-events: auto;
2450
- }
2451
-
2452
- .ext-slot-bottom-left {
2453
- left: 16px;
2454
- }
2455
-
2456
- .ext-slot-bottom-right {
2457
- right: 16px;
2458
2477
  }
2459
2478
 
2460
2479
  .ext-slot-bottom-left.has-icons,
2480
+ .ext-slot-bottom-center.has-icons,
2461
2481
  .ext-slot-bottom-right.has-icons {
2462
2482
  display: flex;
2463
2483
  }
@@ -2487,6 +2507,7 @@ body {
2487
2507
  /* Inside the bottom floating pill, drop the per-icon shadow and
2488
2508
  border so the pill container's chrome wraps the group cleanly. */
2489
2509
  .ext-slot-bottom-left .extension-taskbar-icon,
2510
+ .ext-slot-bottom-center .extension-taskbar-icon,
2490
2511
  .ext-slot-bottom-right .extension-taskbar-icon {
2491
2512
  background: transparent;
2492
2513
  border-color: transparent;
@@ -2500,6 +2521,7 @@ body {
2500
2521
  }
2501
2522
 
2502
2523
  .ext-slot-bottom-left .extension-taskbar-icon:hover,
2524
+ .ext-slot-bottom-center .extension-taskbar-icon:hover,
2503
2525
  .ext-slot-bottom-right .extension-taskbar-icon:hover {
2504
2526
  border-color: var(--border);
2505
2527
  }
@@ -2576,8 +2598,7 @@ body {
2576
2598
  searching and selecting nodes while a panel is fullscreen. */
2577
2599
  #canvas-container.has-fullscreen-panel .canvas-top-left,
2578
2600
  #canvas-container.has-fullscreen-panel .canvas-top-right,
2579
- #canvas-container.has-fullscreen-panel .ext-slot-bottom-left,
2580
- #canvas-container.has-fullscreen-panel .ext-slot-bottom-right,
2601
+ #canvas-container.has-fullscreen-panel .canvas-bottom-bar,
2581
2602
  #canvas-container.has-fullscreen-panel .ext-slot-top-left,
2582
2603
  #canvas-container.has-fullscreen-panel .ext-slot-top-right {
2583
2604
  display: none;
@@ -2701,4 +2722,3 @@ body {
2701
2722
  display: flex;
2702
2723
  flex-direction: column;
2703
2724
  }
2704
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backpack-viewer",
3
- "version": "0.7.3",
3
+ "version": "0.7.5",
4
4
  "description": "Web-based graph visualizer for backpack-ontology — Canvas 2D, force-directed layout, live reload",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Noah Irzinger",
@@ -1,6 +0,0 @@
1
- var Jn=Object.defineProperty;var Kn=(t,e,s)=>e in t?Jn(t,e,{enumerable:!0,configurable:!0,writable:!0,value:s}):t[e]=s;var It=(t,e,s)=>Kn(t,typeof e!="symbol"?e+"":e,s);(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const u of document.querySelectorAll('link[rel="modulepreload"]'))l(u);new MutationObserver(u=>{for(const r of u)if(r.type==="childList")for(const d of r.addedNodes)d.tagName==="LINK"&&d.rel==="modulepreload"&&l(d)}).observe(document,{childList:!0,subtree:!0});function s(u){const r={};return u.integrity&&(r.integrity=u.integrity),u.referrerPolicy&&(r.referrerPolicy=u.referrerPolicy),u.crossOrigin==="use-credentials"?r.credentials="include":u.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function l(u){if(u.ep)return;u.ep=!0;const r=s(u);fetch(u.href,r)}})();const Zn="modulepreload",Qn=function(t){return"/"+t},hn={},eo=function(e,s,l){let u=Promise.resolve();if(s&&s.length>0){let d=function(g){return Promise.all(g.map(i=>Promise.resolve(i).then(a=>({status:"fulfilled",value:a}),a=>({status:"rejected",reason:a}))))};document.getElementsByTagName("link");const o=document.querySelector("meta[property=csp-nonce]"),n=(o==null?void 0:o.nonce)||(o==null?void 0:o.getAttribute("nonce"));u=d(s.map(g=>{if(g=Qn(g),g in hn)return;hn[g]=!0;const i=g.endsWith(".css"),a=i?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${g}"]${a}`))return;const M=document.createElement("link");if(M.rel=i?"stylesheet":Zn,i||(M.as="script"),M.crossOrigin="",M.href=g,n&&M.setAttribute("nonce",n),document.head.appendChild(M),i)return new Promise((T,x)=>{M.addEventListener("load",T),M.addEventListener("error",()=>x(new Error(`Unable to preload CSS for ${g}`)))})}))}function r(d){const o=new Event("vite:preloadError",{cancelable:!0});if(o.payload=d,window.dispatchEvent(o),!o.defaultPrevented)throw d}return u.then(d=>{for(const o of d||[])o.status==="rejected"&&r(o.reason);return e().catch(r)})};async function vt(){const t=await fetch("/api/ontologies");return t.ok?t.json():[]}async function Mt(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 to(){const t=await fetch("/api/remotes");return t.ok?t.json():[]}async function no(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 Ot(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 oo(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 so(t){const e=await fetch(`/api/graphs/${encodeURIComponent(t)}/branches`);return e.ok?e.json():[]}async function fn(t,e,s){const l=await fetch(`/api/graphs/${encodeURIComponent(t)}/branches`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e,from:s})});if(!l.ok){const u=await l.json().catch(()=>({}));throw new Error(u.error||"Failed to create branch")}}async function gn(t,e){const s=await fetch(`/api/graphs/${encodeURIComponent(t)}/branches/switch`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e})});if(!s.ok){const l=await s.json().catch(()=>({}));throw new Error(l.error||"Failed to switch branch")}}async function ao(t,e){const s=await fetch(`/api/graphs/${encodeURIComponent(t)}/branches/${encodeURIComponent(e)}`,{method:"DELETE"});if(!s.ok){const l=await s.json().catch(()=>({}));throw new Error(l.error||"Failed to delete branch")}}async function co(t){const e=await fetch(`/api/graphs/${encodeURIComponent(t)}/snapshots`);return e.ok?e.json():[]}async function io(t,e){const s=await fetch(`/api/graphs/${encodeURIComponent(t)}/snapshots`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({label:e})});if(!s.ok){const l=await s.json().catch(()=>({}));throw new Error(l.error||"Failed to create snapshot")}}async function lo(t,e){const s=await fetch(`/api/graphs/${encodeURIComponent(t)}/rollback`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({version:e})});if(!s.ok){const l=await s.json().catch(()=>({}));throw new Error(l.error||"Failed to rollback")}}async function ro(t){const e=await fetch(`/api/graphs/${encodeURIComponent(t)}/snippets`);return e.ok?e.json():[]}async function yn(t,e,s,l,u){const r=await fetch(`/api/graphs/${encodeURIComponent(t)}/snippets`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({label:e,description:u,nodeIds:s,edgeIds:l})});if(!r.ok)throw new Error("Failed to save snippet");return(await r.json()).id}async function po(t,e){const s=await fetch(`/api/graphs/${encodeURIComponent(t)}/snippets/${encodeURIComponent(e)}`);if(!s.ok)throw new Error("Snippet not found");return s.json()}async function uo(t,e){if(!(await fetch(`/api/graphs/${encodeURIComponent(t)}/snippets/${encodeURIComponent(e)}`,{method:"DELETE"})).ok)throw new Error("Failed to delete snippet")}const Cn="bp-dialog-overlay";function Zt(){var e;(e=document.querySelector(`.${Cn}`))==null||e.remove();const t=document.createElement("div");return t.className=Cn,document.body.appendChild(t),t}function Qt(t,e){const s=document.createElement("div");s.className="bp-dialog";const l=document.createElement("h4");return l.className="bp-dialog-title",l.textContent=e,s.appendChild(l),t.appendChild(s),t.addEventListener("click",u=>{u.target===t&&t.remove()}),s}function en(t,e){const s=document.createElement("div");s.className="bp-dialog-buttons";for(const l of e){const u=document.createElement("button");u.className="bp-dialog-btn",l.accent&&u.classList.add("bp-dialog-btn-accent"),l.danger&&u.classList.add("bp-dialog-btn-danger"),u.textContent=l.label,u.addEventListener("click",l.onClick),s.appendChild(u)}t.appendChild(s)}function jn(t,e){return new Promise(s=>{var d;const l=Zt(),u=Qt(l,t),r=document.createElement("p");r.className="bp-dialog-message",r.textContent=e,u.appendChild(r),en(u,[{label:"Cancel",onClick:()=>{l.remove(),s(!1)}},{label:"Confirm",accent:!0,onClick:()=>{l.remove(),s(!0)}}]),(d=u.querySelector(".bp-dialog-btn-accent"))==null||d.focus()})}function Bt(t,e,s){return new Promise(l=>{const u=Zt(),r=Qt(u,t),d=document.createElement("input");d.type="text",d.className="bp-dialog-input",d.placeholder=e??"",d.value="",r.appendChild(d);const o=()=>{const n=d.value.trim();u.remove(),l(n||null)};d.addEventListener("keydown",n=>{n.key==="Enter"&&o(),n.key==="Escape"&&(u.remove(),l(null))}),en(r,[{label:"Cancel",onClick:()=>{u.remove(),l(null)}},{label:"OK",accent:!0,onClick:o}]),d.focus(),d.select()})}function mo(){return new Promise(t=>{const e=Zt(),s=Qt(e,"Add Backpack"),l=document.createElement("p");l.className="bp-dialog-message",l.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.",s.appendChild(l);const u=document.createElement("label");u.className="bp-dialog-label",u.textContent="Path",s.appendChild(u);const r=document.createElement("div");r.className="bp-dialog-path-row",s.appendChild(r);const d=document.createElement("input");d.type="text",d.className="bp-dialog-input bp-dialog-path-input",d.placeholder="/Users/you/OneDrive/work",r.appendChild(d);const o=document.createElement("button");o.type="button",o.className="bp-dialog-btn bp-dialog-browse-btn",o.textContent="Browse...",r.appendChild(o),typeof window.showDirectoryPicker=="function"||(o.disabled=!0,o.title="Browser doesn't support native folder picker — paste the path manually"),o.addEventListener("click",async x=>{x.preventDefault();try{const F=await window.showDirectoryPicker({mode:"read"});g.textContent=`Picked "${F.name}" — paste the absolute path to it below.`,d.focus()}catch{}});const g=document.createElement("div");g.className="bp-dialog-picker-hint",s.appendChild(g);const i=document.createElement("div");i.className="bp-dialog-activate-row";const a=document.createElement("input");a.type="checkbox",a.id="bp-dialog-activate",a.checked=!0;const M=document.createElement("label");M.htmlFor="bp-dialog-activate",M.textContent="Switch to this backpack after registering",i.appendChild(a),i.appendChild(M),s.appendChild(i),d.addEventListener("dragover",x=>{x.preventDefault(),d.classList.add("bp-dialog-drag-over")}),d.addEventListener("dragleave",()=>{d.classList.remove("bp-dialog-drag-over")}),d.addEventListener("drop",x=>{var V,z,X;x.preventDefault(),d.classList.remove("bp-dialog-drag-over");const F=(V=x.dataTransfer)==null?void 0:V.items;if(!F||F.length===0)return;const W=(X=(z=F[0]).webkitGetAsEntry)==null?void 0:X.call(z);W!=null&&W.isDirectory&&(g.textContent=`Dropped "${W.name}" — paste the absolute path to it below.`)});const T=()=>{const x=d.value.trim();x&&(e.remove(),t({path:x,activate:a.checked}))};d.addEventListener("keydown",x=>{x.key==="Enter"&&T(),x.key==="Escape"&&(e.remove(),t(null))}),en(s,[{label:"Cancel",onClick:()=>{e.remove(),t(null)}},{label:"Register",accent:!0,onClick:T}]),d.focus()})}function wt(t,e=3e3){const s=document.querySelector(".bp-toast");s&&s.remove();const l=document.createElement("div");l.className="bp-toast",l.textContent=t,document.body.appendChild(l),setTimeout(()=>l.classList.add("bp-toast-visible"),10),setTimeout(()=>{l.classList.remove("bp-toast-visible"),setTimeout(()=>l.remove(),300)},e)}const vn="http://www.w3.org/2000/svg";function ct(t,e){const s=document.createElementNS(vn,"svg"),l=t.size??16;s.setAttribute("width",String(l)),s.setAttribute("height",String(l)),s.setAttribute("viewBox",t.viewBox??"0 0 24 24"),s.setAttribute("fill","none"),s.setAttribute("stroke","currentColor"),s.setAttribute("stroke-width",String(t.strokeWidth??2)),t.strokeLinecap&&s.setAttribute("stroke-linecap",t.strokeLinecap),t.strokeLinejoin&&s.setAttribute("stroke-linejoin",t.strokeLinejoin),t.className&&s.setAttribute("class",t.className);for(const u of e){const r=document.createElementNS(vn,u.tag);for(const[d,o]of Object.entries(u.attrs))r.setAttribute(d,String(o));s.appendChild(r)}return s}function ho(t){return Array.from(t.childNodes).map(e=>e.cloneNode(!0))}function fo(t,e){t.replaceChildren(...e)}function bn(t){return t>=1e3?`${(t/1e3).toFixed(1)}k tokens`:`${t} tokens`}function xn(t,e){return t*50+e*25+50}function go(t,e){const s=typeof e=="function"?{onSelect:e}:e,l=document.createElement("h2");l.textContent="Backpack Viewer";const u=document.createElement("input");u.type="text",u.placeholder="Filter...",u.id="filter";const r=document.createElement("ul");r.id="ontology-list";const d=document.createElement("h3");d.className="sidebar-section-heading",d.textContent="REMOTE GRAPHS",d.hidden=!0;const o=document.createElement("ul");o.id="remote-list",o.className="remote-list",o.hidden=!0;const n=document.createElement("div");n.className="sidebar-footer";const g=document.createElement("a");g.href="mailto:support@backpackontology.com",g.textContent="support@backpackontology.com";const i=document.createElement("span");i.textContent="Feedback & support";const a=document.createElement("span");a.className="sidebar-version",a.textContent="v0.7.3",n.append(g,i,a);const M=document.createElement("button");M.className="sidebar-collapse-btn",M.title="Toggle sidebar (Tab)",M.appendChild(ct({size:14},[{tag:"polyline",attrs:{points:"11 17 6 12 11 7"}},{tag:"polyline",attrs:{points:"18 17 13 12 18 7"}}]));let T=!1;function x(){T=!T,t.classList.toggle("sidebar-collapsed",T),ye.classList.toggle("hidden",!T)}M.addEventListener("click",x);const F=document.createElement("div");F.className="sidebar-heading-row",F.appendChild(l),F.appendChild(M),t.appendChild(F);const W=document.createElement("div");W.className="sidebar-stale-banner",W.hidden=!0,t.appendChild(W);const V=document.createElement("button");V.className="backpack-picker-pill",V.type="button",V.setAttribute("aria-haspopup","listbox"),V.setAttribute("aria-expanded","false");const z=document.createElement("span");z.className="backpack-picker-dot";const X=document.createElement("span");X.className="backpack-picker-name",X.textContent="...";const H=document.createElement("span");H.className="backpack-picker-caret",H.textContent="▾",V.appendChild(z),V.appendChild(X),V.appendChild(H);const K=document.createElement("div");K.className="backpack-picker-dropdown",K.hidden=!0,K.setAttribute("role","listbox");const q=document.createElement("div");q.className="backpack-picker-container",q.appendChild(V),q.appendChild(K),t.appendChild(q);let ue=!1;function U(){ue=!1,K.hidden=!0,V.setAttribute("aria-expanded","false")}function de(){ue=!0,K.hidden=!1,V.setAttribute("aria-expanded","true")}V.addEventListener("click",p=>{p.stopPropagation(),ue?U():de()}),document.addEventListener("click",p=>{q.contains(p.target)||U()});let xe=[],ee=null;function G(){K.replaceChildren();for(const v of xe){const m=document.createElement("button");m.className="backpack-picker-item",m.type="button",m.setAttribute("role","option"),v.active&&m.classList.add("active");const b=document.createElement("span");b.className="backpack-picker-item-dot",b.style.setProperty("--backpack-color",v.color);const L=document.createElement("span");L.className="backpack-picker-item-name",L.textContent=v.name;const h=document.createElement("span");h.className="backpack-picker-item-path",h.textContent=v.path,m.appendChild(b),m.appendChild(L),m.appendChild(h),m.addEventListener("click",S=>{S.stopPropagation(),U(),!v.active&&s.onBackpackSwitch&&s.onBackpackSwitch(v.name)}),K.appendChild(m)}const p=document.createElement("div");p.className="backpack-picker-divider",K.appendChild(p);const y=document.createElement("button");y.className="backpack-picker-item backpack-picker-add",y.type="button",y.textContent="+ Add new backpack…",y.addEventListener("click",async v=>{if(v.stopPropagation(),U(),!s.onBackpackRegister)return;const m=await mo();m&&s.onBackpackRegister(m.path,m.activate)}),K.appendChild(y)}const ye=document.createElement("button");ye.className="tools-pane-toggle hidden",ye.title="Show sidebar (Tab)",ye.appendChild(ct({size:14},[{tag:"polyline",attrs:{points:"13 7 18 12 13 17"}},{tag:"polyline",attrs:{points:"6 7 11 12 6 17"}}])),ye.addEventListener("click",x),t.appendChild(u),t.appendChild(r),t.appendChild(d),t.appendChild(o),t.appendChild(n);let Ce=[],se=[],ge="";return u.addEventListener("input",()=>{const p=u.value.toLowerCase();for(const y of Ce){const v=y.dataset.name??"";y.style.display=v.includes(p)?"":"none"}for(const y of se){const v=y.dataset.name??"";y.style.display=v.includes(p)?"":"none"}}),{setStaleVersionBanner(p,y){W.replaceChildren();const v=document.createElement("div");v.className="sidebar-stale-banner-title",v.textContent=`Viewer ${p} is out of date`;const m=document.createElement("div");m.className="sidebar-stale-banner-subtitle",m.textContent=`Latest is ${y}. Your version is stuck because of an npx cache.`;const b=document.createElement("pre");b.className="sidebar-stale-banner-hint",b.textContent=`npm cache clean --force
2
- npx backpack-viewer@latest`,W.appendChild(v),W.appendChild(m),W.appendChild(b),W.hidden=!1},setBackpacks(p){xe=p.slice();const y=p.find(v=>v.active)??null;ee=y,y&&(X.textContent=y.name,z.style.setProperty("--backpack-color",y.color),t.style.setProperty("--backpack-color",y.color)),G()},setActiveBackpack(p){ee=p,xe=xe.map(y=>({...y,active:y.name===p.name})),xe.some(y=>y.name===p.name)||xe.push({...p,active:!0}),X.textContent=p.name,z.style.setProperty("--backpack-color",p.color),t.style.setProperty("--backpack-color",p.color),G()},getActiveBackpack(){return ee},setSummaries(p){r.replaceChildren();const y=fetch("/api/locks").then(v=>v.json()).catch(()=>({}));Ce=p.map(v=>{const m=document.createElement("li");m.className="ontology-item",m.dataset.name=v.name;const b=document.createElement("span");b.className="name",b.textContent=v.name;const L=document.createElement("span");L.className="stats";const h=xn(v.nodeCount,v.edgeCount);L.textContent=`${v.nodeCount} nodes, ${v.edgeCount} edges · ~${bn(h)}`;const S=document.createElement("span");S.className="sidebar-branch",S.dataset.graph=v.name;const w=document.createElement("span");if(w.className="sidebar-lock-badge",w.dataset.graph=v.name,y.then(N=>{if(!w.isConnected)return;const I=N[v.name];I&&typeof I=="object"&&I.author&&(w.textContent=`editing: ${I.author}`,w.title=`Last activity: ${I.lastActivity??""}`,w.classList.add("active"))}),m.appendChild(b),m.appendChild(L),m.appendChild(w),m.appendChild(S),s.onRename){const N=document.createElement("button");N.className="sidebar-edit-btn",N.textContent="✎",N.title="Rename";const I=s.onRename;N.addEventListener("click",C=>{C.stopPropagation();const B=document.createElement("input");B.type="text",B.className="sidebar-rename-input",B.value=v.name,b.textContent="",b.appendChild(B),N.style.display="none",B.focus(),B.select();const $=()=>{const te=B.value.trim();te&&te!==v.name?I(v.name,te):(b.textContent=v.name,N.style.display="")};B.addEventListener("blur",$),B.addEventListener("keydown",te=>{te.key==="Enter"&&B.blur(),te.key==="Escape"&&(B.value=v.name,B.blur())})}),m.appendChild(N)}return m.addEventListener("click",()=>s.onSelect(v.name)),r.appendChild(m),m}),ge&&this.setActive(ge)},setActive(p){ge=p;for(const y of Ce)y.classList.toggle("active",y.dataset.name===p);for(const y of se)y.classList.toggle("active",y.dataset.name===p)},setRemotes(p){o.replaceChildren(),se=p.map(v=>{const m=document.createElement("li");m.className="ontology-item ontology-item-remote",m.dataset.name=v.name;const b=document.createElement("div");b.className="remote-name-row";const L=document.createElement("span");L.className="name",L.textContent=v.name;const h=document.createElement("span");h.className="remote-badge",h.textContent=v.pinned?"remote · pinned":"remote",h.title=`Source: ${v.source??v.url}`,b.appendChild(L),b.appendChild(h);const S=document.createElement("span");S.className="stats";const w=xn(v.nodeCount,v.edgeCount);S.textContent=`${v.nodeCount} nodes, ${v.edgeCount} edges · ~${bn(w)}`;const N=document.createElement("span");return N.className="remote-source",N.textContent=v.source??new URL(v.url).hostname,N.title=v.url,m.appendChild(b),m.appendChild(S),m.appendChild(N),m.addEventListener("click",()=>s.onSelect(v.name)),o.appendChild(m),m});const y=p.length>0;d.hidden=!y,o.hidden=!y,ge&&this.setActive(ge)},setActiveBranch(p,y,v){const m=r.querySelectorAll(`.sidebar-branch[data-graph="${p}"]`);for(const b of m){b.textContent=`/ ${y}`,b.title="Click to switch branch",b.style.cursor="pointer";const L=b.cloneNode(!0);b.replaceWith(L),L.addEventListener("click",h=>{h.stopPropagation(),be(p,L,v??[])})}},setSnippets(p,y){var b;const v=Ce.find(L=>L.dataset.name===p);if(!v||((b=v.querySelector(".sidebar-snippets"))==null||b.remove(),y.length===0))return;const m=document.createElement("div");m.className="sidebar-snippets";for(const L of y){const h=document.createElement("div");h.className="sidebar-snippet";const S=document.createElement("span");S.className="sidebar-snippet-label",S.textContent=`◆ ${L.label}`,S.title=`${L.nodeCount} nodes — click to load`;const w=document.createElement("button");w.className="sidebar-snippet-delete",w.textContent="×",w.title="Delete snippet",w.addEventListener("click",N=>{var I;N.stopPropagation(),(I=s.onSnippetDelete)==null||I.call(s,p,L.id)}),h.appendChild(S),h.appendChild(w),h.addEventListener("click",N=>{var I;N.stopPropagation(),(I=s.onSnippetLoad)==null||I.call(s,p,L.id)}),m.appendChild(h)}v.appendChild(m)},toggle:x,expandBtn:ye};function be(p,y,v){const m=t.querySelector(".branch-picker");m&&m.remove();const b=document.createElement("div");b.className="branch-picker";for(const h of v){const S=document.createElement("div");S.className="branch-picker-item",h.active&&S.classList.add("branch-picker-active");const w=document.createElement("span");if(w.textContent=h.name,S.appendChild(w),!h.active&&s.onBranchDelete){const N=document.createElement("button");N.className="branch-picker-delete",N.textContent="×",N.title=`Delete ${h.name}`,N.addEventListener("click",I=>{I.stopPropagation(),jn("Delete branch",`Delete branch "${h.name}"?`).then(C=>{C&&(s.onBranchDelete(p,h.name),b.remove())})}),S.appendChild(N)}h.active||S.addEventListener("click",()=>{var N;(N=s.onBranchSwitch)==null||N.call(s,p,h.name),b.remove()}),b.appendChild(S)}if(s.onBranchCreate){const h=document.createElement("div");h.className="branch-picker-item branch-picker-create",h.textContent="+ New branch",h.addEventListener("click",()=>{Bt("New branch","Branch name").then(S=>{S&&(s.onBranchCreate(p,S),b.remove())})}),b.appendChild(h)}y.after(b);const L=h=>{b.contains(h.target)||(b.remove(),document.removeEventListener("click",L))};setTimeout(()=>document.addEventListener("click",L),0)}}function Gt(t,e,s,l){return{x0:t,y0:e,x1:s,y1:l,cx:0,cy:0,mass:0,children:[null,null,null,null],body:null}}function En(t,e,s){const l=(t.x0+t.x1)/2,u=(t.y0+t.y1)/2;return(e<l?0:1)+(s<u?0:2)}function wn(t,e){const s=(t.x0+t.x1)/2,l=(t.y0+t.y1)/2;switch(e){case 0:return[t.x0,t.y0,s,l];case 1:return[s,t.y0,t.x1,l];case 2:return[t.x0,l,s,t.y1];default:return[s,l,t.x1,t.y1]}}function Vt(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 u=t.body;t.body=null,u.x===e.x&&u.y===e.y&&(e.x+=(Math.random()-.5)*.1,e.y+=(Math.random()-.5)*.1);const r=En(t,u.x,u.y);if(t.children[r]===null){const[d,o,n,g]=wn(t,r);t.children[r]=Gt(d,o,n,g)}Vt(t.children[r],u)}const s=En(t,e.x,e.y);if(t.children[s]===null){const[u,r,d,o]=wn(t,s);t.children[s]=Gt(u,r,d,o)}Vt(t.children[s],e);const l=t.mass+1;t.cx=(t.cx*t.mass+e.x)/l,t.cy=(t.cy*t.mass+e.y)/l,t.mass=l}function yo(t){if(t.length===0)return null;let e=1/0,s=1/0,l=-1/0,u=-1/0;for(const i of t)i.x<e&&(e=i.x),i.y<s&&(s=i.y),i.x>l&&(l=i.x),i.y>u&&(u=i.y);const r=Math.max(l-e,u-s)*.1+50,d=(e+l)/2,o=(s+u)/2,n=Math.max(l-e,u-s)/2+r,g=Gt(d-n,o-n,d+n,o+n);for(const i of t)Vt(g,i);return g}function Co(t,e,s,l,u,r){zn(t,e,s,l,u,r)}function zn(t,e,s,l,u,r){if(t.mass===0)return;const d=t.cx-e.x,o=t.cy-e.y,n=d*d+o*o;if(t.body!==null){if(t.body!==e){let i=Math.sqrt(n);i<r&&(i=r);const a=l*u/(i*i),M=d/i*a,T=o/i*a;e.vx-=M,e.vy-=T}return}const g=t.x1-t.x0;if(g*g/n<s*s){let i=Math.sqrt(n);i<r&&(i=r);const a=l*t.mass*u/(i*i),M=d/i*a,T=o/i*a;e.vx-=M,e.vy-=T;return}for(let i=0;i<4;i++)t.children[i]!==null&&zn(t.children[i],e,s,l,u,r)}const _n={clusterStrength:.08,spacing:1.5},kn=6e3,vo=12e3,bo=.004,Hn=140,Yn=350,Sn=.9,Nn=.01,bt=30,Wt=50;let et={..._n};function lt(t){t.clusterStrength!==void 0&&(et.clusterStrength=t.clusterStrength),t.spacing!==void 0&&(et.spacing=t.spacing)}function Et(){return{...et}}function xo(t){if(t<=30)return{..._n};const e=Math.log2(t/30);return{clusterStrength:Math.min(.5,.08+.06*e),spacing:Math.min(15,1.5+1.2*e)}}function Eo(t,e){for(const s of Object.values(t))if(typeof s=="string")return s;return e}function Ln(t,e,s){const l=new Set(e);let u=new Set(e);for(let r=0;r<s;r++){const d=new Set;for(const o of t.edges)u.has(o.sourceId)&&!l.has(o.targetId)&&d.add(o.targetId),u.has(o.targetId)&&!l.has(o.sourceId)&&d.add(o.sourceId);for(const o of d)l.add(o);if(u=d,d.size===0)break}return{nodes:t.nodes.filter(r=>l.has(r.id)),edges:t.edges.filter(r=>l.has(r.sourceId)&&l.has(r.targetId)),metadata:t.metadata}}function Ut(t){const e=new Map,s=[...new Set(t.nodes.map(n=>n.type))],l=Math.sqrt(s.length)*Yn*.6*Math.max(1,et.spacing),u=new Map,r=new Map;for(const n of t.nodes)r.set(n.type,(r.get(n.type)??0)+1);const d=t.nodes.map(n=>{const g=s.indexOf(n.type),i=2*Math.PI*g/Math.max(s.length,1),a=Math.cos(i)*l,M=Math.sin(i)*l,T=u.get(n.type)??0;u.set(n.type,T+1);const x=r.get(n.type)??1,F=2*Math.PI*T/x,W=Hn*.6,V={id:n.id,x:a+Math.cos(F)*W,y:M+Math.sin(F)*W,vx:0,vy:0,label:Eo(n.properties,n.id),type:n.type};return e.set(n.id,V),V}),o=t.edges.map(n=>({sourceId:n.sourceId,targetId:n.targetId,type:n.type}));return{nodes:d,edges:o,nodeMap:e}}const wo=.7,ko=80;function So(t,e){const{nodes:s,edges:l,nodeMap:u}=t,r=vo*et.spacing;if(s.length>=ko){const o=yo(s);if(o)for(const g of s)Co(o,g,wo,r,e,bt);const n=r-kn;if(n>0){const g=new Map;for(const i of s){let a=g.get(i.type);a||(a=[],g.set(i.type,a)),a.push(i)}for(const i of g.values())for(let a=0;a<i.length;a++)for(let M=a+1;M<i.length;M++){const T=i[a],x=i[M];let F=x.x-T.x,W=x.y-T.y,V=Math.sqrt(F*F+W*W);V<bt&&(V=bt);const z=n*e/(V*V),X=F/V*z,H=W/V*z;T.vx+=X,T.vy+=H,x.vx-=X,x.vy-=H}}}else for(let o=0;o<s.length;o++)for(let n=o+1;n<s.length;n++){const g=s[o],i=s[n];let a=i.x-g.x,M=i.y-g.y,T=Math.sqrt(a*a+M*M);T<bt&&(T=bt);const F=(g.type===i.type?kn:r)*e/(T*T),W=a/T*F,V=M/T*F;g.vx-=W,g.vy-=V,i.vx+=W,i.vy+=V}for(const o of l){const n=u.get(o.sourceId),g=u.get(o.targetId);if(!n||!g)continue;const i=g.x-n.x,a=g.y-n.y,M=Math.sqrt(i*i+a*a);if(M===0)continue;const T=n.type===g.type?Hn*et.spacing:Yn*et.spacing,x=bo*(M-T)*e,F=i/M*x,W=a/M*x;n.vx+=F,n.vy+=W,g.vx-=F,g.vy-=W}for(const o of s)o.vx-=o.x*Nn*e,o.vy-=o.y*Nn*e;const d=new Map;for(const o of s){const n=d.get(o.type)??{x:0,y:0,count:0};n.x+=o.x,n.y+=o.y,n.count++,d.set(o.type,n)}for(const o of d.values())o.x/=o.count,o.y/=o.count;for(const o of s){const n=d.get(o.type);o.vx+=(n.x-o.x)*et.clusterStrength*e,o.vy+=(n.y-o.y)*et.clusterStrength*e}for(const o of s){if(o.pinned){o.vx=0,o.vy=0;continue}o.vx*=Sn,o.vy*=Sn;const n=Math.sqrt(o.vx*o.vx+o.vy*o.vy);n>Wt&&(o.vx=o.vx/n*Wt,o.vy=o.vy/n*Wt),o.x+=o.vx,o.y+=o.vy}return e*.995}const In=["#d4a27f","#c17856","#b07a5e","#d4956b","#a67c5a","#cc9e7c","#c4866a","#cb8e6c","#b8956e","#a88a70","#d9b08c","#c4a882","#e8b898","#b5927a","#a8886e","#d1a990"],Mn=new Map;function Ae(t){const e=Mn.get(t);if(e)return e;let s=0;for(let u=0;u<t.length;u++)s=(s<<5)-s+t.charCodeAt(u)|0;const l=In[Math.abs(s)%In.length];return Mn.set(t,l),l}class No{constructor(e){It(this,"cells",new Map);It(this,"cellSize");It(this,"invCell");this.cellSize=e,this.invCell=1/e}key(e,s){const l=e+32768|0,u=s+32768|0;return l*73856093^u*19349663}clear(){this.cells.clear()}insert(e){const s=Math.floor(e.x*this.invCell),l=Math.floor(e.y*this.invCell),u=this.key(s,l),r=this.cells.get(u);r?r.push(e):this.cells.set(u,[e])}rebuild(e){this.cells.clear();for(const s of e)this.insert(s)}query(e,s,l){const u=l*l,r=Math.floor((e-l)*this.invCell),d=Math.floor((e+l)*this.invCell),o=Math.floor((s-l)*this.invCell),n=Math.floor((s+l)*this.invCell);let g=null,i=u;for(let a=r;a<=d;a++)for(let M=o;M<=n;M++){const T=this.cells.get(this.key(a,M));if(T)for(const x of T){const F=x.x-e,W=x.y-s,V=F*F+W*W;V<=i&&(i=V,g=x)}}return g}}const pt=new Map,Lo=2e3;function Io(t,e,s){return`${t}|${e}|${s}`}const Mo=new OffscreenCanvas(1,1),Tn=Mo.getContext("2d");function To(t,e,s){Tn.font=e;const l=Tn.measureText(t),u=Math.ceil(l.width)+2,r=Math.ceil(l.actualBoundingBoxAscent+l.actualBoundingBoxDescent)+4,d=new OffscreenCanvas(u,r),o=d.getContext("2d");return o.font=e,o.fillStyle=s,o.textAlign="left",o.textBaseline="top",o.fillText(t,1,1),{canvas:d,width:u,height:r}}function An(t,e,s,l,u,r,d){const o=Io(e,u,r);let n=pt.get(o);if(!n){if(pt.size>=Lo){const a=pt.keys().next().value;a!==void 0&&pt.delete(a)}n=To(e,u,r),pt.set(o,n)}const g=s-n.width/2,i=d==="top"?l:l-n.height;t.drawImage(n.canvas,g,i)}function Ao(){pt.clear()}function Ee(t){return getComputedStyle(document.documentElement).getPropertyValue(t).trim()}const Ue=20,jt=.001,Po={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 Tt(t,e,s,l,u,r=100){const d=(t-s.x)*s.scale,o=(e-s.y)*s.scale;return d>=-r&&d<=l+r&&o>=-r&&o<=u+r}function Bo(t,e,s,l){const u={...Po,...(l==null?void 0:l.lod)??{}},r={...Ro,...(l==null?void 0:l.navigation)??{}},d={pulseSpeed:.02,...(l==null?void 0:l.walk)??{}},o=t.querySelector("canvas"),n=o.getContext("2d"),g=window.devicePixelRatio||1;let i={x:0,y:0,scale:1},a=null,M=1,T=0,x=new Set,F=null,W=!0,V=!0,z=!0,X=!0,H="idle",K=[],q=0,ue=0,U=null,de=new Set;const xe=5;let ee=null,G=null,ye=1,Ce=null,se=null,ge=null,be=!1,p=[],y=0,v=0;const m=400,b=new No(Ue*2);let L=0;function h(){ie(),L||(L=requestAnimationFrame(()=>{L=0,c()}))}const S=150;let w=null,N=!1;function I(){if(!w)try{w=new Worker(new URL("/assets/layout-worker-4xak23M6.js",import.meta.url),{type:"module"}),w.onmessage=C,w.onerror=()=>{N=!1,w=null,we()}}catch{return N=!1,null}return w}function C(f){const k=f.data;if(k.type==="tick"&&a){const P=k.positions,D=a.nodes;for(let _=0;_<D.length;_++)D[_].x=P[_*4],D[_].y=P[_*4+1],D[_].vx=P[_*4+2],D[_].vy=P[_*4+3];M=k.alpha,b.rebuild(D),c()}k.type==="settled"&&(M=0,be&&p.length>0&&!ae&&(ae=requestAnimationFrame(pe)))}let B=null,$=null,te=!0;function ie(){te=!0}let le=null,me=null;const ve=r.panAnimationMs;function je(){o.width=o.clientWidth*g,o.height=o.clientHeight*g,ie(),h()}const qe=new ResizeObserver(je);qe.observe(t),je();function Xe(f,k){return[f/i.scale+i.x,k/i.scale+i.y]}function Je(f,k){if(!a)return null;const[P,D]=Xe(f,k);return b.query(P,D,Ue)}function ut(){if(!U)return;n.save();const f=Math.min(U.x1,U.x2),k=Math.max(U.x1,U.x2),P=Math.min(U.y1,U.y2),D=Math.max(U.y1,U.y2);n.strokeStyle=Ee("--accent")||"#d4a27f",n.fillStyle=Ee("--accent")||"#d4a27f",n.globalAlpha=.12,n.fillRect(f,P,k-f,D-P),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(f,P,k-f,D-P),n.setLineDash([]),n.restore()}function mt(){if(!a)return;y+=d.pulseSpeed;const f=new Set(p);n.save(),n.setTransform(g,0,0,g,0,0),B&&(n.clearRect(0,0,o.clientWidth,o.clientHeight),n.drawImage(B,0,0,o.clientWidth,o.clientHeight)),n.save(),n.translate(-i.x*i.scale,-i.y*i.scale),n.scale(i.scale,i.scale);const k=Ee("--canvas-walk-edge")||"#1a1a1a",P=[];for(const j of a.edges){if(!f.has(j.sourceId)||!f.has(j.targetId)||j.sourceId===j.targetId)continue;const O=a.nodeMap.get(j.sourceId),J=a.nodeMap.get(j.targetId);!O||!J||P.push(O.x,O.y,J.x,J.y)}if(P.length>0){n.beginPath();for(let j=0;j<P.length;j+=4)n.moveTo(P[j],P[j+1]),n.lineTo(P[j+2],P[j+3]);n.strokeStyle=k,n.lineWidth=3,n.globalAlpha=.5+.5*Math.sin(y),n.stroke(),n.globalAlpha=1}const D=i.scale<u.smallNodes?Ue*.5:Ue,_=Ee("--accent")||"#d4a27f";for(const j of p){const O=a.nodeMap.get(j);if(!O||!Tt(O.x,O.y,i,o.clientWidth,o.clientHeight))continue;const J=j===p[p.length-1],Z=.5+.5*Math.sin(y);n.strokeStyle=_,n.lineWidth=J?3:2,n.globalAlpha=J?.5+.5*Z:.3+.4*Z,n.beginPath(),n.arc(O.x,O.y,D+(J?6:4),0,Math.PI*2),n.stroke()}n.globalAlpha=1,ut(),n.restore(),X&&a.nodes.length>1&&E(),n.restore()}function c(){var ln;if(!a){n.clearRect(0,0,o.width,o.height);return}if(!te&&B&&be&&p.length>0&&M<jt){mt();return}const f=M<jt&&be&&p.length>0;be&&p.length>0&&(y+=d.pulseSpeed);const k=be&&!f?new Set(p):null,P=Ee("--canvas-edge"),D=Ee("--canvas-edge-highlight"),_=Ee("--canvas-edge-dim"),j=Ee("--canvas-edge-label"),O=Ee("--canvas-edge-label-highlight"),J=Ee("--canvas-edge-label-dim"),Z=Ee("--canvas-arrow"),re=Ee("--canvas-arrow-highlight"),fe=Ee("--canvas-node-label"),ke=Ee("--canvas-node-label-dim"),Se=Ee("--canvas-type-badge"),Te=Ee("--canvas-type-badge-dim"),Re=Ee("--canvas-selection-border"),ze=Ee("--canvas-node-border");if(n.save(),n.setTransform(g,0,0,g,0,0),n.clearRect(0,0,o.clientWidth,o.clientHeight),n.save(),n.translate(-i.x*i.scale,-i.y*i.scale),n.scale(i.scale,i.scale),z&&i.scale>=u.smallNodes){const Q=new Map;for(const Pe of a.nodes){if(F!==null&&!F.has(Pe.id))continue;const Fe=Q.get(Pe.type)??[];Fe.push(Pe),Q.set(Pe.type,Fe)}for(const[Pe,Fe]of Q){if(Fe.length<2)continue;const at=Ae(Pe),ot=Ue*2.5;let Ye=1/0,Ve=1/0,Be=-1/0,Qe=-1/0;for(const We of Fe)We.x<Ye&&(Ye=We.x),We.y<Ve&&(Ve=We.y),We.x>Be&&(Be=We.x),We.y>Qe&&(Qe=We.y);n.beginPath();const oe=(Be-Ye)/2+ot,Me=(Qe-Ve)/2+ot,Ne=(Ye+Be)/2,_e=(Ve+Qe)/2;n.ellipse(Ne,_e,oe,Me,0,0,Math.PI*2),n.fillStyle=at,n.globalAlpha=.05,n.fill(),n.strokeStyle=at,n.globalAlpha=.12,n.lineWidth=1,n.setLineDash([4,4]),n.stroke(),n.setLineDash([]),n.globalAlpha=1}}let Ge=null;if(x.size>0){Ge=new Set;for(const Q of a.edges)x.has(Q.sourceId)&&Ge.add(Q.targetId),x.has(Q.targetId)&&Ge.add(Q.sourceId)}const nt=Ee("--accent")||"#d4a27f",He=Ee("--canvas-walk-edge")||"#1a1a1a",it=i.scale>=u.hideArrows,Nt=V&&i.scale>=u.hideEdgeLabels;if(W){const Q=[],Pe=[],Fe=[],at=[],ot=[],Ye=[];for(const oe of a.edges){const Me=a.nodeMap.get(oe.sourceId),Ne=a.nodeMap.get(oe.targetId);if(!Me||!Ne||!Tt(Me.x,Me.y,i,o.clientWidth,o.clientHeight,200)&&!Tt(Ne.x,Ne.y,i,o.clientWidth,o.clientHeight,200))continue;const _e=F===null||F.has(oe.sourceId),We=F===null||F.has(oe.targetId),rn=_e&&We;if(F!==null&&!_e&&!We)continue;const Dt=x.size>0&&(x.has(oe.sourceId)||x.has(oe.targetId))||F!==null&&rn,dn=F!==null&&!rn,pn=k!==null&&k.has(oe.sourceId)&&k.has(oe.targetId),un=ge?ee==null?void 0:ee.edges.find(Lt=>Lt.sourceId===oe.sourceId&&Lt.targetId===oe.targetId||Lt.targetId===oe.sourceId&&Lt.sourceId===oe.targetId):null,mn=!!(ge&&un&&ge.edgeIds.has(un.id));if(oe.sourceId===oe.targetId){Y(Me,oe.type,Dt,P,D,j,O);continue}(mn?ot:pn?at:Dt?Pe:dn?Fe:Q).push(Me.x,Me.y,Ne.x,Ne.y),(it||Nt)&&Ye.push({sx:Me.x,sy:Me.y,tx:Ne.x,ty:Ne.y,type:oe.type,highlighted:Dt,edgeDimmed:dn,isPathEdge:mn,isWalkEdge:pn})}const Ve=it?1.5:1,Qe=[{lines:Q,color:P,width:Ve,alpha:1},{lines:Fe,color:_,width:Ve,alpha:1},{lines:Pe,color:D,width:it?2.5:1,alpha:1},{lines:ot,color:nt,width:3,alpha:1},{lines:at,color:He,width:3,alpha:.5+.5*Math.sin(y)}];for(const oe of Qe)if(oe.lines.length!==0){n.beginPath();for(let Me=0;Me<oe.lines.length;Me+=4)n.moveTo(oe.lines[Me],oe.lines[Me+1]),n.lineTo(oe.lines[Me+2],oe.lines[Me+3]);n.strokeStyle=oe.color,n.lineWidth=oe.width,n.globalAlpha=oe.alpha,n.stroke()}n.globalAlpha=1;for(const oe of Ye)if(it&&R(oe.sx,oe.sy,oe.tx,oe.ty,oe.highlighted||oe.isPathEdge,Z,re),Nt){const Me=(oe.sx+oe.tx)/2,Ne=(oe.sy+oe.ty)/2;n.fillStyle=oe.highlighted?O:oe.edgeDimmed?J:j,n.font="9px system-ui, sans-serif",n.textAlign="center",n.textBaseline="bottom",n.fillText(oe.type,Me,Ne-4)}}const Ft=performance.now()-v,Ie=Math.min(1,Ft/m),Ke=1-(1-Ie)*(1-Ie),Ze=Ie<1,cn=i.scale<u.hullsOnly,Vn=!cn&&i.scale<u.dotNodes;if(!cn)for(const Q of a.nodes){if(!Tt(Q.x,Q.y,i,o.clientWidth,o.clientHeight))continue;const Pe=Ae(Q.type);if(Vn){const Ne=F!==null&&!F.has(Q.id);n.fillStyle=Pe;const _e=Ne?.1:.8;n.globalAlpha=Ze?_e*Ke:_e,n.fillRect(Q.x-2,Q.y-2,4,4);continue}const Fe=x.has(Q.id),at=Ge!==null&&Ge.has(Q.id),ot=F!==null&&!F.has(Q.id),Ye=ot||x.size>0&&!Fe&&!at,Ve=i.scale<u.smallNodes?Ue*.5:Ue,Be=Ze?Ve*Ke:Ve;if(k!=null&&k.has(Q.id)){const Ne=p[p.length-1]===Q.id,_e=.5+.5*Math.sin(y),We=Ee("--accent")||"#d4a27f";n.save(),n.strokeStyle=We,n.lineWidth=Ne?3:2,n.globalAlpha=Ne?.5+.5*_e:.3+.4*_e,n.beginPath(),n.arc(Q.x,Q.y,Be+(Ne?6:4),0,Math.PI*2),n.stroke(),n.restore()}Fe&&(n.save(),n.shadowColor=Pe,n.shadowBlur=20,n.beginPath(),n.arc(Q.x,Q.y,Be+3,0,Math.PI*2),n.fillStyle=Pe,n.globalAlpha=.3,n.fill(),n.restore()),n.beginPath(),n.arc(Q.x,Q.y,Be,0,Math.PI*2),n.fillStyle=Pe;const Qe=ot?.1:Ye?.3:1;n.globalAlpha=Ze?Qe*Ke:Qe,n.fill(),n.strokeStyle=Fe?Re:ze,n.lineWidth=Fe?3:1.5,n.stroke(),n.globalAlpha=1,Q.pinned&&(n.save(),n.strokeStyle=ze,n.globalAlpha=.55,n.lineWidth=1,n.setLineDash([3,3]),n.beginPath(),n.arc(Q.x,Q.y,Be+4,0,Math.PI*2),n.stroke(),n.setLineDash([]),n.restore()),ge&&ge.nodeIds.has(Q.id)&&!Fe&&(n.save(),n.shadowColor=Ee("--accent")||"#d4a27f",n.shadowBlur=15,n.beginPath(),n.arc(Q.x,Q.y,Be+2,0,Math.PI*2),n.strokeStyle=Ee("--accent")||"#d4a27f",n.globalAlpha=.5,n.lineWidth=2,n.stroke(),n.restore());const oe=ee==null?void 0:ee.nodes.find(Ne=>Ne.id===Q.id);if(((ln=oe==null?void 0:oe.properties)==null?void 0:ln._starred)===!0&&(n.fillStyle="#ffd700",n.font="10px system-ui, sans-serif",n.textAlign="left",n.textBaseline="bottom",n.fillText("★",Q.x+Be-2,Q.y-Be+2)),i.scale>=u.hideLabels){const Ne=Q.label.length>24?Q.label.slice(0,22)+"...":Q.label,_e=Ye?ke:fe;An(n,Ne,Q.x,Q.y+Be+4,"11px system-ui, sans-serif",_e,"top")}if(i.scale>=u.hideBadges){const Ne=Ye?Te:Se;An(n,Q.type,Q.x,Q.y-Be-3,"9px system-ui, sans-serif",Ne,"bottom")}n.globalAlpha=1}if(n.restore(),n.restore(),f){const Q=o.width,Pe=o.height;(!B||B.width!==Q||B.height!==Pe)&&(B=new OffscreenCanvas(Q,Pe),$=B.getContext("2d")),$&&($.clearRect(0,0,Q,Pe),$.drawImage(o,0,0),te=!1),mt();return}X&&a.nodes.length>1&&E(),U&&(n.save(),n.setTransform(g,0,0,g,0,0),n.translate(-i.x*i.scale,-i.y*i.scale),n.scale(i.scale,i.scale),ut(),n.restore())}function E(){if(!a)return;const f=140,k=100,P=8,D=o.clientWidth-f-16,_=o.clientHeight-k-16;let j=1/0,O=1/0,J=-1/0,Z=-1/0;for(const Ie of a.nodes)Ie.x<j&&(j=Ie.x),Ie.y<O&&(O=Ie.y),Ie.x>J&&(J=Ie.x),Ie.y>Z&&(Z=Ie.y);const re=J-j||1,fe=Z-O||1,ke=Math.min((f-P*2)/re,(k-P*2)/fe),Se=D+P+(f-P*2-re*ke)/2,Te=_+P+(k-P*2-fe*ke)/2;n.save(),n.setTransform(g,0,0,g,0,0),n.fillStyle=Ee("--bg-surface")||"#1a1a1a",n.globalAlpha=.85,n.beginPath(),n.roundRect(D,_,f,k,8),n.fill(),n.strokeStyle=Ee("--border")||"#2a2a2a",n.globalAlpha=1,n.lineWidth=1,n.stroke(),n.globalAlpha=.15,n.strokeStyle=Ee("--canvas-edge")||"#555",n.lineWidth=.5;for(const Ie of a.edges){const Ke=a.nodeMap.get(Ie.sourceId),Ze=a.nodeMap.get(Ie.targetId);!Ke||!Ze||Ie.sourceId===Ie.targetId||(n.beginPath(),n.moveTo(Se+(Ke.x-j)*ke,Te+(Ke.y-O)*ke),n.lineTo(Se+(Ze.x-j)*ke,Te+(Ze.y-O)*ke),n.stroke())}n.globalAlpha=.8;for(const Ie of a.nodes){const Ke=Se+(Ie.x-j)*ke,Ze=Te+(Ie.y-O)*ke;n.beginPath(),n.arc(Ke,Ze,2,0,Math.PI*2),n.fillStyle=Ae(Ie.type),n.fill()}const Re=i.x,ze=i.y,Ge=i.x+o.clientWidth/i.scale,nt=i.y+o.clientHeight/i.scale,He=Se+(Re-j)*ke,it=Te+(ze-O)*ke,Nt=(Ge-Re)*ke,Ft=(nt-ze)*ke;n.globalAlpha=.3,n.strokeStyle=Ee("--accent")||"#d4a27f",n.lineWidth=1.5,n.strokeRect(Math.max(D,Math.min(He,D+f)),Math.max(_,Math.min(it,_+k)),Math.min(Nt,f),Math.min(Ft,k)),n.globalAlpha=1,n.restore()}function R(f,k,P,D,_,j,O){const J=Math.atan2(D-k,P-f),Z=P-Math.cos(J)*Ue,re=D-Math.sin(J)*Ue,fe=8;n.beginPath(),n.moveTo(Z,re),n.lineTo(Z-fe*Math.cos(J-.4),re-fe*Math.sin(J-.4)),n.lineTo(Z-fe*Math.cos(J+.4),re-fe*Math.sin(J+.4)),n.closePath(),n.fillStyle=_?O:j,n.fill()}function Y(f,k,P,D,_,j,O){const J=f.x+Ue+15,Z=f.y-Ue-15;n.beginPath(),n.arc(J,Z,15,0,Math.PI*2),n.strokeStyle=P?_:D,n.lineWidth=P?2.5:1.5,n.stroke(),V&&(n.fillStyle=P?O:j,n.font="9px system-ui, sans-serif",n.textAlign="center",n.fillText(k,J,Z-18))}function ne(){if(!le||!me)return;const f=performance.now()-me.time,k=Math.min(f/ve,1),P=1-Math.pow(1-k,3);i.x=me.x+(le.x-me.x)*P,i.y=me.y+(le.y-me.y)*P,ie(),c(),k<1?requestAnimationFrame(ne):(le=null,me=null)}let ae=0;function pe(){if(!be||p.length===0){ae=0;return}c(),ae=requestAnimationFrame(pe)}function he(){if(!a||a.nodes.length===0)return;let f=1/0,k=1/0,P=-1/0,D=-1/0;for(const re of a.nodes)re.x<f&&(f=re.x),re.y<k&&(k=re.y),re.x>P&&(P=re.x),re.y>D&&(D=re.y);const _=Ue*4,j=P-f+_*2,O=D-k+_*2,J=o.clientWidth/Math.max(j,1),Z=o.clientHeight/Math.max(O,1);i.scale=Math.min(J,Z,2),i.x=(f+P)/2-o.clientWidth/(2*i.scale),i.y=(k+D)/2-o.clientHeight/(2*i.scale),h()}function we(){if(!a||M<jt){T=0,be&&p.length>0&&!ae&&(ae=requestAnimationFrame(pe));return}M=So(a,M),b.rebuild(a.nodes),c(),T=requestAnimationFrame(we)}let Le=!1,De=0,tt=0,ht=0,kt=0;o.addEventListener("mousedown",f=>{H="pending",Le=!1,De=f.clientX,tt=f.clientY,ht=f.clientX,kt=f.clientY;const k=o.getBoundingClientRect(),P=f.clientX-k.left,D=f.clientY-k.top,_=Je(P,D);if(_&&!be){if(K=[],x.has(_.id)&&x.size>1)for(const J of x){const Z=a==null?void 0:a.nodeMap.get(J);Z&&K.push({node:Z,startX:Z.x,startY:Z.y})}else K.push({node:_,startX:_.x,startY:_.y});const[j,O]=Xe(P,D);q=j,ue=O}else if(!_&&f.shiftKey){const[j,O]=Xe(P,D);U={x1:j,y1:O,x2:j,y2:O}}}),o.addEventListener("mousemove",f=>{if(H==="idle")return;const k=f.clientX-De,P=f.clientY-tt,D=Math.abs(f.clientX-ht),_=Math.abs(f.clientY-kt);if(H==="pending"&&(D>xe||_>xe)&&(Le=!0,K.length>0?(H="nodeDrag",N&&w&&w.postMessage({type:"stop"})):U?H="rubberBand":H="pan"),H==="nodeDrag"){const j=o.getBoundingClientRect(),O=f.clientX-j.left,J=f.clientY-j.top,[Z,re]=Xe(O,J),fe=Z-q,ke=re-ue;for(const Se of K)Se.node.x=Se.startX+fe,Se.node.y=Se.startY+ke,Se.node.vx=0,Se.node.vy=0,Se.node.pinned=!0,de.add(Se.node.id);b.rebuild((a==null?void 0:a.nodes)??[]),h()}else if(H==="rubberBand"&&U){const j=o.getBoundingClientRect(),O=f.clientX-j.left,J=f.clientY-j.top,[Z,re]=Xe(O,J);U.x2=Z,U.y2=re,h()}else H==="pan"&&(i.x-=k/i.scale,i.y-=P/i.scale,h());De=f.clientX,tt=f.clientY}),o.addEventListener("mouseup",f=>{const k=H==="nodeDrag",P=H==="rubberBand";if(K.map(Z=>Z.node.id),k){if(N&&w&&a){const Z=K.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 M=Math.max(M,.5),T||we();H="idle",K=[],h();return}if(P&&U&&a){const Z=Math.min(U.x1,U.x2),re=Math.max(U.x1,U.x2),fe=Math.min(U.y1,U.y2),ke=Math.max(U.y1,U.y2);f.shiftKey||x.clear();for(const Te of a.nodes)Te.x>=Z&&Te.x<=re&&Te.y>=fe&&Te.y<=ke&&x.add(Te.id);const Se=[...x];e==null||e(Se.length>0?Se:null),U=null,H="idle",h();return}if(H==="pan"){H="idle";return}if(H="idle",U=null,Le)return;const D=o.getBoundingClientRect(),_=f.clientX-D.left,j=f.clientY-D.top,O=Je(_,j),J=f.ctrlKey||f.metaKey||f.shiftKey;if(be&&G&&O&&a){const Z=p.length>0?p[p.length-1]:G[0],re=new Set([Z]),fe=[{id:Z,path:[Z]}];let ke=null;for(;fe.length>0;){const{id:ze,path:Ge}=fe.shift();if(ze===O.id){ke=Ge;break}for(const nt of a.edges){let He=null;nt.sourceId===ze?He=nt.targetId:nt.targetId===ze&&(He=nt.sourceId),He&&!re.has(He)&&(re.add(He),fe.push({id:He,path:[...Ge,He]}))}}if(!ke)return;for(const ze of ke.slice(1))p.includes(ze)||p.push(ze);G=[O.id];const Se=Math.max(1,ye);ye=Se;const Te=Ln(ee,[O.id],Se);cancelAnimationFrame(T),w&&w.postMessage({type:"stop"}),a=Ut(Te),b.rebuild(a.nodes),M=1,x=new Set([O.id]),F=null,i={x:0,y:0,scale:1},N=Te.nodes.length>=S;const Re=N?I():null;Re?Re.postMessage({type:"start",data:Te}):(N=!1,we()),setTimeout(()=>{a&&he()},300),s==null||s({seedNodeIds:[O.id],hops:Se,totalNodes:Te.nodes.length}),e==null||e([O.id]);return}if(O){J?x.has(O.id)?x.delete(O.id):x.add(O.id):x.size===1&&x.has(O.id)?x.clear():(x.clear(),x.add(O.id));const Z=[...x];e==null||e(Z.length>0?Z:null)}else x.clear(),e==null||e(null);h()}),o.addEventListener("mouseleave",()=>{if(H==="nodeDrag")if(N&&w&&K.length>0){const f=K.map(k=>({id:k.node.id,x:k.node.x,y:k.node.y}));w.postMessage({type:"pin",updates:f}),w.postMessage({type:"resume",alpha:.5})}else M=Math.max(M,.5),T||we();H==="rubberBand"&&(U=null,h()),H="idle",K=[]}),o.addEventListener("wheel",f=>{f.preventDefault();const k=o.getBoundingClientRect(),P=f.clientX-k.left,D=f.clientY-k.top,[_,j]=Xe(P,D),O=f.ctrlKey?1-f.deltaY*.01:f.deltaY>0?.9:1.1;i.scale=Math.max(r.zoomMin,Math.min(r.zoomMax,i.scale*O)),i.x=_-P/i.scale,i.y=j-D/i.scale,h()},{passive:!1});let Oe=[],tn=0,nn=1,on=0,sn=0,$t=!1;o.addEventListener("touchstart",f=>{f.preventDefault(),Oe=Array.from(f.touches),Oe.length===2?(tn=an(Oe[0],Oe[1]),nn=i.scale):Oe.length===1&&(De=Oe[0].clientX,tt=Oe[0].clientY,on=Oe[0].clientX,sn=Oe[0].clientY,$t=!1)},{passive:!1}),o.addEventListener("touchmove",f=>{f.preventDefault();const k=Array.from(f.touches);if(k.length===2&&Oe.length===2){const D=an(k[0],k[1])/tn;i.scale=Math.max(r.zoomMin,Math.min(r.zoomMax,nn*D)),h()}else if(k.length===1){const P=k[0].clientX-De,D=k[0].clientY-tt;(Math.abs(k[0].clientX-on)>10||Math.abs(k[0].clientY-sn)>10)&&($t=!0),i.x-=P/i.scale,i.y-=D/i.scale,De=k[0].clientX,tt=k[0].clientY,h()}Oe=k},{passive:!1}),o.addEventListener("touchend",f=>{if(f.preventDefault(),$t||f.changedTouches.length!==1)return;const k=f.changedTouches[0],P=o.getBoundingClientRect(),D=k.clientX-P.left,_=k.clientY-P.top,j=Je(D,_);if(j){x.size===1&&x.has(j.id)?x.clear():(x.clear(),x.add(j.id));const O=[...x];e==null||e(O.length>0?O:null)}else x.clear(),e==null||e(null);h()},{passive:!1}),o.addEventListener("gesturestart",f=>f.preventDefault()),o.addEventListener("gesturechange",f=>f.preventDefault());function an(f,k){const P=f.clientX-k.clientX,D=f.clientY-k.clientY;return Math.sqrt(P*P+D*D)}const ft=document.createElement("div");ft.className="zoom-controls";const gt=document.createElement("button");gt.className="zoom-btn",gt.textContent="+",gt.title="Zoom in",gt.addEventListener("click",()=>{const f=o.clientWidth/2,k=o.clientHeight/2,[P,D]=Xe(f,k);i.scale=Math.min(r.zoomMax,i.scale*r.zoomFactor),i.x=P-f/i.scale,i.y=D-k/i.scale,h()});const yt=document.createElement("button");yt.className="zoom-btn",yt.textContent="−",yt.title="Zoom out",yt.addEventListener("click",()=>{const f=o.clientWidth/2,k=o.clientHeight/2,[P,D]=Xe(f,k);i.scale=Math.max(r.zoomMin,i.scale/r.zoomFactor),i.x=P-f/i.scale,i.y=D-k/i.scale,h()});const Ct=document.createElement("button");Ct.className="zoom-btn",Ct.textContent="○",Ct.title="Reset zoom",Ct.addEventListener("click",()=>{if(a){if(i={x:0,y:0,scale:1},a.nodes.length>0){let f=1/0,k=1/0,P=-1/0,D=-1/0;for(const O of a.nodes)O.x<f&&(f=O.x),O.y<k&&(k=O.y),O.x>P&&(P=O.x),O.y>D&&(D=O.y);const _=(f+P)/2,j=(k+D)/2;i.x=_-o.clientWidth/2,i.y=j-o.clientHeight/2}h()}}),ft.appendChild(gt),ft.appendChild(Ct),ft.appendChild(yt),t.appendChild(ft);const $e=document.createElement("div");$e.className="node-tooltip",$e.style.display="none",t.appendChild($e);let St=null,st=null;return o.addEventListener("mousemove",f=>{if(H!=="idle"&&H!=="pending"){$e.style.display!=="none"&&($e.style.display="none",St=null);return}const k=o.getBoundingClientRect(),P=f.clientX-k.left,D=f.clientY-k.top,_=Je(P,D),j=(_==null?void 0:_.id)??null;j!==St?(St=j,$e.style.display="none",st&&clearTimeout(st),st=null,j&&_&&(st=setTimeout(()=>{if(!a||!ee)return;const O=a.edges.filter(J=>J.sourceId===j||J.targetId===j).length;$e.textContent=`${_.label} · ${_.type} · ${O} edge${O!==1?"s":""}`,$e.style.left=`${f.clientX-k.left+12}px`,$e.style.top=`${f.clientY-k.top-8}px`,$e.style.display="block"},200))):j&&$e.style.display==="block"&&($e.style.left=`${f.clientX-k.left+12}px`,$e.style.top=`${f.clientY-k.top-8}px`)}),o.addEventListener("mouseleave",()=>{$e.style.display="none",St=null,st&&clearTimeout(st),st=null}),{loadGraph(f){if(cancelAnimationFrame(T),w&&w.postMessage({type:"stop"}),Ao(),ee=f,G=null,Ce=null,se=null,v=performance.now(),a=Ut(f),b.rebuild(a.nodes),M=1,x=new Set,F=null,de.clear(),H="idle",K=[],U=null,i={x:0,y:0,scale:1},a.nodes.length>0){let P=1/0,D=1/0,_=-1/0,j=-1/0;for(const fe of a.nodes)fe.x<P&&(P=fe.x),fe.y<D&&(D=fe.y),fe.x>_&&(_=fe.x),fe.y>j&&(j=fe.y);const O=(P+_)/2,J=(D+j)/2,Z=o.clientWidth,re=o.clientHeight;i.x=O-Z/2,i.y=J-re/2}N=f.nodes.length>=S;const k=N?I():null;k?k.postMessage({type:"start",data:f}):(N=!1,we())},setFilteredNodeIds(f){F=f,h()},releaseAllPins(){if(!a)return!1;let f=!1;for(const k of a.nodes)k.pinned&&(f=!0,k.pinned=!1);return de.clear(),H="idle",K=[],U=null,f&&(N&&w?w.postMessage({type:"unpin",ids:"all"}):(M=Math.max(M,.5),T||we()),h()),f},hasPinnedNodes(){if(!a)return!1;for(const f of a.nodes)if(f.pinned)return!0;return!1},clearSelection(){x.size!==0&&(x.clear(),e==null||e(null),h())},getSelectedNodeIds(){return[...x]},panToNode(f){this.panToNodes([f])},panToNodes(f){if(!a||f.length===0)return;const k=f.map(_=>a.nodeMap.get(_)).filter(Boolean);if(k.length===0)return;x=new Set(f),e==null||e(f);const P=o.clientWidth,D=o.clientHeight;if(k.length===1)me={x:i.x,y:i.y,time:performance.now()},le={x:k[0].x-P/(2*i.scale),y:k[0].y-D/(2*i.scale)};else{let _=1/0,j=1/0,O=-1/0,J=-1/0;for(const Re of k)Re.x<_&&(_=Re.x),Re.y<j&&(j=Re.y),Re.x>O&&(O=Re.x),Re.y>J&&(J=Re.y);const Z=Ue*4,re=O-_+Z*2,fe=J-j+Z*2,ke=Math.min(P/re,D/fe,i.scale);i.scale=ke;const Se=(_+O)/2,Te=(j+J)/2;me={x:i.x,y:i.y,time:performance.now()},le={x:Se-P/(2*i.scale),y:Te-D/(2*i.scale)}}ne()},setEdges(f){W=f,h()},setEdgeLabels(f){V=f,h()},setTypeHulls(f){z=f,h()},setMinimap(f){X=f,h()},centerView(){he()},panBy(f,k){i.x+=f/i.scale,i.y+=k/i.scale,h()},zoomBy(f){const k=o.clientWidth/2,P=o.clientHeight/2,[D,_]=Xe(k,P);i.scale=Math.max(r.zoomMin,Math.min(r.zoomMax,i.scale*f)),i.x=D-k/i.scale,i.y=_-P/i.scale,h()},reheat(){N&&w?w.postMessage({type:"params",params:Et()}):(M=.5,cancelAnimationFrame(T),we())},exportImage(f){if(!a)return"";const k=o.width,P=o.height;if(f==="png"){const O=document.createElement("canvas");O.width=k,O.height=P;const J=O.getContext("2d");return J.fillStyle=Ee("--bg")||"#141414",J.fillRect(0,0,k,P),J.drawImage(o,0,0),Gn(J,k,P),O.toDataURL("image/png")}const D=o.toDataURL("image/png"),_=Math.max(16,Math.round(k/80)),j=`<svg xmlns="http://www.w3.org/2000/svg" width="${k}" height="${P}">
3
- <image href="${D}" width="${k}" height="${P}"/>
4
- <text x="${k-20}" y="${P-16}" text-anchor="end" font-family="system-ui, sans-serif" font-size="${_}" fill="#ffffff" opacity="0.4">backpackontology.com</text>
5
- </svg>`;return"data:image/svg+xml;charset=utf-8,"+encodeURIComponent(j)},enterFocus(f,k){if(!ee||!a)return;for(const _ of a.nodes)_.pinned=!1;de.clear(),H="idle",K=[],U=null,G||(Ce=a,se={...i}),G=f,ye=k;const P=Ln(ee,f,k);cancelAnimationFrame(T),w&&w.postMessage({type:"stop"}),a=Ut(P),b.rebuild(a.nodes),M=1,x=new Set(f),F=null,i={x:0,y:0,scale:1},N=P.nodes.length>=S;const D=N?I():null;D?D.postMessage({type:"start",data:P}):(N=!1,we()),setTimeout(()=>{!a||!G||he()},300),s==null||s({seedNodeIds:f,hops:k,totalNodes:P.nodes.length})},exitFocus(){if(!(!G||!Ce)){cancelAnimationFrame(T),w&&w.postMessage({type:"stop"}),a=Ce;for(const f of a.nodes)f.pinned=!1;de.clear(),H="idle",K=[],U=null,b.rebuild(a.nodes),i=se??{x:0,y:0,scale:1},G=null,Ce=null,se=null,x=new Set,F=null,h(),s==null||s(null)}},isFocused(){return G!==null},getFocusInfo(){return!G||!a?null:{seedNodeIds:G,hops:ye,totalNodes:a.nodes.length}},findPath(f,k){if(!a)return null;const P=new Set([f]),D=[{nodeId:f,path:[f],edges:[]}];for(;D.length>0;){const{nodeId:_,path:j,edges:O}=D.shift();if(_===k)return{nodeIds:j,edgeIds:O};for(const J of a.edges){let Z=null;if(J.sourceId===_?Z=J.targetId:J.targetId===_&&(Z=J.sourceId),Z&&!P.has(Z)){P.add(Z);const re=ee==null?void 0:ee.edges.find(fe=>fe.sourceId===J.sourceId&&fe.targetId===J.targetId||fe.targetId===J.sourceId&&fe.sourceId===J.targetId);D.push({nodeId:Z,path:[...j,Z],edges:[...O,(re==null?void 0:re.id)??""]})}}}return null},setHighlightedPath(f,k){f&&k?ge={nodeIds:new Set(f),edgeIds:new Set(k)}:ge=null,h()},clearHighlightedPath(){ge=null,h()},setWalkMode(f){be=f,this.releaseAllPins(),f?(p=G?[...G]:[...x],ae||(ae=requestAnimationFrame(pe))):(p=[],ae&&(cancelAnimationFrame(ae),ae=0)),h()},getWalkMode(){return be},getWalkTrail(){return[...p]},getFilteredNodeIds(){return F},removeFromWalkTrail(f){p=p.filter(k=>k!==f),h()},nodeAtScreen(f,k){return Je(f,k)},getNodeIds(){if(!a)return[];if(G){const f=new Set(G),k=a.nodes.filter(D=>f.has(D.id)).map(D=>D.id),P=a.nodes.filter(D=>!f.has(D.id)).map(D=>D.id);return[...k,...P]}return a.nodes.map(f=>f.id)},destroy(){cancelAnimationFrame(T),L&&(cancelAnimationFrame(L),L=0),ae&&(cancelAnimationFrame(ae),ae=0),w&&(w.terminate(),w=null),B=null,$=null,qe.disconnect()}};function Gn(f,k,P){const D=Math.max(16,Math.round(k/80));f.save(),f.font=`${D}px system-ui, sans-serif`,f.fillStyle="rgba(255, 255, 255, 0.4)",f.textAlign="right",f.textBaseline="bottom",f.fillText("backpackontology.com",k-20,P-16),f.restore()}}function rt(t){for(const e of Object.values(t.properties))if(typeof e=="string")return e;return t.id}const $o="✎";function Fo(t,e,s,l,u){const r=document.createElement("div");r.className="info-panel-content";let d=[],o=-1,n=!1,g=null,i=[],a=!1,M=[],T=-1;const x=e.mount("info",r,{title:"Node info",persistKey:"info-panel",hideOnClose:!0,onClose:()=>{d=[],o=-1}});x.setVisible(!1);function F(){x.setVisible(!1),d=[],o=-1}function W(q){!g||!l||(o<d.length-1&&(d=d.slice(0,o+1)),d.push(q),o=d.length-1,n=!0,l(q),n=!1)}function V(){if(o<=0||!g)return;o--,n=!0;const q=d[o];l==null||l(q),H(q,g),n=!1}function z(){if(o>=d.length-1||!g)return;o++,n=!0;const q=d[o];l==null||l(q),H(q,g),n=!1}function X(){const q=[{label:"Back",iconText:"←",onClick:V,disabled:o<=0},{label:"Forward",iconText:"→",onClick:z,disabled:o>=d.length-1}];u&&i.length>0&&q.push({label:"Focus",iconText:"◎",onClick:()=>{a||u(i)},disabled:a}),x.setHeaderButtons(q)}function H(q,ue){const U=ue.nodes.find(h=>h.id===q);if(!U)return;const de=ue.edges.filter(h=>h.sourceId===q||h.targetId===q);M=de.map(h=>h.sourceId===q?h.targetId:h.sourceId),T=-1,r.replaceChildren(),x.setTitle(rt(U)),X(),x.setVisible(!0);const xe=document.createElement("div");xe.className="info-panel-header";const ee=document.createElement("div");ee.className="info-header";const G=document.createElement("span");if(G.className="info-type-badge",G.textContent=U.type,G.style.backgroundColor=Ae(U.type),s){G.classList.add("info-editable");const h=document.createElement("button");h.className="info-inline-edit",h.textContent=$o,h.addEventListener("click",S=>{S.stopPropagation();const w=document.createElement("input");w.type="text",w.className="info-edit-inline-input",w.value=U.type,G.textContent="",G.appendChild(w),w.focus(),w.select();const N=()=>{const I=w.value.trim();I&&I!==U.type?s.onChangeNodeType(q,I):(G.textContent=U.type,G.appendChild(h))};w.addEventListener("blur",N),w.addEventListener("keydown",I=>{I.key==="Enter"&&w.blur(),I.key==="Escape"&&(w.value=U.type,w.blur())})}),G.appendChild(h)}const ye=document.createElement("h3");ye.className="info-label",ye.textContent=rt(U);const Ce=document.createElement("span");Ce.className="info-id",Ce.textContent=U.id,ee.appendChild(G),ee.appendChild(ye),ee.appendChild(Ce),xe.appendChild(ee),r.appendChild(xe);const se=document.createElement("div");se.className="info-panel-body";const ge=Object.keys(U.properties),be=xt("Properties");if(ge.length>0){const h=document.createElement("dl");h.className="info-props";for(const S of ge){const w=document.createElement("dt");w.textContent=S;const N=document.createElement("dd");if(s){const I=zt(U.properties[S]),C=document.createElement("textarea");C.className="info-edit-input",C.value=I,C.rows=1,C.addEventListener("input",()=>Pn(C)),C.addEventListener("keydown",$=>{$.key==="Enter"&&!$.shiftKey&&($.preventDefault(),C.blur())}),C.addEventListener("blur",()=>{const $=C.value;$!==I&&s.onUpdateNode(q,{[S]:Oo($)})}),N.appendChild(C),requestAnimationFrame(()=>Pn(C));const B=document.createElement("button");B.className="info-delete-prop",B.textContent="×",B.title=`Remove ${S}`,B.addEventListener("click",()=>{const $={...U.properties};delete $[S],s.onUpdateNode(q,$)}),N.appendChild(B)}else N.appendChild(Do(U.properties[S]));h.appendChild(w),h.appendChild(N)}be.appendChild(h)}if(s){const h=document.createElement("button");h.className="info-add-btn",h.textContent="+ Add property",h.addEventListener("click",()=>{const S=document.createElement("div");S.className="info-add-row";const w=document.createElement("input");w.type="text",w.className="info-edit-input",w.placeholder="key";const N=document.createElement("input");N.type="text",N.className="info-edit-input",N.placeholder="value";const I=document.createElement("button");I.className="info-add-save",I.textContent="Add",I.addEventListener("click",()=>{w.value&&s.onAddProperty(q,w.value,N.value)}),S.appendChild(w),S.appendChild(N),S.appendChild(I),be.appendChild(S),w.focus()}),be.appendChild(h)}if(se.appendChild(be),de.length>0){const h=xt(`Connections (${de.length})`),S=document.createElement("ul");S.className="info-connections";for(const w of de){const N=w.sourceId===q,I=N?w.targetId:w.sourceId,C=ue.nodes.find(ve=>ve.id===I),B=C?rt(C):I,$=document.createElement("li");if($.className="info-connection",l&&C&&($.classList.add("info-connection-link"),$.addEventListener("click",ve=>{ve.target.closest(".info-delete-edge")||W(I)})),C){const ve=document.createElement("span");ve.className="info-target-dot",ve.style.backgroundColor=Ae(C.type),$.appendChild(ve)}const te=document.createElement("span");te.className="info-arrow",te.textContent=N?"→":"←";const ie=document.createElement("span");ie.className="info-edge-type",ie.textContent=w.type;const le=document.createElement("span");le.className="info-target",le.textContent=B,$.appendChild(te),$.appendChild(ie),$.appendChild(le);const me=Object.keys(w.properties);if(me.length>0){const ve=document.createElement("div");ve.className="info-edge-props";for(const je of me){const qe=document.createElement("span");qe.className="info-edge-prop",qe.textContent=`${je}: ${zt(w.properties[je])}`,ve.appendChild(qe)}$.appendChild(ve)}if(s){const ve=document.createElement("button");ve.className="info-delete-edge",ve.textContent="×",ve.title="Remove connection",ve.addEventListener("click",je=>{je.stopPropagation(),s.onDeleteEdge(w.id)}),$.appendChild(ve)}S.appendChild($)}h.appendChild(S),se.appendChild(h)}const p=xt("Timestamps"),y=document.createElement("dl");y.className="info-props";const v=document.createElement("dt");v.textContent="created";const m=document.createElement("dd");m.textContent=Rn(U.createdAt);const b=document.createElement("dt");b.textContent="updated";const L=document.createElement("dd");if(L.textContent=Rn(U.updatedAt),y.appendChild(v),y.appendChild(m),y.appendChild(b),y.appendChild(L),p.appendChild(y),se.appendChild(p),s){const h=document.createElement("div");h.className="info-section info-danger";const S=document.createElement("button");S.className="info-delete-node",S.textContent="Delete node",S.addEventListener("click",()=>{s.onDeleteNode(q),F()}),h.appendChild(S),se.appendChild(h)}r.appendChild(se)}function K(q,ue){const U=new Set(q),de=ue.nodes.filter(p=>U.has(p.id));if(de.length===0)return;const xe=ue.edges.filter(p=>U.has(p.sourceId)&&U.has(p.targetId));r.replaceChildren(),x.setTitle(`${de.length} nodes selected`),X(),x.setVisible(!0);const ee=document.createElement("div");ee.className="info-header";const G=document.createElement("h3");G.className="info-label",G.textContent=`${de.length} nodes selected`,ee.appendChild(G);const ye=document.createElement("div");ye.className="info-badge-row";const Ce=new Map;for(const p of de)Ce.set(p.type,(Ce.get(p.type)??0)+1);for(const[p,y]of Ce){const v=document.createElement("span");v.className="info-type-badge",v.style.backgroundColor=Ae(p),v.textContent=y>1?`${p} (${y})`:p,ye.appendChild(v)}ee.appendChild(ye),r.appendChild(ee);const se=xt("Selected Nodes"),ge=document.createElement("ul");ge.className="info-connections";for(const p of de){const y=document.createElement("li");y.className="info-connection",l&&(y.classList.add("info-connection-link"),y.addEventListener("click",()=>{W(p.id)}));const v=document.createElement("span");v.className="info-target-dot",v.style.backgroundColor=Ae(p.type);const m=document.createElement("span");m.className="info-target",m.textContent=rt(p);const b=document.createElement("span");b.className="info-edge-type",b.textContent=p.type,y.appendChild(v),y.appendChild(m),y.appendChild(b),ge.appendChild(y)}se.appendChild(ge),r.appendChild(se);const be=xt(xe.length>0?`Connections Between Selected (${xe.length})`:"Connections Between Selected");if(xe.length===0){const p=document.createElement("p");p.className="info-empty-message",p.textContent="No direct connections between selected nodes",be.appendChild(p)}else{const p=document.createElement("ul");p.className="info-connections";for(const y of xe){const v=ue.nodes.find($=>$.id===y.sourceId),m=ue.nodes.find($=>$.id===y.targetId),b=v?rt(v):y.sourceId,L=m?rt(m):y.targetId,h=document.createElement("li");if(h.className="info-connection",v){const $=document.createElement("span");$.className="info-target-dot",$.style.backgroundColor=Ae(v.type),h.appendChild($)}const S=document.createElement("span");S.className="info-target",S.textContent=b;const w=document.createElement("span");w.className="info-arrow",w.textContent="→";const N=document.createElement("span");N.className="info-edge-type",N.textContent=y.type;const I=document.createElement("span");if(I.className="info-arrow",I.textContent="→",h.appendChild(S),h.appendChild(w),h.appendChild(N),h.appendChild(I),m){const $=document.createElement("span");$.className="info-target-dot",$.style.backgroundColor=Ae(m.type),h.appendChild($)}const C=document.createElement("span");C.className="info-target",C.textContent=L,h.appendChild(C);const B=Object.keys(y.properties);if(B.length>0){const $=document.createElement("div");$.className="info-edge-props";for(const te of B){const ie=document.createElement("span");ie.className="info-edge-prop",ie.textContent=`${te}: ${zt(y.properties[te])}`,$.appendChild(ie)}h.appendChild($)}p.appendChild(h)}be.appendChild(p)}r.appendChild(be)}return{show(q,ue){if(g=ue,i=q,q.length===1&&!n){const U=q[0];d[o]!==U&&(o<d.length-1&&(d=d.slice(0,o+1)),d.push(U),o=d.length-1)}q.length===1?H(q[0],ue):q.length>1&&K(q,ue)},hide:F,goBack:V,goForward:z,cycleConnection(q){if(M.length===0)return null;T===-1?T=q===1?0:M.length-1:(T+=q,T>=M.length&&(T=0),T<0&&(T=M.length-1));const ue=r.querySelectorAll(".info-connection");return ue.forEach((U,de)=>{U.classList.toggle("info-connection-active",de===T)}),T>=0&&ue[T]&&ue[T].scrollIntoView({block:"nearest"}),M[T]??null},setFocusDisabled(q){a=q,X()},get visible(){return x.isVisible()}}}function xt(t){const e=document.createElement("div");e.className="info-section";const s=document.createElement("h4");return s.className="info-section-title",s.textContent=t,e.appendChild(s),e}function Do(t){if(Array.isArray(t)){const s=document.createElement("div");s.className="info-array";for(const l of t){const u=document.createElement("span");u.className="info-tag",u.textContent=String(l),s.appendChild(u)}return s}if(t!==null&&typeof t=="object"){const s=document.createElement("pre");return s.className="info-json",s.textContent=JSON.stringify(t,null,2),s}const e=document.createElement("span");return e.className="info-value",e.textContent=String(t??""),e}function zt(t){return Array.isArray(t)?t.map(String).join(", "):t!==null&&typeof t=="object"?JSON.stringify(t):String(t??"")}function Oo(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 Pn(t){t.style.height="auto",t.style.height=t.scrollHeight+"px"}function Rn(t){try{return new Date(t).toLocaleString()}catch{return t}}const Jt=30,Wo=39,Uo=45,Bn=380,jo=70,zo=16,$n=80,qn="backpack-viewer:panel:",_o="has-fullscreen-panel";let At=Jt;function dt(t){At++,At>Wo&&(document.querySelectorAll(".extension-panel").forEach(e=>{e.style.zIndex=String(Jt+1)}),At=Jt+2),t.style.zIndex=String(At)}let Pt=0;function Rt(t,e){const s=t.id==="canvas-container"?t:t.closest("#canvas-container")??t;e?Pt++:Pt=Math.max(0,Pt-1),s.classList.toggle(_o,Pt>0)}function _t(t){try{const e=localStorage.getItem(qn+t);if(!e)return null;const s=JSON.parse(e);if(s&&typeof s=="object")return s}catch{}return null}function Fn(t,e){try{localStorage.setItem(qn+t,JSON.stringify(e))}catch{}}function Dn(){return ct({size:13,strokeWidth:1.8,strokeLinecap:"round",strokeLinejoin:"round"},[{tag:"polyline",attrs:{points:"4 9 4 4 9 4"}},{tag:"polyline",attrs:{points:"20 9 20 4 15 4"}},{tag:"polyline",attrs:{points:"4 15 4 20 9 20"}},{tag:"polyline",attrs:{points:"20 15 20 20 15 20"}}])}function Ho(){return ct({size:13,strokeWidth:1.8,strokeLinecap:"round",strokeLinejoin:"round"},[{tag:"polyline",attrs:{points:"9 4 9 9 4 9"}},{tag:"polyline",attrs:{points:"15 4 15 9 20 9"}},{tag:"polyline",attrs:{points:"9 20 9 15 4 15"}},{tag:"polyline",attrs:{points:"15 20 15 15 20 15"}}])}function Yo(t){const e=document.createElement("div");e.className="extension-panel-layer",t.appendChild(e);function s(){const r=t.getBoundingClientRect();return{left:Math.max(0,r.width-Bn-zo),top:jo}}function l(r,d){const o=t.getBoundingClientRect(),n=$n-d.width,g=o.width-$n,i=0,a=o.height-40;return{left:Math.max(n,Math.min(g,r.left)),top:Math.max(i,Math.min(a,r.top))}}function u(r,d,o={}){const n=o.persistKey??r,g=_t(n),i=document.createElement("aside");i.className="extension-panel",i.dataset.panel=n;const a=document.createElement("div");a.className="extension-panel-header";const M=document.createElement("span");M.className="extension-panel-title",M.textContent=o.title??r,a.appendChild(M);const T=document.createElement("div");T.className="extension-panel-custom-btns",a.appendChild(T);function x(m){T.replaceChildren();for(const b of m){const L=document.createElement("button");L.className="extension-panel-btn",L.title=b.label,L.setAttribute("aria-label",b.label),L.textContent=b.iconText??b.label,b.disabled&&(L.disabled=!0),L.addEventListener("click",h=>{h.stopPropagation();try{b.onClick()}catch(S){console.error(`[backpack-viewer] panel header button "${b.label}" threw:`,S)}}),L.addEventListener("mousedown",h=>h.stopPropagation()),T.appendChild(L)}}x(o.headerButtons??[]);let F=!1,W=null,V=null;o.showFullscreenButton!==!1&&(W=document.createElement("button"),W.className="extension-panel-btn extension-panel-btn-fullscreen",W.title="Toggle fullscreen",W.setAttribute("aria-label","Toggle fullscreen"),V=Dn(),W.appendChild(V),W.addEventListener("click",m=>{m.stopPropagation(),p(!F)}),W.addEventListener("mousedown",m=>m.stopPropagation()),a.appendChild(W));let z=null;o.showCloseButton!==!1&&(z=document.createElement("button"),z.className="extension-panel-btn extension-panel-btn-close",z.title="Close panel",z.setAttribute("aria-label","Close panel"),z.textContent="×",z.addEventListener("click",m=>{var b;if(m.stopPropagation(),o.hideOnClose){be(!1);try{(b=o.onClose)==null||b.call(o)}catch(L){console.error("[backpack-viewer] panel onClose threw:",L)}}else ge()}),z.addEventListener("mousedown",m=>m.stopPropagation()),a.appendChild(z)),i.appendChild(a);const X=document.createElement("div");X.className="extension-panel-body",X.appendChild(d),i.appendChild(X),e.appendChild(i);const H=g&&g.left!=null&&g.top!=null?{left:g.left,top:g.top}:o.defaultPosition??s(),q=l(H,{width:Bn});i.style.left=q.left+"px",i.style.top=q.top+"px",dt(i);let ue=0,U=0,de=0,xe=0,ee=!1;function G(m){if(!ee)return;const b=m.clientX-ue,L=m.clientY-U,h=l({left:de+b,top:xe+L},i.getBoundingClientRect());i.style.left=h.left+"px",i.style.top=h.top+"px"}function ye(){if(!ee)return;ee=!1,document.removeEventListener("mousemove",G),document.removeEventListener("mouseup",ye);const m=i.getBoundingClientRect(),b=t.getBoundingClientRect();Fn(n,{..._t(n),left:m.left-b.left,top:m.top-b.top})}a.addEventListener("mousedown",m=>{if(F)return;ee=!0,ue=m.clientX,U=m.clientY;const b=i.getBoundingClientRect(),L=t.getBoundingClientRect();de=b.left-L.left,xe=b.top-L.top,dt(i),document.addEventListener("mousemove",G),document.addEventListener("mouseup",ye),m.preventDefault()}),i.addEventListener("mousedown",()=>{F||dt(i)},{capture:!0});let Ce=!1,se=!0;function ge(){var m;if(!Ce){Ce=!0,document.removeEventListener("mousemove",G),document.removeEventListener("mouseup",ye),F&&(Rt(t,!1),F=!1),i.remove();try{(m=o.onClose)==null||m.call(o)}catch(b){console.error("[backpack-viewer] panel onClose threw:",b)}}}function be(m){m!==se&&(se=m,i.classList.toggle("is-hidden",!se),se?(dt(i),F&&Rt(t,!0)):F&&Rt(t,!1))}function p(m){var b;if(m!==F){F=m,i.classList.toggle("is-fullscreen",F),W&&V&&(V.remove(),V=F?Ho():Dn(),W.appendChild(V)),F?i.style.zIndex=String(Uo):dt(i),Rt(t,F),Fn(n,{..._t(n),fullscreen:F});try{(b=o.onFullscreenChange)==null||b.call(o,F)}catch(L){console.error("[backpack-viewer] panel onFullscreenChange threw:",L)}}}g!=null&&g.fullscreen&&p(!0);function y(m){M.textContent=m}function v(m){x(m)}return{close:ge,setFullscreen:p,isFullscreen:()=>F,setTitle:y,setHeaderButtons:v,setVisible:be,isVisible:()=>se,bringToFront:()=>dt(i),element:d}}return{mount:u}}function Xn(t){for(const e of Object.values(t.properties))if(typeof e=="string")return e;return t.id}function On(t,e){const s=e.toLowerCase();if(Xn(t).toLowerCase().includes(s)||t.type.toLowerCase().includes(s))return!0;for(const l of Object.values(t.properties))if(typeof l=="string"&&l.toLowerCase().includes(s))return!0;return!1}function qo(t,e){const s=(e==null?void 0:e.maxResults)??8,l=(e==null?void 0:e.debounceMs)??150;let u=null,r=null,d=null,o=null;const n=document.createElement("div");n.className="search-overlay hidden";const g=document.createElement("div");g.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 a=document.createElement("kbd");a.className="search-kbd",a.textContent="/",g.appendChild(i),g.appendChild(a);const M=document.createElement("ul");M.className="search-results hidden",n.appendChild(g),n.appendChild(M),t.appendChild(n);function T(){if(!u)return null;const z=i.value.trim();if(z.length===0)return null;const X=new Set;for(const H of u.nodes)On(H,z)&&X.add(H.id);return X}function x(){const z=T();r==null||r(z),F()}function F(){M.replaceChildren(),W=-1;const z=i.value.trim();if(!u||z.length===0){M.classList.add("hidden");return}const X=[];for(const H of u.nodes)if(On(H,z)&&(X.push(H),X.length>=s))break;if(X.length===0){M.classList.add("hidden");return}for(const H of X){const K=document.createElement("li");K.className="search-result-item";const q=document.createElement("span");q.className="search-result-dot",q.style.backgroundColor=Ae(H.type);const ue=document.createElement("span");ue.className="search-result-label";const U=Xn(H);ue.textContent=U.length>36?U.slice(0,34)+"...":U;const de=document.createElement("span");de.className="search-result-type",de.textContent=H.type,K.appendChild(q),K.appendChild(ue),K.appendChild(de),K.addEventListener("click",()=>{d==null||d(H.id),i.value="",M.classList.add("hidden"),x()}),M.appendChild(K)}M.classList.remove("hidden")}i.addEventListener("input",()=>{o&&clearTimeout(o),o=setTimeout(x,l)});let W=-1;function V(){const z=M.querySelectorAll(".search-result-item");z.forEach((X,H)=>{X.classList.toggle("search-result-active",H===W)}),W>=0&&z[W]&&z[W].scrollIntoView({block:"nearest"})}return i.addEventListener("keydown",z=>{const X=M.querySelectorAll(".search-result-item");z.key==="ArrowDown"?(z.preventDefault(),X.length>0&&(W=Math.min(W+1,X.length-1),V())):z.key==="ArrowUp"?(z.preventDefault(),X.length>0&&(W=Math.max(W-1,0),V())):z.key==="Enter"?(z.preventDefault(),W>=0&&X[W]?X[W].click():X.length>0&&X[0].click(),i.blur()):z.key==="Escape"&&(i.value="",i.blur(),M.classList.add("hidden"),W=-1,x())}),document.addEventListener("click",z=>{n.contains(z.target)||M.classList.add("hidden")}),i.addEventListener("focus",()=>a.classList.add("hidden")),i.addEventListener("blur",()=>{i.value.length===0&&a.classList.remove("hidden")}),{setLearningGraphData(z){u=z,i.value="",M.classList.add("hidden"),u&&u.nodes.length>0?n.classList.remove("hidden"):n.classList.add("hidden")},onFilterChange(z){r=z},onNodeSelect(z){d=z},clear(){i.value="",M.classList.add("hidden"),r==null||r(null)},focus(){i.focus()}}}function Xo(t,e){let s=null,l=null,u=!0,r=null,d=!0,o=!0,n=!0,g="types",i="",a="",M=[],T=[];const x={types:new Set,nodeIds:new Set};function F(){if(!s)return[];const p=new Set;for(const y of s.nodes)x.types.has(y.type)&&p.add(y.id);for(const y of x.nodeIds)p.add(y);return[...p]}function W(p){if(x.nodeIds.has(p))return!0;const y=s==null?void 0:s.nodes.find(v=>v.id===p);return y?x.types.has(y.type):!1}function V(){return x.types.size===0&&x.nodeIds.size===0}function z(){const p=F();e.onFocusChange(p.length>0?p:null)}const X=document.createElement("button");X.className="tools-pane-toggle hidden",X.title="Graph Inspector",X.appendChild(ct({size:16,strokeLinecap:"round",strokeLinejoin:"round"},[{tag:"path",attrs:{d:"M4 7h16"}},{tag:"path",attrs:{d:"M4 12h16"}},{tag:"path",attrs:{d:"M4 17h10"}}]));const H=document.createElement("div");H.className="tools-pane-content hidden",t.appendChild(X),t.appendChild(H),X.addEventListener("click",()=>{var p;u=!u,H.classList.toggle("hidden",u),X.classList.toggle("active",!u),u||(p=e.onOpen)==null||p.call(e)});function K(){if(H.replaceChildren(),!l)return;const p=document.createElement("div");p.className="tools-pane-summary";const y=document.createElement("span");y.textContent=`${l.nodeCount} nodes`;const v=document.createElement("span");v.className="tools-pane-sep",v.textContent="·";const m=document.createElement("span");m.textContent=`${l.edgeCount} edges`;const b=document.createElement("span");b.className="tools-pane-sep",b.textContent="·";const L=document.createElement("span");if(L.textContent=`${l.types.length} types`,p.append(y,v,m,b,L),H.appendChild(p),s&&l.nodeCount>0){const N=Math.ceil(JSON.stringify(s).length/4),I=Math.round(N/l.nodeCount),C=Math.max(10,Math.round(I*.3)*Math.min(5,l.nodeCount)),B=N>C?Math.round((1-C/N)*100):0,$=document.createElement("div");$.className="tools-pane-token-card";const te=Math.min(100,B),ie=document.createElement("div");ie.className="token-card-label",ie.textContent="Token Efficiency",$.appendChild(ie);const le=document.createElement("div");le.className="token-card-stat",le.textContent=`~${N.toLocaleString()} tokens stored`,$.appendChild(le);const me=document.createElement("div");me.className="token-card-bar";const ve=document.createElement("div");ve.className="token-card-bar-fill",ve.style.width=`${te}%`,me.appendChild(ve),$.appendChild(me);const je=document.createElement("div");je.className="token-card-stat",je.textContent=`A search returns ~${C} tokens instead of ~${N.toLocaleString()} (${B}% reduction)`,$.appendChild(je),H.appendChild($)}const h=document.createElement("div");h.className="tools-pane-tabs";const S=[{id:"types",label:"Types"},{id:"insights",label:"Insights"},{id:"controls",label:"Controls"}];for(const N of S){const I=document.createElement("button");I.className="tools-pane-tab",g===N.id&&I.classList.add("tools-pane-tab-active"),I.textContent=N.label,I.addEventListener("click",()=>{g=N.id,K()}),h.appendChild(I)}H.appendChild(h),V()||U(),T.length>0&&ue(),g==="types"&&l.types.length>5?H.appendChild(xe("Filter types...",i,N=>{i=N,q()})):g==="insights"&&l.orphans.length+l.singletons.length+l.emptyNodes.length>5&&H.appendChild(xe("Filter issues...",a,I=>{a=I,q()}));const w=document.createElement("div");w.className="tools-pane-tab-content",H.appendChild(w),q()}function q(){const p=H.querySelector(".tools-pane-tab-content");p&&(p.replaceChildren(),g==="types"?ee(p):g==="insights"?G(p):g==="controls"&&ye(p))}function ue(){H.appendChild(se(`Walk Trail (${T.length})`,p=>{for(let v=0;v<T.length;v++){const m=T[v],b=v===T.length-1;if(m.edgeType){const C=document.createElement("div");C.className="walk-trail-edge",C.textContent=`↓ ${m.edgeType}`,p.appendChild(C)}const L=document.createElement("div");L.className="tools-pane-row tools-pane-clickable",b&&(L.style.fontWeight="600");const h=document.createElement("span");h.className="tools-pane-count",h.style.minWidth="18px",h.textContent=`${v+1}`;const S=document.createElement("span");S.className="tools-pane-dot",S.style.backgroundColor=Ae(m.type);const w=document.createElement("span");w.className="tools-pane-name",w.textContent=m.label;const N=document.createElement("span");N.className="tools-pane-count",N.textContent=m.type;const I=document.createElement("button");I.className="tools-pane-edit",I.style.opacity="1",I.textContent="×",I.title="Remove from trail",I.addEventListener("click",C=>{var B;C.stopPropagation(),(B=e.onWalkTrailRemove)==null||B.call(e,m.id)}),L.appendChild(h),L.appendChild(S),L.appendChild(w),L.appendChild(N),L.appendChild(I),L.addEventListener("click",()=>{e.onNavigateToNode(m.id)}),p.appendChild(L)}const y=document.createElement("div");if(y.className="tools-pane-export-row",e.onWalkIsolate){const v=document.createElement("button");v.className="tools-pane-export-btn",v.textContent="Isolate (I)",v.addEventListener("click",()=>e.onWalkIsolate()),y.appendChild(v)}if(e.onWalkSaveSnippet&&T.length>=2){const v=document.createElement("button");v.className="tools-pane-export-btn",v.textContent="Save snippet",v.addEventListener("click",()=>{Bt("Save snippet","Name for this snippet").then(m=>{m&&e.onWalkSaveSnippet(m)})}),y.appendChild(v)}p.appendChild(y)}))}function U(){if(!l||!s)return;const p=F();H.appendChild(se("Focused",y=>{for(const L of x.types){const h=l.types.find(B=>B.name===L);if(!h)continue;const S=document.createElement("div");S.className="tools-pane-row tools-pane-clickable";const w=document.createElement("span");w.className="tools-pane-dot",w.style.backgroundColor=Ae(h.name);const N=document.createElement("span");N.className="tools-pane-name",N.textContent=h.name;const I=document.createElement("span");I.className="tools-pane-count",I.textContent=`${h.count} nodes`;const C=document.createElement("button");C.className="tools-pane-edit tools-pane-focus-active",C.style.opacity="1",C.textContent="×",C.title=`Remove ${h.name} from focus`,S.appendChild(w),S.appendChild(N),S.appendChild(I),S.appendChild(C),C.addEventListener("click",B=>{B.stopPropagation(),x.types.delete(h.name),z(),K()}),y.appendChild(S)}for(const L of x.nodeIds){const h=s.nodes.find($=>$.id===L);if(!h)continue;const S=Wn(h.properties)??h.id,w=document.createElement("div");w.className="tools-pane-row tools-pane-clickable";const N=document.createElement("span");N.className="tools-pane-dot",N.style.backgroundColor=Ae(h.type);const I=document.createElement("span");I.className="tools-pane-name",I.textContent=S;const C=document.createElement("span");C.className="tools-pane-count",C.textContent=h.type;const B=document.createElement("button");B.className="tools-pane-edit tools-pane-focus-active",B.style.opacity="1",B.textContent="×",B.title=`Remove ${S} from focus`,w.appendChild(N),w.appendChild(I),w.appendChild(C),w.appendChild(B),w.addEventListener("click",$=>{$.target.closest(".tools-pane-edit")||e.onNavigateToNode(L)}),B.addEventListener("click",$=>{$.stopPropagation(),x.nodeIds.delete(L),z(),K()}),y.appendChild(w)}const v=document.createElement("div");v.className="tools-pane-row tools-pane-clickable tools-pane-focus-clear";const m=document.createElement("span");m.className="tools-pane-name",m.style.color="var(--accent)",m.textContent=`${p.length} total`;const b=document.createElement("span");b.className="tools-pane-badge",b.textContent="clear all",v.appendChild(m),v.appendChild(b),v.addEventListener("click",()=>{x.types.clear(),x.nodeIds.clear(),z(),K()}),y.appendChild(v)}))}function de(p){const y=document.createElement("div");y.className="tools-pane-row tools-pane-clickable",r===p.name&&y.classList.add("active");const v=document.createElement("span");v.className="tools-pane-dot",v.style.backgroundColor=Ae(p.name);const m=document.createElement("span");m.className="tools-pane-name",m.textContent=p.name;const b=document.createElement("span");b.className="tools-pane-count",b.textContent=String(p.count);const L=document.createElement("button");L.className="tools-pane-edit tools-pane-focus-toggle",x.types.has(p.name)&&L.classList.add("tools-pane-focus-active"),L.textContent="◎",L.title=x.types.has(p.name)?`Remove ${p.name} from focus`:`Add ${p.name} to focus`;const h=document.createElement("button");return h.className="tools-pane-edit",h.textContent="✎",h.title=`Rename all ${p.name} nodes`,y.appendChild(v),y.appendChild(m),y.appendChild(b),y.appendChild(L),y.appendChild(h),y.addEventListener("click",S=>{S.target.closest(".tools-pane-edit")||(r===p.name?(r=null,e.onFilterByType(null)):(r=p.name,e.onFilterByType(p.name)),K())}),L.addEventListener("click",S=>{S.stopPropagation(),x.types.has(p.name)?x.types.delete(p.name):x.types.add(p.name),z(),K()}),h.addEventListener("click",S=>{S.stopPropagation(),ge(y,p.name,w=>{w&&w!==p.name&&e.onRenameNodeType(p.name,w)})}),y}function xe(p,y,v){const m=document.createElement("input");return m.type="text",m.className="tools-pane-search",m.placeholder=p,m.value=y,m.addEventListener("input",()=>v(m.value)),m}function ee(p){if(!l)return;const y=i.toLowerCase();if(l.types.length){const m=l.types.filter(b=>!x.types.has(b.name)).filter(b=>!y||b.name.toLowerCase().includes(y));m.length>0&&p.appendChild(se("Node Types",b=>{for(const L of m)b.appendChild(de(L))}))}const v=l.edgeTypes.filter(m=>!y||m.name.toLowerCase().includes(y));v.length&&p.appendChild(se("Edge Types",m=>{for(const b of v){const L=document.createElement("div");L.className="tools-pane-row tools-pane-clickable";const h=document.createElement("span");h.className="tools-pane-name",h.textContent=b.name;const S=document.createElement("span");S.className="tools-pane-count",S.textContent=String(b.count);const w=document.createElement("button");w.className="tools-pane-edit",w.textContent="✎",w.title=`Rename all ${b.name} edges`,L.appendChild(h),L.appendChild(S),L.appendChild(w),w.addEventListener("click",N=>{N.stopPropagation(),ge(L,b.name,I=>{I&&I!==b.name&&e.onRenameEdgeType(b.name,I)})}),m.appendChild(L)}}))}function G(p){if(!l)return;const y=a.toLowerCase(),v=l.starred.filter(I=>!y||I.label.toLowerCase().includes(y)||I.type.toLowerCase().includes(y));v.length&&p.appendChild(se("★ Starred",I=>{for(const $ of v){const te=document.createElement("div");te.className="tools-pane-row tools-pane-clickable";const ie=document.createElement("span");ie.className="tools-pane-dot",ie.style.backgroundColor=Ae($.type);const le=document.createElement("span");le.className="tools-pane-name",le.textContent=$.label;const me=document.createElement("button");me.className="tools-pane-edit tools-pane-focus-toggle",W($.id)&&me.classList.add("tools-pane-focus-active"),me.textContent="◎",me.title=W($.id)?`Remove ${$.label} from focus`:`Add ${$.label} to focus`,te.appendChild(ie),te.appendChild(le),te.appendChild(me),te.addEventListener("click",ve=>{ve.target.closest(".tools-pane-edit")||e.onNavigateToNode($.id)}),me.addEventListener("click",ve=>{ve.stopPropagation(),x.nodeIds.has($.id)?x.nodeIds.delete($.id):x.nodeIds.add($.id),z(),K()}),I.appendChild(te)}const C=document.createElement("div");C.className="tools-pane-row tools-pane-actions";const B=document.createElement("button");if(B.className="tools-pane-action-btn",B.textContent="Focus all",B.title="Enter focus mode with all starred nodes",B.addEventListener("click",()=>{x.nodeIds.clear(),x.types.clear();for(const $ of l.starred)x.nodeIds.add($.id);z(),K()}),C.appendChild(B),e.onStarredSaveSnippet){const $=document.createElement("button");$.className="tools-pane-action-btn",$.textContent="Save as snippet",$.title="Save starred nodes as a reusable snippet",$.addEventListener("click",async()=>{const te=await Bt("Snippet name","starred");te&&e.onStarredSaveSnippet(te,l.starred.map(ie=>ie.id))}),C.appendChild($)}I.appendChild(C)}));const m=l.mostConnected.filter(I=>!y||I.label.toLowerCase().includes(y)||I.type.toLowerCase().includes(y));m.length&&p.appendChild(se("Most Connected",I=>{for(const C of m){const B=document.createElement("div");B.className="tools-pane-row tools-pane-clickable";const $=document.createElement("span");$.className="tools-pane-dot",$.style.backgroundColor=Ae(C.type);const te=document.createElement("span");te.className="tools-pane-name",te.textContent=C.label;const ie=document.createElement("span");ie.className="tools-pane-count",ie.textContent=`${C.connections}`;const le=document.createElement("button");le.className="tools-pane-edit tools-pane-focus-toggle",W(C.id)&&le.classList.add("tools-pane-focus-active"),le.textContent="◎",le.title=W(C.id)?`Remove ${C.label} from focus`:`Add ${C.label} to focus`,B.appendChild($),B.appendChild(te),B.appendChild(ie),B.appendChild(le),B.addEventListener("click",me=>{me.target.closest(".tools-pane-edit")||e.onNavigateToNode(C.id)}),le.addEventListener("click",me=>{me.stopPropagation(),x.nodeIds.has(C.id)?x.nodeIds.delete(C.id):x.nodeIds.add(C.id),z(),K()}),I.appendChild(B)}}));const b=l.orphans.filter(I=>!y||I.label.toLowerCase().includes(y)||I.type.toLowerCase().includes(y)),L=l.singletons.filter(I=>!y||I.name.toLowerCase().includes(y)),h=l.emptyNodes.filter(I=>!y||I.label.toLowerCase().includes(y)||I.type.toLowerCase().includes(y)),S=b.length>0,w=L.length>0,N=h.length>0;if(!S&&!w&&!N){const I=document.createElement("div");I.className="tools-pane-empty-msg",I.textContent="No issues found",p.appendChild(I);return}S&&p.appendChild(se("Orphans",I=>{for(const C of b.slice(0,5)){const B=document.createElement("div");B.className="tools-pane-row tools-pane-clickable tools-pane-issue";const $=document.createElement("span");$.className="tools-pane-dot",$.style.backgroundColor=Ae(C.type);const te=document.createElement("span");te.className="tools-pane-name",te.textContent=C.label;const ie=document.createElement("span");ie.className="tools-pane-badge",ie.textContent="orphan";const le=document.createElement("button");le.className="tools-pane-edit tools-pane-focus-toggle",W(C.id)&&le.classList.add("tools-pane-focus-active"),le.textContent="◎",le.title=W(C.id)?`Remove ${C.label} from focus`:`Add ${C.label} to focus`,B.appendChild($),B.appendChild(te),B.appendChild(ie),B.appendChild(le),B.addEventListener("click",me=>{me.target.closest(".tools-pane-edit")||e.onNavigateToNode(C.id)}),le.addEventListener("click",me=>{me.stopPropagation(),x.nodeIds.has(C.id)?x.nodeIds.delete(C.id):x.nodeIds.add(C.id),z(),K()}),I.appendChild(B)}if(b.length>5){const C=document.createElement("div");C.className="tools-pane-more",C.textContent=`+ ${b.length-5} more orphans`,I.appendChild(C)}})),w&&p.appendChild(se("Singletons",I=>{for(const C of L.slice(0,5)){const B=document.createElement("div");B.className="tools-pane-row tools-pane-issue";const $=document.createElement("span");$.className="tools-pane-dot",$.style.backgroundColor=Ae(C.name);const te=document.createElement("span");te.className="tools-pane-name",te.textContent=C.name;const ie=document.createElement("span");ie.className="tools-pane-badge",ie.textContent="1 node",B.appendChild($),B.appendChild(te),B.appendChild(ie),I.appendChild(B)}})),N&&p.appendChild(se("Empty Nodes",I=>{for(const C of h.slice(0,5)){const B=document.createElement("div");B.className="tools-pane-row tools-pane-issue";const $=document.createElement("span");$.className="tools-pane-dot",$.style.backgroundColor=Ae(C.type);const te=document.createElement("span");te.className="tools-pane-name",te.textContent=C.label;const ie=document.createElement("span");ie.className="tools-pane-badge",ie.textContent="empty",B.appendChild($),B.appendChild(te),B.appendChild(ie),I.appendChild(B)}if(l.emptyNodes.length>5){const C=document.createElement("div");C.className="tools-pane-more",C.textContent=`+ ${l.emptyNodes.length-5} more empty nodes`,I.appendChild(C)}}))}function ye(p){p.appendChild(se("Display",y=>{const v=document.createElement("div");v.className="tools-pane-row tools-pane-clickable";const m=document.createElement("input");m.type="checkbox",m.checked=d,m.className="tools-pane-checkbox";const b=document.createElement("span");b.className="tools-pane-name",b.textContent="Edge labels",v.appendChild(m),v.appendChild(b),v.addEventListener("click",C=>{C.target!==m&&(m.checked=!m.checked),d=m.checked,e.onToggleEdgeLabels(d)}),y.appendChild(v);const L=document.createElement("div");L.className="tools-pane-row tools-pane-clickable";const h=document.createElement("input");h.type="checkbox",h.checked=o,h.className="tools-pane-checkbox";const S=document.createElement("span");S.className="tools-pane-name",S.textContent="Type regions",L.appendChild(h),L.appendChild(S),L.addEventListener("click",C=>{C.target!==h&&(h.checked=!h.checked),o=h.checked,e.onToggleTypeHulls(o)}),y.appendChild(L);const w=document.createElement("div");w.className="tools-pane-row tools-pane-clickable";const N=document.createElement("input");N.type="checkbox",N.checked=n,N.className="tools-pane-checkbox";const I=document.createElement("span");I.className="tools-pane-name",I.textContent="Minimap",w.appendChild(N),w.appendChild(I),w.addEventListener("click",C=>{C.target!==N&&(N.checked=!N.checked),n=N.checked,e.onToggleMinimap(n)}),y.appendChild(w)})),p.appendChild(se("Layout",y=>{y.appendChild(Ce("Clustering",0,1,.02,.08,v=>{e.onLayoutChange("clusterStrength",v)})),y.appendChild(Ce("Spacing",.5,20,.5,1.5,v=>{e.onLayoutChange("spacing",v)})),y.appendChild(Ce("Pan speed",20,200,10,60,v=>{e.onPanSpeedChange(v)}))})),p.appendChild(se("Export",y=>{const v=document.createElement("div");v.className="tools-pane-export-row";const m=document.createElement("button");m.className="tools-pane-export-btn",m.textContent="Export PNG",m.addEventListener("click",()=>e.onExport("png"));const b=document.createElement("button");b.className="tools-pane-export-btn",b.textContent="Export SVG",b.addEventListener("click",()=>e.onExport("svg")),v.appendChild(m),v.appendChild(b),y.appendChild(v)})),(e.onSnapshot||e.onRollback)&&p.appendChild(se("Versions",y=>{const v=document.createElement("div");v.className="tools-pane-export-row";const m=document.createElement("button");if(m.className="tools-pane-export-btn",m.textContent="Save snapshot",m.addEventListener("click",()=>{Bt("Save snapshot","Label (optional)").then(b=>{var L;(L=e.onSnapshot)==null||L.call(e,b||void 0)})}),v.appendChild(m),y.appendChild(v),M.length>0)for(const b of M){const L=document.createElement("div");L.className="tools-pane-row";const h=document.createElement("span");h.className="tools-pane-name";const S=Go(b.timestamp);h.textContent=b.label?`#${b.version} ${b.label}`:`#${b.version}`,h.title=`${S} — ${b.nodeCount} nodes, ${b.edgeCount} edges`;const w=document.createElement("span");w.className="tools-pane-count",w.textContent=S;const N=document.createElement("button");N.className="tools-pane-edit",N.style.opacity="1",N.textContent="↩",N.title="Restore this snapshot",N.addEventListener("click",()=>{jn("Restore snapshot",`Restore snapshot #${b.version}? Current state will be lost unless you save a snapshot first.`).then(I=>{var C;I&&((C=e.onRollback)==null||C.call(e,b.version))})}),L.appendChild(h),L.appendChild(w),L.appendChild(N),y.appendChild(L)}else{const b=document.createElement("div");b.className="tools-pane-empty-msg",b.textContent="No snapshots yet",y.appendChild(b)}}))}function Ce(p,y,v,m,b,L){const h=document.createElement("div");h.className="tools-pane-slider-row";const S=document.createElement("span");S.className="tools-pane-slider-label",S.textContent=p;const w=document.createElement("input");w.type="range",w.className="tools-pane-slider",w.min=String(y),w.max=String(v),w.step=String(m),w.value=String(b);const N=document.createElement("span");return N.className="tools-pane-slider-value",N.textContent=String(b),w.addEventListener("input",()=>{const I=parseFloat(w.value);N.textContent=I%1===0?String(I):I.toFixed(2),L(I)}),h.appendChild(S),h.appendChild(w),h.appendChild(N),h}function se(p,y){const v=document.createElement("div");v.className="tools-pane-section";const m=document.createElement("div");return m.className="tools-pane-heading",m.textContent=p,v.appendChild(m),y(v),v}function ge(p,y,v){const m=document.createElement("input");m.className="tools-pane-inline-input",m.value=y,m.type="text";const b=ho(p);p.replaceChildren(m),p.classList.add("tools-pane-editing"),m.focus(),m.select();function L(){fo(p,b)}function h(){const S=m.value.trim();p.classList.remove("tools-pane-editing"),S&&S!==y?v(S):L()}m.addEventListener("keydown",S=>{S.key==="Enter"&&(S.preventDefault(),h()),S.key==="Escape"&&(L(),p.classList.remove("tools-pane-editing"))}),m.addEventListener("blur",h)}function be(p){const y=new Map,v=new Map,m=new Map,b=new Set;for(const C of p.nodes)y.set(C.type,(y.get(C.type)??0)+1);for(const C of p.edges)v.set(C.type,(v.get(C.type)??0)+1),m.set(C.sourceId,(m.get(C.sourceId)??0)+1),m.set(C.targetId,(m.get(C.targetId)??0)+1),b.add(C.sourceId),b.add(C.targetId);const L=C=>Wn(C.properties)??C.id,h=p.nodes.filter(C=>C.properties._starred===!0).map(C=>({id:C.id,label:L(C),type:C.type})),S=p.nodes.filter(C=>!b.has(C.id)).map(C=>({id:C.id,label:L(C),type:C.type})),w=[...y.entries()].filter(([,C])=>C===1).map(([C])=>({name:C})),N=p.nodes.filter(C=>Object.keys(C.properties).length===0).map(C=>({id:C.id,label:C.id,type:C.type})),I=p.nodes.map(C=>({id:C.id,label:L(C),type:C.type,connections:m.get(C.id)??0})).filter(C=>C.connections>0).sort((C,B)=>B.connections-C.connections).slice(0,5);return{nodeCount:p.nodes.length,edgeCount:p.edges.length,types:[...y.entries()].sort((C,B)=>B[1]-C[1]).map(([C,B])=>({name:C,count:B})),edgeTypes:[...v.entries()].sort((C,B)=>B[1]-C[1]).map(([C,B])=>({name:C,count:B})),starred:h,orphans:S,singletons:w,emptyNodes:N,mostConnected:I}}return{collapse(){u=!0,H.classList.add("hidden"),X.classList.remove("active")},addToFocusSet(p){for(const y of p)x.nodeIds.add(y);z(),K()},clearFocusSet(){x.types.clear(),x.nodeIds.clear(),z(),K()},setData(p){s=p,r=null,x.types.clear(),x.nodeIds.clear(),s&&s.nodes.length>0?(l=be(s),X.classList.remove("hidden"),K()):(l=null,X.classList.add("hidden"),H.classList.add("hidden"))},setSnapshots(p){M=p,g==="controls"&&q()},setWalkTrail(p){T=p,K()}}}function Go(t){const e=Date.now()-new Date(t).getTime(),s=Math.floor(e/6e4);if(s<1)return"just now";if(s<60)return`${s}m ago`;const l=Math.floor(s/60);return l<24?`${l}h ago`:`${Math.floor(l/24)}d ago`}function Wn(t){for(const e of Object.values(t))if(typeof e=="string")return e;return null}function Vo(t,e){const s=e.split("+"),l=s.pop(),u=s.map(n=>n.toLowerCase()),r=u.includes("ctrl")||u.includes("cmd")||u.includes("meta"),d=u.includes("shift"),o=u.includes("alt");return r!==(t.ctrlKey||t.metaKey)||!r&&(t.ctrlKey||t.metaKey)||d&&!t.shiftKey||o!==t.altKey?!1:l.toLowerCase()==="escape"?t.key==="Escape":l.toLowerCase()==="tab"?t.key==="Tab":u.length>0?t.key.toLowerCase()===l.toLowerCase():t.key===l}function Jo(){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 Ko=[{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"}],Zo=["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 Qo(t){return t.split("+").map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join("+")}function es(t,e){const s=Jo(),l=document.createElement("div");l.className="shortcuts-overlay hidden";const u=document.createElement("div");u.className="shortcuts-modal";const r=document.createElement("h3");r.className="shortcuts-title",r.textContent="Keyboard Shortcuts";const d=document.createElement("div");d.className="shortcuts-list";for(const a of Zo){const M=e[a];if(!M)continue;const T=document.createElement("div");T.className="shortcuts-row";const x=document.createElement("div");x.className="shortcuts-keys";const F=document.createElement("kbd");F.textContent=Qo(M),x.appendChild(F);const W=document.createElement("span");W.className="shortcuts-desc",W.textContent=s[a],T.appendChild(x),T.appendChild(W),d.appendChild(T)}for(const a of Ko){const M=document.createElement("div");M.className="shortcuts-row";const T=document.createElement("div");T.className="shortcuts-keys";const x=document.createElement("kbd");x.textContent=a.key,T.appendChild(x);const F=document.createElement("span");F.className="shortcuts-desc",F.textContent=a.description,M.appendChild(T),M.appendChild(F),d.appendChild(M)}const o=document.createElement("button");o.className="shortcuts-close",o.textContent="×",u.appendChild(o),u.appendChild(r),u.appendChild(d),l.appendChild(u),t.appendChild(l);function n(){l.classList.remove("hidden")}function g(){l.classList.add("hidden")}function i(){l.classList.toggle("hidden")}return o.addEventListener("click",g),l.addEventListener("click",a=>{a.target===l&&g()}),{show:n,hide:g,toggle:i}}function ts(t){const e=document.createElement("div");e.className="empty-state";const s=document.createElement("div");s.className="empty-state-bg";for(const T of["c1","c2","c3","c4","c5"]){const x=document.createElement("div");x.className=`empty-state-circle ${T}`,s.appendChild(x)}s.appendChild(ct({size:0,viewBox:"0 0 400 300",className:"empty-state-lines"},[{tag:"line",attrs:{x1:80,y1:60,x2:220,y2:140,"stroke-width":"0.5",opacity:"0.15"}},{tag:"line",attrs:{x1:220,y1:140,x2:320,y2:80,"stroke-width":"0.5",opacity:"0.15"}},{tag:"line",attrs:{x1:220,y1:140,x2:160,y2:240,"stroke-width":"0.5",opacity:"0.15"}},{tag:"line",attrs:{x1:160,y1:240,x2:300,y2:220,"stroke-width":"0.5",opacity:"0.15"}}]));const l=s.querySelector(".empty-state-lines");l&&(l.removeAttribute("width"),l.removeAttribute("height"),l.setAttribute("preserveAspectRatio","xMidYMid slice")),e.appendChild(s);const u=document.createElement("div");u.className="empty-state-content";const r=document.createElement("div");r.className="empty-state-icon",r.appendChild(ct({size:48,strokeWidth:1.5,strokeLinecap:"round",strokeLinejoin:"round"},[{tag:"path",attrs:{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"}},{tag:"polyline",attrs:{points:"3.27 6.96 12 12.01 20.73 6.96"}},{tag:"line",attrs:{x1:12,y1:22.08,x2:12,y2:12}}])),u.appendChild(r);const d=document.createElement("h2");d.className="empty-state-title",d.textContent="No learning graphs yet",u.appendChild(d);const o=document.createElement("p");o.className="empty-state-desc",o.textContent="Connect Backpack to Claude, then start a conversation. Claude will build your first learning graph automatically.",u.appendChild(o);const n=document.createElement("div");n.className="empty-state-setup";const g=document.createElement("div");g.className="empty-state-label",g.textContent="Add Backpack to Claude Code:";const i=document.createElement("code");i.className="empty-state-code",i.textContent="claude mcp add backpack-local -s user -- npx backpack-ontology@latest",n.append(g,i),u.appendChild(n);const a=document.createElement("p");a.className="empty-state-hint",a.append("Press ");const M=document.createElement("kbd");return M.textContent="?",a.appendChild(M),a.append(" for keyboard shortcuts"),u.appendChild(a),e.appendChild(u),t.appendChild(e),{show(){e.classList.remove("hidden")},hide(){e.classList.add("hidden")}}}const ns=30;function os(){let t=[],e=[];return{push(s){t.push(JSON.stringify(s)),t.length>ns&&t.shift(),e=[]},undo(s){return t.length===0?null:(e.push(JSON.stringify(s)),JSON.parse(t.pop()))},redo(s){return e.length===0?null:(t.push(JSON.stringify(s)),JSON.parse(e.pop()))},canUndo(){return t.length>0},canRedo(){return e.length>0},clear(){t=[],e=[]}}}function ss(t,e){let s=null;function l(d,o,n,g,i){u(),s=document.createElement("div"),s.className="context-menu",s.style.left=`${g}px`,s.style.top=`${i}px`;const a=[{label:n?"★ Unstar":"☆ Star",action:()=>e.onStar(d),premium:!1},{label:"◎ Focus on node",action:()=>e.onFocusNode(d),premium:!1},{label:"⑂ Explore in branch",action:()=>e.onExploreInBranch(d),premium:!1},{label:"⎘ Copy ID",action:()=>e.onCopyId(d),premium:!1}];e.onExpand&&a.push({label:"⊕ Expand node",action:()=>e.onExpand(d),premium:!0}),e.onExplainPath&&a.push({label:"↔ Explain path to…",action:()=>e.onExplainPath(d),premium:!0}),e.onEnrich&&a.push({label:"≡ Enrich from web",action:()=>e.onEnrich(d),premium:!0});let M=!1;for(const x of a){if(!M&&x.premium){const W=document.createElement("div");W.className="context-menu-separator",s.appendChild(W),M=!0}const F=document.createElement("div");F.className="context-menu-item",F.textContent=x.label,F.addEventListener("click",()=>{x.action(),u()}),s.appendChild(F)}t.appendChild(s);const T=s.getBoundingClientRect();T.right>window.innerWidth&&(s.style.left=`${g-T.width}px`),T.bottom>window.innerHeight&&(s.style.top=`${i-T.height}px`),setTimeout(()=>document.addEventListener("click",u),0),document.addEventListener("keydown",r)}function u(){s&&(s.remove(),s=null),document.removeEventListener("click",u),document.removeEventListener("keydown",r)}function r(d){d.key==="Escape"&&u()}return{show:l,hide:u}}function as(t){const e=document.createElement("button");return e.className="copy-prompt-btn",e.title="Copy a prompt about the current view to clipboard",e.textContent="Copy Prompt",e.addEventListener("click",async()=>{const s=t();if(!s.graphName||!s.data){wt("No graph loaded");return}const l=cs(s);try{await navigator.clipboard.writeText(l),wt("Prompt copied — paste into Claude")}catch{wt("Failed to copy to clipboard")}}),e}function Un(t){return Object.values(t.properties).find(s=>typeof s=="string")??t.id}function cs(t){const{graphName:e,data:s,selection:l,focus:u}=t,r=[];r.push(`I'm looking at the Backpack learning graph "${e}" in the viewer.`),r.push(`It has ${s.nodes.length} nodes and ${s.edges.length} edges.`);const d=new Map;for(const o of s.nodes)d.set(o.type,(d.get(o.type)??0)+1);if(d.size>0){const o=Array.from(d.entries()).sort((n,g)=>g[1]-n[1]).slice(0,8).map(([n,g])=>`${n} (${g})`).join(", ");r.push(`Node types: ${o}`)}if(u&&u.seedNodeIds.length>0){r.push(""),r.push(`I'm focused on ${u.seedNodeIds.length} seed node(s) at ${u.hops} hop(s):`);for(const o of u.seedNodeIds.slice(0,10)){const n=s.nodes.find(g=>g.id===o);n&&r.push(`- ${Un(n)} (type: ${n.type})`)}}if(l.length>0){r.push(""),r.push(`I have ${l.length} node(s) selected:`);for(const o of l.slice(0,20)){const n=s.nodes.find(i=>i.id===o);if(!n)continue;const g=Object.entries(n.properties).filter(([i])=>!i.startsWith("_")).slice(0,5).map(([i,a])=>`${i}=${JSON.stringify(a)}`).join(", ");r.push(`- ${Un(n)} (type: ${n.type}, id: ${o}${g?`, ${g}`:""})`)}}return r.push(""),r.push("If you have access to the Backpack MCP tools, please use them to answer questions about this graph (backpack_get_node, backpack_get_neighbors, backpack_search, etc)."),r.push(""),r.push("My question: "),r.join(`
6
- `)}const is=200;let Ht=null,Yt=null;function ls(t){Ht=t,Yt===null&&(Yt=window.setTimeout(async()=>{const e=Ht;if(Ht=null,Yt=null,!!e)try{await fetch("/api/viewer-state",{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({...e,updatedAt:new Date().toISOString()})})}catch{}},is))}function rs(){const t=new Map;return{emit(e){const s=t.get(e);if(s)for(const l of s)try{l()}catch(u){console.error(`[backpack-viewer] event handler for ${e} threw:`,u)}},subscribe(e,s){let l=t.get(e);return l||(l=new Set,t.set(e,l)),l.add(s),()=>{const u=t.get(e);u==null||u.delete(s)}}}}const Kt="1";function ds(t,e,s,l){function u(){return Date.now().toString(36)+Math.random().toString(36).slice(2,10)}function r(){const d=e.getGraph();if(!d)throw new Error("no graph loaded in viewer");return d}return{name:t,viewerApiVersion:Kt,getGraph:()=>e.getGraph(),getGraphName:()=>e.getGraphName(),getSelection:()=>e.getSelection(),getFocus:()=>e.getFocus(),on(d,o){return e.subscribe(d,o)},async addNode(d,o){if(!d)throw new Error("addNode: type is required");const n=r();e.snapshotForUndo();const g=u(),i=new Date().toISOString();return n.nodes.push({id:g,type:d,properties:o,createdAt:i,updatedAt:i}),await e.saveCurrentGraph(),g},async updateNode(d,o){const g=r().nodes.find(i=>i.id===d);if(!g)throw new Error(`updateNode: node not found: ${d}`);e.snapshotForUndo(),g.properties={...g.properties,...o},g.updatedAt=new Date().toISOString(),await e.saveCurrentGraph()},async removeNode(d){const o=r();if(!o.nodes.find(g=>g.id===d))throw new Error(`removeNode: node not found: ${d}`);e.snapshotForUndo(),o.nodes=o.nodes.filter(g=>g.id!==d),o.edges=o.edges.filter(g=>g.sourceId!==d&&g.targetId!==d),await e.saveCurrentGraph()},async addEdge(d,o,n){if(!d||!o||!n)throw new Error("addEdge: sourceId, targetId, and type are required");const g=r();if(!g.nodes.find(a=>a.id===d))throw new Error(`addEdge: source not found: ${d}`);if(!g.nodes.find(a=>a.id===o))throw new Error(`addEdge: target not found: ${o}`);e.snapshotForUndo();const i=u();return g.edges.push({id:i,sourceId:d,targetId:o,type:n}),await e.saveCurrentGraph(),i},async removeEdge(d){const o=r();if(!o.edges.find(g=>g.id===d))throw new Error(`removeEdge: edge not found: ${d}`);e.snapshotForUndo(),o.edges=o.edges.filter(g=>g.id!==d),await e.saveCurrentGraph()},panToNode:d=>e.panToNode(d),focusNodes:(d,o)=>e.focusNodes(d,o),exitFocus:()=>e.exitFocus(),registerTaskbarIcon(d){return s.register(d)},mountPanel(d,o){return l.mount(t,d,o)},settings:{async get(d){const o=`/api/extensions/${encodeURIComponent(t)}/settings`,n=await fetch(o);if(!n.ok)return null;const g=await n.json();return d in g?g[d]:null},async set(d,o){const n=`/api/extensions/${encodeURIComponent(t)}/settings/${encodeURIComponent(d)}`,g=await fetch(n,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({value:o})});if(!g.ok){const i=await g.text().catch(()=>"");throw new Error(`settings.set failed: ${i||g.status}`)}},async remove(d){const o=`/api/extensions/${encodeURIComponent(t)}/settings/${encodeURIComponent(d)}`,n=await fetch(o,{method:"DELETE"});if(!n.ok){const g=await n.text().catch(()=>"");throw new Error(`settings.remove failed: ${g||n.status}`)}}},async fetch(d,o={}){const n=`/api/extensions/${encodeURIComponent(t)}/fetch`,g={};if(o.headers)if(o.headers instanceof Headers)o.headers.forEach((a,M)=>{g[M]=a});else if(Array.isArray(o.headers))for(const[a,M]of o.headers)g[a]=M;else Object.assign(g,o.headers);const i=typeof o.body=="string"||o.body==null?o.body:(()=>{throw new Error("viewer.fetch only supports string bodies in v1")})();return fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:d,method:o.method??"POST",headers:g,body:i})})}}}const ps="bottom-right";function us(t){const e={"top-left":t.topLeft,"top-right":t.topRight,"bottom-left":t.bottomLeft,"bottom-right":t.bottomRight},s={"top-left":0,"top-right":0,"bottom-left":0,"bottom-right":0};function l(r){e[r].classList.toggle("has-icons",s[r]>0)}l("top-left"),l("top-right"),l("bottom-left"),l("bottom-right");function u(r){const d=r.position??ps,o=e[d];if(!o)throw new Error(`unknown taskbar position: ${d}`);const n=document.createElement("button");if(n.className="extension-taskbar-icon",n.title=r.label,n.setAttribute("aria-label",r.label),r.iconText){const i=document.createElement("span");i.className="extension-taskbar-icon-symbol",i.textContent=r.iconText,n.appendChild(i)}const g=document.createElement("span");return g.className="extension-taskbar-icon-label",g.textContent=r.label,n.appendChild(g),n.addEventListener("click",()=>{try{r.onClick()}catch(i){console.error(`[backpack-viewer] taskbar icon "${r.label}" onClick threw:`,i)}}),o.appendChild(n),s[d]++,l(d),()=>{n.remove(),s[d]=Math.max(0,s[d]-1),l(d)}}return{register:u}}async function ms(t,e){const s=us(t.taskbarSlots);let l=[];try{const r=await fetch("/api/extensions");if(!r.ok)return console.warn(`[backpack-viewer] /api/extensions returned ${r.status}; no extensions will be loaded`),[];l=await r.json()}catch(r){return console.error(`[backpack-viewer] failed to fetch extensions list: ${r.message}`),[]}const u=[];for(const r of l){if(r.viewerApi!==Kt){console.warn(`[backpack-viewer] extension "${r.name}" targets viewerApi "${r.viewerApi}" but this viewer supports "${Kt}"; skipping`);continue}if(r.stylesheet)try{hs(r.name,r.stylesheet)}catch(o){console.error(`[backpack-viewer] extension "${r.name}" stylesheet load failed:`,o)}const d=ds(r.name,t,s,e);try{const g=(await import(`/extensions/${encodeURIComponent(r.name)}/${r.entry}`)).activate;if(typeof g!="function"){console.error(`[backpack-viewer] extension "${r.name}" has no exported activate(api) function; skipping`);continue}await g(d),u.push({info:r,api:d}),console.log(`[backpack-viewer] loaded extension "${r.name}" v${r.version}`)}catch(o){console.error(`[backpack-viewer] extension "${r.name}" failed to activate:`,o)}}return u}function hs(t,e){if(e.includes(".."))throw new Error(`stylesheet path "${e}" is invalid`);const s=document.createElement("link");s.rel="stylesheet",s.href=`/extensions/${encodeURIComponent(t)}/${e}`,s.dataset.extension=t,document.head.appendChild(s)}const fs={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"},gs={host:"127.0.0.1",port:5173},ys={edges:!0,edgeLabels:!0,typeHulls:!0,minimap:!0,theme:"system"},Cs={spacing:1.5,clustering:.08},vs={panSpeed:60,panFastMultiplier:3,zoomFactor:1.3,zoomMin:.05,zoomMax:10,panAnimationMs:300},bs={hideBadges:.4,hideLabels:.25,hideEdgeLabels:.35,smallNodes:.2,hideArrows:.15},xs={pulseSpeed:.02},Es={maxSearchResults:8,maxQualityItems:5,maxMostConnected:5,searchDebounceMs:150},ws={disabled:[],external:[]},ks={keybindings:fs,server:gs,display:ys,layout:Cs,navigation:vs,lod:bs,walk:xs,limits:Es,extensions:ws};let ce="",A=null,qt=new Set,Xt=!1;async function Ss(){var ut,mt;const t=document.getElementById("canvas-container"),e={...ks};try{const c=await fetch("/api/config");if(c.ok){const E=await c.json();Object.assign(e.keybindings,E.keybindings??{}),Object.assign(e.display,E.display??{}),Object.assign(e.layout,E.layout??{}),Object.assign(e.navigation,E.navigation??{}),Object.assign(e.lod,E.lod??{}),Object.assign(e.limits,E.limits??{})}}catch{}const s=e.keybindings,l=window.matchMedia("(prefers-color-scheme: dark)"),u=e.display.theme==="system"?l.matches?"dark":"light":e.display.theme,d=localStorage.getItem("backpack-theme")??u;document.documentElement.setAttribute("data-theme",d);const o=document.createElement("button");o.className="theme-toggle",o.textContent=d==="light"?"☾":"☼",o.title="Toggle light/dark mode",o.addEventListener("click",()=>{const E=document.documentElement.getAttribute("data-theme")==="light"?"dark":"light";document.documentElement.setAttribute("data-theme",E),localStorage.setItem("backpack-theme",E),o.textContent=E==="light"?"☾":"☼"}),t.appendChild(o);const n=os();async function g(){if(!ce||!A)return;A.metadata.updatedAt=new Date().toISOString(),await Ot(ce,A),a.loadGraph(A),ee.setLearningGraphData(A),G.setData(A);const c=await vt();S.setSummaries(c),de.emit("graph-changed")}async function i(c){A=c,await Ot(ce,A),a.loadGraph(A),ee.setLearningGraphData(A),G.setData(A);const E=await vt();S.setSummaries(E)}let a;const M=Yo(t),T=Fo(t,M,{onUpdateNode(c,E){if(!A)return;n.push(A);const R=A.nodes.find(Y=>Y.id===c);R&&(R.properties={...R.properties,...E},R.updatedAt=new Date().toISOString(),g().then(()=>T.show([c],A)))},onChangeNodeType(c,E){if(!A)return;n.push(A);const R=A.nodes.find(Y=>Y.id===c);R&&(R.type=E,R.updatedAt=new Date().toISOString(),g().then(()=>T.show([c],A)))},onDeleteNode(c){A&&(n.push(A),A.nodes=A.nodes.filter(E=>E.id!==c),A.edges=A.edges.filter(E=>E.sourceId!==c&&E.targetId!==c),g())},onDeleteEdge(c){var R;if(!A)return;n.push(A);const E=(R=A.edges.find(Y=>Y.id===c))==null?void 0:R.sourceId;A.edges=A.edges.filter(Y=>Y.id!==c),g().then(()=>{E&&A&&T.show([E],A)})},onAddProperty(c,E,R){if(!A)return;n.push(A);const Y=A.nodes.find(ne=>ne.id===c);Y&&(Y.properties[E]=R,Y.updatedAt=new Date().toISOString(),g().then(()=>T.show([c],A)))}},c=>{a.panToNode(c)},c=>{G.addToFocusSet(c)}),x=window.matchMedia("(max-width: 768px)");let F=[],W=e.display.edges,V=e.navigation.panSpeed,z=-1,X=null;function H(c){X&&X.remove(),X=document.createElement("div"),X.className="focus-indicator";const E=document.createElement("span");E.className="focus-indicator-label",E.textContent=`Focused: ${c.totalNodes} nodes`;const R=document.createElement("span");R.className="focus-indicator-hops",R.textContent=`${c.hops}`;const Y=document.createElement("button");Y.className="focus-indicator-btn",Y.textContent="−",Y.title="Fewer hops",Y.disabled=c.hops===0,Y.addEventListener("click",()=>{a.enterFocus(c.seedNodeIds,Math.max(0,c.hops-1))});const ne=document.createElement("button");ne.className="focus-indicator-btn",ne.textContent="+",ne.title="More hops",ne.disabled=!1,ne.addEventListener("click",()=>{a.enterFocus(c.seedNodeIds,c.hops+1)});const ae=document.createElement("button");ae.className="focus-indicator-btn focus-indicator-exit",ae.textContent="×",ae.title="Exit focus (Esc)",ae.addEventListener("click",()=>G.clearFocusSet());const pe=document.createElement("button");pe.className="walk-indicator",a.getWalkMode()&&pe.classList.add("active"),pe.textContent="Walk",pe.title="Toggle walk mode (W) — click nodes to traverse",pe.addEventListener("click",()=>{a.setWalkMode(!a.getWalkMode()),pe.classList.toggle("active",a.getWalkMode())}),X.appendChild(E),X.appendChild(Y),X.appendChild(R),X.appendChild(ne),X.appendChild(pe),X.appendChild(ae)}function K(){X&&(X.remove(),X=null)}const q=document.createElement("div");q.className="path-bar hidden",t.appendChild(q);function ue(c){if(q.replaceChildren(),!A)return;for(let R=0;R<c.nodeIds.length;R++){const Y=c.nodeIds[R],ne=A.nodes.find(he=>he.id===Y);if(!ne)continue;const ae=Object.values(ne.properties).find(he=>typeof he=="string")??ne.id;if(R>0){const he=c.edgeIds[R-1],we=A.edges.find(De=>De.id===he),Le=document.createElement("span");Le.className="path-bar-edge",Le.textContent=we?`→ ${we.type} →`:"→",q.appendChild(Le)}const pe=document.createElement("span");pe.className="path-bar-node",pe.textContent=ae,pe.addEventListener("click",()=>a.panToNode(Y)),q.appendChild(pe)}const E=document.createElement("button");E.className="path-bar-close",E.textContent="×",E.addEventListener("click",U),q.appendChild(E),q.classList.remove("hidden")}function U(){q.classList.add("hidden"),q.replaceChildren(),a.clearHighlightedPath()}const de=rs();function xe(){ce&&ls({graph:ce,selection:F,focus:(a==null?void 0:a.getFocusInfo())??null})}a=Bo(t,c=>{if(F=c??[],xe(),de.emit("selection-changed"),!a.getWalkMode())if(c&&c.length===2){const E=a.findPath(c[0],c[1]);E&&E.nodeIds.length>0?(a.setHighlightedPath(E.nodeIds,E.edgeIds),ue(E)):U()}else U();c&&c.length>0&&A?(T.show(c,A),x.matches&&G.collapse(),le(ce,c)):(T.hide(),ce&&le(ce))},c=>{if(c){H(c);const E=t.querySelector(".canvas-top-left");E&&X&&E.appendChild(X),le(ce,c.seedNodeIds),T.setFocusDisabled(c.hops===0),N()}else K(),T.setFocusDisabled(!1),ce&&le(ce),N();xe(),de.emit("focus-changed")},{lod:e.lod,navigation:e.navigation,walk:e.walk});const ee=qo(t,{maxResults:e.limits.maxSearchResults,debounceMs:e.limits.searchDebounceMs}),G=Xo(t,{onFilterByType(c){if(A)if(c===null)a.setFilteredNodeIds(null);else{const E=new Set(((A==null?void 0:A.nodes)??[]).filter(R=>R.type===c).map(R=>R.id));a.setFilteredNodeIds(E)}},onNavigateToNode(c){a.panToNode(c),A&&T.show([c],A)},onWalkTrailRemove(c){a.removeFromWalkTrail(c),N()},onWalkIsolate(){if(!A)return;const c=a.getWalkTrail();c.length!==0&&a.enterFocus(c,0)},async onWalkSaveSnippet(c){if(!ce||!A)return;const E=a.getWalkTrail();if(E.length<2)return;const R=new Set(E),Y=A.edges.filter(ne=>R.has(ne.sourceId)&&R.has(ne.targetId)).map(ne=>ne.id);await yn(ce,c,E,Y),await B(ce)},async onStarredSaveSnippet(c,E){if(!ce||!A)return;const R=new Set(E),Y=A.edges.filter(ne=>R.has(ne.sourceId)&&R.has(ne.targetId)).map(ne=>ne.id);await yn(ce,c,E,Y),await B(ce)},onFocusChange(c){c&&c.length>0?a.enterFocus(c,0):a.isFocused()&&a.exitFocus()},onRenameNodeType(c,E){if(A){n.push(A);for(const R of A.nodes)R.type===c&&(R.type=E,R.updatedAt=new Date().toISOString());g()}},onRenameEdgeType(c,E){if(A){n.push(A);for(const R of A.edges)R.type===c&&(R.type=E);g()}},onToggleEdgeLabels(c){a.setEdgeLabels(c)},onToggleTypeHulls(c){a.setTypeHulls(c)},onToggleMinimap(c){a.setMinimap(c)},onLayoutChange(c,E){lt({[c]:E}),a.reheat()},onPanSpeedChange(c){V=c},onExport(c){const E=a.exportImage(c);if(!E)return;const R=document.createElement("a");R.download=`${ce||"graph"}.${c}`,R.href=E,R.click()},onSnapshot:async c=>{ce&&(await io(ce,c),await C(ce))},onRollback:async c=>{ce&&(await lo(ce,c),A=await Mt(ce),a.loadGraph(A),ee.setLearningGraphData(A),G.setData(A),await C(ce))},onOpen(){x.matches&&T.hide()}}),ye=document.createElement("div");ye.className="canvas-top-bar";const Ce=document.createElement("div");Ce.className="canvas-top-left";const se=document.createElement("div");se.className="canvas-top-center";const ge=document.createElement("div");ge.className="canvas-top-right";const be=t.querySelector(".tools-pane-toggle");be&&Ce.appendChild(be);const p=t.querySelector(".zoom-controls");p&&ge.appendChild(p),ge.appendChild(o);const y=document.createElement("div");y.className="ext-slot ext-slot-top-left",se.appendChild(y);const v=t.querySelector(".search-overlay");v&&se.appendChild(v);const m=document.createElement("div");m.className="ext-slot ext-slot-top-right",se.appendChild(m);const b=as(()=>({graphName:ce,data:A,selection:F,focus:a.getFocusInfo()}));se.appendChild(b);const L=document.createElement("div");L.className="ext-slot ext-slot-bottom-left",t.appendChild(L);const h=document.createElement("div");h.className="ext-slot ext-slot-bottom-right",t.appendChild(h),ye.appendChild(Ce),ye.appendChild(se),ye.appendChild(ge),t.appendChild(ye),ee.onFilterChange(c=>{a.setFilteredNodeIds(c)}),ee.onNodeSelect(c=>{a.isFocused()&&G.clearFocusSet(),a.panToNode(c),A&&T.show([c],A)});const S=go(document.getElementById("sidebar"),{onSelect:c=>ve(c),onRename:async(c,E)=>{await oo(c,E),ce===c&&(ce=E);const R=await vt();S.setSummaries(R),S.setActive(ce),ce===E&&(A=await Mt(E),a.loadGraph(A),ee.setLearningGraphData(A),G.setData(A))},onBranchSwitch:async(c,E)=>{await gn(c,E),await I(c),A=await Mt(c),a.loadGraph(A),ee.setLearningGraphData(A),G.setData(A),await C(c)},onBranchCreate:async(c,E)=>{await fn(c,E),await I(c)},onBranchDelete:async(c,E)=>{await ao(c,E),await I(c)},onSnippetLoad:async(c,E)=>{var Y;const R=await po(c,E);((Y=R==null?void 0:R.nodeIds)==null?void 0:Y.length)>0&&a.enterFocus(R.nodeIds,0)},onSnippetDelete:async(c,E)=>{await uo(c,E),await B(c)},onBackpackSwitch:async c=>{await fetch("/api/backpacks/switch",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:c})}),await w()},onBackpackRegister:async(c,E)=>{await fetch("/api/backpacks",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:c,activate:E})}),await w()}});async function w(){try{const E=await(await fetch("/api/backpacks")).json();S.setBackpacks(E)}catch{}try{const c=await vt();S.setSummaries(c),ce&&!c.some(E=>E.name===ce)&&(ce="",A=null,a.loadGraph({metadata:{name:"",description:"",createdAt:"",updatedAt:""},nodes:[],edges:[]}))}catch{}}function N(){const c=a.getWalkTrail();if(!A||c.length===0){G.setWalkTrail([]),U();return}const E=[],R=c.map((Y,ne)=>{const ae=A.nodes.find(he=>he.id===Y);let pe;if(ne>0){const he=c[ne-1],we=A.edges.find(Le=>Le.sourceId===he&&Le.targetId===Y||Le.targetId===he&&Le.sourceId===Y);pe=we==null?void 0:we.type,we&&E.push(we.id)}return{id:Y,label:ae?Object.values(ae.properties).find(he=>typeof he=="string")??ae.id:Y,type:(ae==null?void 0:ae.type)??"?",edgeType:pe}});G.setWalkTrail(R),c.length>=2?(a.setHighlightedPath(c,E),ue({nodeIds:c,edgeIds:E})):U()}async function I(c){const E=await so(c),R=E.find(Y=>Y.active);R&&S.setActiveBranch(c,R.name,E)}async function C(c){const E=await co(c);G.setSnapshots(E)}async function B(c){const E=await ro(c);S.setSnippets(c,E)}Ce.insertBefore(S.expandBtn,Ce.firstChild);const $=es(t,s),te=ts(t),ie=ss(t,{onStar(c){if(!A)return;const E=A.nodes.find(Y=>Y.id===c);if(!E)return;const R=E.properties._starred===!0;E.properties._starred=!R,Ot(ce,A),a.loadGraph(A)},onFocusNode(c){G.addToFocusSet([c])},onExploreInBranch(c){if(ce){const E=`explore-${c.slice(0,8)}`;fn(ce,E).then(()=>{gn(ce,E).then(()=>{a.enterFocus([c],1)})})}},onCopyId(c){navigator.clipboard.writeText(c)}});t.addEventListener("contextmenu",c=>{c.preventDefault();const E=t.querySelector("canvas");if(!E||!A)return;const R=E.getBoundingClientRect(),Y=c.clientX-R.left,ne=c.clientY-R.top,ae=a.nodeAtScreen(Y,ne);if(!ae)return;const pe=A.nodes.find(Le=>Le.id===ae.id);if(!pe)return;const he=Object.values(pe.properties).find(Le=>typeof Le=="string")??pe.id,we=pe.properties._starred===!0;ie.show(pe.id,he,we,c.clientX-R.left,c.clientY-R.top)}),e.display.edges||a.setEdges(!1),e.display.edgeLabels||a.setEdgeLabels(!1),e.display.typeHulls||a.setTypeHulls(!1),e.display.minimap||a.setMinimap(!1);function le(c,E){const R=[];E!=null&&E.length&&R.push("node="+E.map(encodeURIComponent).join(","));const Y=a.getFocusInfo();Y&&(R.push("focus="+Y.seedNodeIds.map(encodeURIComponent).join(",")),R.push("hops="+Y.hops));const ne="#"+encodeURIComponent(c)+(R.length?"?"+R.join("&"):"");history.replaceState(null,"",ne)}function me(){const c=window.location.hash.slice(1);if(!c)return{graph:null,nodes:[],focus:[],hops:1};const[E,R]=c.split("?"),Y=E?decodeURIComponent(E):null;let ne=[],ae=[],pe=1;if(R){const he=new URLSearchParams(R),we=he.get("node");we&&(ne=we.split(",").map(decodeURIComponent));const Le=he.get("focus");Le&&(ae=Le.split(",").map(decodeURIComponent));const De=he.get("hops");De&&(pe=Math.max(0,parseInt(De,10)||1))}return{graph:Y,nodes:ne,focus:ae,hops:pe}}async function ve(c,E,R,Y){ce=c,Xt=qt.has(c),S.setActive(c),T.hide(),K(),ee.clear(),n.clear(),A=Xt?await no(c):await Mt(c);const ne=xo(A.nodes.length);if(lt({spacing:Math.max(e.layout.spacing,ne.spacing),clusterStrength:Math.max(e.layout.clustering,ne.clusterStrength)}),a.loadGraph(A),ee.setLearningGraphData(A),G.setData(A),te.hide(),le(c),xe(),de.emit("graph-switched"),de.emit("graph-changed"),Xt||(await I(c),await C(c),await B(c)),R!=null&&R.length&&A){const ae=R.filter(pe=>A.nodes.some(he=>he.id===pe));if(ae.length){setTimeout(()=>{a.enterFocus(ae,Y??1)},500);return}}if(E!=null&&E.length&&A){const ae=E.filter(pe=>A.nodes.some(he=>he.id===pe));ae.length&&setTimeout(()=>{a.panToNodes(ae),A&&T.show(ae,A),le(c,ae)},500)}}try{const E=await(await fetch("/api/backpacks")).json();S.setBackpacks(E)}catch{}fetch("/api/version-check").then(c=>c.json()).then(c=>{c.stale&&c.latest&&S.setStaleVersionBanner(c.current,c.latest)}).catch(()=>{});const qe=new URLSearchParams(window.location.search).get("share");if(qe)try{const c=await fetch(`/v1/share/${qe}/meta`);if(!c.ok)throw new Error("Share link not found or expired");const E=await c.json(),R=await fetch(`/v1/share/${qe}`);if(!R.ok)throw new Error("Failed to download shared backpack");const Y=new Uint8Array(await R.arrayBuffer());if(Y.length<9||Y[0]!==66||Y[1]!==80||Y[2]!==65||Y[3]!==75)throw new Error("Invalid share data: not a BPAK envelope");const ne=new DataView(Y.buffer,Y.byteOffset,Y.byteLength).getUint32(5,!1);if(9+ne>Y.length)throw new Error("Invalid envelope: header length exceeds data");const ae=JSON.parse(new TextDecoder().decode(Y.slice(9,9+ne))),pe=Y.slice(9+ne);let he;if(ae.format!=="plaintext"){const we=window.location.hash.slice(1),Le=new URLSearchParams(we).get("k")??we.split("k=")[1];if(!Le)throw new Error("Missing decryption key in URL fragment");const{Decrypter:De}=await eo(async()=>{const{Decrypter:Oe}=await import("./index-D-H7agBH.js");return{Decrypter:Oe}},[]),tt=atob(Le.replace(/-/g,"+").replace(/_/g,"/")),ht=new De;ht.addIdentity(tt);const kt=await ht.decrypt(pe);he=JSON.parse(new TextDecoder().decode(kt))}else he=JSON.parse(new TextDecoder().decode(pe));ce=E.backpack_name||"Shared Backpack",A=he,a.loadGraph(he),ee.setLearningGraphData(he),S.setSummaries([{name:ce,description:"",nodeCount:((ut=he.nodes)==null?void 0:ut.length)??0,edgeCount:((mt=he.edges)==null?void 0:mt.length)??0,nodeTypes:[]}]),S.setActive(ce),document.title=`${ce} — Backpack`}catch(c){const E=c instanceof Error?c.message:"Failed to load shared backpack";wt(E,5e3),te.show()}else{const[c,E]=await Promise.all([vt(),to().catch(()=>[])]);S.setSummaries(c),S.setRemotes(E),qt=new Set(E.map(ne=>ne.name));const R=me(),Y=R.graph&&c.some(ne=>ne.name===R.graph)||R.graph&&qt.has(R.graph)?R.graph:c.length>0?c[0].name:E.length>0?E[0].name:null;Y?await ve(Y,R.nodes.length?R.nodes:void 0,R.focus.length?R.focus:void 0,R.hops):te.show()}ms({getGraph:()=>A,getGraphName:()=>ce,getSelection:()=>[...F],getFocus:()=>a.getFocusInfo(),saveCurrentGraph:async()=>{await g()},snapshotForUndo:()=>{A&&n.push(A)},panToNode:c=>a.panToNode(c),focusNodes:(c,E)=>a.enterFocus(c,E),exitFocus:()=>{a.isFocused()&&a.exitFocus()},taskbarSlots:{topLeft:y,topRight:m,bottomLeft:L,bottomRight:h},subscribe:(c,E)=>de.subscribe(c,E)},M).catch(c=>{console.error("[backpack-viewer] extension loader failed:",c)});const Je={search(){ee.focus()},searchAlt(){ee.focus()},undo(){if(A){const c=n.undo(A);c&&i(c)}},redo(){if(A){const c=n.redo(A);c&&i(c)}},focus(){a.isFocused()?G.clearFocusSet():F.length>0&&G.addToFocusSet(F)},hopsDecrease(){const c=a.getFocusInfo();c&&c.hops>0&&a.enterFocus(c.seedNodeIds,c.hops-1)},hopsIncrease(){const c=a.getFocusInfo();c&&a.enterFocus(c.seedNodeIds,c.hops+1)},nextNode(){const c=a.getNodeIds();c.length>0&&(z=(z+1)%c.length,a.panToNode(c[z]),A&&T.show([c[z]],A))},prevNode(){const c=a.getNodeIds();c.length>0&&(z=z<=0?c.length-1:z-1,a.panToNode(c[z]),A&&T.show([c[z]],A))},nextConnection(){const c=T.cycleConnection(1);c&&a.panToNode(c)},prevConnection(){const c=T.cycleConnection(-1);c&&a.panToNode(c)},historyBack(){T.goBack()},historyForward(){T.goForward()},center(){a.centerView()},toggleEdges(){W=!W,a.setEdges(W)},panLeft(){a.panBy(-V,0)},panDown(){a.panBy(0,V)},panUp(){a.panBy(0,-V)},panRight(){a.panBy(V,0)},panFastLeft(){a.panBy(-V*e.navigation.panFastMultiplier,0)},zoomOut(){a.zoomBy(1/e.navigation.zoomFactor)},zoomIn(){a.zoomBy(e.navigation.zoomFactor)},panFastRight(){a.panBy(V*e.navigation.panFastMultiplier,0)},spacingDecrease(){const c=Et();lt({spacing:Math.max(.5,c.spacing-.5)}),a.reheat()},spacingIncrease(){const c=Et();lt({spacing:Math.min(20,c.spacing+.5)}),a.reheat()},clusteringDecrease(){const c=Et();lt({clusterStrength:Math.max(0,c.clusterStrength-.03)}),a.reheat()},clusteringIncrease(){const c=Et();lt({clusterStrength:Math.min(1,c.clusterStrength+.03)}),a.reheat()},help(){$.toggle()},toggleSidebar(){S.toggle()},resetPins(){a.releaseAllPins()&&wt("Manual layout reset — pins released")},walkIsolate(){if(!A)return;const c=a.getWalkTrail();c.length!==0&&a.enterFocus(c,0)},walkMode(){!a.isFocused()&&F.length>0&&G.addToFocusSet(F),a.setWalkMode(!a.getWalkMode());const c=t.querySelector(".walk-indicator");c&&c.classList.toggle("active",a.getWalkMode()),N()},escape(){a.getSelectedNodeIds().length>0?a.clearSelection():a.isFocused()?G.clearFocusSet():$.hide()}};document.addEventListener("keydown",c=>{var E;if(!(c.target instanceof HTMLInputElement||c.target instanceof HTMLTextAreaElement)){for(const[R,Y]of Object.entries(s))if(Vo(c,Y)){(R==="search"||R==="searchAlt"||R==="undo"||R==="redo"||R==="toggleSidebar")&&c.preventDefault(),(E=Je[R])==null||E.call(Je);return}}}),window.addEventListener("hashchange",()=>{const c=me();if(c.graph&&c.graph!==ce)ve(c.graph,c.nodes.length?c.nodes:void 0,c.focus.length?c.focus:void 0,c.hops);else if(c.graph&&c.focus.length&&A)a.enterFocus(c.focus,c.hops);else if(c.graph&&c.nodes.length&&A){a.isFocused()&&a.exitFocus();const E=c.nodes.filter(R=>A.nodes.some(Y=>Y.id===R));E.length&&(a.panToNodes(E),T.show(E,A))}})}Ss();