backpack-viewer 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/serve.js CHANGED
@@ -14,11 +14,30 @@ const hasDistBuild = fs.existsSync(path.join(distDir, "index.html"));
14
14
 
15
15
  if (hasDistBuild) {
16
16
  // --- Production: static file server + API (zero native deps) ---
17
- const { JsonFileBackend, dataDir, RemoteRegistry } = await import("backpack-ontology");
17
+ const {
18
+ JsonFileBackend,
19
+ dataDir,
20
+ RemoteRegistry,
21
+ listBackpacks,
22
+ getActiveBackpack,
23
+ setActiveBackpack,
24
+ registerBackpack,
25
+ unregisterBackpack,
26
+ } = await import("backpack-ontology");
18
27
  const { loadViewerConfig } = await import("../dist/config.js");
19
28
 
20
- const storage = new JsonFileBackend();
21
- await storage.initialize();
29
+ // Storage points at the active backpack. Wrapped in a mutable
30
+ // holder so a `/api/backpacks/switch` POST can swap it out in place
31
+ // without restarting the whole server.
32
+ async function makeBackend() {
33
+ const entry = await getActiveBackpack();
34
+ const backend = new JsonFileBackend(undefined, {
35
+ graphsDirOverride: entry.path,
36
+ });
37
+ await backend.initialize();
38
+ return { backend, entry };
39
+ }
40
+ let { backend: storage, entry: activeEntry } = await makeBackend();
22
41
  const remoteRegistry = new RemoteRegistry();
23
42
  await remoteRegistry.initialize();
24
43
  const viewerConfig = loadViewerConfig();
@@ -327,6 +346,100 @@ if (hasDistBuild) {
327
346
  return;
328
347
  }
329
348
 
349
+ // --- Backpacks (meta: list, active, switch) ---
350
+ if (url === "/api/backpacks" && req.method === "GET") {
351
+ try {
352
+ const list = await listBackpacks();
353
+ const active = await getActiveBackpack();
354
+ res.writeHead(200, { "Content-Type": "application/json" });
355
+ res.end(
356
+ JSON.stringify(
357
+ list.map((b) => ({ ...b, active: b.name === active.name })),
358
+ ),
359
+ );
360
+ } catch (err) {
361
+ res.writeHead(500, { "Content-Type": "application/json" });
362
+ res.end(JSON.stringify({ error: err.message }));
363
+ }
364
+ return;
365
+ }
366
+
367
+ if (url === "/api/backpacks/active" && req.method === "GET") {
368
+ try {
369
+ const active = await getActiveBackpack();
370
+ res.writeHead(200, { "Content-Type": "application/json" });
371
+ res.end(JSON.stringify(active));
372
+ } catch (err) {
373
+ res.writeHead(500, { "Content-Type": "application/json" });
374
+ res.end(JSON.stringify({ error: err.message }));
375
+ }
376
+ return;
377
+ }
378
+
379
+ if (url === "/api/backpacks/switch" && req.method === "POST") {
380
+ let body = "";
381
+ req.on("data", (chunk) => { body += chunk.toString(); });
382
+ req.on("end", async () => {
383
+ try {
384
+ const { name } = JSON.parse(body);
385
+ await setActiveBackpack(name);
386
+ const swapped = await makeBackend();
387
+ storage = swapped.backend;
388
+ activeEntry = swapped.entry;
389
+ res.writeHead(200, { "Content-Type": "application/json" });
390
+ res.end(JSON.stringify({ ok: true, active: activeEntry }));
391
+ } catch (err) {
392
+ res.writeHead(400, { "Content-Type": "application/json" });
393
+ res.end(JSON.stringify({ error: err.message }));
394
+ }
395
+ });
396
+ return;
397
+ }
398
+
399
+ if (url === "/api/backpacks" && req.method === "POST") {
400
+ let body = "";
401
+ req.on("data", (chunk) => { body += chunk.toString(); });
402
+ req.on("end", async () => {
403
+ try {
404
+ const { name, path: p, activate } = JSON.parse(body);
405
+ const entry = await registerBackpack(name, p);
406
+ if (activate) {
407
+ await setActiveBackpack(name);
408
+ const swapped = await makeBackend();
409
+ storage = swapped.backend;
410
+ activeEntry = swapped.entry;
411
+ }
412
+ res.writeHead(200, { "Content-Type": "application/json" });
413
+ res.end(JSON.stringify({ ok: true, entry }));
414
+ } catch (err) {
415
+ res.writeHead(400, { "Content-Type": "application/json" });
416
+ res.end(JSON.stringify({ error: err.message }));
417
+ }
418
+ });
419
+ return;
420
+ }
421
+
422
+ const backpackDeleteMatch = url.match(/^\/api\/backpacks\/(.+)$/);
423
+ if (backpackDeleteMatch && req.method === "DELETE") {
424
+ const name = decodeURIComponent(backpackDeleteMatch[1]);
425
+ try {
426
+ await unregisterBackpack(name);
427
+ // If we just removed the active one, the registry switched us;
428
+ // rebuild the backend to match.
429
+ if (activeEntry && activeEntry.name === name) {
430
+ const swapped = await makeBackend();
431
+ storage = swapped.backend;
432
+ activeEntry = swapped.entry;
433
+ }
434
+ res.writeHead(200, { "Content-Type": "application/json" });
435
+ res.end(JSON.stringify({ ok: true }));
436
+ } catch (err) {
437
+ res.writeHead(400, { "Content-Type": "application/json" });
438
+ res.end(JSON.stringify({ error: err.message }));
439
+ }
440
+ return;
441
+ }
442
+
330
443
  // --- Lock heartbeat ---
331
444
  if (url === "/api/locks" && req.method === "GET") {
332
445
  // Batch endpoint: returns { graphName: lockInfo|null } for all graphs.
@@ -0,0 +1,34 @@
1
+ var En=Object.defineProperty;var wn=(t,e,s)=>e in t?En(t,e,{enumerable:!0,configurable:!0,writable:!0,value:s}):t[e]=s;var ht=(t,e,s)=>wn(t,typeof e!="symbol"?e+"":e,s);(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const l of document.querySelectorAll('link[rel="modulepreload"]'))r(l);new MutationObserver(l=>{for(const y of l)if(y.type==="childList")for(const k of y.addedNodes)k.tagName==="LINK"&&k.rel==="modulepreload"&&r(k)}).observe(document,{childList:!0,subtree:!0});function s(l){const y={};return l.integrity&&(y.integrity=l.integrity),l.referrerPolicy&&(y.referrerPolicy=l.referrerPolicy),l.crossOrigin==="use-credentials"?y.credentials="include":l.crossOrigin==="anonymous"?y.credentials="omit":y.credentials="same-origin",y}function r(l){if(l.ep)return;l.ep=!0;const y=s(l);fetch(l.href,y)}})();async function ct(){const t=await fetch("/api/ontologies");return t.ok?t.json():[]}async function ft(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 kn(){const t=await fetch("/api/remotes");return t.ok?t.json():[]}async function Sn(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 wt(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 Nn(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 Ln(t){const e=await fetch(`/api/graphs/${encodeURIComponent(t)}/branches`);return e.ok?e.json():[]}async function Yt(t,e,s){const r=await fetch(`/api/graphs/${encodeURIComponent(t)}/branches`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e,from:s})});if(!r.ok){const l=await r.json().catch(()=>({}));throw new Error(l.error||"Failed to create branch")}}async function Xt(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 r=await s.json().catch(()=>({}));throw new Error(r.error||"Failed to switch branch")}}async function In(t,e){const s=await fetch(`/api/graphs/${encodeURIComponent(t)}/branches/${encodeURIComponent(e)}`,{method:"DELETE"});if(!s.ok){const r=await s.json().catch(()=>({}));throw new Error(r.error||"Failed to delete branch")}}async function Mn(t){const e=await fetch(`/api/graphs/${encodeURIComponent(t)}/snapshots`);return e.ok?e.json():[]}async function Tn(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 r=await s.json().catch(()=>({}));throw new Error(r.error||"Failed to create snapshot")}}async function An(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 r=await s.json().catch(()=>({}));throw new Error(r.error||"Failed to rollback")}}async function Bn(t){const e=await fetch(`/api/graphs/${encodeURIComponent(t)}/snippets`);return e.ok?e.json():[]}async function _t(t,e,s,r,l){const y=await fetch(`/api/graphs/${encodeURIComponent(t)}/snippets`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({label:e,description:l,nodeIds:s,edgeIds:r})});if(!y.ok)throw new Error("Failed to save snippet");return(await y.json()).id}async function Rn(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 Pn(t,e){if(!(await fetch(`/api/graphs/${encodeURIComponent(t)}/snippets/${encodeURIComponent(e)}`,{method:"DELETE"})).ok)throw new Error("Failed to delete snippet")}const Gt="bp-dialog-overlay";function un(){var e;(e=document.querySelector(`.${Gt}`))==null||e.remove();const t=document.createElement("div");return t.className=Gt,document.body.appendChild(t),t}function mn(t,e){const s=document.createElement("div");s.className="bp-dialog";const r=document.createElement("h4");return r.className="bp-dialog-title",r.textContent=e,s.appendChild(r),t.appendChild(s),t.addEventListener("click",l=>{l.target===t&&t.remove()}),s}function hn(t,e){const s=document.createElement("div");s.className="bp-dialog-buttons";for(const r of e){const l=document.createElement("button");l.className="bp-dialog-btn",r.accent&&l.classList.add("bp-dialog-btn-accent"),r.danger&&l.classList.add("bp-dialog-btn-danger"),l.textContent=r.label,l.addEventListener("click",r.onClick),s.appendChild(l)}t.appendChild(s)}function Tt(t,e){return new Promise(s=>{var k;const r=un(),l=mn(r,t),y=document.createElement("p");y.className="bp-dialog-message",y.textContent=e,l.appendChild(y),hn(l,[{label:"Cancel",onClick:()=>{r.remove(),s(!1)}},{label:"Confirm",accent:!0,onClick:()=>{r.remove(),s(!0)}}]),(k=l.querySelector(".bp-dialog-btn-accent"))==null||k.focus()})}function tt(t,e,s){return new Promise(r=>{const l=un(),y=mn(l,t),k=document.createElement("input");k.type="text",k.className="bp-dialog-input",k.placeholder=e??"",k.value=s??"",y.appendChild(k);const c=()=>{const o=k.value.trim();l.remove(),r(o||null)};k.addEventListener("keydown",o=>{o.key==="Enter"&&c(),o.key==="Escape"&&(l.remove(),r(null))}),hn(y,[{label:"Cancel",onClick:()=>{l.remove(),r(null)}},{label:"OK",accent:!0,onClick:c}]),k.focus(),k.select()})}function Vt(t){return t>=1e3?`${(t/1e3).toFixed(1)}k tokens`:`${t} tokens`}function Jt(t,e){return t*50+e*25+50}function Fn(t,e){const s=typeof e=="function"?{onSelect:e}:e,r=document.createElement("h2");r.textContent="Backpack Viewer";const l=document.createElement("input");l.type="text",l.placeholder="Filter...",l.id="filter";const y=document.createElement("ul");y.id="ontology-list";const k=document.createElement("h3");k.className="sidebar-section-heading",k.textContent="REMOTE GRAPHS",k.hidden=!0;const c=document.createElement("ul");c.id="remote-list",c.className="remote-list",c.hidden=!0;const o=document.createElement("div");o.className="sidebar-footer",o.innerHTML='<a href="mailto:support@backpackontology.com">support@backpackontology.com</a><span>Feedback & support</span><span class="sidebar-version">v0.4.0</span>';const P=document.createElement("button");P.className="sidebar-collapse-btn",P.title="Toggle sidebar (Tab)",P.innerHTML='<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="11 17 6 12 11 7"/><polyline points="18 17 13 12 18 7"/></svg>';let i=!1;function a(){i=!i,t.classList.toggle("sidebar-collapsed",i),J.classList.toggle("hidden",!i)}P.addEventListener("click",a);const T=document.createElement("div");T.className="sidebar-heading-row",T.appendChild(r),T.appendChild(P),t.appendChild(T);const F=document.createElement("button");F.className="backpack-picker-pill",F.type="button",F.setAttribute("aria-haspopup","listbox"),F.setAttribute("aria-expanded","false");const b=document.createElement("span");b.className="backpack-picker-dot";const U=document.createElement("span");U.className="backpack-picker-name",U.textContent="...";const X=document.createElement("span");X.className="backpack-picker-caret",X.textContent="▾",F.appendChild(b),F.appendChild(U),F.appendChild(X);const te=document.createElement("div");te.className="backpack-picker-dropdown",te.hidden=!0,te.setAttribute("role","listbox");const z=document.createElement("div");z.className="backpack-picker-container",z.appendChild(F),z.appendChild(te),t.appendChild(z);let ce=!1;function V(){ce=!1,te.hidden=!0,F.setAttribute("aria-expanded","false")}function B(){ce=!0,te.hidden=!1,F.setAttribute("aria-expanded","true")}F.addEventListener("click",_=>{_.stopPropagation(),ce?V():B()}),document.addEventListener("click",_=>{z.contains(_.target)||V()});let Q=[],K=null;function ae(){te.replaceChildren();for(const D of Q){const E=document.createElement("button");E.className="backpack-picker-item",E.type="button",E.setAttribute("role","option"),D.active&&E.classList.add("active");const d=document.createElement("span");d.className="backpack-picker-item-dot",d.style.setProperty("--backpack-color",D.color);const m=document.createElement("span");m.className="backpack-picker-item-name",m.textContent=D.name;const g=document.createElement("span");g.className="backpack-picker-item-path",g.textContent=D.path,E.appendChild(d),E.appendChild(m),E.appendChild(g),E.addEventListener("click",C=>{C.stopPropagation(),V(),!D.active&&s.onBackpackSwitch&&s.onBackpackSwitch(D.name)}),te.appendChild(E)}const _=document.createElement("div");_.className="backpack-picker-divider",te.appendChild(_);const W=document.createElement("button");W.className="backpack-picker-item backpack-picker-add",W.type="button",W.textContent="+ Add new backpack…",W.addEventListener("click",async D=>{if(D.stopPropagation(),V(),!s.onBackpackRegister)return;const E=await tt("Backpack name","Short kebab-case name (e.g. work, family, project-alpha)","");if(!E)return;const d=await tt("Backpack path","Absolute or tilde-expanded path to the graphs directory","");if(!d)return;const m=await Tt("Switch to new backpack?",`Make "${E}" the active backpack immediately?`);s.onBackpackRegister(E,d,m)}),te.appendChild(W)}const J=document.createElement("button");J.className="tools-pane-toggle hidden",J.title="Show sidebar (Tab)",J.innerHTML='<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="13 7 18 12 13 17"/><polyline points="6 7 11 12 6 17"/></svg>',J.addEventListener("click",a),t.appendChild(l),t.appendChild(y),t.appendChild(k),t.appendChild(c),t.appendChild(o);let ie=[],ee=[],ye="";return l.addEventListener("input",()=>{const _=l.value.toLowerCase();for(const W of ie){const D=W.dataset.name??"";W.style.display=D.includes(_)?"":"none"}for(const W of ee){const D=W.dataset.name??"";W.style.display=D.includes(_)?"":"none"}}),{setBackpacks(_){Q=_.slice();const W=_.find(D=>D.active)??null;K=W,W&&(U.textContent=W.name,b.style.setProperty("--backpack-color",W.color),t.style.setProperty("--backpack-color",W.color)),ae()},setActiveBackpack(_){K=_,Q=Q.map(W=>({...W,active:W.name===_.name})),Q.some(W=>W.name===_.name)||Q.push({..._,active:!0}),U.textContent=_.name,b.style.setProperty("--backpack-color",_.color),t.style.setProperty("--backpack-color",_.color),ae()},getActiveBackpack(){return K},setSummaries(_){y.innerHTML="";const W=fetch("/api/locks").then(D=>D.json()).catch(()=>({}));ie=_.map(D=>{const E=document.createElement("li");E.className="ontology-item",E.dataset.name=D.name;const d=document.createElement("span");d.className="name",d.textContent=D.name;const m=document.createElement("span");m.className="stats";const g=Jt(D.nodeCount,D.edgeCount);m.textContent=`${D.nodeCount} nodes, ${D.edgeCount} edges · ~${Vt(g)}`;const C=document.createElement("span");C.className="sidebar-branch",C.dataset.graph=D.name;const S=document.createElement("span");if(S.className="sidebar-lock-badge",S.dataset.graph=D.name,W.then(u=>{if(!S.isConnected)return;const x=u[D.name];x&&typeof x=="object"&&x.author&&(S.textContent=`editing: ${x.author}`,S.title=`Last activity: ${x.lastActivity??""}`,S.classList.add("active"))}),E.appendChild(d),E.appendChild(m),E.appendChild(S),E.appendChild(C),s.onRename){const u=document.createElement("button");u.className="sidebar-edit-btn",u.textContent="✎",u.title="Rename";const x=s.onRename;u.addEventListener("click",R=>{R.stopPropagation();const I=document.createElement("input");I.type="text",I.className="sidebar-rename-input",I.value=D.name,d.textContent="",d.appendChild(I),u.style.display="none",I.focus(),I.select();const O=()=>{const w=I.value.trim();w&&w!==D.name?x(D.name,w):(d.textContent=D.name,u.style.display="")};I.addEventListener("blur",O),I.addEventListener("keydown",w=>{w.key==="Enter"&&I.blur(),w.key==="Escape"&&(I.value=D.name,I.blur())})}),E.appendChild(u)}return E.addEventListener("click",()=>s.onSelect(D.name)),y.appendChild(E),E}),ye&&this.setActive(ye)},setActive(_){ye=_;for(const W of ie)W.classList.toggle("active",W.dataset.name===_);for(const W of ee)W.classList.toggle("active",W.dataset.name===_)},setRemotes(_){c.replaceChildren(),ee=_.map(D=>{const E=document.createElement("li");E.className="ontology-item ontology-item-remote",E.dataset.name=D.name;const d=document.createElement("div");d.className="remote-name-row";const m=document.createElement("span");m.className="name",m.textContent=D.name;const g=document.createElement("span");g.className="remote-badge",g.textContent=D.pinned?"remote · pinned":"remote",g.title=`Source: ${D.source??D.url}`,d.appendChild(m),d.appendChild(g);const C=document.createElement("span");C.className="stats";const S=Jt(D.nodeCount,D.edgeCount);C.textContent=`${D.nodeCount} nodes, ${D.edgeCount} edges · ~${Vt(S)}`;const u=document.createElement("span");return u.className="remote-source",u.textContent=D.source??new URL(D.url).hostname,u.title=D.url,E.appendChild(d),E.appendChild(C),E.appendChild(u),E.addEventListener("click",()=>s.onSelect(D.name)),c.appendChild(E),E});const W=_.length>0;k.hidden=!W,c.hidden=!W,ye&&this.setActive(ye)},setActiveBranch(_,W,D){const E=y.querySelectorAll(`.sidebar-branch[data-graph="${_}"]`);for(const d of E){d.textContent=`/ ${W}`,d.title="Click to switch branch",d.style.cursor="pointer";const m=d.cloneNode(!0);d.replaceWith(m),m.addEventListener("click",g=>{g.stopPropagation(),ke(_,m,D??[])})}},setSnippets(_,W){var d;const D=ie.find(m=>m.dataset.name===_);if(!D||((d=D.querySelector(".sidebar-snippets"))==null||d.remove(),W.length===0))return;const E=document.createElement("div");E.className="sidebar-snippets";for(const m of W){const g=document.createElement("div");g.className="sidebar-snippet";const C=document.createElement("span");C.className="sidebar-snippet-label",C.textContent=`◆ ${m.label}`,C.title=`${m.nodeCount} nodes — click to load`;const S=document.createElement("button");S.className="sidebar-snippet-delete",S.textContent="×",S.title="Delete snippet",S.addEventListener("click",u=>{var x;u.stopPropagation(),(x=s.onSnippetDelete)==null||x.call(s,_,m.id)}),g.appendChild(C),g.appendChild(S),g.addEventListener("click",u=>{var x;u.stopPropagation(),(x=s.onSnippetLoad)==null||x.call(s,_,m.id)}),E.appendChild(g)}D.appendChild(E)},toggle:a,expandBtn:J};function ke(_,W,D){const E=t.querySelector(".branch-picker");E&&E.remove();const d=document.createElement("div");d.className="branch-picker";for(const g of D){const C=document.createElement("div");C.className="branch-picker-item",g.active&&C.classList.add("branch-picker-active");const S=document.createElement("span");if(S.textContent=g.name,C.appendChild(S),!g.active&&s.onBranchDelete){const u=document.createElement("button");u.className="branch-picker-delete",u.textContent="×",u.title=`Delete ${g.name}`,u.addEventListener("click",x=>{x.stopPropagation(),Tt("Delete branch",`Delete branch "${g.name}"?`).then(R=>{R&&(s.onBranchDelete(_,g.name),d.remove())})}),C.appendChild(u)}g.active||C.addEventListener("click",()=>{var u;(u=s.onBranchSwitch)==null||u.call(s,_,g.name),d.remove()}),d.appendChild(C)}if(s.onBranchCreate){const g=document.createElement("div");g.className="branch-picker-item branch-picker-create",g.textContent="+ New branch",g.addEventListener("click",()=>{tt("New branch","Branch name").then(C=>{C&&(s.onBranchCreate(_,C),d.remove())})}),d.appendChild(g)}W.after(d);const m=g=>{d.contains(g.target)||(d.remove(),document.removeEventListener("click",m))};setTimeout(()=>document.addEventListener("click",m),0)}}function At(t,e,s,r){return{x0:t,y0:e,x1:s,y1:r,cx:0,cy:0,mass:0,children:[null,null,null,null],body:null}}function Kt(t,e,s){const r=(t.x0+t.x1)/2,l=(t.y0+t.y1)/2;return(e<r?0:1)+(s<l?0:2)}function Zt(t,e){const s=(t.x0+t.x1)/2,r=(t.y0+t.y1)/2;switch(e){case 0:return[t.x0,t.y0,s,r];case 1:return[s,t.y0,t.x1,r];case 2:return[t.x0,r,s,t.y1];default:return[s,r,t.x1,t.y1]}}function Bt(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 l=t.body;t.body=null,l.x===e.x&&l.y===e.y&&(e.x+=(Math.random()-.5)*.1,e.y+=(Math.random()-.5)*.1);const y=Kt(t,l.x,l.y);if(t.children[y]===null){const[k,c,o,P]=Zt(t,y);t.children[y]=At(k,c,o,P)}Bt(t.children[y],l)}const s=Kt(t,e.x,e.y);if(t.children[s]===null){const[l,y,k,c]=Zt(t,s);t.children[s]=At(l,y,k,c)}Bt(t.children[s],e);const r=t.mass+1;t.cx=(t.cx*t.mass+e.x)/r,t.cy=(t.cy*t.mass+e.y)/r,t.mass=r}function $n(t){if(t.length===0)return null;let e=1/0,s=1/0,r=-1/0,l=-1/0;for(const i of t)i.x<e&&(e=i.x),i.y<s&&(s=i.y),i.x>r&&(r=i.x),i.y>l&&(l=i.y);const y=Math.max(r-e,l-s)*.1+50,k=(e+r)/2,c=(s+l)/2,o=Math.max(r-e,l-s)/2+y,P=At(k-o,c-o,k+o,c+o);for(const i of t)Bt(P,i);return P}function Dn(t,e,s,r,l,y){fn(t,e,s,r,l,y)}function fn(t,e,s,r,l,y){if(t.mass===0)return;const k=t.cx-e.x,c=t.cy-e.y,o=k*k+c*c;if(t.body!==null){if(t.body!==e){let i=Math.sqrt(o);i<y&&(i=y);const a=r*l/(i*i),T=k/i*a,F=c/i*a;e.vx-=T,e.vy-=F}return}const P=t.x1-t.x0;if(P*P/o<s*s){let i=Math.sqrt(o);i<y&&(i=y);const a=r*t.mass*l/(i*i),T=k/i*a,F=c/i*a;e.vx-=T,e.vy-=F;return}for(let i=0;i<4;i++)t.children[i]!==null&&fn(t.children[i],e,s,r,l,y)}const gn={clusterStrength:.08,spacing:1.5},Qt=6e3,On=12e3,Wn=.004,yn=140,Cn=350,en=.9,tn=.01,it=30,kt=50;let Xe={...gn};function Qe(t){t.clusterStrength!==void 0&&(Xe.clusterStrength=t.clusterStrength),t.spacing!==void 0&&(Xe.spacing=t.spacing)}function dt(){return{...Xe}}function Hn(t){if(t<=30)return{...gn};const e=Math.log2(t/30);return{clusterStrength:Math.min(.5,.08+.06*e),spacing:Math.min(15,1.5+1.2*e)}}function zn(t,e){for(const s of Object.values(t))if(typeof s=="string")return s;return e}function nn(t,e,s){const r=new Set(e);let l=new Set(e);for(let y=0;y<s;y++){const k=new Set;for(const c of t.edges)l.has(c.sourceId)&&!r.has(c.targetId)&&k.add(c.targetId),l.has(c.targetId)&&!r.has(c.sourceId)&&k.add(c.sourceId);for(const c of k)r.add(c);if(l=k,k.size===0)break}return{nodes:t.nodes.filter(y=>r.has(y.id)),edges:t.edges.filter(y=>r.has(y.sourceId)&&r.has(y.targetId)),metadata:t.metadata}}function St(t){const e=new Map,s=[...new Set(t.nodes.map(o=>o.type))],r=Math.sqrt(s.length)*Cn*.6*Math.max(1,Xe.spacing),l=new Map,y=new Map;for(const o of t.nodes)y.set(o.type,(y.get(o.type)??0)+1);const k=t.nodes.map(o=>{const P=s.indexOf(o.type),i=2*Math.PI*P/Math.max(s.length,1),a=Math.cos(i)*r,T=Math.sin(i)*r,F=l.get(o.type)??0;l.set(o.type,F+1);const b=y.get(o.type)??1,U=2*Math.PI*F/b,X=yn*.6,te={id:o.id,x:a+Math.cos(U)*X,y:T+Math.sin(U)*X,vx:0,vy:0,label:zn(o.properties,o.id),type:o.type};return e.set(o.id,te),te}),c=t.edges.map(o=>({sourceId:o.sourceId,targetId:o.targetId,type:o.type}));return{nodes:k,edges:c,nodeMap:e}}const jn=.7,Un=80;function qn(t,e){const{nodes:s,edges:r,nodeMap:l}=t,y=On*Xe.spacing;if(s.length>=Un){const c=$n(s);if(c)for(const P of s)Dn(c,P,jn,y,e,it);const o=y-Qt;if(o>0){const P=new Map;for(const i of s){let a=P.get(i.type);a||(a=[],P.set(i.type,a)),a.push(i)}for(const i of P.values())for(let a=0;a<i.length;a++)for(let T=a+1;T<i.length;T++){const F=i[a],b=i[T];let U=b.x-F.x,X=b.y-F.y,te=Math.sqrt(U*U+X*X);te<it&&(te=it);const z=o*e/(te*te),ce=U/te*z,V=X/te*z;F.vx+=ce,F.vy+=V,b.vx-=ce,b.vy-=V}}}else for(let c=0;c<s.length;c++)for(let o=c+1;o<s.length;o++){const P=s[c],i=s[o];let a=i.x-P.x,T=i.y-P.y,F=Math.sqrt(a*a+T*T);F<it&&(F=it);const U=(P.type===i.type?Qt:y)*e/(F*F),X=a/F*U,te=T/F*U;P.vx-=X,P.vy-=te,i.vx+=X,i.vy+=te}for(const c of r){const o=l.get(c.sourceId),P=l.get(c.targetId);if(!o||!P)continue;const i=P.x-o.x,a=P.y-o.y,T=Math.sqrt(i*i+a*a);if(T===0)continue;const F=o.type===P.type?yn*Xe.spacing:Cn*Xe.spacing,b=Wn*(T-F)*e,U=i/T*b,X=a/T*b;o.vx+=U,o.vy+=X,P.vx-=U,P.vy-=X}for(const c of s)c.vx-=c.x*tn*e,c.vy-=c.y*tn*e;const k=new Map;for(const c of s){const o=k.get(c.type)??{x:0,y:0,count:0};o.x+=c.x,o.y+=c.y,o.count++,k.set(c.type,o)}for(const c of k.values())c.x/=c.count,c.y/=c.count;for(const c of s){const o=k.get(c.type);c.vx+=(o.x-c.x)*Xe.clusterStrength*e,c.vy+=(o.y-c.y)*Xe.clusterStrength*e}for(const c of s){c.vx*=en,c.vy*=en;const o=Math.sqrt(c.vx*c.vx+c.vy*c.vy);o>kt&&(c.vx=c.vx/o*kt,c.vy=c.vy/o*kt),c.x+=c.vx,c.y+=c.vy}return e*.995}const on=["#d4a27f","#c17856","#b07a5e","#d4956b","#a67c5a","#cc9e7c","#c4866a","#cb8e6c","#b8956e","#a88a70","#d9b08c","#c4a882","#e8b898","#b5927a","#a8886e","#d1a990"],sn=new Map;function Se(t){const e=sn.get(t);if(e)return e;let s=0;for(let l=0;l<t.length;l++)s=(s<<5)-s+t.charCodeAt(l)|0;const r=on[Math.abs(s)%on.length];return sn.set(t,r),r}class Yn{constructor(e){ht(this,"cells",new Map);ht(this,"cellSize");ht(this,"invCell");this.cellSize=e,this.invCell=1/e}key(e,s){const r=e+32768|0,l=s+32768|0;return r*73856093^l*19349663}clear(){this.cells.clear()}insert(e){const s=Math.floor(e.x*this.invCell),r=Math.floor(e.y*this.invCell),l=this.key(s,r),y=this.cells.get(l);y?y.push(e):this.cells.set(l,[e])}rebuild(e){this.cells.clear();for(const s of e)this.insert(s)}query(e,s,r){const l=r*r,y=Math.floor((e-r)*this.invCell),k=Math.floor((e+r)*this.invCell),c=Math.floor((s-r)*this.invCell),o=Math.floor((s+r)*this.invCell);let P=null,i=l;for(let a=y;a<=k;a++)for(let T=c;T<=o;T++){const F=this.cells.get(this.key(a,T));if(F)for(const b of F){const U=b.x-e,X=b.y-s,te=U*U+X*X;te<=i&&(i=te,P=b)}}return P}}const et=new Map,Xn=2e3;function _n(t,e,s){return`${t}|${e}|${s}`}const Gn=new OffscreenCanvas(1,1),an=Gn.getContext("2d");function Vn(t,e,s){an.font=e;const r=an.measureText(t),l=Math.ceil(r.width)+2,y=Math.ceil(r.actualBoundingBoxAscent+r.actualBoundingBoxDescent)+4,k=new OffscreenCanvas(l,y),c=k.getContext("2d");return c.font=e,c.fillStyle=s,c.textAlign="left",c.textBaseline="top",c.fillText(t,1,1),{canvas:k,width:l,height:y}}function cn(t,e,s,r,l,y,k){const c=_n(e,l,y);let o=et.get(c);if(!o){if(et.size>=Xn){const a=et.keys().next().value;a!==void 0&&et.delete(a)}o=Vn(e,l,y),et.set(c,o)}const P=s-o.width/2,i=k==="top"?r:r-o.height;t.drawImage(o.canvas,P,i)}function Jn(){et.clear()}function Ce(t){return getComputedStyle(document.documentElement).getPropertyValue(t).trim()}const Fe=20,Nt=.001,Kn={hideBadges:.4,hideLabels:.25,hideEdgeLabels:.35,smallNodes:.2,hideArrows:.15,dotNodes:.1,hullsOnly:.05},Zn={zoomFactor:1.3,zoomMin:.05,zoomMax:10,panAnimationMs:300};function gt(t,e,s,r,l,y=100){const k=(t-s.x)*s.scale,c=(e-s.y)*s.scale;return k>=-y&&k<=r+y&&c>=-y&&c<=l+y}function Qn(t,e,s,r){const l={...Kn,...(r==null?void 0:r.lod)??{}},y={...Zn,...(r==null?void 0:r.navigation)??{}},k={pulseSpeed:.02,...(r==null?void 0:r.walk)??{}},c=t.querySelector("canvas"),o=c.getContext("2d"),P=window.devicePixelRatio||1;let i={x:0,y:0,scale:1},a=null,T=1,F=0,b=new Set,U=null,X=!0,te=!0,z=!0,ce=!0,V=null,B=null,Q=1,K=null,ae=null,J=null,ie=!1,ee=[],ye=0,ke=0;const _=400,W=new Yn(Fe*2);let D=0;function E(){I(),D||(D=requestAnimationFrame(()=>{D=0,p()}))}const d=150;let m=null,g=!1;function C(){if(!m)try{m=new Worker(new URL("/assets/layout-worker-BZXiBoiC.js",import.meta.url),{type:"module"}),m.onmessage=S,m.onerror=()=>{g=!1,m=null,xe()}}catch{return g=!1,null}return m}function S(h){const v=h.data;if(v.type==="tick"&&a){const A=v.positions,$=a.nodes;for(let H=0;H<$.length;H++)$[H].x=A[H*4],$[H].y=A[H*4+1],$[H].vx=A[H*4+2],$[H].vy=A[H*4+3];T=v.alpha,W.rebuild($),p()}v.type==="settled"&&(T=0,ie&&ee.length>0&&!le&&(le=requestAnimationFrame(ge)))}let u=null,x=null,R=!0;function I(){R=!0}let O=null,w=null;const f=y.panAnimationMs;function N(){c.width=c.clientWidth*P,c.height=c.clientHeight*P,I(),E()}const j=new ResizeObserver(N);j.observe(t),N();function oe(h,v){return[h/i.scale+i.x,v/i.scale+i.y]}function pe(h,v){if(!a)return null;const[A,$]=oe(h,v);return W.query(A,$,Fe)}function n(){if(!a)return;ye+=k.pulseSpeed;const h=new Set(ee);o.save(),o.setTransform(P,0,0,P,0,0),u&&(o.clearRect(0,0,c.clientWidth,c.clientHeight),o.drawImage(u,0,0,c.clientWidth,c.clientHeight)),o.save(),o.translate(-i.x*i.scale,-i.y*i.scale),o.scale(i.scale,i.scale);const v=Ce("--canvas-walk-edge")||"#1a1a1a",A=[];for(const Y of a.edges){if(!h.has(Y.sourceId)||!h.has(Y.targetId)||Y.sourceId===Y.targetId)continue;const q=a.nodeMap.get(Y.sourceId),Z=a.nodeMap.get(Y.targetId);!q||!Z||A.push(q.x,q.y,Z.x,Z.y)}if(A.length>0){o.beginPath();for(let Y=0;Y<A.length;Y+=4)o.moveTo(A[Y],A[Y+1]),o.lineTo(A[Y+2],A[Y+3]);o.strokeStyle=v,o.lineWidth=3,o.globalAlpha=.5+.5*Math.sin(ye),o.stroke(),o.globalAlpha=1}const $=i.scale<l.smallNodes?Fe*.5:Fe,H=Ce("--accent")||"#d4a27f";for(const Y of ee){const q=a.nodeMap.get(Y);if(!q||!gt(q.x,q.y,i,c.clientWidth,c.clientHeight))continue;const Z=Y===ee[ee.length-1],ue=.5+.5*Math.sin(ye);o.strokeStyle=H,o.lineWidth=Z?3:2,o.globalAlpha=Z?.5+.5*ue:.3+.4*ue,o.beginPath(),o.arc(q.x,q.y,$+(Z?6:4),0,Math.PI*2),o.stroke()}o.globalAlpha=1,o.restore(),ce&&a.nodes.length>1&&M(),o.restore()}function p(){var Wt;if(!a){o.clearRect(0,0,c.width,c.height);return}if(!R&&u&&ie&&ee.length>0&&T<Nt){n();return}const h=T<Nt&&ie&&ee.length>0;ie&&ee.length>0&&(ye+=k.pulseSpeed);const v=ie&&!h?new Set(ee):null,A=Ce("--canvas-edge"),$=Ce("--canvas-edge-highlight"),H=Ce("--canvas-edge-dim"),Y=Ce("--canvas-edge-label"),q=Ce("--canvas-edge-label-highlight"),Z=Ce("--canvas-edge-label-dim"),ue=Ce("--canvas-arrow"),he=Ce("--canvas-arrow-highlight"),fe=Ce("--canvas-node-label"),be=Ce("--canvas-node-label-dim"),Oe=Ce("--canvas-type-badge"),Me=Ce("--canvas-type-badge-dim"),Ie=Ce("--canvas-selection-border"),je=Ce("--canvas-node-border");if(o.save(),o.setTransform(P,0,0,P,0,0),o.clearRect(0,0,c.clientWidth,c.clientHeight),o.save(),o.translate(-i.x*i.scale,-i.y*i.scale),o.scale(i.scale,i.scale),z&&i.scale>=l.smallNodes){const ne=new Map;for(const Ne of a.nodes){if(U!==null&&!U.has(Ne.id))continue;const Be=ne.get(Ne.type)??[];Be.push(Ne),ne.set(Ne.type,Be)}for(const[Ne,Be]of ne){if(Be.length<2)continue;const Ve=Se(Ne),_e=Fe*2.5;let We=1/0,ze=1/0,Re=-1/0,Ye=-1/0;for(const Pe of Be)Pe.x<We&&(We=Pe.x),Pe.y<ze&&(ze=Pe.y),Pe.x>Re&&(Re=Pe.x),Pe.y>Ye&&(Ye=Pe.y);o.beginPath();const se=(Re-We)/2+_e,we=(Ye-ze)/2+_e,ve=(We+Re)/2,$e=(ze+Ye)/2;o.ellipse(ve,$e,se,we,0,0,Math.PI*2),o.fillStyle=Ve,o.globalAlpha=.05,o.fill(),o.strokeStyle=Ve,o.globalAlpha=.12,o.lineWidth=1,o.setLineDash([4,4]),o.stroke(),o.setLineDash([]),o.globalAlpha=1}}let Te=null;if(b.size>0){Te=new Set;for(const ne of a.edges)b.has(ne.sourceId)&&Te.add(ne.targetId),b.has(ne.targetId)&&Te.add(ne.sourceId)}const vt=Ce("--accent")||"#d4a27f",xt=Ce("--canvas-walk-edge")||"#1a1a1a",Ze=i.scale>=l.hideArrows,ut=te&&i.scale>=l.hideEdgeLabels;if(X){const ne=[],Ne=[],Be=[],Ve=[],_e=[],We=[];for(const se of a.edges){const we=a.nodeMap.get(se.sourceId),ve=a.nodeMap.get(se.targetId);if(!we||!ve||!gt(we.x,we.y,i,c.clientWidth,c.clientHeight,200)&&!gt(ve.x,ve.y,i,c.clientWidth,c.clientHeight,200))continue;const $e=U===null||U.has(se.sourceId),Pe=U===null||U.has(se.targetId),Ht=$e&&Pe;if(U!==null&&!$e&&!Pe)continue;const Et=b.size>0&&(b.has(se.sourceId)||b.has(se.targetId))||U!==null&&Ht,zt=U!==null&&!Ht,jt=v!==null&&v.has(se.sourceId)&&v.has(se.targetId),Ut=J?V==null?void 0:V.edges.find(mt=>mt.sourceId===se.sourceId&&mt.targetId===se.targetId||mt.targetId===se.sourceId&&mt.sourceId===se.targetId):null,qt=!!(J&&Ut&&J.edgeIds.has(Ut.id));if(se.sourceId===se.targetId){re(we,se.type,Et,A,$,Y,q);continue}(qt?_e:jt?Ve:Et?Ne:zt?Be:ne).push(we.x,we.y,ve.x,ve.y),(Ze||ut)&&We.push({sx:we.x,sy:we.y,tx:ve.x,ty:ve.y,type:se.type,highlighted:Et,edgeDimmed:zt,isPathEdge:qt,isWalkEdge:jt})}const ze=Ze?1.5:1,Ye=[{lines:ne,color:A,width:ze,alpha:1},{lines:Be,color:H,width:ze,alpha:1},{lines:Ne,color:$,width:Ze?2.5:1,alpha:1},{lines:_e,color:vt,width:3,alpha:1},{lines:Ve,color:xt,width:3,alpha:.5+.5*Math.sin(ye)}];for(const se of Ye)if(se.lines.length!==0){o.beginPath();for(let we=0;we<se.lines.length;we+=4)o.moveTo(se.lines[we],se.lines[we+1]),o.lineTo(se.lines[we+2],se.lines[we+3]);o.strokeStyle=se.color,o.lineWidth=se.width,o.globalAlpha=se.alpha,o.stroke()}o.globalAlpha=1;for(const se of We)if(Ze&&G(se.sx,se.sy,se.tx,se.ty,se.highlighted||se.isPathEdge,ue,he),ut){const we=(se.sx+se.tx)/2,ve=(se.sy+se.ty)/2;o.fillStyle=se.highlighted?q:se.edgeDimmed?Z:Y,o.font="9px system-ui, sans-serif",o.textAlign="center",o.textBaseline="bottom",o.fillText(se.type,we,ve-4)}}const bt=performance.now()-ke,Ee=Math.min(1,bt/_),Ue=1-(1-Ee)*(1-Ee),qe=Ee<1,Ot=i.scale<l.hullsOnly,bn=!Ot&&i.scale<l.dotNodes;if(!Ot)for(const ne of a.nodes){if(!gt(ne.x,ne.y,i,c.clientWidth,c.clientHeight))continue;const Ne=Se(ne.type);if(bn){const ve=U!==null&&!U.has(ne.id);o.fillStyle=Ne;const $e=ve?.1:.8;o.globalAlpha=qe?$e*Ue:$e,o.fillRect(ne.x-2,ne.y-2,4,4);continue}const Be=b.has(ne.id),Ve=Te!==null&&Te.has(ne.id),_e=U!==null&&!U.has(ne.id),We=_e||b.size>0&&!Be&&!Ve,ze=i.scale<l.smallNodes?Fe*.5:Fe,Re=qe?ze*Ue:ze;if(v!=null&&v.has(ne.id)){const ve=ee[ee.length-1]===ne.id,$e=.5+.5*Math.sin(ye),Pe=Ce("--accent")||"#d4a27f";o.save(),o.strokeStyle=Pe,o.lineWidth=ve?3:2,o.globalAlpha=ve?.5+.5*$e:.3+.4*$e,o.beginPath(),o.arc(ne.x,ne.y,Re+(ve?6:4),0,Math.PI*2),o.stroke(),o.restore()}Be&&(o.save(),o.shadowColor=Ne,o.shadowBlur=20,o.beginPath(),o.arc(ne.x,ne.y,Re+3,0,Math.PI*2),o.fillStyle=Ne,o.globalAlpha=.3,o.fill(),o.restore()),o.beginPath(),o.arc(ne.x,ne.y,Re,0,Math.PI*2),o.fillStyle=Ne;const Ye=_e?.1:We?.3:1;o.globalAlpha=qe?Ye*Ue:Ye,o.fill(),o.strokeStyle=Be?Ie:je,o.lineWidth=Be?3:1.5,o.stroke(),o.globalAlpha=1,J&&J.nodeIds.has(ne.id)&&!Be&&(o.save(),o.shadowColor=Ce("--accent")||"#d4a27f",o.shadowBlur=15,o.beginPath(),o.arc(ne.x,ne.y,Re+2,0,Math.PI*2),o.strokeStyle=Ce("--accent")||"#d4a27f",o.globalAlpha=.5,o.lineWidth=2,o.stroke(),o.restore());const se=V==null?void 0:V.nodes.find(ve=>ve.id===ne.id);if(((Wt=se==null?void 0:se.properties)==null?void 0:Wt._starred)===!0&&(o.fillStyle="#ffd700",o.font="10px system-ui, sans-serif",o.textAlign="left",o.textBaseline="bottom",o.fillText("★",ne.x+Re-2,ne.y-Re+2)),i.scale>=l.hideLabels){const ve=ne.label.length>24?ne.label.slice(0,22)+"...":ne.label,$e=We?be:fe;cn(o,ve,ne.x,ne.y+Re+4,"11px system-ui, sans-serif",$e,"top")}if(i.scale>=l.hideBadges){const ve=We?Me:Oe;cn(o,ne.type,ne.x,ne.y-Re-3,"9px system-ui, sans-serif",ve,"bottom")}o.globalAlpha=1}if(o.restore(),o.restore(),h){const ne=c.width,Ne=c.height;(!u||u.width!==ne||u.height!==Ne)&&(u=new OffscreenCanvas(ne,Ne),x=u.getContext("2d")),x&&(x.clearRect(0,0,ne,Ne),x.drawImage(c,0,0),R=!1),n();return}ce&&a.nodes.length>1&&M()}function M(){if(!a)return;const h=140,v=100,A=8,$=c.clientWidth-h-16,H=c.clientHeight-v-16;let Y=1/0,q=1/0,Z=-1/0,ue=-1/0;for(const Ee of a.nodes)Ee.x<Y&&(Y=Ee.x),Ee.y<q&&(q=Ee.y),Ee.x>Z&&(Z=Ee.x),Ee.y>ue&&(ue=Ee.y);const he=Z-Y||1,fe=ue-q||1,be=Math.min((h-A*2)/he,(v-A*2)/fe),Oe=$+A+(h-A*2-he*be)/2,Me=H+A+(v-A*2-fe*be)/2;o.save(),o.setTransform(P,0,0,P,0,0),o.fillStyle=Ce("--bg-surface")||"#1a1a1a",o.globalAlpha=.85,o.beginPath(),o.roundRect($,H,h,v,8),o.fill(),o.strokeStyle=Ce("--border")||"#2a2a2a",o.globalAlpha=1,o.lineWidth=1,o.stroke(),o.globalAlpha=.15,o.strokeStyle=Ce("--canvas-edge")||"#555",o.lineWidth=.5;for(const Ee of a.edges){const Ue=a.nodeMap.get(Ee.sourceId),qe=a.nodeMap.get(Ee.targetId);!Ue||!qe||Ee.sourceId===Ee.targetId||(o.beginPath(),o.moveTo(Oe+(Ue.x-Y)*be,Me+(Ue.y-q)*be),o.lineTo(Oe+(qe.x-Y)*be,Me+(qe.y-q)*be),o.stroke())}o.globalAlpha=.8;for(const Ee of a.nodes){const Ue=Oe+(Ee.x-Y)*be,qe=Me+(Ee.y-q)*be;o.beginPath(),o.arc(Ue,qe,2,0,Math.PI*2),o.fillStyle=Se(Ee.type),o.fill()}const Ie=i.x,je=i.y,Te=i.x+c.clientWidth/i.scale,vt=i.y+c.clientHeight/i.scale,xt=Oe+(Ie-Y)*be,Ze=Me+(je-q)*be,ut=(Te-Ie)*be,bt=(vt-je)*be;o.globalAlpha=.3,o.strokeStyle=Ce("--accent")||"#d4a27f",o.lineWidth=1.5,o.strokeRect(Math.max($,Math.min(xt,$+h)),Math.max(H,Math.min(Ze,H+v)),Math.min(ut,h),Math.min(bt,v)),o.globalAlpha=1,o.restore()}function G(h,v,A,$,H,Y,q){const Z=Math.atan2($-v,A-h),ue=A-Math.cos(Z)*Fe,he=$-Math.sin(Z)*Fe,fe=8;o.beginPath(),o.moveTo(ue,he),o.lineTo(ue-fe*Math.cos(Z-.4),he-fe*Math.sin(Z-.4)),o.lineTo(ue-fe*Math.cos(Z+.4),he-fe*Math.sin(Z+.4)),o.closePath(),o.fillStyle=H?q:Y,o.fill()}function re(h,v,A,$,H,Y,q){const Z=h.x+Fe+15,ue=h.y-Fe-15;o.beginPath(),o.arc(Z,ue,15,0,Math.PI*2),o.strokeStyle=A?H:$,o.lineWidth=A?2.5:1.5,o.stroke(),te&&(o.fillStyle=A?q:Y,o.font="9px system-ui, sans-serif",o.textAlign="center",o.fillText(v,Z,ue-18))}function me(){if(!O||!w)return;const h=performance.now()-w.time,v=Math.min(h/f,1),A=1-Math.pow(1-v,3);i.x=w.x+(O.x-w.x)*A,i.y=w.y+(O.y-w.y)*A,I(),p(),v<1?requestAnimationFrame(me):(O=null,w=null)}let le=0;function ge(){if(!ie||ee.length===0){le=0;return}p(),le=requestAnimationFrame(ge)}function Le(){if(!a||a.nodes.length===0)return;let h=1/0,v=1/0,A=-1/0,$=-1/0;for(const he of a.nodes)he.x<h&&(h=he.x),he.y<v&&(v=he.y),he.x>A&&(A=he.x),he.y>$&&($=he.y);const H=Fe*4,Y=A-h+H*2,q=$-v+H*2,Z=c.clientWidth/Math.max(Y,1),ue=c.clientHeight/Math.max(q,1);i.scale=Math.min(Z,ue,2),i.x=(h+A)/2-c.clientWidth/(2*i.scale),i.y=(v+$)/2-c.clientHeight/(2*i.scale),E()}function xe(){if(!a||T<Nt){ie&&ee.length>0&&!le&&(le=requestAnimationFrame(ge));return}T=qn(a,T),W.rebuild(a.nodes),p(),F=requestAnimationFrame(xe)}let He=!1,yt=!1,Je=0,Ke=0;c.addEventListener("mousedown",h=>{He=!0,yt=!1,Je=h.clientX,Ke=h.clientY}),c.addEventListener("mousemove",h=>{if(!He)return;const v=h.clientX-Je,A=h.clientY-Ke;(Math.abs(v)>5||Math.abs(A)>5)&&(yt=!0),i.x-=v/i.scale,i.y-=A/i.scale,Je=h.clientX,Ke=h.clientY,E()}),c.addEventListener("mouseup",h=>{if(He=!1,yt)return;const v=c.getBoundingClientRect(),A=h.clientX-v.left,$=h.clientY-v.top,H=pe(A,$),Y=h.ctrlKey||h.metaKey;if(ie&&B&&H&&a){const q=ee.length>0?ee[ee.length-1]:B[0],Z=new Set([q]),ue=[{id:q,path:[q]}];let he=null;for(;ue.length>0;){const{id:Me,path:Ie}=ue.shift();if(Me===H.id){he=Ie;break}for(const je of a.edges){let Te=null;je.sourceId===Me?Te=je.targetId:je.targetId===Me&&(Te=je.sourceId),Te&&!Z.has(Te)&&(Z.add(Te),ue.push({id:Te,path:[...Ie,Te]}))}}if(!he)return;for(const Me of he.slice(1))ee.includes(Me)||ee.push(Me);B=[H.id];const fe=Math.max(1,Q);Q=fe;const be=nn(V,[H.id],fe);cancelAnimationFrame(F),m&&m.postMessage({type:"stop"}),a=St(be),W.rebuild(a.nodes),T=1,b=new Set([H.id]),U=null,i={x:0,y:0,scale:1},g=be.nodes.length>=d;const Oe=g?C():null;Oe?Oe.postMessage({type:"start",data:be}):(g=!1,xe()),setTimeout(()=>{a&&Le()},300),s==null||s({seedNodeIds:[H.id],hops:fe,totalNodes:be.nodes.length}),e==null||e([H.id]);return}if(H){Y?b.has(H.id)?b.delete(H.id):b.add(H.id):b.size===1&&b.has(H.id)?b.clear():(b.clear(),b.add(H.id));const q=[...b];e==null||e(q.length>0?q:null)}else b.clear(),e==null||e(null);E()}),c.addEventListener("mouseleave",()=>{He=!1}),c.addEventListener("wheel",h=>{h.preventDefault();const v=c.getBoundingClientRect(),A=h.clientX-v.left,$=h.clientY-v.top,[H,Y]=oe(A,$),q=h.ctrlKey?1-h.deltaY*.01:h.deltaY>0?.9:1.1;i.scale=Math.max(y.zoomMin,Math.min(y.zoomMax,i.scale*q)),i.x=H-A/i.scale,i.y=Y-$/i.scale,E()},{passive:!1});let De=[],Rt=0,Pt=1,Ft=0,$t=0,Ct=!1;c.addEventListener("touchstart",h=>{h.preventDefault(),De=Array.from(h.touches),De.length===2?(Rt=Dt(De[0],De[1]),Pt=i.scale):De.length===1&&(Je=De[0].clientX,Ke=De[0].clientY,Ft=De[0].clientX,$t=De[0].clientY,Ct=!1)},{passive:!1}),c.addEventListener("touchmove",h=>{h.preventDefault();const v=Array.from(h.touches);if(v.length===2&&De.length===2){const $=Dt(v[0],v[1])/Rt;i.scale=Math.max(y.zoomMin,Math.min(y.zoomMax,Pt*$)),E()}else if(v.length===1){const A=v[0].clientX-Je,$=v[0].clientY-Ke;(Math.abs(v[0].clientX-Ft)>10||Math.abs(v[0].clientY-$t)>10)&&(Ct=!0),i.x-=A/i.scale,i.y-=$/i.scale,Je=v[0].clientX,Ke=v[0].clientY,E()}De=v},{passive:!1}),c.addEventListener("touchend",h=>{if(h.preventDefault(),Ct||h.changedTouches.length!==1)return;const v=h.changedTouches[0],A=c.getBoundingClientRect(),$=v.clientX-A.left,H=v.clientY-A.top,Y=pe($,H);if(Y){b.size===1&&b.has(Y.id)?b.clear():(b.clear(),b.add(Y.id));const q=[...b];e==null||e(q.length>0?q:null)}else b.clear(),e==null||e(null);E()},{passive:!1}),c.addEventListener("gesturestart",h=>h.preventDefault()),c.addEventListener("gesturechange",h=>h.preventDefault());function Dt(h,v){const A=h.clientX-v.clientX,$=h.clientY-v.clientY;return Math.sqrt(A*A+$*$)}const nt=document.createElement("div");nt.className="zoom-controls";const ot=document.createElement("button");ot.className="zoom-btn",ot.textContent="+",ot.title="Zoom in",ot.addEventListener("click",()=>{const h=c.clientWidth/2,v=c.clientHeight/2,[A,$]=oe(h,v);i.scale=Math.min(y.zoomMax,i.scale*y.zoomFactor),i.x=A-h/i.scale,i.y=$-v/i.scale,E()});const st=document.createElement("button");st.className="zoom-btn",st.textContent="−",st.title="Zoom out",st.addEventListener("click",()=>{const h=c.clientWidth/2,v=c.clientHeight/2,[A,$]=oe(h,v);i.scale=Math.max(y.zoomMin,i.scale/y.zoomFactor),i.x=A-h/i.scale,i.y=$-v/i.scale,E()});const at=document.createElement("button");at.className="zoom-btn",at.textContent="○",at.title="Reset zoom",at.addEventListener("click",()=>{if(a){if(i={x:0,y:0,scale:1},a.nodes.length>0){let h=1/0,v=1/0,A=-1/0,$=-1/0;for(const q of a.nodes)q.x<h&&(h=q.x),q.y<v&&(v=q.y),q.x>A&&(A=q.x),q.y>$&&($=q.y);const H=(h+A)/2,Y=(v+$)/2;i.x=H-c.clientWidth/2,i.y=Y-c.clientHeight/2}E()}}),nt.appendChild(ot),nt.appendChild(at),nt.appendChild(st),t.appendChild(nt);const Ae=document.createElement("div");Ae.className="node-tooltip",Ae.style.display="none",t.appendChild(Ae);let pt=null,Ge=null;return c.addEventListener("mousemove",h=>{if(He){Ae.style.display!=="none"&&(Ae.style.display="none",pt=null);return}const v=c.getBoundingClientRect(),A=h.clientX-v.left,$=h.clientY-v.top,H=pe(A,$),Y=(H==null?void 0:H.id)??null;Y!==pt?(pt=Y,Ae.style.display="none",Ge&&clearTimeout(Ge),Ge=null,Y&&H&&(Ge=setTimeout(()=>{if(!a||!V)return;const q=a.edges.filter(Z=>Z.sourceId===Y||Z.targetId===Y).length;Ae.textContent=`${H.label} · ${H.type} · ${q} edge${q!==1?"s":""}`,Ae.style.left=`${h.clientX-v.left+12}px`,Ae.style.top=`${h.clientY-v.top-8}px`,Ae.style.display="block"},200))):Y&&Ae.style.display==="block"&&(Ae.style.left=`${h.clientX-v.left+12}px`,Ae.style.top=`${h.clientY-v.top-8}px`)}),c.addEventListener("mouseleave",()=>{Ae.style.display="none",pt=null,Ge&&clearTimeout(Ge),Ge=null}),{loadGraph(h){if(cancelAnimationFrame(F),m&&m.postMessage({type:"stop"}),Jn(),V=h,B=null,K=null,ae=null,ke=performance.now(),a=St(h),W.rebuild(a.nodes),T=1,b=new Set,U=null,i={x:0,y:0,scale:1},a.nodes.length>0){let A=1/0,$=1/0,H=-1/0,Y=-1/0;for(const fe of a.nodes)fe.x<A&&(A=fe.x),fe.y<$&&($=fe.y),fe.x>H&&(H=fe.x),fe.y>Y&&(Y=fe.y);const q=(A+H)/2,Z=($+Y)/2,ue=c.clientWidth,he=c.clientHeight;i.x=q-ue/2,i.y=Z-he/2}g=h.nodes.length>=d;const v=g?C():null;v?v.postMessage({type:"start",data:h}):(g=!1,xe())},setFilteredNodeIds(h){U=h,E()},panToNode(h){this.panToNodes([h])},panToNodes(h){if(!a||h.length===0)return;const v=h.map(H=>a.nodeMap.get(H)).filter(Boolean);if(v.length===0)return;b=new Set(h),e==null||e(h);const A=c.clientWidth,$=c.clientHeight;if(v.length===1)w={x:i.x,y:i.y,time:performance.now()},O={x:v[0].x-A/(2*i.scale),y:v[0].y-$/(2*i.scale)};else{let H=1/0,Y=1/0,q=-1/0,Z=-1/0;for(const Ie of v)Ie.x<H&&(H=Ie.x),Ie.y<Y&&(Y=Ie.y),Ie.x>q&&(q=Ie.x),Ie.y>Z&&(Z=Ie.y);const ue=Fe*4,he=q-H+ue*2,fe=Z-Y+ue*2,be=Math.min(A/he,$/fe,i.scale);i.scale=be;const Oe=(H+q)/2,Me=(Y+Z)/2;w={x:i.x,y:i.y,time:performance.now()},O={x:Oe-A/(2*i.scale),y:Me-$/(2*i.scale)}}me()},setEdges(h){X=h,E()},setEdgeLabels(h){te=h,E()},setTypeHulls(h){z=h,E()},setMinimap(h){ce=h,E()},centerView(){Le()},panBy(h,v){i.x+=h/i.scale,i.y+=v/i.scale,E()},zoomBy(h){const v=c.clientWidth/2,A=c.clientHeight/2,[$,H]=oe(v,A);i.scale=Math.max(y.zoomMin,Math.min(y.zoomMax,i.scale*h)),i.x=$-v/i.scale,i.y=H-A/i.scale,E()},reheat(){g&&m?m.postMessage({type:"params",params:dt()}):(T=.5,cancelAnimationFrame(F),xe())},exportImage(h){if(!a)return"";const v=c.width,A=c.height;if(h==="png"){const q=document.createElement("canvas");q.width=v,q.height=A;const Z=q.getContext("2d");return Z.fillStyle=Ce("--bg")||"#141414",Z.fillRect(0,0,v,A),Z.drawImage(c,0,0),xn(Z,v,A),q.toDataURL("image/png")}const $=c.toDataURL("image/png"),H=Math.max(16,Math.round(v/80)),Y=`<svg xmlns="http://www.w3.org/2000/svg" width="${v}" height="${A}">
2
+ <image href="${$}" width="${v}" height="${A}"/>
3
+ <text x="${v-20}" y="${A-16}" text-anchor="end" font-family="system-ui, sans-serif" font-size="${H}" fill="#ffffff" opacity="0.4">backpackontology.com</text>
4
+ </svg>`;return"data:image/svg+xml;charset=utf-8,"+encodeURIComponent(Y)},enterFocus(h,v){if(!V||!a)return;B||(K=a,ae={...i}),B=h,Q=v;const A=nn(V,h,v);cancelAnimationFrame(F),m&&m.postMessage({type:"stop"}),a=St(A),W.rebuild(a.nodes),T=1,b=new Set(h),U=null,i={x:0,y:0,scale:1},g=A.nodes.length>=d;const $=g?C():null;$?$.postMessage({type:"start",data:A}):(g=!1,xe()),setTimeout(()=>{!a||!B||Le()},300),s==null||s({seedNodeIds:h,hops:v,totalNodes:A.nodes.length})},exitFocus(){!B||!K||(cancelAnimationFrame(F),m&&m.postMessage({type:"stop"}),a=K,W.rebuild(a.nodes),i=ae??{x:0,y:0,scale:1},B=null,K=null,ae=null,b=new Set,U=null,E(),s==null||s(null))},isFocused(){return B!==null},getFocusInfo(){return!B||!a?null:{seedNodeIds:B,hops:Q,totalNodes:a.nodes.length}},findPath(h,v){if(!a)return null;const A=new Set([h]),$=[{nodeId:h,path:[h],edges:[]}];for(;$.length>0;){const{nodeId:H,path:Y,edges:q}=$.shift();if(H===v)return{nodeIds:Y,edgeIds:q};for(const Z of a.edges){let ue=null;if(Z.sourceId===H?ue=Z.targetId:Z.targetId===H&&(ue=Z.sourceId),ue&&!A.has(ue)){A.add(ue);const he=V==null?void 0:V.edges.find(fe=>fe.sourceId===Z.sourceId&&fe.targetId===Z.targetId||fe.targetId===Z.sourceId&&fe.sourceId===Z.targetId);$.push({nodeId:ue,path:[...Y,ue],edges:[...q,(he==null?void 0:he.id)??""]})}}}return null},setHighlightedPath(h,v){h&&v?J={nodeIds:new Set(h),edgeIds:new Set(v)}:J=null,E()},clearHighlightedPath(){J=null,E()},setWalkMode(h){ie=h,h?(ee=B?[...B]:[...b],le||(le=requestAnimationFrame(ge))):(ee=[],le&&(cancelAnimationFrame(le),le=0)),E()},getWalkMode(){return ie},getWalkTrail(){return[...ee]},getFilteredNodeIds(){return U},removeFromWalkTrail(h){ee=ee.filter(v=>v!==h),E()},nodeAtScreen(h,v){return pe(h,v)},getNodeIds(){if(!a)return[];if(B){const h=new Set(B),v=a.nodes.filter($=>h.has($.id)).map($=>$.id),A=a.nodes.filter($=>!h.has($.id)).map($=>$.id);return[...v,...A]}return a.nodes.map(h=>h.id)},destroy(){cancelAnimationFrame(F),D&&(cancelAnimationFrame(D),D=0),le&&(cancelAnimationFrame(le),le=0),m&&(m.terminate(),m=null),u=null,x=null,j.disconnect()}};function xn(h,v,A){const $=Math.max(16,Math.round(v/80));h.save(),h.font=`${$}px system-ui, sans-serif`,h.fillStyle="rgba(255, 255, 255, 0.4)",h.textAlign="right",h.textBaseline="bottom",h.fillText("backpackontology.com",v-20,A-16),h.restore()}}function lt(t){for(const e of Object.values(t.properties))if(typeof e=="string")return e;return t.id}const eo="✎";function to(t,e,s,r){const l=document.createElement("div");l.id="info-panel",l.className="info-panel hidden",t.appendChild(l);let y=!1,k=[],c=-1,o=!1,P=null,i=[],a=!1,T=[],F=-1;function b(){l.classList.add("hidden"),l.classList.remove("info-panel-maximized"),l.innerHTML="",y=!1,k=[],c=-1}function U(B){!P||!s||(c<k.length-1&&(k=k.slice(0,c+1)),k.push(B),c=k.length-1,o=!0,s(B),o=!1)}function X(){if(c<=0||!P)return;c--,o=!0;const B=k[c];s==null||s(B),ce(B,P),o=!1}function te(){if(c>=k.length-1||!P)return;c++,o=!0;const B=k[c];s==null||s(B),ce(B,P),o=!1}function z(){const B=document.createElement("div");B.className="info-panel-toolbar";const Q=document.createElement("button");Q.className="info-toolbar-btn",Q.textContent="←",Q.title="Back",Q.disabled=c<=0,Q.addEventListener("click",X),B.appendChild(Q);const K=document.createElement("button");if(K.className="info-toolbar-btn",K.textContent="→",K.title="Forward",K.disabled=c>=k.length-1,K.addEventListener("click",te),B.appendChild(K),r&&i.length>0){const ie=document.createElement("button");ie.className="info-toolbar-btn info-focus-btn",ie.textContent="◎",ie.title="Focus on neighborhood (F)",ie.disabled=a,a&&(ie.style.opacity="0.3"),ie.addEventListener("click",()=>{a||r(i)}),B.appendChild(ie)}const ae=document.createElement("button");ae.className="info-toolbar-btn",ae.textContent=y?"⎘":"⛶",ae.title=y?"Restore":"Maximize",ae.addEventListener("click",()=>{y=!y,l.classList.toggle("info-panel-maximized",y),ae.textContent=y?"⎘":"⛶",ae.title=y?"Restore":"Maximize"}),B.appendChild(ae);const J=document.createElement("button");return J.className="info-toolbar-btn info-close-btn",J.textContent="×",J.title="Close",J.addEventListener("click",b),B.appendChild(J),B}function ce(B,Q){const K=Q.nodes.find(u=>u.id===B);if(!K)return;const ae=Q.edges.filter(u=>u.sourceId===B||u.targetId===B);T=ae.map(u=>u.sourceId===B?u.targetId:u.sourceId),F=-1,l.innerHTML="",l.classList.remove("hidden"),y&&l.classList.add("info-panel-maximized");const J=document.createElement("div");J.className="info-panel-header",J.appendChild(z());const ie=document.createElement("div");ie.className="info-header";const ee=document.createElement("span");if(ee.className="info-type-badge",ee.textContent=K.type,ee.style.backgroundColor=Se(K.type),e){ee.classList.add("info-editable");const u=document.createElement("button");u.className="info-inline-edit",u.textContent=eo,u.addEventListener("click",x=>{x.stopPropagation();const R=document.createElement("input");R.type="text",R.className="info-edit-inline-input",R.value=K.type,ee.textContent="",ee.appendChild(R),R.focus(),R.select();const I=()=>{const O=R.value.trim();O&&O!==K.type?e.onChangeNodeType(B,O):(ee.textContent=K.type,ee.appendChild(u))};R.addEventListener("blur",I),R.addEventListener("keydown",O=>{O.key==="Enter"&&R.blur(),O.key==="Escape"&&(R.value=K.type,R.blur())})}),ee.appendChild(u)}const ye=document.createElement("h3");ye.className="info-label",ye.textContent=lt(K);const ke=document.createElement("span");ke.className="info-id",ke.textContent=K.id,ie.appendChild(ee),ie.appendChild(ye),ie.appendChild(ke),J.appendChild(ie),l.appendChild(J);const _=document.createElement("div");_.className="info-panel-body";const W=Object.keys(K.properties),D=rt("Properties");if(W.length>0){const u=document.createElement("dl");u.className="info-props";for(const x of W){const R=document.createElement("dt");R.textContent=x;const I=document.createElement("dd");if(e){const O=Lt(K.properties[x]),w=document.createElement("textarea");w.className="info-edit-input",w.value=O,w.rows=1,w.addEventListener("input",()=>ln(w)),w.addEventListener("keydown",N=>{N.key==="Enter"&&!N.shiftKey&&(N.preventDefault(),w.blur())}),w.addEventListener("blur",()=>{const N=w.value;N!==O&&e.onUpdateNode(B,{[x]:oo(N)})}),I.appendChild(w),requestAnimationFrame(()=>ln(w));const f=document.createElement("button");f.className="info-delete-prop",f.textContent="×",f.title=`Remove ${x}`,f.addEventListener("click",()=>{const N={...K.properties};delete N[x],e.onUpdateNode(B,N)}),I.appendChild(f)}else I.appendChild(no(K.properties[x]));u.appendChild(R),u.appendChild(I)}D.appendChild(u)}if(e){const u=document.createElement("button");u.className="info-add-btn",u.textContent="+ Add property",u.addEventListener("click",()=>{const x=document.createElement("div");x.className="info-add-row";const R=document.createElement("input");R.type="text",R.className="info-edit-input",R.placeholder="key";const I=document.createElement("input");I.type="text",I.className="info-edit-input",I.placeholder="value";const O=document.createElement("button");O.className="info-add-save",O.textContent="Add",O.addEventListener("click",()=>{R.value&&e.onAddProperty(B,R.value,I.value)}),x.appendChild(R),x.appendChild(I),x.appendChild(O),D.appendChild(x),R.focus()}),D.appendChild(u)}if(_.appendChild(D),ae.length>0){const u=rt(`Connections (${ae.length})`),x=document.createElement("ul");x.className="info-connections";for(const R of ae){const I=R.sourceId===B,O=I?R.targetId:R.sourceId,w=Q.nodes.find(p=>p.id===O),f=w?lt(w):O,N=document.createElement("li");if(N.className="info-connection",s&&w&&(N.classList.add("info-connection-link"),N.addEventListener("click",p=>{p.target.closest(".info-delete-edge")||U(O)})),w){const p=document.createElement("span");p.className="info-target-dot",p.style.backgroundColor=Se(w.type),N.appendChild(p)}const j=document.createElement("span");j.className="info-arrow",j.textContent=I?"→":"←";const oe=document.createElement("span");oe.className="info-edge-type",oe.textContent=R.type;const pe=document.createElement("span");pe.className="info-target",pe.textContent=f,N.appendChild(j),N.appendChild(oe),N.appendChild(pe);const n=Object.keys(R.properties);if(n.length>0){const p=document.createElement("div");p.className="info-edge-props";for(const M of n){const G=document.createElement("span");G.className="info-edge-prop",G.textContent=`${M}: ${Lt(R.properties[M])}`,p.appendChild(G)}N.appendChild(p)}if(e){const p=document.createElement("button");p.className="info-delete-edge",p.textContent="×",p.title="Remove connection",p.addEventListener("click",M=>{M.stopPropagation(),e.onDeleteEdge(R.id)}),N.appendChild(p)}x.appendChild(N)}u.appendChild(x),_.appendChild(u)}const E=rt("Timestamps"),d=document.createElement("dl");d.className="info-props";const m=document.createElement("dt");m.textContent="created";const g=document.createElement("dd");g.textContent=rn(K.createdAt);const C=document.createElement("dt");C.textContent="updated";const S=document.createElement("dd");if(S.textContent=rn(K.updatedAt),d.appendChild(m),d.appendChild(g),d.appendChild(C),d.appendChild(S),E.appendChild(d),_.appendChild(E),e){const u=document.createElement("div");u.className="info-section info-danger";const x=document.createElement("button");x.className="info-delete-node",x.textContent="Delete node",x.addEventListener("click",()=>{e.onDeleteNode(B),b()}),u.appendChild(x),_.appendChild(u)}l.appendChild(_)}function V(B,Q){const K=new Set(B),ae=Q.nodes.filter(E=>K.has(E.id));if(ae.length===0)return;const J=Q.edges.filter(E=>K.has(E.sourceId)&&K.has(E.targetId));l.innerHTML="",l.classList.remove("hidden"),y&&l.classList.add("info-panel-maximized"),l.appendChild(z());const ie=document.createElement("div");ie.className="info-header";const ee=document.createElement("h3");ee.className="info-label",ee.textContent=`${ae.length} nodes selected`,ie.appendChild(ee);const ye=document.createElement("div");ye.className="info-badge-row";const ke=new Map;for(const E of ae)ke.set(E.type,(ke.get(E.type)??0)+1);for(const[E,d]of ke){const m=document.createElement("span");m.className="info-type-badge",m.style.backgroundColor=Se(E),m.textContent=d>1?`${E} (${d})`:E,ye.appendChild(m)}ie.appendChild(ye),l.appendChild(ie);const _=rt("Selected Nodes"),W=document.createElement("ul");W.className="info-connections";for(const E of ae){const d=document.createElement("li");d.className="info-connection",s&&(d.classList.add("info-connection-link"),d.addEventListener("click",()=>{U(E.id)}));const m=document.createElement("span");m.className="info-target-dot",m.style.backgroundColor=Se(E.type);const g=document.createElement("span");g.className="info-target",g.textContent=lt(E);const C=document.createElement("span");C.className="info-edge-type",C.textContent=E.type,d.appendChild(m),d.appendChild(g),d.appendChild(C),W.appendChild(d)}_.appendChild(W),l.appendChild(_);const D=rt(J.length>0?`Connections Between Selected (${J.length})`:"Connections Between Selected");if(J.length===0){const E=document.createElement("p");E.className="info-empty-message",E.textContent="No direct connections between selected nodes",D.appendChild(E)}else{const E=document.createElement("ul");E.className="info-connections";for(const d of J){const m=Q.nodes.find(N=>N.id===d.sourceId),g=Q.nodes.find(N=>N.id===d.targetId),C=m?lt(m):d.sourceId,S=g?lt(g):d.targetId,u=document.createElement("li");if(u.className="info-connection",m){const N=document.createElement("span");N.className="info-target-dot",N.style.backgroundColor=Se(m.type),u.appendChild(N)}const x=document.createElement("span");x.className="info-target",x.textContent=C;const R=document.createElement("span");R.className="info-arrow",R.textContent="→";const I=document.createElement("span");I.className="info-edge-type",I.textContent=d.type;const O=document.createElement("span");if(O.className="info-arrow",O.textContent="→",u.appendChild(x),u.appendChild(R),u.appendChild(I),u.appendChild(O),g){const N=document.createElement("span");N.className="info-target-dot",N.style.backgroundColor=Se(g.type),u.appendChild(N)}const w=document.createElement("span");w.className="info-target",w.textContent=S,u.appendChild(w);const f=Object.keys(d.properties);if(f.length>0){const N=document.createElement("div");N.className="info-edge-props";for(const j of f){const oe=document.createElement("span");oe.className="info-edge-prop",oe.textContent=`${j}: ${Lt(d.properties[j])}`,N.appendChild(oe)}u.appendChild(N)}E.appendChild(u)}D.appendChild(E)}l.appendChild(D)}return{show(B,Q){if(P=Q,i=B,B.length===1&&!o){const K=B[0];k[c]!==K&&(c<k.length-1&&(k=k.slice(0,c+1)),k.push(K),c=k.length-1)}B.length===1?ce(B[0],Q):B.length>1&&V(B,Q)},hide:b,goBack:X,goForward:te,cycleConnection(B){if(T.length===0)return null;F===-1?F=B===1?0:T.length-1:(F+=B,F>=T.length&&(F=0),F<0&&(F=T.length-1));const Q=l.querySelectorAll(".info-connection");return Q.forEach((K,ae)=>{K.classList.toggle("info-connection-active",ae===F)}),F>=0&&Q[F]&&Q[F].scrollIntoView({block:"nearest"}),T[F]??null},setFocusDisabled(B){a=B;const Q=l.querySelector(".info-focus-btn");Q&&(Q.disabled=B,Q.style.opacity=B?"0.3":"")},get visible(){return!l.classList.contains("hidden")}}}function rt(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 no(t){if(Array.isArray(t)){const s=document.createElement("div");s.className="info-array";for(const r of t){const l=document.createElement("span");l.className="info-tag",l.textContent=String(r),s.appendChild(l)}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 Lt(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 ln(t){t.style.height="auto",t.style.height=t.scrollHeight+"px"}function rn(t){try{return new Date(t).toLocaleString()}catch{return t}}function vn(t){for(const e of Object.values(t.properties))if(typeof e=="string")return e;return t.id}function dn(t,e){const s=e.toLowerCase();if(vn(t).toLowerCase().includes(s)||t.type.toLowerCase().includes(s))return!0;for(const r of Object.values(t.properties))if(typeof r=="string"&&r.toLowerCase().includes(s))return!0;return!1}function so(t,e){const s=(e==null?void 0:e.maxResults)??8,r=(e==null?void 0:e.debounceMs)??150;let l=null,y=null,k=null,c=null;const o=document.createElement("div");o.className="search-overlay hidden";const P=document.createElement("div");P.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="/",P.appendChild(i),P.appendChild(a);const T=document.createElement("ul");T.className="search-results hidden",o.appendChild(P),o.appendChild(T),t.appendChild(o);function F(){if(!l)return null;const z=i.value.trim();if(z.length===0)return null;const ce=new Set;for(const V of l.nodes)dn(V,z)&&ce.add(V.id);return ce}function b(){const z=F();y==null||y(z),U()}function U(){T.innerHTML="",X=-1;const z=i.value.trim();if(!l||z.length===0){T.classList.add("hidden");return}const ce=[];for(const V of l.nodes)if(dn(V,z)&&(ce.push(V),ce.length>=s))break;if(ce.length===0){T.classList.add("hidden");return}for(const V of ce){const B=document.createElement("li");B.className="search-result-item";const Q=document.createElement("span");Q.className="search-result-dot",Q.style.backgroundColor=Se(V.type);const K=document.createElement("span");K.className="search-result-label";const ae=vn(V);K.textContent=ae.length>36?ae.slice(0,34)+"...":ae;const J=document.createElement("span");J.className="search-result-type",J.textContent=V.type,B.appendChild(Q),B.appendChild(K),B.appendChild(J),B.addEventListener("click",()=>{k==null||k(V.id),i.value="",T.classList.add("hidden"),b()}),T.appendChild(B)}T.classList.remove("hidden")}i.addEventListener("input",()=>{c&&clearTimeout(c),c=setTimeout(b,r)});let X=-1;function te(){const z=T.querySelectorAll(".search-result-item");z.forEach((ce,V)=>{ce.classList.toggle("search-result-active",V===X)}),X>=0&&z[X]&&z[X].scrollIntoView({block:"nearest"})}return i.addEventListener("keydown",z=>{const ce=T.querySelectorAll(".search-result-item");z.key==="ArrowDown"?(z.preventDefault(),ce.length>0&&(X=Math.min(X+1,ce.length-1),te())):z.key==="ArrowUp"?(z.preventDefault(),ce.length>0&&(X=Math.max(X-1,0),te())):z.key==="Enter"?(z.preventDefault(),X>=0&&ce[X]?ce[X].click():ce.length>0&&ce[0].click(),i.blur()):z.key==="Escape"&&(i.value="",i.blur(),T.classList.add("hidden"),X=-1,b())}),document.addEventListener("click",z=>{o.contains(z.target)||T.classList.add("hidden")}),i.addEventListener("focus",()=>a.classList.add("hidden")),i.addEventListener("blur",()=>{i.value.length===0&&a.classList.remove("hidden")}),{setLearningGraphData(z){l=z,i.value="",T.classList.add("hidden"),l&&l.nodes.length>0?o.classList.remove("hidden"):o.classList.add("hidden")},onFilterChange(z){y=z},onNodeSelect(z){k=z},clear(){i.value="",T.classList.add("hidden"),y==null||y(null)},focus(){i.focus()}}}function ao(t,e){let s=null,r=null,l=!0,y=null,k=!0,c=!0,o=!0,P="types",i="",a="",T=[],F=[];const b={types:new Set,nodeIds:new Set};function U(){if(!s)return[];const d=new Set;for(const m of s.nodes)b.types.has(m.type)&&d.add(m.id);for(const m of b.nodeIds)d.add(m);return[...d]}function X(d){if(b.nodeIds.has(d))return!0;const m=s==null?void 0:s.nodes.find(g=>g.id===d);return m?b.types.has(m.type):!1}function te(){return b.types.size===0&&b.nodeIds.size===0}function z(){const d=U();e.onFocusChange(d.length>0?d:null)}const ce=document.createElement("button");ce.className="tools-pane-toggle hidden",ce.title="Graph Inspector",ce.innerHTML='<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 7h16"/><path d="M4 12h16"/><path d="M4 17h10"/></svg>';const V=document.createElement("div");V.className="tools-pane-content hidden",t.appendChild(ce),t.appendChild(V),ce.addEventListener("click",()=>{var d;l=!l,V.classList.toggle("hidden",l),ce.classList.toggle("active",!l),l||(d=e.onOpen)==null||d.call(e)});function B(){if(V.innerHTML="",!r)return;const d=document.createElement("div");if(d.className="tools-pane-summary",d.innerHTML=`<span>${r.nodeCount} nodes</span><span class="tools-pane-sep">&middot;</span><span>${r.edgeCount} edges</span><span class="tools-pane-sep">&middot;</span><span>${r.types.length} types</span>`,V.appendChild(d),s&&r.nodeCount>0){const S=Math.ceil(JSON.stringify(s).length/4),u=Math.round(S/r.nodeCount),x=Math.max(10,Math.round(u*.3)*Math.min(5,r.nodeCount)),R=S>x?Math.round((1-x/S)*100):0,I=document.createElement("div");I.className="tools-pane-token-card";const O=Math.min(100,R),w=document.createElement("div");w.className="token-card-label",w.textContent="Token Efficiency",I.appendChild(w);const f=document.createElement("div");f.className="token-card-stat",f.textContent=`~${S.toLocaleString()} tokens stored`,I.appendChild(f);const N=document.createElement("div");N.className="token-card-bar";const j=document.createElement("div");j.className="token-card-bar-fill",j.style.width=`${O}%`,N.appendChild(j),I.appendChild(N);const oe=document.createElement("div");oe.className="token-card-stat",oe.textContent=`A search returns ~${x} tokens instead of ~${S.toLocaleString()} (${R}% reduction)`,I.appendChild(oe),V.appendChild(I)}const m=document.createElement("div");m.className="tools-pane-tabs";const g=[{id:"types",label:"Types"},{id:"insights",label:"Insights"},{id:"controls",label:"Controls"}];for(const S of g){const u=document.createElement("button");u.className="tools-pane-tab",P===S.id&&u.classList.add("tools-pane-tab-active"),u.textContent=S.label,u.addEventListener("click",()=>{P=S.id,B()}),m.appendChild(u)}V.appendChild(m),te()||ae(),F.length>0&&K(),P==="types"&&r.types.length>5?V.appendChild(ie("Filter types...",i,S=>{i=S,Q()})):P==="insights"&&r.orphans.length+r.singletons.length+r.emptyNodes.length>5&&V.appendChild(ie("Filter issues...",a,u=>{a=u,Q()}));const C=document.createElement("div");C.className="tools-pane-tab-content",V.appendChild(C),Q()}function Q(){const d=V.querySelector(".tools-pane-tab-content");d&&(d.innerHTML="",P==="types"?ee(d):P==="insights"?ye(d):P==="controls"&&ke(d))}function K(){V.appendChild(W(`Walk Trail (${F.length})`,d=>{for(let g=0;g<F.length;g++){const C=F[g],S=g===F.length-1;if(C.edgeType){const f=document.createElement("div");f.className="walk-trail-edge",f.textContent=`↓ ${C.edgeType}`,d.appendChild(f)}const u=document.createElement("div");u.className="tools-pane-row tools-pane-clickable",S&&(u.style.fontWeight="600");const x=document.createElement("span");x.className="tools-pane-count",x.style.minWidth="18px",x.textContent=`${g+1}`;const R=document.createElement("span");R.className="tools-pane-dot",R.style.backgroundColor=Se(C.type);const I=document.createElement("span");I.className="tools-pane-name",I.textContent=C.label;const O=document.createElement("span");O.className="tools-pane-count",O.textContent=C.type;const w=document.createElement("button");w.className="tools-pane-edit",w.style.opacity="1",w.textContent="×",w.title="Remove from trail",w.addEventListener("click",f=>{var N;f.stopPropagation(),(N=e.onWalkTrailRemove)==null||N.call(e,C.id)}),u.appendChild(x),u.appendChild(R),u.appendChild(I),u.appendChild(O),u.appendChild(w),u.addEventListener("click",()=>{e.onNavigateToNode(C.id)}),d.appendChild(u)}const m=document.createElement("div");if(m.className="tools-pane-export-row",e.onWalkIsolate){const g=document.createElement("button");g.className="tools-pane-export-btn",g.textContent="Isolate (I)",g.addEventListener("click",()=>e.onWalkIsolate()),m.appendChild(g)}if(e.onWalkSaveSnippet&&F.length>=2){const g=document.createElement("button");g.className="tools-pane-export-btn",g.textContent="Save snippet",g.addEventListener("click",()=>{tt("Save snippet","Name for this snippet").then(C=>{C&&e.onWalkSaveSnippet(C)})}),m.appendChild(g)}d.appendChild(m)}))}function ae(){if(!r||!s)return;const d=U();V.appendChild(W("Focused",m=>{for(const u of b.types){const x=r.types.find(N=>N.name===u);if(!x)continue;const R=document.createElement("div");R.className="tools-pane-row tools-pane-clickable";const I=document.createElement("span");I.className="tools-pane-dot",I.style.backgroundColor=Se(x.name);const O=document.createElement("span");O.className="tools-pane-name",O.textContent=x.name;const w=document.createElement("span");w.className="tools-pane-count",w.textContent=`${x.count} nodes`;const f=document.createElement("button");f.className="tools-pane-edit tools-pane-focus-active",f.style.opacity="1",f.textContent="×",f.title=`Remove ${x.name} from focus`,R.appendChild(I),R.appendChild(O),R.appendChild(w),R.appendChild(f),f.addEventListener("click",N=>{N.stopPropagation(),b.types.delete(x.name),z(),B()}),m.appendChild(R)}for(const u of b.nodeIds){const x=s.nodes.find(j=>j.id===u);if(!x)continue;const R=pn(x.properties)??x.id,I=document.createElement("div");I.className="tools-pane-row tools-pane-clickable";const O=document.createElement("span");O.className="tools-pane-dot",O.style.backgroundColor=Se(x.type);const w=document.createElement("span");w.className="tools-pane-name",w.textContent=R;const f=document.createElement("span");f.className="tools-pane-count",f.textContent=x.type;const N=document.createElement("button");N.className="tools-pane-edit tools-pane-focus-active",N.style.opacity="1",N.textContent="×",N.title=`Remove ${R} from focus`,I.appendChild(O),I.appendChild(w),I.appendChild(f),I.appendChild(N),I.addEventListener("click",j=>{j.target.closest(".tools-pane-edit")||e.onNavigateToNode(u)}),N.addEventListener("click",j=>{j.stopPropagation(),b.nodeIds.delete(u),z(),B()}),m.appendChild(I)}const g=document.createElement("div");g.className="tools-pane-row tools-pane-clickable tools-pane-focus-clear";const C=document.createElement("span");C.className="tools-pane-name",C.style.color="var(--accent)",C.textContent=`${d.length} total`;const S=document.createElement("span");S.className="tools-pane-badge",S.textContent="clear all",g.appendChild(C),g.appendChild(S),g.addEventListener("click",()=>{b.types.clear(),b.nodeIds.clear(),z(),B()}),m.appendChild(g)}))}function J(d){const m=document.createElement("div");m.className="tools-pane-row tools-pane-clickable",y===d.name&&m.classList.add("active");const g=document.createElement("span");g.className="tools-pane-dot",g.style.backgroundColor=Se(d.name);const C=document.createElement("span");C.className="tools-pane-name",C.textContent=d.name;const S=document.createElement("span");S.className="tools-pane-count",S.textContent=String(d.count);const u=document.createElement("button");u.className="tools-pane-edit tools-pane-focus-toggle",b.types.has(d.name)&&u.classList.add("tools-pane-focus-active"),u.textContent="◎",u.title=b.types.has(d.name)?`Remove ${d.name} from focus`:`Add ${d.name} to focus`;const x=document.createElement("button");return x.className="tools-pane-edit",x.textContent="✎",x.title=`Rename all ${d.name} nodes`,m.appendChild(g),m.appendChild(C),m.appendChild(S),m.appendChild(u),m.appendChild(x),m.addEventListener("click",R=>{R.target.closest(".tools-pane-edit")||(y===d.name?(y=null,e.onFilterByType(null)):(y=d.name,e.onFilterByType(d.name)),B())}),u.addEventListener("click",R=>{R.stopPropagation(),b.types.has(d.name)?b.types.delete(d.name):b.types.add(d.name),z(),B()}),x.addEventListener("click",R=>{R.stopPropagation(),D(m,d.name,I=>{I&&I!==d.name&&e.onRenameNodeType(d.name,I)})}),m}function ie(d,m,g){const C=document.createElement("input");return C.type="text",C.className="tools-pane-search",C.placeholder=d,C.value=m,C.addEventListener("input",()=>g(C.value)),C}function ee(d){if(!r)return;const m=i.toLowerCase();if(r.types.length){const C=r.types.filter(S=>!b.types.has(S.name)).filter(S=>!m||S.name.toLowerCase().includes(m));C.length>0&&d.appendChild(W("Node Types",S=>{for(const u of C)S.appendChild(J(u))}))}const g=r.edgeTypes.filter(C=>!m||C.name.toLowerCase().includes(m));g.length&&d.appendChild(W("Edge Types",C=>{for(const S of g){const u=document.createElement("div");u.className="tools-pane-row tools-pane-clickable";const x=document.createElement("span");x.className="tools-pane-name",x.textContent=S.name;const R=document.createElement("span");R.className="tools-pane-count",R.textContent=String(S.count);const I=document.createElement("button");I.className="tools-pane-edit",I.textContent="✎",I.title=`Rename all ${S.name} edges`,u.appendChild(x),u.appendChild(R),u.appendChild(I),I.addEventListener("click",O=>{O.stopPropagation(),D(u,S.name,w=>{w&&w!==S.name&&e.onRenameEdgeType(S.name,w)})}),C.appendChild(u)}}))}function ye(d){if(!r)return;const m=a.toLowerCase(),g=r.starred.filter(w=>!m||w.label.toLowerCase().includes(m)||w.type.toLowerCase().includes(m));g.length&&d.appendChild(W("★ Starred",w=>{for(const j of g){const oe=document.createElement("div");oe.className="tools-pane-row tools-pane-clickable";const pe=document.createElement("span");pe.className="tools-pane-dot",pe.style.backgroundColor=Se(j.type);const n=document.createElement("span");n.className="tools-pane-name",n.textContent=j.label;const p=document.createElement("button");p.className="tools-pane-edit tools-pane-focus-toggle",X(j.id)&&p.classList.add("tools-pane-focus-active"),p.textContent="◎",p.title=X(j.id)?`Remove ${j.label} from focus`:`Add ${j.label} to focus`,oe.appendChild(pe),oe.appendChild(n),oe.appendChild(p),oe.addEventListener("click",M=>{M.target.closest(".tools-pane-edit")||e.onNavigateToNode(j.id)}),p.addEventListener("click",M=>{M.stopPropagation(),b.nodeIds.has(j.id)?b.nodeIds.delete(j.id):b.nodeIds.add(j.id),z(),B()}),w.appendChild(oe)}const f=document.createElement("div");f.className="tools-pane-row tools-pane-actions";const N=document.createElement("button");if(N.className="tools-pane-action-btn",N.textContent="Focus all",N.title="Enter focus mode with all starred nodes",N.addEventListener("click",()=>{b.nodeIds.clear(),b.types.clear();for(const j of r.starred)b.nodeIds.add(j.id);z(),B()}),f.appendChild(N),e.onStarredSaveSnippet){const j=document.createElement("button");j.className="tools-pane-action-btn",j.textContent="Save as snippet",j.title="Save starred nodes as a reusable snippet",j.addEventListener("click",async()=>{const oe=await tt("Snippet name","starred");oe&&e.onStarredSaveSnippet(oe,r.starred.map(pe=>pe.id))}),f.appendChild(j)}w.appendChild(f)}));const C=r.mostConnected.filter(w=>!m||w.label.toLowerCase().includes(m)||w.type.toLowerCase().includes(m));C.length&&d.appendChild(W("Most Connected",w=>{for(const f of C){const N=document.createElement("div");N.className="tools-pane-row tools-pane-clickable";const j=document.createElement("span");j.className="tools-pane-dot",j.style.backgroundColor=Se(f.type);const oe=document.createElement("span");oe.className="tools-pane-name",oe.textContent=f.label;const pe=document.createElement("span");pe.className="tools-pane-count",pe.textContent=`${f.connections}`;const n=document.createElement("button");n.className="tools-pane-edit tools-pane-focus-toggle",X(f.id)&&n.classList.add("tools-pane-focus-active"),n.textContent="◎",n.title=X(f.id)?`Remove ${f.label} from focus`:`Add ${f.label} to focus`,N.appendChild(j),N.appendChild(oe),N.appendChild(pe),N.appendChild(n),N.addEventListener("click",p=>{p.target.closest(".tools-pane-edit")||e.onNavigateToNode(f.id)}),n.addEventListener("click",p=>{p.stopPropagation(),b.nodeIds.has(f.id)?b.nodeIds.delete(f.id):b.nodeIds.add(f.id),z(),B()}),w.appendChild(N)}}));const S=r.orphans.filter(w=>!m||w.label.toLowerCase().includes(m)||w.type.toLowerCase().includes(m)),u=r.singletons.filter(w=>!m||w.name.toLowerCase().includes(m)),x=r.emptyNodes.filter(w=>!m||w.label.toLowerCase().includes(m)||w.type.toLowerCase().includes(m)),R=S.length>0,I=u.length>0,O=x.length>0;if(!R&&!I&&!O){const w=document.createElement("div");w.className="tools-pane-empty-msg",w.textContent="No issues found",d.appendChild(w);return}R&&d.appendChild(W("Orphans",w=>{for(const f of S.slice(0,5)){const N=document.createElement("div");N.className="tools-pane-row tools-pane-clickable tools-pane-issue";const j=document.createElement("span");j.className="tools-pane-dot",j.style.backgroundColor=Se(f.type);const oe=document.createElement("span");oe.className="tools-pane-name",oe.textContent=f.label;const pe=document.createElement("span");pe.className="tools-pane-badge",pe.textContent="orphan";const n=document.createElement("button");n.className="tools-pane-edit tools-pane-focus-toggle",X(f.id)&&n.classList.add("tools-pane-focus-active"),n.textContent="◎",n.title=X(f.id)?`Remove ${f.label} from focus`:`Add ${f.label} to focus`,N.appendChild(j),N.appendChild(oe),N.appendChild(pe),N.appendChild(n),N.addEventListener("click",p=>{p.target.closest(".tools-pane-edit")||e.onNavigateToNode(f.id)}),n.addEventListener("click",p=>{p.stopPropagation(),b.nodeIds.has(f.id)?b.nodeIds.delete(f.id):b.nodeIds.add(f.id),z(),B()}),w.appendChild(N)}if(S.length>5){const f=document.createElement("div");f.className="tools-pane-more",f.textContent=`+ ${S.length-5} more orphans`,w.appendChild(f)}})),I&&d.appendChild(W("Singletons",w=>{for(const f of u.slice(0,5)){const N=document.createElement("div");N.className="tools-pane-row tools-pane-issue";const j=document.createElement("span");j.className="tools-pane-dot",j.style.backgroundColor=Se(f.name);const oe=document.createElement("span");oe.className="tools-pane-name",oe.textContent=f.name;const pe=document.createElement("span");pe.className="tools-pane-badge",pe.textContent="1 node",N.appendChild(j),N.appendChild(oe),N.appendChild(pe),w.appendChild(N)}})),O&&d.appendChild(W("Empty Nodes",w=>{for(const f of x.slice(0,5)){const N=document.createElement("div");N.className="tools-pane-row tools-pane-issue";const j=document.createElement("span");j.className="tools-pane-dot",j.style.backgroundColor=Se(f.type);const oe=document.createElement("span");oe.className="tools-pane-name",oe.textContent=f.label;const pe=document.createElement("span");pe.className="tools-pane-badge",pe.textContent="empty",N.appendChild(j),N.appendChild(oe),N.appendChild(pe),w.appendChild(N)}if(r.emptyNodes.length>5){const f=document.createElement("div");f.className="tools-pane-more",f.textContent=`+ ${r.emptyNodes.length-5} more empty nodes`,w.appendChild(f)}}))}function ke(d){d.appendChild(W("Display",m=>{const g=document.createElement("div");g.className="tools-pane-row tools-pane-clickable";const C=document.createElement("input");C.type="checkbox",C.checked=k,C.className="tools-pane-checkbox";const S=document.createElement("span");S.className="tools-pane-name",S.textContent="Edge labels",g.appendChild(C),g.appendChild(S),g.addEventListener("click",f=>{f.target!==C&&(C.checked=!C.checked),k=C.checked,e.onToggleEdgeLabels(k)}),m.appendChild(g);const u=document.createElement("div");u.className="tools-pane-row tools-pane-clickable";const x=document.createElement("input");x.type="checkbox",x.checked=c,x.className="tools-pane-checkbox";const R=document.createElement("span");R.className="tools-pane-name",R.textContent="Type regions",u.appendChild(x),u.appendChild(R),u.addEventListener("click",f=>{f.target!==x&&(x.checked=!x.checked),c=x.checked,e.onToggleTypeHulls(c)}),m.appendChild(u);const I=document.createElement("div");I.className="tools-pane-row tools-pane-clickable";const O=document.createElement("input");O.type="checkbox",O.checked=o,O.className="tools-pane-checkbox";const w=document.createElement("span");w.className="tools-pane-name",w.textContent="Minimap",I.appendChild(O),I.appendChild(w),I.addEventListener("click",f=>{f.target!==O&&(O.checked=!O.checked),o=O.checked,e.onToggleMinimap(o)}),m.appendChild(I)})),d.appendChild(W("Layout",m=>{m.appendChild(_("Clustering",0,1,.02,.08,g=>{e.onLayoutChange("clusterStrength",g)})),m.appendChild(_("Spacing",.5,20,.5,1.5,g=>{e.onLayoutChange("spacing",g)})),m.appendChild(_("Pan speed",20,200,10,60,g=>{e.onPanSpeedChange(g)}))})),d.appendChild(W("Export",m=>{const g=document.createElement("div");g.className="tools-pane-export-row";const C=document.createElement("button");C.className="tools-pane-export-btn",C.textContent="Export PNG",C.addEventListener("click",()=>e.onExport("png"));const S=document.createElement("button");S.className="tools-pane-export-btn",S.textContent="Export SVG",S.addEventListener("click",()=>e.onExport("svg")),g.appendChild(C),g.appendChild(S),m.appendChild(g)})),(e.onSnapshot||e.onRollback)&&d.appendChild(W("Versions",m=>{const g=document.createElement("div");g.className="tools-pane-export-row";const C=document.createElement("button");if(C.className="tools-pane-export-btn",C.textContent="Save snapshot",C.addEventListener("click",()=>{tt("Save snapshot","Label (optional)").then(S=>{var u;(u=e.onSnapshot)==null||u.call(e,S||void 0)})}),g.appendChild(C),m.appendChild(g),T.length>0)for(const S of T){const u=document.createElement("div");u.className="tools-pane-row";const x=document.createElement("span");x.className="tools-pane-name";const R=co(S.timestamp);x.textContent=S.label?`#${S.version} ${S.label}`:`#${S.version}`,x.title=`${R} — ${S.nodeCount} nodes, ${S.edgeCount} edges`;const I=document.createElement("span");I.className="tools-pane-count",I.textContent=R;const O=document.createElement("button");O.className="tools-pane-edit",O.style.opacity="1",O.textContent="↩",O.title="Restore this snapshot",O.addEventListener("click",()=>{Tt("Restore snapshot",`Restore snapshot #${S.version}? Current state will be lost unless you save a snapshot first.`).then(w=>{var f;w&&((f=e.onRollback)==null||f.call(e,S.version))})}),u.appendChild(x),u.appendChild(I),u.appendChild(O),m.appendChild(u)}else{const S=document.createElement("div");S.className="tools-pane-empty-msg",S.textContent="No snapshots yet",m.appendChild(S)}}))}function _(d,m,g,C,S,u){const x=document.createElement("div");x.className="tools-pane-slider-row";const R=document.createElement("span");R.className="tools-pane-slider-label",R.textContent=d;const I=document.createElement("input");I.type="range",I.className="tools-pane-slider",I.min=String(m),I.max=String(g),I.step=String(C),I.value=String(S);const O=document.createElement("span");return O.className="tools-pane-slider-value",O.textContent=String(S),I.addEventListener("input",()=>{const w=parseFloat(I.value);O.textContent=w%1===0?String(w):w.toFixed(2),u(w)}),x.appendChild(R),x.appendChild(I),x.appendChild(O),x}function W(d,m){const g=document.createElement("div");g.className="tools-pane-section";const C=document.createElement("div");return C.className="tools-pane-heading",C.textContent=d,g.appendChild(C),m(g),g}function D(d,m,g){const C=document.createElement("input");C.className="tools-pane-inline-input",C.value=m,C.type="text";const S=d.innerHTML;d.innerHTML="",d.classList.add("tools-pane-editing"),d.appendChild(C),C.focus(),C.select();function u(){const x=C.value.trim();d.classList.remove("tools-pane-editing"),x&&x!==m?g(x):d.innerHTML=S}C.addEventListener("keydown",x=>{x.key==="Enter"&&(x.preventDefault(),u()),x.key==="Escape"&&(d.innerHTML=S,d.classList.remove("tools-pane-editing"))}),C.addEventListener("blur",u)}function E(d){const m=new Map,g=new Map,C=new Map,S=new Set;for(const f of d.nodes)m.set(f.type,(m.get(f.type)??0)+1);for(const f of d.edges)g.set(f.type,(g.get(f.type)??0)+1),C.set(f.sourceId,(C.get(f.sourceId)??0)+1),C.set(f.targetId,(C.get(f.targetId)??0)+1),S.add(f.sourceId),S.add(f.targetId);const u=f=>pn(f.properties)??f.id,x=d.nodes.filter(f=>f.properties._starred===!0).map(f=>({id:f.id,label:u(f),type:f.type})),R=d.nodes.filter(f=>!S.has(f.id)).map(f=>({id:f.id,label:u(f),type:f.type})),I=[...m.entries()].filter(([,f])=>f===1).map(([f])=>({name:f})),O=d.nodes.filter(f=>Object.keys(f.properties).length===0).map(f=>({id:f.id,label:f.id,type:f.type})),w=d.nodes.map(f=>({id:f.id,label:u(f),type:f.type,connections:C.get(f.id)??0})).filter(f=>f.connections>0).sort((f,N)=>N.connections-f.connections).slice(0,5);return{nodeCount:d.nodes.length,edgeCount:d.edges.length,types:[...m.entries()].sort((f,N)=>N[1]-f[1]).map(([f,N])=>({name:f,count:N})),edgeTypes:[...g.entries()].sort((f,N)=>N[1]-f[1]).map(([f,N])=>({name:f,count:N})),starred:x,orphans:R,singletons:I,emptyNodes:O,mostConnected:w}}return{collapse(){l=!0,V.classList.add("hidden"),ce.classList.remove("active")},addToFocusSet(d){for(const m of d)b.nodeIds.add(m);z(),B()},clearFocusSet(){b.types.clear(),b.nodeIds.clear(),z(),B()},setData(d){s=d,y=null,b.types.clear(),b.nodeIds.clear(),s&&s.nodes.length>0?(r=E(s),ce.classList.remove("hidden"),B()):(r=null,ce.classList.add("hidden"),V.classList.add("hidden"))},setSnapshots(d){T=d,P==="controls"&&Q()},setWalkTrail(d){F=d,B()}}}function co(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 r=Math.floor(s/60);return r<24?`${r}h ago`:`${Math.floor(r/24)}d ago`}function pn(t){for(const e of Object.values(t))if(typeof e=="string")return e;return null}function io(t,e){const s=e.split("+"),r=s.pop(),l=s.map(o=>o.toLowerCase()),y=l.includes("ctrl")||l.includes("cmd")||l.includes("meta"),k=l.includes("shift"),c=l.includes("alt");return y!==(t.ctrlKey||t.metaKey)||!y&&(t.ctrlKey||t.metaKey)||k&&!t.shiftKey||c!==t.altKey?!1:r.toLowerCase()==="escape"?t.key==="Escape":r.toLowerCase()==="tab"?t.key==="Tab":l.length>0?t.key.toLowerCase()===r.toLowerCase():t.key===r}function lo(){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"}}const ro=[{key:"Click",description:"Select node"},{key:"Ctrl+Click",description:"Multi-select nodes"},{key:"Drag",description:"Pan canvas"},{key:"Scroll",description:"Zoom in/out"}],po=["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","escape"];function uo(t){return t.split("+").map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join("+")}function mo(t,e){const s=lo(),r=document.createElement("div");r.className="shortcuts-overlay hidden";const l=document.createElement("div");l.className="shortcuts-modal";const y=document.createElement("h3");y.className="shortcuts-title",y.textContent="Keyboard Shortcuts";const k=document.createElement("div");k.className="shortcuts-list";for(const a of po){const T=e[a];if(!T)continue;const F=document.createElement("div");F.className="shortcuts-row";const b=document.createElement("div");b.className="shortcuts-keys";const U=document.createElement("kbd");U.textContent=uo(T),b.appendChild(U);const X=document.createElement("span");X.className="shortcuts-desc",X.textContent=s[a],F.appendChild(b),F.appendChild(X),k.appendChild(F)}for(const a of ro){const T=document.createElement("div");T.className="shortcuts-row";const F=document.createElement("div");F.className="shortcuts-keys";const b=document.createElement("kbd");b.textContent=a.key,F.appendChild(b);const U=document.createElement("span");U.className="shortcuts-desc",U.textContent=a.description,T.appendChild(F),T.appendChild(U),k.appendChild(T)}const c=document.createElement("button");c.className="shortcuts-close",c.textContent="×",l.appendChild(c),l.appendChild(y),l.appendChild(k),r.appendChild(l),t.appendChild(r);function o(){r.classList.remove("hidden")}function P(){r.classList.add("hidden")}function i(){r.classList.toggle("hidden")}return c.addEventListener("click",P),r.addEventListener("click",a=>{a.target===r&&P()}),{show:o,hide:P,toggle:i}}function ho(t){const e=document.createElement("div");return e.className="empty-state",e.innerHTML=`
5
+ <div class="empty-state-bg">
6
+ <div class="empty-state-circle c1"></div>
7
+ <div class="empty-state-circle c2"></div>
8
+ <div class="empty-state-circle c3"></div>
9
+ <div class="empty-state-circle c4"></div>
10
+ <div class="empty-state-circle c5"></div>
11
+ <svg class="empty-state-lines" viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice">
12
+ <line x1="80" y1="60" x2="220" y2="140" stroke="currentColor" stroke-width="0.5" opacity="0.15"/>
13
+ <line x1="220" y1="140" x2="320" y2="80" stroke="currentColor" stroke-width="0.5" opacity="0.15"/>
14
+ <line x1="220" y1="140" x2="160" y2="240" stroke="currentColor" stroke-width="0.5" opacity="0.15"/>
15
+ <line x1="160" y1="240" x2="300" y2="220" stroke="currentColor" stroke-width="0.5" opacity="0.15"/>
16
+ </svg>
17
+ </div>
18
+ <div class="empty-state-content">
19
+ <div class="empty-state-icon">
20
+ <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
21
+ <path d="M21 16V8a2 2 0 00-1-1.73l-7-4a2 2 0 00-2 0l-7 4A2 2 0 002 8v8a2 2 0 001 1.73l7 4a2 2 0 002 0l7-4A2 2 0 0022 16z"/>
22
+ <polyline points="3.27 6.96 12 12.01 20.73 6.96"/>
23
+ <line x1="12" y1="22.08" x2="12" y2="12"/>
24
+ </svg>
25
+ </div>
26
+ <h2 class="empty-state-title">No learning graphs yet</h2>
27
+ <p class="empty-state-desc">Connect Backpack to Claude, then start a conversation. Claude will build your first learning graph automatically.</p>
28
+ <div class="empty-state-setup">
29
+ <div class="empty-state-label">Add Backpack to Claude Code:</div>
30
+ <code class="empty-state-code">claude mcp add backpack-local -s user -- npx backpack-ontology@latest</code>
31
+ </div>
32
+ <p class="empty-state-hint">Press <kbd>?</kbd> for keyboard shortcuts</p>
33
+ </div>
34
+ `,t.appendChild(e),{show(){e.classList.remove("hidden")},hide(){e.classList.add("hidden")}}}const fo=30;function go(){let t=[],e=[];return{push(s){t.push(JSON.stringify(s)),t.length>fo&&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 yo(t,e){let s=null;function r(k,c,o,P,i){l(),s=document.createElement("div"),s.className="context-menu",s.style.left=`${P}px`,s.style.top=`${i}px`;const a=[{label:o?"★ Unstar":"☆ Star",action:()=>e.onStar(k),premium:!1},{label:"◎ Focus on node",action:()=>e.onFocusNode(k),premium:!1},{label:"⑂ Explore in branch",action:()=>e.onExploreInBranch(k),premium:!1},{label:"⎘ Copy ID",action:()=>e.onCopyId(k),premium:!1}];e.onExpand&&a.push({label:"⊕ Expand node",action:()=>e.onExpand(k),premium:!0}),e.onExplainPath&&a.push({label:"↔ Explain path to…",action:()=>e.onExplainPath(k),premium:!0}),e.onEnrich&&a.push({label:"≡ Enrich from web",action:()=>e.onEnrich(k),premium:!0});let T=!1;for(const b of a){if(!T&&b.premium){const X=document.createElement("div");X.className="context-menu-separator",s.appendChild(X),T=!0}const U=document.createElement("div");U.className="context-menu-item",U.textContent=b.label,U.addEventListener("click",()=>{b.action(),l()}),s.appendChild(U)}t.appendChild(s);const F=s.getBoundingClientRect();F.right>window.innerWidth&&(s.style.left=`${P-F.width}px`),F.bottom>window.innerHeight&&(s.style.top=`${i-F.height}px`),setTimeout(()=>document.addEventListener("click",l),0),document.addEventListener("keydown",y)}function l(){s&&(s.remove(),s=null),document.removeEventListener("click",l),document.removeEventListener("keydown",y)}function y(k){k.key==="Escape"&&l()}return{show:r,hide:l}}const Co={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"},vo={edges:!0,edgeLabels:!0,typeHulls:!0,minimap:!0,theme:"system"},xo={spacing:1.5,clustering:.08},bo={panSpeed:60,panFastMultiplier:3,zoomFactor:1.3,zoomMin:.05,zoomMax:10,panAnimationMs:300},Eo={hideBadges:.4,hideLabels:.25,hideEdgeLabels:.35,smallNodes:.2,hideArrows:.15},wo={pulseSpeed:.02},ko={maxSearchResults:8,maxQualityItems:5,maxMostConnected:5,searchDebounceMs:150},So={keybindings:Co,display:vo,layout:xo,navigation:bo,lod:Eo,walk:wo,limits:ko};let de="",L=null,It=new Set,Mt=!1;async function No(){const t=document.getElementById("canvas-container"),e={...So};try{const n=await fetch("/api/config");if(n.ok){const p=await n.json();Object.assign(e.keybindings,p.keybindings??{}),Object.assign(e.display,p.display??{}),Object.assign(e.layout,p.layout??{}),Object.assign(e.navigation,p.navigation??{}),Object.assign(e.lod,p.lod??{}),Object.assign(e.limits,p.limits??{})}}catch{}const s=e.keybindings,r=window.matchMedia("(prefers-color-scheme: dark)"),l=e.display.theme==="system"?r.matches?"dark":"light":e.display.theme,k=localStorage.getItem("backpack-theme")??l;document.documentElement.setAttribute("data-theme",k);const c=document.createElement("button");c.className="theme-toggle",c.textContent=k==="light"?"☾":"☼",c.title="Toggle light/dark mode",c.addEventListener("click",()=>{const p=document.documentElement.getAttribute("data-theme")==="light"?"dark":"light";document.documentElement.setAttribute("data-theme",p),localStorage.setItem("backpack-theme",p),c.textContent=p==="light"?"☾":"☼"}),t.appendChild(c);const o=go();async function P(){if(!de||!L)return;L.metadata.updatedAt=new Date().toISOString(),await wt(de,L),a.loadGraph(L),ae.setLearningGraphData(L),J.setData(L);const n=await ct();E.setSummaries(n)}async function i(n){L=n,await wt(de,L),a.loadGraph(L),ae.setLearningGraphData(L),J.setData(L);const p=await ct();E.setSummaries(p)}let a;const T=to(t,{onUpdateNode(n,p){if(!L)return;o.push(L);const M=L.nodes.find(G=>G.id===n);M&&(M.properties={...M.properties,...p},M.updatedAt=new Date().toISOString(),P().then(()=>T.show([n],L)))},onChangeNodeType(n,p){if(!L)return;o.push(L);const M=L.nodes.find(G=>G.id===n);M&&(M.type=p,M.updatedAt=new Date().toISOString(),P().then(()=>T.show([n],L)))},onDeleteNode(n){L&&(o.push(L),L.nodes=L.nodes.filter(p=>p.id!==n),L.edges=L.edges.filter(p=>p.sourceId!==n&&p.targetId!==n),P())},onDeleteEdge(n){var M;if(!L)return;o.push(L);const p=(M=L.edges.find(G=>G.id===n))==null?void 0:M.sourceId;L.edges=L.edges.filter(G=>G.id!==n),P().then(()=>{p&&L&&T.show([p],L)})},onAddProperty(n,p,M){if(!L)return;o.push(L);const G=L.nodes.find(re=>re.id===n);G&&(G.properties[p]=M,G.updatedAt=new Date().toISOString(),P().then(()=>T.show([n],L)))}},n=>{a.panToNode(n)},n=>{J.addToFocusSet(n)}),F=window.matchMedia("(max-width: 768px)");let b=[],U=e.display.edges,X=e.navigation.panSpeed,te=-1,z=null;function ce(n){z&&z.remove(),z=document.createElement("div"),z.className="focus-indicator";const p=document.createElement("span");p.className="focus-indicator-label",p.textContent=`Focused: ${n.totalNodes} nodes`;const M=document.createElement("span");M.className="focus-indicator-hops",M.textContent=`${n.hops}`;const G=document.createElement("button");G.className="focus-indicator-btn",G.textContent="−",G.title="Fewer hops",G.disabled=n.hops===0,G.addEventListener("click",()=>{a.enterFocus(n.seedNodeIds,Math.max(0,n.hops-1))});const re=document.createElement("button");re.className="focus-indicator-btn",re.textContent="+",re.title="More hops",re.disabled=!1,re.addEventListener("click",()=>{a.enterFocus(n.seedNodeIds,n.hops+1)});const me=document.createElement("button");me.className="focus-indicator-btn focus-indicator-exit",me.textContent="×",me.title="Exit focus (Esc)",me.addEventListener("click",()=>J.clearFocusSet());const le=document.createElement("button");le.className="walk-indicator",a.getWalkMode()&&le.classList.add("active"),le.textContent="Walk",le.title="Toggle walk mode (W) — click nodes to traverse",le.addEventListener("click",()=>{a.setWalkMode(!a.getWalkMode()),le.classList.toggle("active",a.getWalkMode())}),z.appendChild(p),z.appendChild(G),z.appendChild(M),z.appendChild(re),z.appendChild(le),z.appendChild(me)}function V(){z&&(z.remove(),z=null)}const B=document.createElement("div");B.className="path-bar hidden",t.appendChild(B);function Q(n){if(B.innerHTML="",!L)return;for(let M=0;M<n.nodeIds.length;M++){const G=n.nodeIds[M],re=L.nodes.find(ge=>ge.id===G);if(!re)continue;const me=Object.values(re.properties).find(ge=>typeof ge=="string")??re.id;if(M>0){const ge=n.edgeIds[M-1],Le=L.edges.find(He=>He.id===ge),xe=document.createElement("span");xe.className="path-bar-edge",xe.textContent=Le?`→ ${Le.type} →`:"→",B.appendChild(xe)}const le=document.createElement("span");le.className="path-bar-node",le.textContent=me,le.addEventListener("click",()=>a.panToNode(G)),B.appendChild(le)}const p=document.createElement("button");p.className="path-bar-close",p.textContent="×",p.addEventListener("click",K),B.appendChild(p),B.classList.remove("hidden")}function K(){B.classList.add("hidden"),B.innerHTML="",a.clearHighlightedPath()}a=Qn(t,n=>{if(b=n??[],!a.getWalkMode())if(n&&n.length===2){const p=a.findPath(n[0],n[1]);p&&p.nodeIds.length>0?(a.setHighlightedPath(p.nodeIds,p.edgeIds),Q(p)):K()}else K();n&&n.length>0&&L?(T.show(n,L),F.matches&&J.collapse(),I(de,n)):(T.hide(),de&&I(de))},n=>{if(n){ce(n);const p=t.querySelector(".canvas-top-left");p&&z&&p.appendChild(z),I(de,n.seedNodeIds),T.setFocusDisabled(n.hops===0),m()}else V(),T.setFocusDisabled(!1),de&&I(de),m()},{lod:e.lod,navigation:e.navigation,walk:e.walk});const ae=so(t,{maxResults:e.limits.maxSearchResults,debounceMs:e.limits.searchDebounceMs}),J=ao(t,{onFilterByType(n){if(L)if(n===null)a.setFilteredNodeIds(null);else{const p=new Set(((L==null?void 0:L.nodes)??[]).filter(M=>M.type===n).map(M=>M.id));a.setFilteredNodeIds(p)}},onNavigateToNode(n){a.panToNode(n),L&&T.show([n],L)},onWalkTrailRemove(n){a.removeFromWalkTrail(n),m()},onWalkIsolate(){if(!L)return;const n=a.getWalkTrail();n.length!==0&&a.enterFocus(n,0)},async onWalkSaveSnippet(n){if(!de||!L)return;const p=a.getWalkTrail();if(p.length<2)return;const M=new Set(p),G=L.edges.filter(re=>M.has(re.sourceId)&&M.has(re.targetId)).map(re=>re.id);await _t(de,n,p,G),await S(de)},async onStarredSaveSnippet(n,p){if(!de||!L)return;const M=new Set(p),G=L.edges.filter(re=>M.has(re.sourceId)&&M.has(re.targetId)).map(re=>re.id);await _t(de,n,p,G),await S(de)},onFocusChange(n){n&&n.length>0?a.enterFocus(n,0):a.isFocused()&&a.exitFocus()},onRenameNodeType(n,p){if(L){o.push(L);for(const M of L.nodes)M.type===n&&(M.type=p,M.updatedAt=new Date().toISOString());P()}},onRenameEdgeType(n,p){if(L){o.push(L);for(const M of L.edges)M.type===n&&(M.type=p);P()}},onToggleEdgeLabels(n){a.setEdgeLabels(n)},onToggleTypeHulls(n){a.setTypeHulls(n)},onToggleMinimap(n){a.setMinimap(n)},onLayoutChange(n,p){Qe({[n]:p}),a.reheat()},onPanSpeedChange(n){X=n},onExport(n){const p=a.exportImage(n);if(!p)return;const M=document.createElement("a");M.download=`${de||"graph"}.${n}`,M.href=p,M.click()},onSnapshot:async n=>{de&&(await Tn(de,n),await C(de))},onRollback:async n=>{de&&(await An(de,n),L=await ft(de),a.loadGraph(L),ae.setLearningGraphData(L),J.setData(L),await C(de))},onOpen(){F.matches&&T.hide()}}),ie=document.createElement("div");ie.className="canvas-top-bar";const ee=document.createElement("div");ee.className="canvas-top-left";const ye=document.createElement("div");ye.className="canvas-top-center";const ke=document.createElement("div");ke.className="canvas-top-right";const _=t.querySelector(".tools-pane-toggle");_&&ee.appendChild(_);const W=t.querySelector(".search-overlay");W&&ye.appendChild(W);const D=t.querySelector(".zoom-controls");D&&ke.appendChild(D),ke.appendChild(c),ie.appendChild(ee),ie.appendChild(ye),ie.appendChild(ke),t.appendChild(ie),ae.onFilterChange(n=>{a.setFilteredNodeIds(n)}),ae.onNodeSelect(n=>{a.isFocused()&&J.clearFocusSet(),a.panToNode(n),L&&T.show([n],L)});const E=Fn(document.getElementById("sidebar"),{onSelect:n=>w(n),onRename:async(n,p)=>{await Nn(n,p),de===n&&(de=p);const M=await ct();E.setSummaries(M),E.setActive(de),de===p&&(L=await ft(p),a.loadGraph(L),ae.setLearningGraphData(L),J.setData(L))},onBranchSwitch:async(n,p)=>{await Xt(n,p),await g(n),L=await ft(n),a.loadGraph(L),ae.setLearningGraphData(L),J.setData(L),await C(n)},onBranchCreate:async(n,p)=>{await Yt(n,p),await g(n)},onBranchDelete:async(n,p)=>{await In(n,p),await g(n)},onSnippetLoad:async(n,p)=>{var G;const M=await Rn(n,p);((G=M==null?void 0:M.nodeIds)==null?void 0:G.length)>0&&a.enterFocus(M.nodeIds,0)},onSnippetDelete:async(n,p)=>{await Pn(n,p),await S(n)},onBackpackSwitch:async n=>{await fetch("/api/backpacks/switch",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:n})}),await d()},onBackpackRegister:async(n,p,M)=>{await fetch("/api/backpacks",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:n,path:p,activate:M})}),await d()}});async function d(){try{const p=await(await fetch("/api/backpacks")).json();E.setBackpacks(p)}catch{}try{const n=await ct();E.setSummaries(n),de&&!n.some(p=>p.name===de)&&(de="",L=null,a.loadGraph({metadata:{name:"",description:"",createdAt:"",updatedAt:""},nodes:[],edges:[]}))}catch{}}function m(){const n=a.getWalkTrail();if(!L||n.length===0){J.setWalkTrail([]),K();return}const p=[],M=n.map((G,re)=>{const me=L.nodes.find(ge=>ge.id===G);let le;if(re>0){const ge=n[re-1],Le=L.edges.find(xe=>xe.sourceId===ge&&xe.targetId===G||xe.targetId===ge&&xe.sourceId===G);le=Le==null?void 0:Le.type,Le&&p.push(Le.id)}return{id:G,label:me?Object.values(me.properties).find(ge=>typeof ge=="string")??me.id:G,type:(me==null?void 0:me.type)??"?",edgeType:le}});J.setWalkTrail(M),n.length>=2?(a.setHighlightedPath(n,p),Q({nodeIds:n,edgeIds:p})):K()}async function g(n){const p=await Ln(n),M=p.find(G=>G.active);M&&E.setActiveBranch(n,M.name,p)}async function C(n){const p=await Mn(n);J.setSnapshots(p)}async function S(n){const p=await Bn(n);E.setSnippets(n,p)}ee.insertBefore(E.expandBtn,ee.firstChild);const u=mo(t,s),x=ho(t),R=yo(t,{onStar(n){if(!L)return;const p=L.nodes.find(G=>G.id===n);if(!p)return;const M=p.properties._starred===!0;p.properties._starred=!M,wt(de,L),a.loadGraph(L)},onFocusNode(n){J.addToFocusSet([n])},onExploreInBranch(n){if(de){const p=`explore-${n.slice(0,8)}`;Yt(de,p).then(()=>{Xt(de,p).then(()=>{a.enterFocus([n],1)})})}},onCopyId(n){navigator.clipboard.writeText(n)}});t.addEventListener("contextmenu",n=>{n.preventDefault();const p=t.querySelector("canvas");if(!p||!L)return;const M=p.getBoundingClientRect(),G=n.clientX-M.left,re=n.clientY-M.top,me=a.nodeAtScreen(G,re);if(!me)return;const le=L.nodes.find(xe=>xe.id===me.id);if(!le)return;const ge=Object.values(le.properties).find(xe=>typeof xe=="string")??le.id,Le=le.properties._starred===!0;R.show(le.id,ge,Le,n.clientX-M.left,n.clientY-M.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 I(n,p){const M=[];p!=null&&p.length&&M.push("node="+p.map(encodeURIComponent).join(","));const G=a.getFocusInfo();G&&(M.push("focus="+G.seedNodeIds.map(encodeURIComponent).join(",")),M.push("hops="+G.hops));const re="#"+encodeURIComponent(n)+(M.length?"?"+M.join("&"):"");history.replaceState(null,"",re)}function O(){const n=window.location.hash.slice(1);if(!n)return{graph:null,nodes:[],focus:[],hops:1};const[p,M]=n.split("?"),G=p?decodeURIComponent(p):null;let re=[],me=[],le=1;if(M){const ge=new URLSearchParams(M),Le=ge.get("node");Le&&(re=Le.split(",").map(decodeURIComponent));const xe=ge.get("focus");xe&&(me=xe.split(",").map(decodeURIComponent));const He=ge.get("hops");He&&(le=Math.max(0,parseInt(He,10)||1))}return{graph:G,nodes:re,focus:me,hops:le}}async function w(n,p,M,G){de=n,Mt=It.has(n),E.setActive(n),T.hide(),V(),ae.clear(),o.clear(),L=Mt?await Sn(n):await ft(n);const re=Hn(L.nodes.length);if(Qe({spacing:Math.max(e.layout.spacing,re.spacing),clusterStrength:Math.max(e.layout.clustering,re.clusterStrength)}),a.loadGraph(L),ae.setLearningGraphData(L),J.setData(L),x.hide(),I(n),Mt||(await g(n),await C(n),await S(n)),M!=null&&M.length&&L){const me=M.filter(le=>L.nodes.some(ge=>ge.id===le));if(me.length){setTimeout(()=>{a.enterFocus(me,G??1)},500);return}}if(p!=null&&p.length&&L){const me=p.filter(le=>L.nodes.some(ge=>ge.id===le));me.length&&setTimeout(()=>{a.panToNodes(me),L&&T.show(me,L),I(n,me)},500)}}try{const p=await(await fetch("/api/backpacks")).json();E.setBackpacks(p)}catch{}const[f,N]=await Promise.all([ct(),kn().catch(()=>[])]);E.setSummaries(f),E.setRemotes(N),It=new Set(N.map(n=>n.name));const j=O(),oe=j.graph&&f.some(n=>n.name===j.graph)||j.graph&&It.has(j.graph)?j.graph:f.length>0?f[0].name:N.length>0?N[0].name:null;oe?await w(oe,j.nodes.length?j.nodes:void 0,j.focus.length?j.focus:void 0,j.hops):x.show();const pe={search(){ae.focus()},searchAlt(){ae.focus()},undo(){if(L){const n=o.undo(L);n&&i(n)}},redo(){if(L){const n=o.redo(L);n&&i(n)}},focus(){a.isFocused()?J.clearFocusSet():b.length>0&&J.addToFocusSet(b)},hopsDecrease(){const n=a.getFocusInfo();n&&n.hops>0&&a.enterFocus(n.seedNodeIds,n.hops-1)},hopsIncrease(){const n=a.getFocusInfo();n&&a.enterFocus(n.seedNodeIds,n.hops+1)},nextNode(){const n=a.getNodeIds();n.length>0&&(te=(te+1)%n.length,a.panToNode(n[te]),L&&T.show([n[te]],L))},prevNode(){const n=a.getNodeIds();n.length>0&&(te=te<=0?n.length-1:te-1,a.panToNode(n[te]),L&&T.show([n[te]],L))},nextConnection(){const n=T.cycleConnection(1);n&&a.panToNode(n)},prevConnection(){const n=T.cycleConnection(-1);n&&a.panToNode(n)},historyBack(){T.goBack()},historyForward(){T.goForward()},center(){a.centerView()},toggleEdges(){U=!U,a.setEdges(U)},panLeft(){a.panBy(-X,0)},panDown(){a.panBy(0,X)},panUp(){a.panBy(0,-X)},panRight(){a.panBy(X,0)},panFastLeft(){a.panBy(-X*e.navigation.panFastMultiplier,0)},zoomOut(){a.zoomBy(1/e.navigation.zoomFactor)},zoomIn(){a.zoomBy(e.navigation.zoomFactor)},panFastRight(){a.panBy(X*e.navigation.panFastMultiplier,0)},spacingDecrease(){const n=dt();Qe({spacing:Math.max(.5,n.spacing-.5)}),a.reheat()},spacingIncrease(){const n=dt();Qe({spacing:Math.min(20,n.spacing+.5)}),a.reheat()},clusteringDecrease(){const n=dt();Qe({clusterStrength:Math.max(0,n.clusterStrength-.03)}),a.reheat()},clusteringIncrease(){const n=dt();Qe({clusterStrength:Math.min(1,n.clusterStrength+.03)}),a.reheat()},help(){u.toggle()},toggleSidebar(){E.toggle()},walkIsolate(){if(!L)return;const n=a.getWalkTrail();n.length!==0&&a.enterFocus(n,0)},walkMode(){!a.isFocused()&&b.length>0&&J.addToFocusSet(b),a.setWalkMode(!a.getWalkMode());const n=t.querySelector(".walk-indicator");n&&n.classList.toggle("active",a.getWalkMode()),m()},escape(){a.isFocused()?J.clearFocusSet():u.hide()}};document.addEventListener("keydown",n=>{var p;if(!(n.target instanceof HTMLInputElement||n.target instanceof HTMLTextAreaElement)){for(const[M,G]of Object.entries(s))if(io(n,G)){(M==="search"||M==="searchAlt"||M==="undo"||M==="redo"||M==="toggleSidebar")&&n.preventDefault(),(p=pe[M])==null||p.call(pe);return}}}),window.addEventListener("hashchange",()=>{const n=O();if(n.graph&&n.graph!==de)w(n.graph,n.nodes.length?n.nodes:void 0,n.focus.length?n.focus:void 0,n.hops);else if(n.graph&&n.focus.length&&L)a.enterFocus(n.focus,n.hops);else if(n.graph&&n.nodes.length&&L){a.isFocused()&&a.exitFocus();const p=n.nodes.filter(M=>L.nodes.some(G=>G.id===M));p.length&&(a.panToNodes(p),T.show(p,L))}})}No();
@@ -1 +1 @@
1
- *{margin:0;padding:0;box-sizing:border-box}:root{--bg: #141414;--bg-surface: #1a1a1a;--bg-hover: #222222;--bg-active: #2a2a2a;--bg-elevated: #1e1e1e;--bg-inset: #111111;--border: #2a2a2a;--text: #d4d4d4;--text-strong: #e5e5e5;--text-muted: #737373;--text-dim: #525252;--accent: #d4a27f;--accent-hover: #e8b898;--badge-text: #141414;--glass-bg: rgba(20, 20, 20, .85);--glass-border: rgba(255, 255, 255, .08);--chip-bg: rgba(42, 42, 42, .7);--chip-bg-active: rgba(42, 42, 42, .9);--chip-bg-hover: rgba(50, 50, 50, .9);--chip-border-active: rgba(255, 255, 255, .06);--shadow: rgba(0, 0, 0, .6);--shadow-strong: rgba(0, 0, 0, .5);--canvas-edge: rgba(255, 255, 255, .08);--canvas-edge-highlight: rgba(212, 162, 127, .5);--canvas-edge-dim: rgba(255, 255, 255, .03);--canvas-edge-label: rgba(255, 255, 255, .2);--canvas-edge-label-highlight: rgba(212, 162, 127, .7);--canvas-edge-label-dim: rgba(255, 255, 255, .05);--canvas-arrow: rgba(255, 255, 255, .12);--canvas-arrow-highlight: rgba(212, 162, 127, .5);--canvas-node-label: #a3a3a3;--canvas-node-label-dim: rgba(212, 212, 212, .2);--canvas-type-badge: rgba(115, 115, 115, .5);--canvas-type-badge-dim: rgba(115, 115, 115, .15);--canvas-selection-border: #d4d4d4;--canvas-node-border: rgba(255, 255, 255, .15);--canvas-walk-edge: #e8d5c4}[data-theme=light]{--bg: #f5f5f4;--bg-surface: #fafaf9;--bg-hover: #f0efee;--bg-active: #e7e5e4;--bg-elevated: #f0efee;--bg-inset: #e7e5e4;--border: #d6d3d1;--text: #292524;--text-strong: #1c1917;--text-muted: #78716c;--text-dim: #a8a29e;--accent: #c17856;--accent-hover: #b07a5e;--badge-text: #fafaf9;--glass-bg: rgba(250, 250, 249, .85);--glass-border: rgba(0, 0, 0, .08);--chip-bg: rgba(214, 211, 209, .5);--chip-bg-active: rgba(214, 211, 209, .8);--chip-bg-hover: rgba(200, 197, 195, .8);--chip-border-active: rgba(0, 0, 0, .08);--shadow: rgba(0, 0, 0, .1);--shadow-strong: rgba(0, 0, 0, .15);--canvas-edge: rgba(0, 0, 0, .1);--canvas-edge-highlight: rgba(193, 120, 86, .6);--canvas-edge-dim: rgba(0, 0, 0, .03);--canvas-edge-label: rgba(0, 0, 0, .25);--canvas-edge-label-highlight: rgba(193, 120, 86, .8);--canvas-edge-label-dim: rgba(0, 0, 0, .06);--canvas-arrow: rgba(0, 0, 0, .15);--canvas-arrow-highlight: rgba(193, 120, 86, .6);--canvas-node-label: #57534e;--canvas-node-label-dim: rgba(87, 83, 78, .2);--canvas-type-badge: rgba(87, 83, 78, .5);--canvas-type-badge-dim: rgba(87, 83, 78, .15);--canvas-selection-border: #292524;--canvas-node-border: rgba(0, 0, 0, .1);--canvas-walk-edge: #1a1a1a}body{font-family:system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--text);overflow:hidden}#app{display:flex;height:100vh;width:100vw}#sidebar{width:280px;min-width:280px;background:var(--bg-surface);border-right:1px solid var(--border);display:flex;flex-direction:column;padding:16px;overflow-y:auto}#sidebar.sidebar-collapsed{width:0;min-width:0;padding:0;overflow:hidden;border-right:none}.sidebar-heading-row{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px}.sidebar-heading-row h2{margin-bottom:0}.sidebar-collapse-btn{background:none;border:1px solid var(--border);border-radius:6px;color:var(--text-dim);cursor:pointer;padding:4px 6px;line-height:1;transition:color .15s,border-color .15s}.sidebar-collapse-btn:hover{color:var(--text);border-color:var(--text-muted)}#sidebar h2{font-size:13px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted);margin-bottom:14px}#sidebar input{width:100%;padding:8px 12px;border:1px solid var(--border);border-radius:6px;background:var(--bg);color:var(--text);font-size:13px;outline:none;margin-bottom:12px}#sidebar input:focus{border-color:var(--accent)}#sidebar input::placeholder{color:var(--text-dim)}#ontology-list{list-style:none;display:flex;flex-direction:column;gap:2px}.ontology-item{padding:10px 12px;border-radius:6px;cursor:pointer;transition:background .15s}.ontology-item:hover{background:var(--bg-hover)}.ontology-item.active{background:var(--bg-active)}.ontology-item .name{display:block;font-size:13px;font-weight:500;color:var(--text)}.ontology-item .stats{display:block;font-size:11px;color:var(--text-dim);margin-top:2px}.sidebar-edit-btn{position:absolute;right:8px;top:10px;background:none;border:none;color:var(--text-dim);font-size:11px;cursor:pointer;opacity:0;transition:opacity .1s}.ontology-item{position:relative}.ontology-item:hover .sidebar-edit-btn{opacity:.7}.sidebar-section-heading{font-size:10px;font-weight:600;color:var(--text-dim);letter-spacing:.08em;margin:16px 12px 6px}.remote-list{list-style:none;padding:0;margin:0}.ontology-item-remote .name{display:inline}.remote-name-row{display:flex;align-items:center;gap:8px}.remote-badge{font-size:9px;font-weight:600;color:var(--text-dim);background:var(--bg-hover);padding:1px 6px;border-radius:4px;text-transform:uppercase;letter-spacing:.04em;border:1px solid var(--border)}.remote-source{display:block;font-size:10px;color:var(--text-dim);margin-top:1px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sidebar-edit-btn:hover{opacity:1!important;color:var(--text)}.sidebar-rename-input{background:transparent;border:none;border-bottom:1px solid var(--accent);color:var(--text);font:inherit;font-size:13px;font-weight:500;outline:none;width:100%;padding:0}.sidebar-branch{font-size:10px;color:var(--accent);opacity:.7;display:block;margin-top:2px}.sidebar-branch:hover{opacity:1}.sidebar-lock-badge{font-size:10px;color:#c08c00;display:none;margin-top:2px}.sidebar-lock-badge.active{display:block}.sidebar-lock-badge.active:before{content:"● ";color:#c08c00}.branch-picker{background:var(--bg-surface);border:1px solid var(--border);border-radius:8px;padding:4px;margin-top:4px;box-shadow:0 4px 16px var(--shadow);z-index:50}.branch-picker-item{display:flex;align-items:center;justify-content:space-between;padding:6px 8px;font-size:12px;color:var(--text);border-radius:4px;cursor:pointer}.branch-picker-item:hover{background:var(--bg-hover)}.branch-picker-active{color:var(--accent);font-weight:600;cursor:default}.branch-picker-active:hover{background:none}.branch-picker-delete{background:none;border:none;color:var(--text-dim);cursor:pointer;font-size:14px;padding:0 4px}.sidebar-snippets{margin-top:4px;padding-left:8px}.sidebar-snippet{display:flex;align-items:center;justify-content:space-between;padding:2px 4px;border-radius:4px;cursor:pointer}.sidebar-snippet:hover{background:var(--bg-hover)}.sidebar-snippet-label{font-size:10px;color:var(--text-dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sidebar-snippet-delete{background:none;border:none;color:var(--text-dim);font-size:12px;cursor:pointer;padding:0 2px;opacity:0}.sidebar-snippet:hover .sidebar-snippet-delete{opacity:1}.branch-picker-delete:hover{color:var(--danger, #e55)}.branch-picker-create{color:var(--accent);font-size:11px;border-top:1px solid var(--border);margin-top:4px;padding-top:8px}.sidebar-footer{margin-top:auto;padding-top:16px;border-top:1px solid var(--border);text-align:center}.sidebar-footer a{display:block;font-size:12px;font-weight:500;color:var(--accent);text-decoration:none;margin-bottom:4px}.sidebar-footer a:hover{color:var(--accent-hover)}.sidebar-footer span{display:block;font-size:10px;color:var(--text-dim)}.sidebar-version{font-size:9px;color:var(--text-dim);opacity:.5;margin-top:4px}.canvas-top-bar{position:absolute;top:16px;left:16px;right:16px;z-index:30;display:flex;justify-content:space-between;align-items:flex-start;pointer-events:none}.canvas-top-left,.canvas-top-center,.canvas-top-right{pointer-events:auto;display:flex;align-items:center;gap:4px}.canvas-top-left{flex-shrink:0;min-width:var(--tools-width, 264px)}.canvas-top-center{flex:1;justify-content:center}.focus-indicator{display:flex;align-items:center;gap:2px;background:var(--bg-surface);border:1px solid rgba(212,162,127,.4);border-radius:8px;padding:4px 6px 4px 10px;box-shadow:0 2px 8px var(--shadow)}.focus-indicator-label{font-size:11px;color:var(--accent);font-weight:500;white-space:nowrap;margin-right:4px}.focus-indicator-hops{font-size:11px;color:var(--text-muted);font-family:monospace;min-width:12px;text-align:center}.focus-indicator-btn{background:none;border:none;color:var(--text-muted);font-size:14px;cursor:pointer;padding:2px 4px;line-height:1;border-radius:4px;transition:color .15s,background .15s}.focus-indicator-btn:hover:not(:disabled){color:var(--text);background:var(--bg-hover)}.focus-indicator-btn:disabled{color:var(--text-dim);cursor:default;opacity:.3}.focus-indicator-exit{font-size:16px;margin-left:2px}.focus-indicator-exit:hover{color:#ef4444!important}.info-focus-btn{font-size:14px}.theme-toggle{background:var(--bg-surface);border:1px solid var(--border);border-radius:8px;color:var(--text-muted);font-size:18px;cursor:pointer;padding:6px 10px;line-height:1;transition:color .15s,border-color .15s,background .15s;box-shadow:0 2px 8px var(--shadow)}.theme-toggle:hover{color:var(--text);border-color:var(--text-muted);background:var(--bg-hover)}.zoom-controls{display:flex;gap:4px}.zoom-btn{background:var(--bg-surface);border:1px solid var(--border);border-radius:8px;color:var(--text-muted);font-size:18px;cursor:pointer;padding:6px 10px;line-height:1;transition:color .15s,border-color .15s,background .15s;box-shadow:0 2px 8px var(--shadow)}.zoom-btn:hover{color:var(--text);border-color:var(--text-muted);background:var(--bg-hover)}.node-tooltip{position:absolute;pointer-events:none;background:var(--bg);color:var(--text);border:1px solid var(--border);border-radius:6px;padding:4px 8px;font-size:12px;white-space:nowrap;z-index:20;box-shadow:0 2px 8px #00000026;opacity:.95}#canvas-container{flex:1;position:relative;overflow:hidden;touch-action:none}#graph-canvas{position:absolute;top:0;left:0;touch-action:none;width:100%;height:100%;cursor:grab}#graph-canvas:active{cursor:grabbing}.search-overlay{position:relative;display:flex;flex-direction:column;align-items:center;gap:8px;max-height:calc(100vh - 48px);pointer-events:none}.search-overlay>*{pointer-events:auto}.search-overlay.hidden{display:none}.search-input-wrap{position:relative;display:flex;align-items:center;gap:6px;width:380px;max-width:calc(100vw - 340px)}.search-input{flex:1;min-width:0;padding:10px 36px 10px 16px;border:1px solid var(--glass-border);border-radius:10px;background:var(--glass-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);color:var(--text);font-size:14px;outline:none;transition:border-color .15s,box-shadow .15s}.search-input:focus{border-color:#d4a27f66;box-shadow:0 0 0 3px #d4a27f1a}.search-input::placeholder{color:var(--text-dim)}.search-kbd{position:absolute;right:10px;top:50%;transform:translateY(-50%);padding:2px 7px;border:1px solid var(--border);border-radius:4px;background:var(--bg-surface);color:var(--text-dim);font-size:11px;font-family:monospace;pointer-events:none}.search-kbd.hidden{display:none}.search-results{list-style:none;width:380px;max-width:calc(100vw - 340px);background:var(--glass-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--border);border-radius:10px;overflow:hidden;box-shadow:0 8px 32px var(--shadow-strong)}.search-results.hidden{display:none}.search-result-item{display:flex;align-items:center;gap:8px;padding:8px 14px;cursor:pointer;transition:background .1s}.search-result-item:hover,.search-result-active{background:var(--bg-hover)}.search-result-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}.search-result-label{font-size:13px;color:var(--text);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.search-result-type{font-size:11px;color:var(--text-dim);flex-shrink:0}.chip-toggle{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:36px;height:36px;border:1px solid var(--glass-border);border-radius:10px;background:var(--glass-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);color:var(--text-dim);cursor:pointer;transition:border-color .15s,color .15s;pointer-events:auto}.chip-toggle:hover{border-color:#d4a27f4d;color:var(--text)}.chip-toggle.active{border-color:#d4a27f66;color:var(--accent)}.type-chips{display:flex;flex-wrap:wrap;gap:4px;justify-content:center;max-width:500px;max-height:200px;overflow-y:auto;padding:4px;border-radius:10px}.type-chips.hidden{display:none}.type-chip{display:flex;align-items:center;gap:4px;padding:3px 10px;border:1px solid transparent;border-radius:12px;background:var(--chip-bg);color:var(--text-dim);font-size:11px;cursor:pointer;transition:all .15s;white-space:nowrap}.type-chip.active{background:var(--chip-bg-active);color:var(--text-muted);border-color:var(--chip-border-active)}.type-chip:hover{background:var(--chip-bg-hover)}.type-chip-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}.type-chip:not(.active) .type-chip-dot{opacity:.3}.info-panel{position:absolute;top:56px;right:16px;bottom:16px;width:360px;background:var(--bg-surface);border:1px solid var(--border);border-radius:10px;overflow:hidden;display:flex;flex-direction:column;padding:0;z-index:10;box-shadow:0 8px 32px var(--shadow);transition:top .25s ease,right .25s ease,bottom .25s ease,left .25s ease,width .25s ease,max-height .25s ease,border-radius .25s ease}.info-panel-header{flex-shrink:0;padding:20px 20px 12px;position:relative}.info-panel-body{flex:1;overflow-y:auto;min-height:0;padding:0 20px 20px}.info-panel.hidden{display:none}.info-panel.info-panel-maximized{top:0;right:0;bottom:0;left:0;width:auto;max-height:none;border-radius:0;z-index:40}.info-panel-toolbar{position:absolute;top:12px;right:14px;display:flex;align-items:center;gap:2px;z-index:1}.info-toolbar-btn{background:none;border:none;color:var(--text-muted);font-size:16px;cursor:pointer;padding:4px 6px;line-height:1;border-radius:4px;transition:color .15s,background .15s}.info-toolbar-btn:hover:not(:disabled){color:var(--text);background:var(--bg-hover)}.info-toolbar-btn:disabled{color:var(--text-dim);cursor:default;opacity:.3}.info-close-btn{font-size:20px}.info-connection-link{cursor:pointer;transition:background .15s}.info-connection-link:hover{background:var(--bg-active)}.info-connection-link .info-target{color:var(--accent);text-decoration:underline;text-decoration-color:transparent;transition:text-decoration-color .15s}.info-connection-link:hover .info-target{text-decoration-color:var(--accent)}.info-header{margin-bottom:16px}.info-type-badge{display:inline-block;padding:3px 10px;border-radius:12px;font-size:11px;font-weight:600;color:var(--badge-text);margin-bottom:8px}.info-badge-row{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px}.info-empty-message{font-size:12px;color:var(--text-dim)}.share-list-message{font-size:13px;color:var(--text-dim);text-align:center;padding:12px}.info-label{font-size:18px;font-weight:600;color:var(--text-strong);margin-bottom:4px;word-break:break-word}.info-id{display:block;font-size:11px;color:var(--text-dim);font-family:monospace}.info-section{margin-bottom:16px}.info-section-title{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted);margin-bottom:8px;padding-bottom:4px;border-bottom:1px solid var(--border)}.info-props{display:grid;grid-template-columns:auto 1fr;gap:4px 12px}.info-props dt{font-size:12px;color:var(--text-muted);padding-top:2px}.info-props dd{font-size:12px;color:var(--text);word-break:break-word;display:flex;align-items:center;gap:4px}.info-value{white-space:pre-wrap}.info-array{display:flex;flex-wrap:wrap;gap:4px}.info-tag{display:inline-block;padding:2px 8px;background:var(--bg-hover);border-radius:4px;font-size:11px;color:var(--text-muted)}.info-json{font-size:11px;font-family:monospace;color:var(--text-muted);background:var(--bg-inset);padding:6px 8px;border-radius:4px;overflow-x:auto;white-space:pre}.info-connections{list-style:none;display:flex;flex-direction:column;gap:6px}.info-connection{display:flex;align-items:center;gap:6px;padding:6px 8px;background:var(--bg-elevated);border-radius:6px;font-size:12px;flex-wrap:wrap}.info-connection-active{outline:1.5px solid var(--accent);background:var(--bg-hover)}.info-target-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}.info-arrow{color:var(--text-dim);font-size:14px;flex-shrink:0}.info-edge-type{color:var(--text-muted);font-size:11px;font-weight:500}.info-target{color:var(--text);font-weight:500}.info-edge-props{width:100%;padding-top:4px;padding-left:20px}.info-edge-prop{display:block;font-size:11px;color:var(--text-dim)}.info-editable{cursor:default;position:relative}.info-inline-edit{background:none;border:none;color:var(--badge-text);opacity:0;font-size:10px;cursor:pointer;margin-left:4px;transition:opacity .15s}.info-editable:hover .info-inline-edit{opacity:.8}.info-inline-edit:hover{opacity:1!important}.info-edit-inline-input{background:transparent;border:none;border-bottom:1px solid var(--accent);color:var(--badge-text);font:inherit;font-size:inherit;outline:none;width:100%;padding:0}.info-edit-input{background:var(--bg-inset);border:1px solid var(--border);border-radius:4px;padding:3px 6px;font-size:12px;font-family:inherit;color:var(--text);flex:1;min-width:0;resize:vertical;overflow:hidden;line-height:1.4;max-height:300px}.info-edit-input:focus{outline:none;border-color:var(--accent)}.info-delete-prop{background:none;border:none;color:var(--text-dim);font-size:14px;cursor:pointer;padding:0 2px;flex-shrink:0;opacity:0;transition:opacity .1s,color .1s}.info-props dd:hover .info-delete-prop{opacity:1}.info-delete-prop:hover{color:#ef4444}.info-add-btn{background:none;border:1px dashed var(--border);border-radius:4px;padding:6px 10px;font-size:12px;color:var(--text-dim);cursor:pointer;width:100%;margin-top:8px;transition:border-color .15s,color .15s}.info-add-btn:hover{border-color:var(--accent);color:var(--text)}.info-add-row{display:flex;gap:4px;margin-top:6px}.info-add-save{background:var(--accent);border:none;border-radius:4px;padding:3px 10px;font-size:12px;color:var(--badge-text);cursor:pointer;flex-shrink:0}.info-add-save:hover{background:var(--accent-hover)}.info-delete-edge{background:none;border:none;color:var(--text-dim);font-size:14px;cursor:pointer;margin-left:auto;padding:0 2px;opacity:0;transition:opacity .1s,color .1s}.info-connection:hover .info-delete-edge{opacity:1}.info-delete-edge:hover{color:#ef4444}.info-danger{margin-top:8px;padding-top:12px;border-top:1px solid var(--border)}.info-delete-node{background:none;border:1px solid rgba(239,68,68,.3);border-radius:6px;padding:6px 12px;font-size:12px;color:#ef4444;cursor:pointer;width:100%;transition:background .15s}.info-delete-node:hover{background:#ef44441a}.tools-pane-toggle{background:var(--bg-surface);border:1px solid var(--border);border-radius:8px;color:var(--text-muted);font-size:18px;cursor:pointer;padding:6px 10px;line-height:1;transition:color .15s,border-color .15s,background .15s;box-shadow:0 2px 8px var(--shadow)}.tools-pane-toggle.hidden{display:none}.tools-pane-toggle:hover{color:var(--text);border-color:var(--text-muted);background:var(--bg-hover)}.tools-pane-toggle.active{color:var(--accent);border-color:#d4a27f66}.tools-pane-content{position:absolute;top:56px;left:16px;bottom:16px;z-index:20;width:var(--tools-width, 264px);box-sizing:border-box;overflow:hidden;display:flex;flex-direction:column;background:var(--bg-surface);border:1px solid var(--border);border-radius:10px;padding:12px;box-shadow:0 8px 32px var(--shadow)}.tools-pane-content.hidden{display:none}.tools-pane-section{margin-bottom:12px}.tools-pane-section:last-child{margin-bottom:0}.tools-pane-heading{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-dim);margin-bottom:6px}.tools-pane-row{display:flex;align-items:center;gap:6px;padding:3px 0;font-size:12px}.tools-pane-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}.tools-pane-name{flex:1;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tools-pane-count{color:var(--text-dim);font-size:11px;flex-shrink:0}.tools-pane-summary{display:flex;align-items:center;gap:4px;font-size:11px;color:var(--text-muted);padding-bottom:10px;margin-bottom:10px;border-bottom:1px solid var(--border)}.tools-pane-sep{color:var(--text-dim)}.tools-pane-token-card{padding:8px 10px;margin-bottom:10px;border:1px solid var(--border);border-radius:6px;background:var(--bg-hover);font-size:11px}.token-card-label{font-weight:600;color:var(--text);margin-bottom:4px}.token-card-stat{color:var(--text-muted);margin-bottom:4px}.token-card-bar{height:4px;background:var(--border);border-radius:2px;margin:6px 0;overflow:hidden}.token-card-bar-fill{height:100%;background:var(--accent);border-radius:2px;transition:width .3s ease}.tools-pane-clickable{cursor:pointer;border-radius:4px;padding:3px 4px;margin:0 -4px;transition:background .1s}.tools-pane-clickable:hover{background:var(--bg-hover)}.tools-pane-clickable.active{background:var(--bg-hover);outline:1px solid var(--border)}.tools-pane-badge{font-size:9px;color:var(--accent);flex-shrink:0;opacity:.8}.tools-pane-issue .tools-pane-name{color:var(--text-muted)}.tools-pane-more{font-size:10px;color:var(--text-dim);padding:4px 0 0}.tools-pane-edit{background:none;border:none;color:var(--text-dim);font-size:11px;cursor:pointer;padding:0 2px;opacity:0;transition:opacity .1s,color .1s;flex-shrink:0}.tools-pane-row:hover .tools-pane-edit{opacity:1}.tools-pane-edit:hover{color:var(--accent)}.tools-pane-actions{display:flex;gap:6px;padding-top:4px}.tools-pane-action-btn{background:none;border:1px solid var(--border);color:var(--text-muted);font-size:10px;padding:2px 8px;border-radius:3px;cursor:pointer;transition:color .1s,border-color .1s}.tools-pane-action-btn:hover{color:var(--accent);border-color:var(--accent)}.tools-pane-focus-toggle{opacity:.4;font-size:11px}.tools-pane-focus-active{opacity:1!important;color:var(--accent)!important}.tools-pane-focus-clear{margin-top:4px;border-top:1px solid var(--border);padding-top:6px}.tools-pane-editing{background:none!important}.tools-pane-inline-input{width:100%;background:var(--bg);border:1px solid var(--accent);border-radius:4px;color:var(--text);font-size:12px;padding:2px 6px;outline:none}.tools-pane-slider-row{display:flex;align-items:center;gap:6px;padding:4px 0}.tools-pane-slider-label{font-size:11px;color:var(--text-muted);white-space:nowrap;min-width:56px}.tools-pane-slider{flex:1;min-width:0;height:4px;accent-color:var(--accent);cursor:pointer}.tools-pane-slider-value{font-size:10px;color:var(--text-dim);min-width:28px;text-align:right;font-family:monospace}.tools-pane-checkbox{width:14px;height:14px;accent-color:var(--accent);cursor:pointer;flex-shrink:0}.tools-pane-export-row{display:flex;gap:4px;margin-top:6px}.tools-pane-export-btn{flex:1;padding:4px 8px;font-size:11px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text-muted);cursor:pointer;transition:color .15s,border-color .15s}.tools-pane-export-btn:hover{color:var(--text);border-color:var(--text-muted)}.tools-pane-empty{font-size:11px;color:var(--text-dim);text-align:center;padding:8px 0}.tools-pane-tabs{display:flex;gap:2px;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--border)}.tools-pane-tab{flex:1;padding:4px 0;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.03em;background:none;border:1px solid transparent;border-radius:5px;color:var(--text-dim);cursor:pointer;transition:color .15s,background .15s,border-color .15s}.tools-pane-tab:hover{color:var(--text-muted);background:var(--bg-hover)}.tools-pane-tab-active{color:var(--text);background:var(--bg-hover);border-color:var(--border)}.tools-pane-tab-content{flex:1;overflow-y:auto;overflow-x:hidden;min-height:0}.tools-pane-search{width:100%;padding:4px 8px;font-size:11px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);outline:none;margin-bottom:8px;box-sizing:border-box}.tools-pane-search:focus{border-color:var(--accent)}.tools-pane-search::placeholder{color:var(--text-dim)}.tools-pane-empty-msg{font-size:11px;color:var(--text-dim);text-align:center;padding:16px 0}.empty-state{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;z-index:5;pointer-events:none;overflow:hidden}.empty-state.hidden{display:none}.empty-state-bg{position:absolute;top:0;right:0;bottom:0;left:0;overflow:hidden}.empty-state-circle{position:absolute;border-radius:50%;background:var(--accent);opacity:.07}.empty-state-circle.c1{width:80px;height:80px;left:20%;top:15%;animation:float-circle 8s ease-in-out infinite}.empty-state-circle.c2{width:50px;height:50px;right:25%;top:25%;animation:float-circle 6s ease-in-out 1s infinite}.empty-state-circle.c3{width:65px;height:65px;left:55%;bottom:20%;animation:float-circle 7s ease-in-out 2s infinite}.empty-state-circle.c4{width:40px;height:40px;left:15%;bottom:30%;animation:float-circle 9s ease-in-out .5s infinite}.empty-state-circle.c5{width:55px;height:55px;right:15%;bottom:35%;animation:float-circle 7.5s ease-in-out 1.5s infinite}.empty-state-lines{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;color:var(--text-dim);animation:float-circle 10s ease-in-out infinite}@keyframes float-circle{0%,to{transform:translateY(0)}50%{transform:translateY(-12px)}}.empty-state-content{text-align:center;max-width:420px;padding:40px 24px;position:relative;z-index:1}.empty-state-icon{color:var(--text-dim);margin-bottom:16px}.empty-state-title{font-size:18px;font-weight:600;color:var(--text);margin-bottom:8px}.empty-state-desc{font-size:13px;color:var(--text-muted);line-height:1.5;margin-bottom:20px}.empty-state-setup{background:var(--bg-surface);border:1px solid var(--border);border-radius:8px;padding:12px 16px;margin-bottom:16px}.empty-state-label{font-size:11px;color:var(--text-dim);margin-bottom:8px}.empty-state-code{display:block;font-size:12px;color:var(--accent);font-family:monospace;word-break:break-all}.empty-state-hint{font-size:11px;color:var(--text-dim)}.empty-state-hint kbd{padding:1px 5px;border:1px solid var(--border);border-radius:3px;background:var(--bg-surface);font-family:monospace;font-size:11px}.shortcuts-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:100}.shortcuts-overlay.hidden{display:none}.shortcuts-modal{background:var(--bg-surface);border:1px solid var(--border);border-radius:12px;padding:24px;min-width:300px;max-width:400px;box-shadow:0 16px 48px var(--shadow);position:relative}.shortcuts-close{position:absolute;top:12px;right:14px;background:none;border:none;color:var(--text-dim);font-size:20px;cursor:pointer;padding:0 4px;line-height:1}.shortcuts-close:hover{color:var(--text)}.shortcuts-title{font-size:15px;font-weight:600;color:var(--text);margin-bottom:16px}.shortcuts-list{display:flex;flex-direction:column;gap:8px}.shortcuts-row{display:flex;align-items:center;justify-content:space-between;gap:12px}.shortcuts-keys{display:flex;align-items:center;gap:4px}.shortcuts-keys kbd{padding:2px 7px;border:1px solid var(--border);border-radius:4px;background:var(--bg);color:var(--text);font-size:11px;font-family:monospace}.shortcuts-or{font-size:10px;color:var(--text-dim)}.shortcuts-desc{font-size:12px;color:var(--text-muted)}@media(max-width:768px){#app{flex-direction:column}#sidebar{width:100%;min-width:0;max-height:35vh;border-right:none;border-bottom:1px solid var(--border)}.info-panel{top:auto;bottom:72px;right:8px;left:8px;width:auto;max-height:calc(100% - 200px);overflow-y:auto}.info-panel.info-panel-maximized{bottom:0;left:0;right:0}.canvas-top-bar{top:8px;left:8px;right:8px}.tools-pane-content{top:48px;left:8px;bottom:80px;width:160px;max-width:calc(100vw - 24px)}.tools-pane-edit{opacity:.6}}.bp-dialog-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:1000;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.bp-dialog{background:var(--bg-surface);border:1px solid var(--border);border-radius:12px;padding:20px;min-width:280px;max-width:400px;box-shadow:0 16px 48px #0000004d}.bp-dialog-title{font-size:14px;font-weight:600;color:var(--text);margin-bottom:12px}.bp-dialog-message{font-size:13px;color:var(--text-muted);margin-bottom:16px;line-height:1.5}.bp-dialog-input{width:100%;padding:8px 12px;font-size:13px;background:var(--bg);border:1px solid var(--border);border-radius:8px;color:var(--text);outline:none;margin-bottom:16px}.bp-dialog-input:focus{border-color:var(--accent)}.bp-dialog-buttons{display:flex;justify-content:flex-end;gap:8px}.bp-dialog-btn{padding:6px 16px;font-size:12px;border-radius:6px;border:1px solid var(--border);background:var(--bg);color:var(--text-muted);cursor:pointer;transition:all .15s}.bp-dialog-btn:hover{background:var(--bg-hover);color:var(--text)}.bp-dialog-btn-accent{background:var(--accent);color:#fff;border-color:var(--accent)}.bp-dialog-btn-accent:hover{opacity:.9;color:#fff;background:var(--accent)}.bp-dialog-btn-danger{background:#e55;color:#fff;border-color:#e55}.bp-dialog-btn-danger:hover{opacity:.9;color:#fff;background:#e55}.bp-toast{position:fixed;bottom:24px;left:50%;transform:translate(-50%) translateY(20px);background:var(--bg-surface);border:1px solid var(--border);color:var(--text);padding:8px 20px;border-radius:8px;font-size:12px;z-index:1001;opacity:0;transition:opacity .3s,transform .3s;box-shadow:0 4px 16px var(--shadow)}.bp-toast-visible{opacity:1;transform:translate(-50%) translateY(0)}.context-menu{position:absolute;z-index:100;background:var(--bg-surface);border:1px solid var(--border);border-radius:8px;padding:4px;min-width:180px;box-shadow:0 8px 24px var(--shadow)}.context-menu-item{padding:6px 12px;font-size:12px;color:var(--text);border-radius:4px;cursor:pointer;white-space:nowrap}.context-menu-item:hover{background:var(--bg-hover)}.context-menu-separator{height:1px;background:var(--border);margin:4px 8px}.path-bar{position:absolute;bottom:16px;left:50%;transform:translate(-50%);z-index:25;display:flex;align-items:center;gap:4px;background:var(--bg-surface);border:1px solid var(--border);border-radius:8px;padding:6px 12px;box-shadow:0 4px 16px var(--shadow);max-width:80%;overflow-x:auto}.path-bar.hidden{display:none}.path-bar-node{font-size:11px;color:var(--text);padding:2px 8px;border-radius:4px;cursor:pointer;white-space:nowrap;flex-shrink:0}.path-bar-node:hover{background:var(--bg-hover)}.path-bar-edge{font-size:9px;color:var(--text-dim);white-space:nowrap;flex-shrink:0}.path-bar-close{background:none;border:none;color:var(--text-dim);font-size:14px;cursor:pointer;padding:0 4px;margin-left:8px;flex-shrink:0}.path-bar-close:hover{color:var(--text)}.walk-trail-edge{font-size:9px;color:var(--text-dim);padding:1px 0 1px 24px;opacity:.7}.walk-indicator{font-size:10px;color:var(--accent);padding:2px 8px;border:1px solid rgba(212,162,127,.4);border-radius:4px;cursor:pointer;opacity:.4}.walk-indicator.active{opacity:1;background:#d4a27f26;animation:walk-strobe 2s ease-in-out infinite}@keyframes walk-strobe{0%,to{opacity:.6;border-color:#d4a27f4d}50%{opacity:1;border-color:#d4a27fcc}}
1
+ *{margin:0;padding:0;box-sizing:border-box}:root{--bg: #141414;--bg-surface: #1a1a1a;--bg-hover: #222222;--bg-active: #2a2a2a;--bg-elevated: #1e1e1e;--bg-inset: #111111;--border: #2a2a2a;--text: #d4d4d4;--text-strong: #e5e5e5;--text-muted: #737373;--text-dim: #525252;--accent: #d4a27f;--accent-hover: #e8b898;--badge-text: #141414;--glass-bg: rgba(20, 20, 20, .85);--glass-border: rgba(255, 255, 255, .08);--chip-bg: rgba(42, 42, 42, .7);--chip-bg-active: rgba(42, 42, 42, .9);--chip-bg-hover: rgba(50, 50, 50, .9);--chip-border-active: rgba(255, 255, 255, .06);--shadow: rgba(0, 0, 0, .6);--shadow-strong: rgba(0, 0, 0, .5);--canvas-edge: rgba(255, 255, 255, .08);--canvas-edge-highlight: rgba(212, 162, 127, .5);--canvas-edge-dim: rgba(255, 255, 255, .03);--canvas-edge-label: rgba(255, 255, 255, .2);--canvas-edge-label-highlight: rgba(212, 162, 127, .7);--canvas-edge-label-dim: rgba(255, 255, 255, .05);--canvas-arrow: rgba(255, 255, 255, .12);--canvas-arrow-highlight: rgba(212, 162, 127, .5);--canvas-node-label: #a3a3a3;--canvas-node-label-dim: rgba(212, 212, 212, .2);--canvas-type-badge: rgba(115, 115, 115, .5);--canvas-type-badge-dim: rgba(115, 115, 115, .15);--canvas-selection-border: #d4d4d4;--canvas-node-border: rgba(255, 255, 255, .15);--canvas-walk-edge: #e8d5c4}[data-theme=light]{--bg: #f5f5f4;--bg-surface: #fafaf9;--bg-hover: #f0efee;--bg-active: #e7e5e4;--bg-elevated: #f0efee;--bg-inset: #e7e5e4;--border: #d6d3d1;--text: #292524;--text-strong: #1c1917;--text-muted: #78716c;--text-dim: #a8a29e;--accent: #c17856;--accent-hover: #b07a5e;--badge-text: #fafaf9;--glass-bg: rgba(250, 250, 249, .85);--glass-border: rgba(0, 0, 0, .08);--chip-bg: rgba(214, 211, 209, .5);--chip-bg-active: rgba(214, 211, 209, .8);--chip-bg-hover: rgba(200, 197, 195, .8);--chip-border-active: rgba(0, 0, 0, .08);--shadow: rgba(0, 0, 0, .1);--shadow-strong: rgba(0, 0, 0, .15);--canvas-edge: rgba(0, 0, 0, .1);--canvas-edge-highlight: rgba(193, 120, 86, .6);--canvas-edge-dim: rgba(0, 0, 0, .03);--canvas-edge-label: rgba(0, 0, 0, .25);--canvas-edge-label-highlight: rgba(193, 120, 86, .8);--canvas-edge-label-dim: rgba(0, 0, 0, .06);--canvas-arrow: rgba(0, 0, 0, .15);--canvas-arrow-highlight: rgba(193, 120, 86, .6);--canvas-node-label: #57534e;--canvas-node-label-dim: rgba(87, 83, 78, .2);--canvas-type-badge: rgba(87, 83, 78, .5);--canvas-type-badge-dim: rgba(87, 83, 78, .15);--canvas-selection-border: #292524;--canvas-node-border: rgba(0, 0, 0, .1);--canvas-walk-edge: #1a1a1a}body{font-family:system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--text);overflow:hidden}#app{display:flex;height:100vh;width:100vw}#sidebar{width:280px;min-width:280px;background:var(--bg-surface);border-right:1px solid var(--border);border-left:3px solid var(--backpack-color, transparent);display:flex;flex-direction:column;padding:16px;overflow-y:auto}#sidebar.sidebar-collapsed{width:0;min-width:0;padding:0;overflow:hidden;border-right:none}.sidebar-heading-row{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px}.sidebar-heading-row h2{margin-bottom:0}.sidebar-collapse-btn{background:none;border:1px solid var(--border);border-radius:6px;color:var(--text-dim);cursor:pointer;padding:4px 6px;line-height:1;transition:color .15s,border-color .15s}.sidebar-collapse-btn:hover{color:var(--text);border-color:var(--text-muted)}#sidebar h2{font-size:13px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted);margin-bottom:14px}#sidebar input{width:100%;padding:8px 12px;border:1px solid var(--border);border-radius:6px;background:var(--bg);color:var(--text);font-size:13px;outline:none;margin-bottom:12px}#sidebar input:focus{border-color:var(--accent)}#sidebar input::placeholder{color:var(--text-dim)}#ontology-list{list-style:none;display:flex;flex-direction:column;gap:2px}.ontology-item{padding:10px 12px;border-radius:6px;cursor:pointer;transition:background .15s}.ontology-item:hover{background:var(--bg-hover)}.ontology-item.active{background:var(--bg-active)}.ontology-item .name{display:block;font-size:13px;font-weight:500;color:var(--text)}.ontology-item .stats{display:block;font-size:11px;color:var(--text-dim);margin-top:2px}.sidebar-edit-btn{position:absolute;right:8px;top:10px;background:none;border:none;color:var(--text-dim);font-size:11px;cursor:pointer;opacity:0;transition:opacity .1s}.ontology-item{position:relative}.ontology-item:hover .sidebar-edit-btn{opacity:.7}.sidebar-section-heading{font-size:10px;font-weight:600;color:var(--text-dim);letter-spacing:.08em;margin:16px 12px 6px}.remote-list{list-style:none;padding:0;margin:0}.ontology-item-remote .name{display:inline}.remote-name-row{display:flex;align-items:center;gap:8px}.remote-badge{font-size:9px;font-weight:600;color:var(--text-dim);background:var(--bg-hover);padding:1px 6px;border-radius:4px;text-transform:uppercase;letter-spacing:.04em;border:1px solid var(--border)}.remote-source{display:block;font-size:10px;color:var(--text-dim);margin-top:1px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sidebar-edit-btn:hover{opacity:1!important;color:var(--text)}.sidebar-rename-input{background:transparent;border:none;border-bottom:1px solid var(--accent);color:var(--text);font:inherit;font-size:13px;font-weight:500;outline:none;width:100%;padding:0}.sidebar-branch{font-size:10px;color:var(--accent);opacity:.7;display:block;margin-top:2px}.sidebar-branch:hover{opacity:1}.backpack-picker-container{position:relative;margin-bottom:10px}.backpack-picker-pill{display:flex;align-items:center;gap:6px;width:100%;padding:4px 8px;background:var(--bg-base);border:1px solid var(--border);border-radius:999px;color:var(--fg);font-size:11px;font-family:inherit;cursor:pointer;text-align:left}.backpack-picker-pill:hover{border-color:var(--backpack-color, var(--accent))}.backpack-picker-dot{display:inline-block;width:8px;height:8px;border-radius:50%;background:var(--backpack-color, var(--accent));flex-shrink:0}.backpack-picker-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:500}.backpack-picker-caret{opacity:.6;font-size:10px}.backpack-picker-dropdown{position:absolute;top:calc(100% + 4px);left:0;right:0;background:var(--bg-surface);border:1px solid var(--border);border-radius:8px;box-shadow:0 4px 16px var(--shadow);z-index:50;max-height:300px;overflow-y:auto;padding:4px}.backpack-picker-item{display:flex;align-items:center;gap:6px;width:100%;padding:6px 8px;background:transparent;border:none;border-radius:4px;color:var(--fg);font-family:inherit;font-size:11px;cursor:pointer;text-align:left}.backpack-picker-item:hover{background:var(--bg-hover)}.backpack-picker-item.active{background:var(--bg-hover);font-weight:600}.backpack-picker-item-dot{display:inline-block;width:8px;height:8px;border-radius:50%;background:var(--backpack-color, var(--accent));flex-shrink:0}.backpack-picker-item-name{flex-shrink:0}.backpack-picker-item-path{flex:1;opacity:.55;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:9px;text-align:right}.backpack-picker-divider{height:1px;background:var(--border);margin:4px 0}.backpack-picker-add{opacity:.75;font-style:italic}.sidebar-lock-badge{font-size:10px;color:#c08c00;display:none;margin-top:2px}.sidebar-lock-badge.active{display:block}.sidebar-lock-badge.active:before{content:"● ";color:#c08c00}.branch-picker{background:var(--bg-surface);border:1px solid var(--border);border-radius:8px;padding:4px;margin-top:4px;box-shadow:0 4px 16px var(--shadow);z-index:50}.branch-picker-item{display:flex;align-items:center;justify-content:space-between;padding:6px 8px;font-size:12px;color:var(--text);border-radius:4px;cursor:pointer}.branch-picker-item:hover{background:var(--bg-hover)}.branch-picker-active{color:var(--accent);font-weight:600;cursor:default}.branch-picker-active:hover{background:none}.branch-picker-delete{background:none;border:none;color:var(--text-dim);cursor:pointer;font-size:14px;padding:0 4px}.sidebar-snippets{margin-top:4px;padding-left:8px}.sidebar-snippet{display:flex;align-items:center;justify-content:space-between;padding:2px 4px;border-radius:4px;cursor:pointer}.sidebar-snippet:hover{background:var(--bg-hover)}.sidebar-snippet-label{font-size:10px;color:var(--text-dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sidebar-snippet-delete{background:none;border:none;color:var(--text-dim);font-size:12px;cursor:pointer;padding:0 2px;opacity:0}.sidebar-snippet:hover .sidebar-snippet-delete{opacity:1}.branch-picker-delete:hover{color:var(--danger, #e55)}.branch-picker-create{color:var(--accent);font-size:11px;border-top:1px solid var(--border);margin-top:4px;padding-top:8px}.sidebar-footer{margin-top:auto;padding-top:16px;border-top:1px solid var(--border);text-align:center}.sidebar-footer a{display:block;font-size:12px;font-weight:500;color:var(--accent);text-decoration:none;margin-bottom:4px}.sidebar-footer a:hover{color:var(--accent-hover)}.sidebar-footer span{display:block;font-size:10px;color:var(--text-dim)}.sidebar-version{font-size:9px;color:var(--text-dim);opacity:.5;margin-top:4px}.canvas-top-bar{position:absolute;top:16px;left:16px;right:16px;z-index:30;display:flex;justify-content:space-between;align-items:flex-start;pointer-events:none}.canvas-top-left,.canvas-top-center,.canvas-top-right{pointer-events:auto;display:flex;align-items:center;gap:4px}.canvas-top-left{flex-shrink:0;min-width:var(--tools-width, 264px)}.canvas-top-center{flex:1;justify-content:center}.focus-indicator{display:flex;align-items:center;gap:2px;background:var(--bg-surface);border:1px solid rgba(212,162,127,.4);border-radius:8px;padding:4px 6px 4px 10px;box-shadow:0 2px 8px var(--shadow)}.focus-indicator-label{font-size:11px;color:var(--accent);font-weight:500;white-space:nowrap;margin-right:4px}.focus-indicator-hops{font-size:11px;color:var(--text-muted);font-family:monospace;min-width:12px;text-align:center}.focus-indicator-btn{background:none;border:none;color:var(--text-muted);font-size:14px;cursor:pointer;padding:2px 4px;line-height:1;border-radius:4px;transition:color .15s,background .15s}.focus-indicator-btn:hover:not(:disabled){color:var(--text);background:var(--bg-hover)}.focus-indicator-btn:disabled{color:var(--text-dim);cursor:default;opacity:.3}.focus-indicator-exit{font-size:16px;margin-left:2px}.focus-indicator-exit:hover{color:#ef4444!important}.info-focus-btn{font-size:14px}.theme-toggle{background:var(--bg-surface);border:1px solid var(--border);border-radius:8px;color:var(--text-muted);font-size:18px;cursor:pointer;padding:6px 10px;line-height:1;transition:color .15s,border-color .15s,background .15s;box-shadow:0 2px 8px var(--shadow)}.theme-toggle:hover{color:var(--text);border-color:var(--text-muted);background:var(--bg-hover)}.zoom-controls{display:flex;gap:4px}.zoom-btn{background:var(--bg-surface);border:1px solid var(--border);border-radius:8px;color:var(--text-muted);font-size:18px;cursor:pointer;padding:6px 10px;line-height:1;transition:color .15s,border-color .15s,background .15s;box-shadow:0 2px 8px var(--shadow)}.zoom-btn:hover{color:var(--text);border-color:var(--text-muted);background:var(--bg-hover)}.node-tooltip{position:absolute;pointer-events:none;background:var(--bg);color:var(--text);border:1px solid var(--border);border-radius:6px;padding:4px 8px;font-size:12px;white-space:nowrap;z-index:20;box-shadow:0 2px 8px #00000026;opacity:.95}#canvas-container{flex:1;position:relative;overflow:hidden;touch-action:none}#graph-canvas{position:absolute;top:0;left:0;touch-action:none;width:100%;height:100%;cursor:grab}#graph-canvas:active{cursor:grabbing}.search-overlay{position:relative;display:flex;flex-direction:column;align-items:center;gap:8px;max-height:calc(100vh - 48px);pointer-events:none}.search-overlay>*{pointer-events:auto}.search-overlay.hidden{display:none}.search-input-wrap{position:relative;display:flex;align-items:center;gap:6px;width:380px;max-width:calc(100vw - 340px)}.search-input{flex:1;min-width:0;padding:10px 36px 10px 16px;border:1px solid var(--glass-border);border-radius:10px;background:var(--glass-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);color:var(--text);font-size:14px;outline:none;transition:border-color .15s,box-shadow .15s}.search-input:focus{border-color:#d4a27f66;box-shadow:0 0 0 3px #d4a27f1a}.search-input::placeholder{color:var(--text-dim)}.search-kbd{position:absolute;right:10px;top:50%;transform:translateY(-50%);padding:2px 7px;border:1px solid var(--border);border-radius:4px;background:var(--bg-surface);color:var(--text-dim);font-size:11px;font-family:monospace;pointer-events:none}.search-kbd.hidden{display:none}.search-results{list-style:none;width:380px;max-width:calc(100vw - 340px);background:var(--glass-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--border);border-radius:10px;overflow:hidden;box-shadow:0 8px 32px var(--shadow-strong)}.search-results.hidden{display:none}.search-result-item{display:flex;align-items:center;gap:8px;padding:8px 14px;cursor:pointer;transition:background .1s}.search-result-item:hover,.search-result-active{background:var(--bg-hover)}.search-result-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}.search-result-label{font-size:13px;color:var(--text);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.search-result-type{font-size:11px;color:var(--text-dim);flex-shrink:0}.chip-toggle{flex-shrink:0;display:flex;align-items:center;justify-content:center;width:36px;height:36px;border:1px solid var(--glass-border);border-radius:10px;background:var(--glass-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);color:var(--text-dim);cursor:pointer;transition:border-color .15s,color .15s;pointer-events:auto}.chip-toggle:hover{border-color:#d4a27f4d;color:var(--text)}.chip-toggle.active{border-color:#d4a27f66;color:var(--accent)}.type-chips{display:flex;flex-wrap:wrap;gap:4px;justify-content:center;max-width:500px;max-height:200px;overflow-y:auto;padding:4px;border-radius:10px}.type-chips.hidden{display:none}.type-chip{display:flex;align-items:center;gap:4px;padding:3px 10px;border:1px solid transparent;border-radius:12px;background:var(--chip-bg);color:var(--text-dim);font-size:11px;cursor:pointer;transition:all .15s;white-space:nowrap}.type-chip.active{background:var(--chip-bg-active);color:var(--text-muted);border-color:var(--chip-border-active)}.type-chip:hover{background:var(--chip-bg-hover)}.type-chip-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}.type-chip:not(.active) .type-chip-dot{opacity:.3}.info-panel{position:absolute;top:56px;right:16px;bottom:16px;width:360px;background:var(--bg-surface);border:1px solid var(--border);border-radius:10px;overflow:hidden;display:flex;flex-direction:column;padding:0;z-index:10;box-shadow:0 8px 32px var(--shadow);transition:top .25s ease,right .25s ease,bottom .25s ease,left .25s ease,width .25s ease,max-height .25s ease,border-radius .25s ease}.info-panel-header{flex-shrink:0;padding:20px 20px 12px;position:relative}.info-panel-body{flex:1;overflow-y:auto;min-height:0;padding:0 20px 20px}.info-panel.hidden{display:none}.info-panel.info-panel-maximized{top:0;right:0;bottom:0;left:0;width:auto;max-height:none;border-radius:0;z-index:40}.info-panel-toolbar{position:absolute;top:12px;right:14px;display:flex;align-items:center;gap:2px;z-index:1}.info-toolbar-btn{background:none;border:none;color:var(--text-muted);font-size:16px;cursor:pointer;padding:4px 6px;line-height:1;border-radius:4px;transition:color .15s,background .15s}.info-toolbar-btn:hover:not(:disabled){color:var(--text);background:var(--bg-hover)}.info-toolbar-btn:disabled{color:var(--text-dim);cursor:default;opacity:.3}.info-close-btn{font-size:20px}.info-connection-link{cursor:pointer;transition:background .15s}.info-connection-link:hover{background:var(--bg-active)}.info-connection-link .info-target{color:var(--accent);text-decoration:underline;text-decoration-color:transparent;transition:text-decoration-color .15s}.info-connection-link:hover .info-target{text-decoration-color:var(--accent)}.info-header{margin-bottom:16px}.info-type-badge{display:inline-block;padding:3px 10px;border-radius:12px;font-size:11px;font-weight:600;color:var(--badge-text);margin-bottom:8px}.info-badge-row{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px}.info-empty-message{font-size:12px;color:var(--text-dim)}.share-list-message{font-size:13px;color:var(--text-dim);text-align:center;padding:12px}.info-label{font-size:18px;font-weight:600;color:var(--text-strong);margin-bottom:4px;word-break:break-word}.info-id{display:block;font-size:11px;color:var(--text-dim);font-family:monospace}.info-section{margin-bottom:16px}.info-section-title{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted);margin-bottom:8px;padding-bottom:4px;border-bottom:1px solid var(--border)}.info-props{display:grid;grid-template-columns:auto 1fr;gap:4px 12px}.info-props dt{font-size:12px;color:var(--text-muted);padding-top:2px}.info-props dd{font-size:12px;color:var(--text);word-break:break-word;display:flex;align-items:center;gap:4px}.info-value{white-space:pre-wrap}.info-array{display:flex;flex-wrap:wrap;gap:4px}.info-tag{display:inline-block;padding:2px 8px;background:var(--bg-hover);border-radius:4px;font-size:11px;color:var(--text-muted)}.info-json{font-size:11px;font-family:monospace;color:var(--text-muted);background:var(--bg-inset);padding:6px 8px;border-radius:4px;overflow-x:auto;white-space:pre}.info-connections{list-style:none;display:flex;flex-direction:column;gap:6px}.info-connection{display:flex;align-items:center;gap:6px;padding:6px 8px;background:var(--bg-elevated);border-radius:6px;font-size:12px;flex-wrap:wrap}.info-connection-active{outline:1.5px solid var(--accent);background:var(--bg-hover)}.info-target-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}.info-arrow{color:var(--text-dim);font-size:14px;flex-shrink:0}.info-edge-type{color:var(--text-muted);font-size:11px;font-weight:500}.info-target{color:var(--text);font-weight:500}.info-edge-props{width:100%;padding-top:4px;padding-left:20px}.info-edge-prop{display:block;font-size:11px;color:var(--text-dim)}.info-editable{cursor:default;position:relative}.info-inline-edit{background:none;border:none;color:var(--badge-text);opacity:0;font-size:10px;cursor:pointer;margin-left:4px;transition:opacity .15s}.info-editable:hover .info-inline-edit{opacity:.8}.info-inline-edit:hover{opacity:1!important}.info-edit-inline-input{background:transparent;border:none;border-bottom:1px solid var(--accent);color:var(--badge-text);font:inherit;font-size:inherit;outline:none;width:100%;padding:0}.info-edit-input{background:var(--bg-inset);border:1px solid var(--border);border-radius:4px;padding:3px 6px;font-size:12px;font-family:inherit;color:var(--text);flex:1;min-width:0;resize:vertical;overflow:hidden;line-height:1.4;max-height:300px}.info-edit-input:focus{outline:none;border-color:var(--accent)}.info-delete-prop{background:none;border:none;color:var(--text-dim);font-size:14px;cursor:pointer;padding:0 2px;flex-shrink:0;opacity:0;transition:opacity .1s,color .1s}.info-props dd:hover .info-delete-prop{opacity:1}.info-delete-prop:hover{color:#ef4444}.info-add-btn{background:none;border:1px dashed var(--border);border-radius:4px;padding:6px 10px;font-size:12px;color:var(--text-dim);cursor:pointer;width:100%;margin-top:8px;transition:border-color .15s,color .15s}.info-add-btn:hover{border-color:var(--accent);color:var(--text)}.info-add-row{display:flex;gap:4px;margin-top:6px}.info-add-save{background:var(--accent);border:none;border-radius:4px;padding:3px 10px;font-size:12px;color:var(--badge-text);cursor:pointer;flex-shrink:0}.info-add-save:hover{background:var(--accent-hover)}.info-delete-edge{background:none;border:none;color:var(--text-dim);font-size:14px;cursor:pointer;margin-left:auto;padding:0 2px;opacity:0;transition:opacity .1s,color .1s}.info-connection:hover .info-delete-edge{opacity:1}.info-delete-edge:hover{color:#ef4444}.info-danger{margin-top:8px;padding-top:12px;border-top:1px solid var(--border)}.info-delete-node{background:none;border:1px solid rgba(239,68,68,.3);border-radius:6px;padding:6px 12px;font-size:12px;color:#ef4444;cursor:pointer;width:100%;transition:background .15s}.info-delete-node:hover{background:#ef44441a}.tools-pane-toggle{background:var(--bg-surface);border:1px solid var(--border);border-radius:8px;color:var(--text-muted);font-size:18px;cursor:pointer;padding:6px 10px;line-height:1;transition:color .15s,border-color .15s,background .15s;box-shadow:0 2px 8px var(--shadow)}.tools-pane-toggle.hidden{display:none}.tools-pane-toggle:hover{color:var(--text);border-color:var(--text-muted);background:var(--bg-hover)}.tools-pane-toggle.active{color:var(--accent);border-color:#d4a27f66}.tools-pane-content{position:absolute;top:56px;left:16px;bottom:16px;z-index:20;width:var(--tools-width, 264px);box-sizing:border-box;overflow:hidden;display:flex;flex-direction:column;background:var(--bg-surface);border:1px solid var(--border);border-radius:10px;padding:12px;box-shadow:0 8px 32px var(--shadow)}.tools-pane-content.hidden{display:none}.tools-pane-section{margin-bottom:12px}.tools-pane-section:last-child{margin-bottom:0}.tools-pane-heading{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--text-dim);margin-bottom:6px}.tools-pane-row{display:flex;align-items:center;gap:6px;padding:3px 0;font-size:12px}.tools-pane-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0}.tools-pane-name{flex:1;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tools-pane-count{color:var(--text-dim);font-size:11px;flex-shrink:0}.tools-pane-summary{display:flex;align-items:center;gap:4px;font-size:11px;color:var(--text-muted);padding-bottom:10px;margin-bottom:10px;border-bottom:1px solid var(--border)}.tools-pane-sep{color:var(--text-dim)}.tools-pane-token-card{padding:8px 10px;margin-bottom:10px;border:1px solid var(--border);border-radius:6px;background:var(--bg-hover);font-size:11px}.token-card-label{font-weight:600;color:var(--text);margin-bottom:4px}.token-card-stat{color:var(--text-muted);margin-bottom:4px}.token-card-bar{height:4px;background:var(--border);border-radius:2px;margin:6px 0;overflow:hidden}.token-card-bar-fill{height:100%;background:var(--accent);border-radius:2px;transition:width .3s ease}.tools-pane-clickable{cursor:pointer;border-radius:4px;padding:3px 4px;margin:0 -4px;transition:background .1s}.tools-pane-clickable:hover{background:var(--bg-hover)}.tools-pane-clickable.active{background:var(--bg-hover);outline:1px solid var(--border)}.tools-pane-badge{font-size:9px;color:var(--accent);flex-shrink:0;opacity:.8}.tools-pane-issue .tools-pane-name{color:var(--text-muted)}.tools-pane-more{font-size:10px;color:var(--text-dim);padding:4px 0 0}.tools-pane-edit{background:none;border:none;color:var(--text-dim);font-size:11px;cursor:pointer;padding:0 2px;opacity:0;transition:opacity .1s,color .1s;flex-shrink:0}.tools-pane-row:hover .tools-pane-edit{opacity:1}.tools-pane-edit:hover{color:var(--accent)}.tools-pane-actions{display:flex;gap:6px;padding-top:4px}.tools-pane-action-btn{background:none;border:1px solid var(--border);color:var(--text-muted);font-size:10px;padding:2px 8px;border-radius:3px;cursor:pointer;transition:color .1s,border-color .1s}.tools-pane-action-btn:hover{color:var(--accent);border-color:var(--accent)}.tools-pane-focus-toggle{opacity:.4;font-size:11px}.tools-pane-focus-active{opacity:1!important;color:var(--accent)!important}.tools-pane-focus-clear{margin-top:4px;border-top:1px solid var(--border);padding-top:6px}.tools-pane-editing{background:none!important}.tools-pane-inline-input{width:100%;background:var(--bg);border:1px solid var(--accent);border-radius:4px;color:var(--text);font-size:12px;padding:2px 6px;outline:none}.tools-pane-slider-row{display:flex;align-items:center;gap:6px;padding:4px 0}.tools-pane-slider-label{font-size:11px;color:var(--text-muted);white-space:nowrap;min-width:56px}.tools-pane-slider{flex:1;min-width:0;height:4px;accent-color:var(--accent);cursor:pointer}.tools-pane-slider-value{font-size:10px;color:var(--text-dim);min-width:28px;text-align:right;font-family:monospace}.tools-pane-checkbox{width:14px;height:14px;accent-color:var(--accent);cursor:pointer;flex-shrink:0}.tools-pane-export-row{display:flex;gap:4px;margin-top:6px}.tools-pane-export-btn{flex:1;padding:4px 8px;font-size:11px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text-muted);cursor:pointer;transition:color .15s,border-color .15s}.tools-pane-export-btn:hover{color:var(--text);border-color:var(--text-muted)}.tools-pane-empty{font-size:11px;color:var(--text-dim);text-align:center;padding:8px 0}.tools-pane-tabs{display:flex;gap:2px;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--border)}.tools-pane-tab{flex:1;padding:4px 0;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.03em;background:none;border:1px solid transparent;border-radius:5px;color:var(--text-dim);cursor:pointer;transition:color .15s,background .15s,border-color .15s}.tools-pane-tab:hover{color:var(--text-muted);background:var(--bg-hover)}.tools-pane-tab-active{color:var(--text);background:var(--bg-hover);border-color:var(--border)}.tools-pane-tab-content{flex:1;overflow-y:auto;overflow-x:hidden;min-height:0}.tools-pane-search{width:100%;padding:4px 8px;font-size:11px;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);outline:none;margin-bottom:8px;box-sizing:border-box}.tools-pane-search:focus{border-color:var(--accent)}.tools-pane-search::placeholder{color:var(--text-dim)}.tools-pane-empty-msg{font-size:11px;color:var(--text-dim);text-align:center;padding:16px 0}.empty-state{position:absolute;top:0;right:0;bottom:0;left:0;display:flex;align-items:center;justify-content:center;z-index:5;pointer-events:none;overflow:hidden}.empty-state.hidden{display:none}.empty-state-bg{position:absolute;top:0;right:0;bottom:0;left:0;overflow:hidden}.empty-state-circle{position:absolute;border-radius:50%;background:var(--accent);opacity:.07}.empty-state-circle.c1{width:80px;height:80px;left:20%;top:15%;animation:float-circle 8s ease-in-out infinite}.empty-state-circle.c2{width:50px;height:50px;right:25%;top:25%;animation:float-circle 6s ease-in-out 1s infinite}.empty-state-circle.c3{width:65px;height:65px;left:55%;bottom:20%;animation:float-circle 7s ease-in-out 2s infinite}.empty-state-circle.c4{width:40px;height:40px;left:15%;bottom:30%;animation:float-circle 9s ease-in-out .5s infinite}.empty-state-circle.c5{width:55px;height:55px;right:15%;bottom:35%;animation:float-circle 7.5s ease-in-out 1.5s infinite}.empty-state-lines{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;color:var(--text-dim);animation:float-circle 10s ease-in-out infinite}@keyframes float-circle{0%,to{transform:translateY(0)}50%{transform:translateY(-12px)}}.empty-state-content{text-align:center;max-width:420px;padding:40px 24px;position:relative;z-index:1}.empty-state-icon{color:var(--text-dim);margin-bottom:16px}.empty-state-title{font-size:18px;font-weight:600;color:var(--text);margin-bottom:8px}.empty-state-desc{font-size:13px;color:var(--text-muted);line-height:1.5;margin-bottom:20px}.empty-state-setup{background:var(--bg-surface);border:1px solid var(--border);border-radius:8px;padding:12px 16px;margin-bottom:16px}.empty-state-label{font-size:11px;color:var(--text-dim);margin-bottom:8px}.empty-state-code{display:block;font-size:12px;color:var(--accent);font-family:monospace;word-break:break-all}.empty-state-hint{font-size:11px;color:var(--text-dim)}.empty-state-hint kbd{padding:1px 5px;border:1px solid var(--border);border-radius:3px;background:var(--bg-surface);font-family:monospace;font-size:11px}.shortcuts-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:100}.shortcuts-overlay.hidden{display:none}.shortcuts-modal{background:var(--bg-surface);border:1px solid var(--border);border-radius:12px;padding:24px;min-width:300px;max-width:400px;box-shadow:0 16px 48px var(--shadow);position:relative}.shortcuts-close{position:absolute;top:12px;right:14px;background:none;border:none;color:var(--text-dim);font-size:20px;cursor:pointer;padding:0 4px;line-height:1}.shortcuts-close:hover{color:var(--text)}.shortcuts-title{font-size:15px;font-weight:600;color:var(--text);margin-bottom:16px}.shortcuts-list{display:flex;flex-direction:column;gap:8px}.shortcuts-row{display:flex;align-items:center;justify-content:space-between;gap:12px}.shortcuts-keys{display:flex;align-items:center;gap:4px}.shortcuts-keys kbd{padding:2px 7px;border:1px solid var(--border);border-radius:4px;background:var(--bg);color:var(--text);font-size:11px;font-family:monospace}.shortcuts-or{font-size:10px;color:var(--text-dim)}.shortcuts-desc{font-size:12px;color:var(--text-muted)}@media(max-width:768px){#app{flex-direction:column}#sidebar{width:100%;min-width:0;max-height:35vh;border-right:none;border-bottom:1px solid var(--border)}.info-panel{top:auto;bottom:72px;right:8px;left:8px;width:auto;max-height:calc(100% - 200px);overflow-y:auto}.info-panel.info-panel-maximized{bottom:0;left:0;right:0}.canvas-top-bar{top:8px;left:8px;right:8px}.tools-pane-content{top:48px;left:8px;bottom:80px;width:160px;max-width:calc(100vw - 24px)}.tools-pane-edit{opacity:.6}}.bp-dialog-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:1000;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.bp-dialog{background:var(--bg-surface);border:1px solid var(--border);border-radius:12px;padding:20px;min-width:280px;max-width:400px;box-shadow:0 16px 48px #0000004d}.bp-dialog-title{font-size:14px;font-weight:600;color:var(--text);margin-bottom:12px}.bp-dialog-message{font-size:13px;color:var(--text-muted);margin-bottom:16px;line-height:1.5}.bp-dialog-input{width:100%;padding:8px 12px;font-size:13px;background:var(--bg);border:1px solid var(--border);border-radius:8px;color:var(--text);outline:none;margin-bottom:16px}.bp-dialog-input:focus{border-color:var(--accent)}.bp-dialog-buttons{display:flex;justify-content:flex-end;gap:8px}.bp-dialog-btn{padding:6px 16px;font-size:12px;border-radius:6px;border:1px solid var(--border);background:var(--bg);color:var(--text-muted);cursor:pointer;transition:all .15s}.bp-dialog-btn:hover{background:var(--bg-hover);color:var(--text)}.bp-dialog-btn-accent{background:var(--accent);color:#fff;border-color:var(--accent)}.bp-dialog-btn-accent:hover{opacity:.9;color:#fff;background:var(--accent)}.bp-dialog-btn-danger{background:#e55;color:#fff;border-color:#e55}.bp-dialog-btn-danger:hover{opacity:.9;color:#fff;background:#e55}.bp-toast{position:fixed;bottom:24px;left:50%;transform:translate(-50%) translateY(20px);background:var(--bg-surface);border:1px solid var(--border);color:var(--text);padding:8px 20px;border-radius:8px;font-size:12px;z-index:1001;opacity:0;transition:opacity .3s,transform .3s;box-shadow:0 4px 16px var(--shadow)}.bp-toast-visible{opacity:1;transform:translate(-50%) translateY(0)}.context-menu{position:absolute;z-index:100;background:var(--bg-surface);border:1px solid var(--border);border-radius:8px;padding:4px;min-width:180px;box-shadow:0 8px 24px var(--shadow)}.context-menu-item{padding:6px 12px;font-size:12px;color:var(--text);border-radius:4px;cursor:pointer;white-space:nowrap}.context-menu-item:hover{background:var(--bg-hover)}.context-menu-separator{height:1px;background:var(--border);margin:4px 8px}.path-bar{position:absolute;bottom:16px;left:50%;transform:translate(-50%);z-index:25;display:flex;align-items:center;gap:4px;background:var(--bg-surface);border:1px solid var(--border);border-radius:8px;padding:6px 12px;box-shadow:0 4px 16px var(--shadow);max-width:80%;overflow-x:auto}.path-bar.hidden{display:none}.path-bar-node{font-size:11px;color:var(--text);padding:2px 8px;border-radius:4px;cursor:pointer;white-space:nowrap;flex-shrink:0}.path-bar-node:hover{background:var(--bg-hover)}.path-bar-edge{font-size:9px;color:var(--text-dim);white-space:nowrap;flex-shrink:0}.path-bar-close{background:none;border:none;color:var(--text-dim);font-size:14px;cursor:pointer;padding:0 4px;margin-left:8px;flex-shrink:0}.path-bar-close:hover{color:var(--text)}.walk-trail-edge{font-size:9px;color:var(--text-dim);padding:1px 0 1px 24px;opacity:.7}.walk-indicator{font-size:10px;color:var(--accent);padding:2px 8px;border:1px solid rgba(212,162,127,.4);border-radius:4px;cursor:pointer;opacity:.4}.walk-indicator.active{opacity:1;background:#d4a27f26;animation:walk-strobe 2s ease-in-out infinite}@keyframes walk-strobe{0%,to{opacity:.6;border-color:#d4a27f4d}50%{opacity:1;border-color:#d4a27fcc}}
@@ -4,8 +4,8 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Backpack Viewer</title>
7
- <script type="module" crossorigin src="/assets/index-j3SowZae.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-CvkozBSE.css">
7
+ <script type="module" crossorigin src="/assets/index-DBZCyAjY.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-DlVz8Lz7.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="app">