backpack-viewer 0.4.0 → 0.5.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/README.md CHANGED
@@ -128,11 +128,31 @@ Bindings are strings with optional modifier prefixes separated by `+`:
128
128
  - With modifiers: `"ctrl+z"`, `"ctrl+shift+z"`, `"alt+s"`
129
129
  - `ctrl` and `cmd`/`meta` are treated as equivalent (works on both Mac and Linux)
130
130
 
131
+ ## Network binding
132
+
133
+ The viewer binds to **loopback only (`127.0.0.1`) by default** — its API exposes read/write access to your learning graphs, so it must never be reachable from other machines on your network without explicit opt-in. To change the bind host or port, edit `~/.config/backpack/viewer.json`:
134
+
135
+ ```json
136
+ {
137
+ "server": {
138
+ "host": "127.0.0.1",
139
+ "port": 5173
140
+ }
141
+ }
142
+ ```
143
+
144
+ Setting `host` to anything other than `127.0.0.1`, `localhost`, or `::1` prints a startup warning because the API becomes reachable from other machines on the network. Only do this for devcontainer / VM scenarios where you understand the trust boundary, and consider putting a reverse proxy with auth in front of it.
145
+
146
+ Environment variables override the config file:
147
+ - `BACKPACK_VIEWER_HOST` — bind host (use `0.0.0.0` for all interfaces)
148
+ - `PORT` — bind port
149
+
131
150
  ## Reference
132
151
 
133
152
  | Variable | Effect |
134
153
  |---|---|
135
- | `PORT` | Override the default port (default: `5173`) |
154
+ | `PORT` | Override the default port (default: `5173` or `server.port` in config) |
155
+ | `BACKPACK_VIEWER_HOST` | Override the bind host (default: `127.0.0.1` or `server.host` in config) |
136
156
  | `XDG_CONFIG_HOME` | Override config location (default: `~/.config`) |
137
157
  | `XDG_DATA_HOME` | Override data location (default: `~/.local/share`) |
138
158
  | `BACKPACK_DIR` | Override both config and data directories |
package/bin/serve.js CHANGED
@@ -8,7 +8,6 @@ import http from "node:http";
8
8
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
9
  const root = path.resolve(__dirname, "..");
10
10
  const distDir = path.resolve(root, "dist/app");
11
- const port = parseInt(process.env.PORT || "5173", 10);
12
11
 
13
12
  const hasDistBuild = fs.existsSync(path.join(distDir, "index.html"));
14
13
 
@@ -26,6 +25,16 @@ if (hasDistBuild) {
26
25
  } = await import("backpack-ontology");
27
26
  const { loadViewerConfig } = await import("../dist/config.js");
28
27
 
28
+ // Resolve host + port from the viewer config, with env vars taking
29
+ // precedence for quick overrides. Default is 127.0.0.1 loopback —
30
+ // the viewer must never bind to all interfaces by default because
31
+ // its API exposes read/write access to the user's learning graphs.
32
+ const viewerConfigForServer = loadViewerConfig();
33
+ const configuredHost = viewerConfigForServer?.server?.host ?? "127.0.0.1";
34
+ const configuredPort = viewerConfigForServer?.server?.port ?? 5173;
35
+ const bindHost = process.env.BACKPACK_VIEWER_HOST ?? configuredHost;
36
+ const port = parseInt(process.env.PORT || String(configuredPort), 10);
37
+
29
38
  // Storage points at the active backpack. Wrapped in a mutable
30
39
  // holder so a `/api/backpacks/switch` POST can swap it out in place
31
40
  // without restarting the whole server.
@@ -529,8 +538,25 @@ if (hasDistBuild) {
529
538
  }
530
539
  });
531
540
 
532
- server.listen(port, () => {
533
- console.log(` Backpack Viewer running at http://localhost:${port}/`);
541
+ // Bind to whatever the config + env var resolved to. The default is
542
+ // 127.0.0.1 because the viewer API exposes read/write access to the
543
+ // user's learning graphs and must never be reachable from other
544
+ // machines on the same network by default. Users who really need to
545
+ // bind to another interface (e.g. for a devcontainer scenario) can
546
+ // set server.host in ~/.config/backpack/viewer.json or the
547
+ // BACKPACK_VIEWER_HOST env var, and will see a loud warning.
548
+ server.listen(port, bindHost, () => {
549
+ const displayHost = bindHost === "0.0.0.0" || bindHost === "::" ? "localhost" : bindHost;
550
+ console.log(` Backpack Viewer running at http://${displayHost}:${port}/`);
551
+ const isLoopback =
552
+ bindHost === "127.0.0.1" ||
553
+ bindHost === "localhost" ||
554
+ bindHost === "::1";
555
+ if (!isLoopback) {
556
+ console.warn(
557
+ ` WARNING: viewer is bound to ${bindHost}, not loopback. The API exposes read/write access to your learning graphs — anyone on your network can reach it. Set server.host to "127.0.0.1" in ~/.config/backpack/viewer.json to restrict to localhost.`,
558
+ );
559
+ }
534
560
  });
535
561
  } else {
536
562
  // --- Development: use Vite for HMR + TypeScript compilation ---
@@ -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 mt=(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 x of y.addedNodes)x.tagName==="LINK"&&x.rel==="modulepreload"&&r(x)}).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 at(){const t=await fetch("/api/ontologies");return t.ok?t.json():[]}async function ht(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 Nn(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 Sn(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 _t(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 Gt(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 Vt(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 Pn(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 Rn(t,e){if(!(await fetch(`/api/graphs/${encodeURIComponent(t)}/snippets/${encodeURIComponent(e)}`,{method:"DELETE"})).ok)throw new Error("Failed to delete snippet")}const Jt="bp-dialog-overlay";function Bt(){var e;(e=document.querySelector(`.${Jt}`))==null||e.remove();const t=document.createElement("div");return t.className=Jt,document.body.appendChild(t),t}function Pt(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 Rt(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 hn(t,e){return new Promise(s=>{var x;const r=Bt(),l=Pt(r,t),y=document.createElement("p");y.className="bp-dialog-message",y.textContent=e,l.appendChild(y),Rt(l,[{label:"Cancel",onClick:()=>{r.remove(),s(!1)}},{label:"Confirm",accent:!0,onClick:()=>{r.remove(),s(!0)}}]),(x=l.querySelector(".bp-dialog-btn-accent"))==null||x.focus()})}function gt(t,e,s){return new Promise(r=>{const l=Bt(),y=Pt(l,t),x=document.createElement("input");x.type="text",x.className="bp-dialog-input",x.placeholder=e??"",x.value="",y.appendChild(x);const c=()=>{const o=x.value.trim();l.remove(),r(o||null)};x.addEventListener("keydown",o=>{o.key==="Enter"&&c(),o.key==="Escape"&&(l.remove(),r(null))}),Rt(y,[{label:"Cancel",onClick:()=>{l.remove(),r(null)}},{label:"OK",accent:!0,onClick:c}]),x.focus(),x.select()})}function Fn(){return new Promise(t=>{const e=Bt(),s=Pt(e,"Add Backpack"),r=document.createElement("p");r.className="bp-dialog-message",r.textContent="Enter the absolute path to a directory that should become a backpack. It will be shown in the sidebar using the last segment of the path as its display name.",s.appendChild(r);const l=document.createElement("label");l.className="bp-dialog-label",l.textContent="Path",s.appendChild(l);const y=document.createElement("div");y.className="bp-dialog-path-row",s.appendChild(y);const x=document.createElement("input");x.type="text",x.className="bp-dialog-input bp-dialog-path-input",x.placeholder="/Users/you/OneDrive/work",y.appendChild(x);const c=document.createElement("button");c.type="button",c.className="bp-dialog-btn bp-dialog-browse-btn",c.textContent="Browse...",y.appendChild(c),typeof window.showDirectoryPicker=="function"||(c.disabled=!0,c.title="Browser doesn't support native folder picker — paste the path manually"),c.addEventListener("click",async C=>{C.preventDefault();try{const j=await window.showDirectoryPicker({mode:"read"});P.textContent=`Picked "${j.name}" — paste the absolute path to it below.`,x.focus()}catch{}});const P=document.createElement("div");P.className="bp-dialog-picker-hint",s.appendChild(P);const i=document.createElement("div");i.className="bp-dialog-activate-row";const a=document.createElement("input");a.type="checkbox",a.id="bp-dialog-activate",a.checked=!0;const I=document.createElement("label");I.htmlFor="bp-dialog-activate",I.textContent="Switch to this backpack after registering",i.appendChild(a),i.appendChild(I),s.appendChild(i),x.addEventListener("dragover",C=>{C.preventDefault(),x.classList.add("bp-dialog-drag-over")}),x.addEventListener("dragleave",()=>{x.classList.remove("bp-dialog-drag-over")}),x.addEventListener("drop",C=>{var Q,W,ne;C.preventDefault(),x.classList.remove("bp-dialog-drag-over");const j=(Q=C.dataTransfer)==null?void 0:Q.items;if(!j||j.length===0)return;const U=(ne=(W=j[0]).webkitGetAsEntry)==null?void 0:ne.call(W);U!=null&&U.isDirectory&&(P.textContent=`Dropped "${U.name}" — paste the absolute path to it below.`)});const F=()=>{const C=x.value.trim();C&&(e.remove(),t({path:C,activate:a.checked}))};x.addEventListener("keydown",C=>{C.key==="Enter"&&F(),C.key==="Escape"&&(e.remove(),t(null))}),Rt(s,[{label:"Cancel",onClick:()=>{e.remove(),t(null)}},{label:"Register",accent:!0,onClick:F}]),x.focus()})}function Kt(t){return t>=1e3?`${(t/1e3).toFixed(1)}k tokens`:`${t} tokens`}function Zt(t,e){return t*50+e*25+50}function $n(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 x=document.createElement("h3");x.className="sidebar-section-heading",x.textContent="REMOTE GRAPHS",x.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.5.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 I=document.createElement("div");I.className="sidebar-heading-row",I.appendChild(r),I.appendChild(P),t.appendChild(I);const F=document.createElement("button");F.className="backpack-picker-pill",F.type="button",F.setAttribute("aria-haspopup","listbox"),F.setAttribute("aria-expanded","false");const C=document.createElement("span");C.className="backpack-picker-dot";const j=document.createElement("span");j.className="backpack-picker-name",j.textContent="...";const U=document.createElement("span");U.className="backpack-picker-caret",U.textContent="▾",F.appendChild(C),F.appendChild(j),F.appendChild(U);const Q=document.createElement("div");Q.className="backpack-picker-dropdown",Q.hidden=!0,Q.setAttribute("role","listbox");const W=document.createElement("div");W.className="backpack-picker-container",W.appendChild(F),W.appendChild(Q),t.appendChild(W);let ne=!1;function V(){ne=!1,Q.hidden=!0,F.setAttribute("aria-expanded","false")}function B(){ne=!0,Q.hidden=!1,F.setAttribute("aria-expanded","true")}F.addEventListener("click",_=>{_.stopPropagation(),ne?V():B()}),document.addEventListener("click",_=>{W.contains(_.target)||V()});let ee=[],K=null;function ce(){Q.replaceChildren();for(const D of ee){const w=document.createElement("button");w.className="backpack-picker-item",w.type="button",w.setAttribute("role","option"),D.active&&w.classList.add("active");const p=document.createElement("span");p.className="backpack-picker-item-dot",p.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,w.appendChild(p),w.appendChild(m),w.appendChild(g),w.addEventListener("click",v=>{v.stopPropagation(),V(),!D.active&&s.onBackpackSwitch&&s.onBackpackSwitch(D.name)}),Q.appendChild(w)}const _=document.createElement("div");_.className="backpack-picker-divider",Q.appendChild(_);const H=document.createElement("button");H.className="backpack-picker-item backpack-picker-add",H.type="button",H.textContent="+ Add new backpack…",H.addEventListener("click",async D=>{if(D.stopPropagation(),V(),!s.onBackpackRegister)return;const w=await Fn();w&&s.onBackpackRegister(w.path,w.activate)}),Q.appendChild(H)}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(x),t.appendChild(c),t.appendChild(o);let ie=[],te=[],ye="";return l.addEventListener("input",()=>{const _=l.value.toLowerCase();for(const H of ie){const D=H.dataset.name??"";H.style.display=D.includes(_)?"":"none"}for(const H of te){const D=H.dataset.name??"";H.style.display=D.includes(_)?"":"none"}}),{setBackpacks(_){ee=_.slice();const H=_.find(D=>D.active)??null;K=H,H&&(j.textContent=H.name,C.style.setProperty("--backpack-color",H.color),t.style.setProperty("--backpack-color",H.color)),ce()},setActiveBackpack(_){K=_,ee=ee.map(H=>({...H,active:H.name===_.name})),ee.some(H=>H.name===_.name)||ee.push({..._,active:!0}),j.textContent=_.name,C.style.setProperty("--backpack-color",_.color),t.style.setProperty("--backpack-color",_.color),ce()},getActiveBackpack(){return K},setSummaries(_){y.innerHTML="";const H=fetch("/api/locks").then(D=>D.json()).catch(()=>({}));ie=_.map(D=>{const w=document.createElement("li");w.className="ontology-item",w.dataset.name=D.name;const p=document.createElement("span");p.className="name",p.textContent=D.name;const m=document.createElement("span");m.className="stats";const g=Zt(D.nodeCount,D.edgeCount);m.textContent=`${D.nodeCount} nodes, ${D.edgeCount} edges · ~${Kt(g)}`;const v=document.createElement("span");v.className="sidebar-branch",v.dataset.graph=D.name;const N=document.createElement("span");if(N.className="sidebar-lock-badge",N.dataset.graph=D.name,H.then(u=>{if(!N.isConnected)return;const E=u[D.name];E&&typeof E=="object"&&E.author&&(N.textContent=`editing: ${E.author}`,N.title=`Last activity: ${E.lastActivity??""}`,N.classList.add("active"))}),w.appendChild(p),w.appendChild(m),w.appendChild(N),w.appendChild(v),s.onRename){const u=document.createElement("button");u.className="sidebar-edit-btn",u.textContent="✎",u.title="Rename";const E=s.onRename;u.addEventListener("click",R=>{R.stopPropagation();const M=document.createElement("input");M.type="text",M.className="sidebar-rename-input",M.value=D.name,p.textContent="",p.appendChild(M),u.style.display="none",M.focus(),M.select();const O=()=>{const k=M.value.trim();k&&k!==D.name?E(D.name,k):(p.textContent=D.name,u.style.display="")};M.addEventListener("blur",O),M.addEventListener("keydown",k=>{k.key==="Enter"&&M.blur(),k.key==="Escape"&&(M.value=D.name,M.blur())})}),w.appendChild(u)}return w.addEventListener("click",()=>s.onSelect(D.name)),y.appendChild(w),w}),ye&&this.setActive(ye)},setActive(_){ye=_;for(const H of ie)H.classList.toggle("active",H.dataset.name===_);for(const H of te)H.classList.toggle("active",H.dataset.name===_)},setRemotes(_){c.replaceChildren(),te=_.map(D=>{const w=document.createElement("li");w.className="ontology-item ontology-item-remote",w.dataset.name=D.name;const p=document.createElement("div");p.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}`,p.appendChild(m),p.appendChild(g);const v=document.createElement("span");v.className="stats";const N=Zt(D.nodeCount,D.edgeCount);v.textContent=`${D.nodeCount} nodes, ${D.edgeCount} edges · ~${Kt(N)}`;const u=document.createElement("span");return u.className="remote-source",u.textContent=D.source??new URL(D.url).hostname,u.title=D.url,w.appendChild(p),w.appendChild(v),w.appendChild(u),w.addEventListener("click",()=>s.onSelect(D.name)),c.appendChild(w),w});const H=_.length>0;x.hidden=!H,c.hidden=!H,ye&&this.setActive(ye)},setActiveBranch(_,H,D){const w=y.querySelectorAll(`.sidebar-branch[data-graph="${_}"]`);for(const p of w){p.textContent=`/ ${H}`,p.title="Click to switch branch",p.style.cursor="pointer";const m=p.cloneNode(!0);p.replaceWith(m),m.addEventListener("click",g=>{g.stopPropagation(),ke(_,m,D??[])})}},setSnippets(_,H){var p;const D=ie.find(m=>m.dataset.name===_);if(!D||((p=D.querySelector(".sidebar-snippets"))==null||p.remove(),H.length===0))return;const w=document.createElement("div");w.className="sidebar-snippets";for(const m of H){const g=document.createElement("div");g.className="sidebar-snippet";const v=document.createElement("span");v.className="sidebar-snippet-label",v.textContent=`◆ ${m.label}`,v.title=`${m.nodeCount} nodes — click to load`;const N=document.createElement("button");N.className="sidebar-snippet-delete",N.textContent="×",N.title="Delete snippet",N.addEventListener("click",u=>{var E;u.stopPropagation(),(E=s.onSnippetDelete)==null||E.call(s,_,m.id)}),g.appendChild(v),g.appendChild(N),g.addEventListener("click",u=>{var E;u.stopPropagation(),(E=s.onSnippetLoad)==null||E.call(s,_,m.id)}),w.appendChild(g)}D.appendChild(w)},toggle:a,expandBtn:J};function ke(_,H,D){const w=t.querySelector(".branch-picker");w&&w.remove();const p=document.createElement("div");p.className="branch-picker";for(const g of D){const v=document.createElement("div");v.className="branch-picker-item",g.active&&v.classList.add("branch-picker-active");const N=document.createElement("span");if(N.textContent=g.name,v.appendChild(N),!g.active&&s.onBranchDelete){const u=document.createElement("button");u.className="branch-picker-delete",u.textContent="×",u.title=`Delete ${g.name}`,u.addEventListener("click",E=>{E.stopPropagation(),hn("Delete branch",`Delete branch "${g.name}"?`).then(R=>{R&&(s.onBranchDelete(_,g.name),p.remove())})}),v.appendChild(u)}g.active||v.addEventListener("click",()=>{var u;(u=s.onBranchSwitch)==null||u.call(s,_,g.name),p.remove()}),p.appendChild(v)}if(s.onBranchCreate){const g=document.createElement("div");g.className="branch-picker-item branch-picker-create",g.textContent="+ New branch",g.addEventListener("click",()=>{gt("New branch","Branch name").then(v=>{v&&(s.onBranchCreate(_,v),p.remove())})}),p.appendChild(g)}H.after(p);const m=g=>{p.contains(g.target)||(p.remove(),document.removeEventListener("click",m))};setTimeout(()=>document.addEventListener("click",m),0)}}function Tt(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 Qt(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 en(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 At(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=Qt(t,l.x,l.y);if(t.children[y]===null){const[x,c,o,P]=en(t,y);t.children[y]=Tt(x,c,o,P)}At(t.children[y],l)}const s=Qt(t,e.x,e.y);if(t.children[s]===null){const[l,y,x,c]=en(t,s);t.children[s]=Tt(l,y,x,c)}At(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 Dn(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,x=(e+r)/2,c=(s+l)/2,o=Math.max(r-e,l-s)/2+y,P=Tt(x-o,c-o,x+o,c+o);for(const i of t)At(P,i);return P}function On(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 x=t.cx-e.x,c=t.cy-e.y,o=x*x+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),I=x/i*a,F=c/i*a;e.vx-=I,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),I=x/i*a,F=c/i*a;e.vx-=I,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},tn=6e3,Wn=12e3,Hn=.004,yn=140,Cn=350,nn=.9,on=.01,ct=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 rt(){return{...Xe}}function zn(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 jn(t,e){for(const s of Object.values(t))if(typeof s=="string")return s;return e}function sn(t,e,s){const r=new Set(e);let l=new Set(e);for(let y=0;y<s;y++){const x=new Set;for(const c of t.edges)l.has(c.sourceId)&&!r.has(c.targetId)&&x.add(c.targetId),l.has(c.targetId)&&!r.has(c.sourceId)&&x.add(c.sourceId);for(const c of x)r.add(c);if(l=x,x.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 Nt(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 x=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,I=Math.sin(i)*r,F=l.get(o.type)??0;l.set(o.type,F+1);const C=y.get(o.type)??1,j=2*Math.PI*F/C,U=yn*.6,Q={id:o.id,x:a+Math.cos(j)*U,y:I+Math.sin(j)*U,vx:0,vy:0,label:jn(o.properties,o.id),type:o.type};return e.set(o.id,Q),Q}),c=t.edges.map(o=>({sourceId:o.sourceId,targetId:o.targetId,type:o.type}));return{nodes:x,edges:c,nodeMap:e}}const Un=.7,qn=80;function Yn(t,e){const{nodes:s,edges:r,nodeMap:l}=t,y=Wn*Xe.spacing;if(s.length>=qn){const c=Dn(s);if(c)for(const P of s)On(c,P,Un,y,e,ct);const o=y-tn;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 I=a+1;I<i.length;I++){const F=i[a],C=i[I];let j=C.x-F.x,U=C.y-F.y,Q=Math.sqrt(j*j+U*U);Q<ct&&(Q=ct);const W=o*e/(Q*Q),ne=j/Q*W,V=U/Q*W;F.vx+=ne,F.vy+=V,C.vx-=ne,C.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,I=i.y-P.y,F=Math.sqrt(a*a+I*I);F<ct&&(F=ct);const j=(P.type===i.type?tn:y)*e/(F*F),U=a/F*j,Q=I/F*j;P.vx-=U,P.vy-=Q,i.vx+=U,i.vy+=Q}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,I=Math.sqrt(i*i+a*a);if(I===0)continue;const F=o.type===P.type?yn*Xe.spacing:Cn*Xe.spacing,C=Hn*(I-F)*e,j=i/I*C,U=a/I*C;o.vx+=j,o.vy+=U,P.vx-=j,P.vy-=U}for(const c of s)c.vx-=c.x*on*e,c.vy-=c.y*on*e;const x=new Map;for(const c of s){const o=x.get(c.type)??{x:0,y:0,count:0};o.x+=c.x,o.y+=c.y,o.count++,x.set(c.type,o)}for(const c of x.values())c.x/=c.count,c.y/=c.count;for(const c of s){const o=x.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*=nn,c.vy*=nn;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 an=["#d4a27f","#c17856","#b07a5e","#d4956b","#a67c5a","#cc9e7c","#c4866a","#cb8e6c","#b8956e","#a88a70","#d9b08c","#c4a882","#e8b898","#b5927a","#a8886e","#d1a990"],cn=new Map;function Ne(t){const e=cn.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=an[Math.abs(s)%an.length];return cn.set(t,r),r}class Xn{constructor(e){mt(this,"cells",new Map);mt(this,"cellSize");mt(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),x=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<=x;a++)for(let I=c;I<=o;I++){const F=this.cells.get(this.key(a,I));if(F)for(const C of F){const j=C.x-e,U=C.y-s,Q=j*j+U*U;Q<=i&&(i=Q,P=C)}}return P}}const et=new Map,_n=2e3;function Gn(t,e,s){return`${t}|${e}|${s}`}const Vn=new OffscreenCanvas(1,1),ln=Vn.getContext("2d");function Jn(t,e,s){ln.font=e;const r=ln.measureText(t),l=Math.ceil(r.width)+2,y=Math.ceil(r.actualBoundingBoxAscent+r.actualBoundingBoxDescent)+4,x=new OffscreenCanvas(l,y),c=x.getContext("2d");return c.font=e,c.fillStyle=s,c.textAlign="left",c.textBaseline="top",c.fillText(t,1,1),{canvas:x,width:l,height:y}}function rn(t,e,s,r,l,y,x){const c=Gn(e,l,y);let o=et.get(c);if(!o){if(et.size>=_n){const a=et.keys().next().value;a!==void 0&&et.delete(a)}o=Jn(e,l,y),et.set(c,o)}const P=s-o.width/2,i=x==="top"?r:r-o.height;t.drawImage(o.canvas,P,i)}function Kn(){et.clear()}function Ce(t){return getComputedStyle(document.documentElement).getPropertyValue(t).trim()}const Fe=20,St=.001,Zn={hideBadges:.4,hideLabels:.25,hideEdgeLabels:.35,smallNodes:.2,hideArrows:.15,dotNodes:.1,hullsOnly:.05},Qn={zoomFactor:1.3,zoomMin:.05,zoomMax:10,panAnimationMs:300};function ft(t,e,s,r,l,y=100){const x=(t-s.x)*s.scale,c=(e-s.y)*s.scale;return x>=-y&&x<=r+y&&c>=-y&&c<=l+y}function eo(t,e,s,r){const l={...Zn,...(r==null?void 0:r.lod)??{}},y={...Qn,...(r==null?void 0:r.navigation)??{}},x={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,I=1,F=0,C=new Set,j=null,U=!0,Q=!0,W=!0,ne=!0,V=null,B=null,ee=1,K=null,ce=null,J=null,ie=!1,te=[],ye=0,ke=0;const _=400,H=new Xn(Fe*2);let D=0;function w(){M(),D||(D=requestAnimationFrame(()=>{D=0,d()}))}const p=150;let m=null,g=!1;function v(){if(!m)try{m=new Worker(new URL("/assets/layout-worker-BZXiBoiC.js",import.meta.url),{type:"module"}),m.onmessage=N,m.onerror=()=>{g=!1,m=null,xe()}}catch{return g=!1,null}return m}function N(h){const b=h.data;if(b.type==="tick"&&a){const A=b.positions,$=a.nodes;for(let z=0;z<$.length;z++)$[z].x=A[z*4],$[z].y=A[z*4+1],$[z].vx=A[z*4+2],$[z].vy=A[z*4+3];I=b.alpha,H.rebuild($),d()}b.type==="settled"&&(I=0,ie&&te.length>0&&!le&&(le=requestAnimationFrame(ge)))}let u=null,E=null,R=!0;function M(){R=!0}let O=null,k=null;const f=y.panAnimationMs;function S(){c.width=c.clientWidth*P,c.height=c.clientHeight*P,M(),w()}const q=new ResizeObserver(S);q.observe(t),S();function se(h,b){return[h/i.scale+i.x,b/i.scale+i.y]}function pe(h,b){if(!a)return null;const[A,$]=se(h,b);return H.query(A,$,Fe)}function n(){if(!a)return;ye+=x.pulseSpeed;const h=new Set(te);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 b=Ce("--canvas-walk-edge")||"#1a1a1a",A=[];for(const X of a.edges){if(!h.has(X.sourceId)||!h.has(X.targetId)||X.sourceId===X.targetId)continue;const Y=a.nodeMap.get(X.sourceId),Z=a.nodeMap.get(X.targetId);!Y||!Z||A.push(Y.x,Y.y,Z.x,Z.y)}if(A.length>0){o.beginPath();for(let X=0;X<A.length;X+=4)o.moveTo(A[X],A[X+1]),o.lineTo(A[X+2],A[X+3]);o.strokeStyle=b,o.lineWidth=3,o.globalAlpha=.5+.5*Math.sin(ye),o.stroke(),o.globalAlpha=1}const $=i.scale<l.smallNodes?Fe*.5:Fe,z=Ce("--accent")||"#d4a27f";for(const X of te){const Y=a.nodeMap.get(X);if(!Y||!ft(Y.x,Y.y,i,c.clientWidth,c.clientHeight))continue;const Z=X===te[te.length-1],ue=.5+.5*Math.sin(ye);o.strokeStyle=z,o.lineWidth=Z?3:2,o.globalAlpha=Z?.5+.5*ue:.3+.4*ue,o.beginPath(),o.arc(Y.x,Y.y,$+(Z?6:4),0,Math.PI*2),o.stroke()}o.globalAlpha=1,o.restore(),ne&&a.nodes.length>1&&T(),o.restore()}function d(){var zt;if(!a){o.clearRect(0,0,c.width,c.height);return}if(!R&&u&&ie&&te.length>0&&I<St){n();return}const h=I<St&&ie&&te.length>0;ie&&te.length>0&&(ye+=x.pulseSpeed);const b=ie&&!h?new Set(te):null,A=Ce("--canvas-edge"),$=Ce("--canvas-edge-highlight"),z=Ce("--canvas-edge-dim"),X=Ce("--canvas-edge-label"),Y=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),W&&i.scale>=l.smallNodes){const oe=new Map;for(const Se of a.nodes){if(j!==null&&!j.has(Se.id))continue;const Be=oe.get(Se.type)??[];Be.push(Se),oe.set(Se.type,Be)}for(const[Se,Be]of oe){if(Be.length<2)continue;const Ve=Ne(Se),_e=Fe*2.5;let We=1/0,ze=1/0,Pe=-1/0,Ye=-1/0;for(const Re of Be)Re.x<We&&(We=Re.x),Re.y<ze&&(ze=Re.y),Re.x>Pe&&(Pe=Re.x),Re.y>Ye&&(Ye=Re.y);o.beginPath();const ae=(Pe-We)/2+_e,we=(Ye-ze)/2+_e,ve=(We+Pe)/2,$e=(ze+Ye)/2;o.ellipse(ve,$e,ae,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(C.size>0){Te=new Set;for(const oe of a.edges)C.has(oe.sourceId)&&Te.add(oe.targetId),C.has(oe.targetId)&&Te.add(oe.sourceId)}const vt=Ce("--accent")||"#d4a27f",xt=Ce("--canvas-walk-edge")||"#1a1a1a",Ze=i.scale>=l.hideArrows,pt=Q&&i.scale>=l.hideEdgeLabels;if(U){const oe=[],Se=[],Be=[],Ve=[],_e=[],We=[];for(const ae of a.edges){const we=a.nodeMap.get(ae.sourceId),ve=a.nodeMap.get(ae.targetId);if(!we||!ve||!ft(we.x,we.y,i,c.clientWidth,c.clientHeight,200)&&!ft(ve.x,ve.y,i,c.clientWidth,c.clientHeight,200))continue;const $e=j===null||j.has(ae.sourceId),Re=j===null||j.has(ae.targetId),jt=$e&&Re;if(j!==null&&!$e&&!Re)continue;const Et=C.size>0&&(C.has(ae.sourceId)||C.has(ae.targetId))||j!==null&&jt,Ut=j!==null&&!jt,qt=b!==null&&b.has(ae.sourceId)&&b.has(ae.targetId),Yt=J?V==null?void 0:V.edges.find(ut=>ut.sourceId===ae.sourceId&&ut.targetId===ae.targetId||ut.targetId===ae.sourceId&&ut.sourceId===ae.targetId):null,Xt=!!(J&&Yt&&J.edgeIds.has(Yt.id));if(ae.sourceId===ae.targetId){re(we,ae.type,Et,A,$,X,Y);continue}(Xt?_e:qt?Ve:Et?Se:Ut?Be:oe).push(we.x,we.y,ve.x,ve.y),(Ze||pt)&&We.push({sx:we.x,sy:we.y,tx:ve.x,ty:ve.y,type:ae.type,highlighted:Et,edgeDimmed:Ut,isPathEdge:Xt,isWalkEdge:qt})}const ze=Ze?1.5:1,Ye=[{lines:oe,color:A,width:ze,alpha:1},{lines:Be,color:z,width:ze,alpha:1},{lines:Se,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 ae of Ye)if(ae.lines.length!==0){o.beginPath();for(let we=0;we<ae.lines.length;we+=4)o.moveTo(ae.lines[we],ae.lines[we+1]),o.lineTo(ae.lines[we+2],ae.lines[we+3]);o.strokeStyle=ae.color,o.lineWidth=ae.width,o.globalAlpha=ae.alpha,o.stroke()}o.globalAlpha=1;for(const ae of We)if(Ze&&G(ae.sx,ae.sy,ae.tx,ae.ty,ae.highlighted||ae.isPathEdge,ue,he),pt){const we=(ae.sx+ae.tx)/2,ve=(ae.sy+ae.ty)/2;o.fillStyle=ae.highlighted?Y:ae.edgeDimmed?Z:X,o.font="9px system-ui, sans-serif",o.textAlign="center",o.textBaseline="bottom",o.fillText(ae.type,we,ve-4)}}const bt=performance.now()-ke,Ee=Math.min(1,bt/_),Ue=1-(1-Ee)*(1-Ee),qe=Ee<1,Ht=i.scale<l.hullsOnly,bn=!Ht&&i.scale<l.dotNodes;if(!Ht)for(const oe of a.nodes){if(!ft(oe.x,oe.y,i,c.clientWidth,c.clientHeight))continue;const Se=Ne(oe.type);if(bn){const ve=j!==null&&!j.has(oe.id);o.fillStyle=Se;const $e=ve?.1:.8;o.globalAlpha=qe?$e*Ue:$e,o.fillRect(oe.x-2,oe.y-2,4,4);continue}const Be=C.has(oe.id),Ve=Te!==null&&Te.has(oe.id),_e=j!==null&&!j.has(oe.id),We=_e||C.size>0&&!Be&&!Ve,ze=i.scale<l.smallNodes?Fe*.5:Fe,Pe=qe?ze*Ue:ze;if(b!=null&&b.has(oe.id)){const ve=te[te.length-1]===oe.id,$e=.5+.5*Math.sin(ye),Re=Ce("--accent")||"#d4a27f";o.save(),o.strokeStyle=Re,o.lineWidth=ve?3:2,o.globalAlpha=ve?.5+.5*$e:.3+.4*$e,o.beginPath(),o.arc(oe.x,oe.y,Pe+(ve?6:4),0,Math.PI*2),o.stroke(),o.restore()}Be&&(o.save(),o.shadowColor=Se,o.shadowBlur=20,o.beginPath(),o.arc(oe.x,oe.y,Pe+3,0,Math.PI*2),o.fillStyle=Se,o.globalAlpha=.3,o.fill(),o.restore()),o.beginPath(),o.arc(oe.x,oe.y,Pe,0,Math.PI*2),o.fillStyle=Se;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(oe.id)&&!Be&&(o.save(),o.shadowColor=Ce("--accent")||"#d4a27f",o.shadowBlur=15,o.beginPath(),o.arc(oe.x,oe.y,Pe+2,0,Math.PI*2),o.strokeStyle=Ce("--accent")||"#d4a27f",o.globalAlpha=.5,o.lineWidth=2,o.stroke(),o.restore());const ae=V==null?void 0:V.nodes.find(ve=>ve.id===oe.id);if(((zt=ae==null?void 0:ae.properties)==null?void 0:zt._starred)===!0&&(o.fillStyle="#ffd700",o.font="10px system-ui, sans-serif",o.textAlign="left",o.textBaseline="bottom",o.fillText("★",oe.x+Pe-2,oe.y-Pe+2)),i.scale>=l.hideLabels){const ve=oe.label.length>24?oe.label.slice(0,22)+"...":oe.label,$e=We?be:fe;rn(o,ve,oe.x,oe.y+Pe+4,"11px system-ui, sans-serif",$e,"top")}if(i.scale>=l.hideBadges){const ve=We?Me:Oe;rn(o,oe.type,oe.x,oe.y-Pe-3,"9px system-ui, sans-serif",ve,"bottom")}o.globalAlpha=1}if(o.restore(),o.restore(),h){const oe=c.width,Se=c.height;(!u||u.width!==oe||u.height!==Se)&&(u=new OffscreenCanvas(oe,Se),E=u.getContext("2d")),E&&(E.clearRect(0,0,oe,Se),E.drawImage(c,0,0),R=!1),n();return}ne&&a.nodes.length>1&&T()}function T(){if(!a)return;const h=140,b=100,A=8,$=c.clientWidth-h-16,z=c.clientHeight-b-16;let X=1/0,Y=1/0,Z=-1/0,ue=-1/0;for(const Ee of a.nodes)Ee.x<X&&(X=Ee.x),Ee.y<Y&&(Y=Ee.y),Ee.x>Z&&(Z=Ee.x),Ee.y>ue&&(ue=Ee.y);const he=Z-X||1,fe=ue-Y||1,be=Math.min((h-A*2)/he,(b-A*2)/fe),Oe=$+A+(h-A*2-he*be)/2,Me=z+A+(b-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($,z,h,b,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-X)*be,Me+(Ue.y-Y)*be),o.lineTo(Oe+(qe.x-X)*be,Me+(qe.y-Y)*be),o.stroke())}o.globalAlpha=.8;for(const Ee of a.nodes){const Ue=Oe+(Ee.x-X)*be,qe=Me+(Ee.y-Y)*be;o.beginPath(),o.arc(Ue,qe,2,0,Math.PI*2),o.fillStyle=Ne(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-X)*be,Ze=Me+(je-Y)*be,pt=(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(z,Math.min(Ze,z+b)),Math.min(pt,h),Math.min(bt,b)),o.globalAlpha=1,o.restore()}function G(h,b,A,$,z,X,Y){const Z=Math.atan2($-b,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=z?Y:X,o.fill()}function re(h,b,A,$,z,X,Y){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?z:$,o.lineWidth=A?2.5:1.5,o.stroke(),Q&&(o.fillStyle=A?Y:X,o.font="9px system-ui, sans-serif",o.textAlign="center",o.fillText(b,Z,ue-18))}function me(){if(!O||!k)return;const h=performance.now()-k.time,b=Math.min(h/f,1),A=1-Math.pow(1-b,3);i.x=k.x+(O.x-k.x)*A,i.y=k.y+(O.y-k.y)*A,M(),d(),b<1?requestAnimationFrame(me):(O=null,k=null)}let le=0;function ge(){if(!ie||te.length===0){le=0;return}d(),le=requestAnimationFrame(ge)}function Le(){if(!a||a.nodes.length===0)return;let h=1/0,b=1/0,A=-1/0,$=-1/0;for(const he of a.nodes)he.x<h&&(h=he.x),he.y<b&&(b=he.y),he.x>A&&(A=he.x),he.y>$&&($=he.y);const z=Fe*4,X=A-h+z*2,Y=$-b+z*2,Z=c.clientWidth/Math.max(X,1),ue=c.clientHeight/Math.max(Y,1);i.scale=Math.min(Z,ue,2),i.x=(h+A)/2-c.clientWidth/(2*i.scale),i.y=(b+$)/2-c.clientHeight/(2*i.scale),w()}function xe(){if(!a||I<St){ie&&te.length>0&&!le&&(le=requestAnimationFrame(ge));return}I=Yn(a,I),H.rebuild(a.nodes),d(),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 b=h.clientX-Je,A=h.clientY-Ke;(Math.abs(b)>5||Math.abs(A)>5)&&(yt=!0),i.x-=b/i.scale,i.y-=A/i.scale,Je=h.clientX,Ke=h.clientY,w()}),c.addEventListener("mouseup",h=>{if(He=!1,yt)return;const b=c.getBoundingClientRect(),A=h.clientX-b.left,$=h.clientY-b.top,z=pe(A,$),X=h.ctrlKey||h.metaKey;if(ie&&B&&z&&a){const Y=te.length>0?te[te.length-1]:B[0],Z=new Set([Y]),ue=[{id:Y,path:[Y]}];let he=null;for(;ue.length>0;){const{id:Me,path:Ie}=ue.shift();if(Me===z.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))te.includes(Me)||te.push(Me);B=[z.id];const fe=Math.max(1,ee);ee=fe;const be=sn(V,[z.id],fe);cancelAnimationFrame(F),m&&m.postMessage({type:"stop"}),a=Nt(be),H.rebuild(a.nodes),I=1,C=new Set([z.id]),j=null,i={x:0,y:0,scale:1},g=be.nodes.length>=p;const Oe=g?v():null;Oe?Oe.postMessage({type:"start",data:be}):(g=!1,xe()),setTimeout(()=>{a&&Le()},300),s==null||s({seedNodeIds:[z.id],hops:fe,totalNodes:be.nodes.length}),e==null||e([z.id]);return}if(z){X?C.has(z.id)?C.delete(z.id):C.add(z.id):C.size===1&&C.has(z.id)?C.clear():(C.clear(),C.add(z.id));const Y=[...C];e==null||e(Y.length>0?Y:null)}else C.clear(),e==null||e(null);w()}),c.addEventListener("mouseleave",()=>{He=!1}),c.addEventListener("wheel",h=>{h.preventDefault();const b=c.getBoundingClientRect(),A=h.clientX-b.left,$=h.clientY-b.top,[z,X]=se(A,$),Y=h.ctrlKey?1-h.deltaY*.01:h.deltaY>0?.9:1.1;i.scale=Math.max(y.zoomMin,Math.min(y.zoomMax,i.scale*Y)),i.x=z-A/i.scale,i.y=X-$/i.scale,w()},{passive:!1});let De=[],Ft=0,$t=1,Dt=0,Ot=0,Ct=!1;c.addEventListener("touchstart",h=>{h.preventDefault(),De=Array.from(h.touches),De.length===2?(Ft=Wt(De[0],De[1]),$t=i.scale):De.length===1&&(Je=De[0].clientX,Ke=De[0].clientY,Dt=De[0].clientX,Ot=De[0].clientY,Ct=!1)},{passive:!1}),c.addEventListener("touchmove",h=>{h.preventDefault();const b=Array.from(h.touches);if(b.length===2&&De.length===2){const $=Wt(b[0],b[1])/Ft;i.scale=Math.max(y.zoomMin,Math.min(y.zoomMax,$t*$)),w()}else if(b.length===1){const A=b[0].clientX-Je,$=b[0].clientY-Ke;(Math.abs(b[0].clientX-Dt)>10||Math.abs(b[0].clientY-Ot)>10)&&(Ct=!0),i.x-=A/i.scale,i.y-=$/i.scale,Je=b[0].clientX,Ke=b[0].clientY,w()}De=b},{passive:!1}),c.addEventListener("touchend",h=>{if(h.preventDefault(),Ct||h.changedTouches.length!==1)return;const b=h.changedTouches[0],A=c.getBoundingClientRect(),$=b.clientX-A.left,z=b.clientY-A.top,X=pe($,z);if(X){C.size===1&&C.has(X.id)?C.clear():(C.clear(),C.add(X.id));const Y=[...C];e==null||e(Y.length>0?Y:null)}else C.clear(),e==null||e(null);w()},{passive:!1}),c.addEventListener("gesturestart",h=>h.preventDefault()),c.addEventListener("gesturechange",h=>h.preventDefault());function Wt(h,b){const A=h.clientX-b.clientX,$=h.clientY-b.clientY;return Math.sqrt(A*A+$*$)}const tt=document.createElement("div");tt.className="zoom-controls";const nt=document.createElement("button");nt.className="zoom-btn",nt.textContent="+",nt.title="Zoom in",nt.addEventListener("click",()=>{const h=c.clientWidth/2,b=c.clientHeight/2,[A,$]=se(h,b);i.scale=Math.min(y.zoomMax,i.scale*y.zoomFactor),i.x=A-h/i.scale,i.y=$-b/i.scale,w()});const ot=document.createElement("button");ot.className="zoom-btn",ot.textContent="−",ot.title="Zoom out",ot.addEventListener("click",()=>{const h=c.clientWidth/2,b=c.clientHeight/2,[A,$]=se(h,b);i.scale=Math.max(y.zoomMin,i.scale/y.zoomFactor),i.x=A-h/i.scale,i.y=$-b/i.scale,w()});const st=document.createElement("button");st.className="zoom-btn",st.textContent="○",st.title="Reset zoom",st.addEventListener("click",()=>{if(a){if(i={x:0,y:0,scale:1},a.nodes.length>0){let h=1/0,b=1/0,A=-1/0,$=-1/0;for(const Y of a.nodes)Y.x<h&&(h=Y.x),Y.y<b&&(b=Y.y),Y.x>A&&(A=Y.x),Y.y>$&&($=Y.y);const z=(h+A)/2,X=(b+$)/2;i.x=z-c.clientWidth/2,i.y=X-c.clientHeight/2}w()}}),tt.appendChild(nt),tt.appendChild(st),tt.appendChild(ot),t.appendChild(tt);const Ae=document.createElement("div");Ae.className="node-tooltip",Ae.style.display="none",t.appendChild(Ae);let dt=null,Ge=null;return c.addEventListener("mousemove",h=>{if(He){Ae.style.display!=="none"&&(Ae.style.display="none",dt=null);return}const b=c.getBoundingClientRect(),A=h.clientX-b.left,$=h.clientY-b.top,z=pe(A,$),X=(z==null?void 0:z.id)??null;X!==dt?(dt=X,Ae.style.display="none",Ge&&clearTimeout(Ge),Ge=null,X&&z&&(Ge=setTimeout(()=>{if(!a||!V)return;const Y=a.edges.filter(Z=>Z.sourceId===X||Z.targetId===X).length;Ae.textContent=`${z.label} · ${z.type} · ${Y} edge${Y!==1?"s":""}`,Ae.style.left=`${h.clientX-b.left+12}px`,Ae.style.top=`${h.clientY-b.top-8}px`,Ae.style.display="block"},200))):X&&Ae.style.display==="block"&&(Ae.style.left=`${h.clientX-b.left+12}px`,Ae.style.top=`${h.clientY-b.top-8}px`)}),c.addEventListener("mouseleave",()=>{Ae.style.display="none",dt=null,Ge&&clearTimeout(Ge),Ge=null}),{loadGraph(h){if(cancelAnimationFrame(F),m&&m.postMessage({type:"stop"}),Kn(),V=h,B=null,K=null,ce=null,ke=performance.now(),a=Nt(h),H.rebuild(a.nodes),I=1,C=new Set,j=null,i={x:0,y:0,scale:1},a.nodes.length>0){let A=1/0,$=1/0,z=-1/0,X=-1/0;for(const fe of a.nodes)fe.x<A&&(A=fe.x),fe.y<$&&($=fe.y),fe.x>z&&(z=fe.x),fe.y>X&&(X=fe.y);const Y=(A+z)/2,Z=($+X)/2,ue=c.clientWidth,he=c.clientHeight;i.x=Y-ue/2,i.y=Z-he/2}g=h.nodes.length>=p;const b=g?v():null;b?b.postMessage({type:"start",data:h}):(g=!1,xe())},setFilteredNodeIds(h){j=h,w()},panToNode(h){this.panToNodes([h])},panToNodes(h){if(!a||h.length===0)return;const b=h.map(z=>a.nodeMap.get(z)).filter(Boolean);if(b.length===0)return;C=new Set(h),e==null||e(h);const A=c.clientWidth,$=c.clientHeight;if(b.length===1)k={x:i.x,y:i.y,time:performance.now()},O={x:b[0].x-A/(2*i.scale),y:b[0].y-$/(2*i.scale)};else{let z=1/0,X=1/0,Y=-1/0,Z=-1/0;for(const Ie of b)Ie.x<z&&(z=Ie.x),Ie.y<X&&(X=Ie.y),Ie.x>Y&&(Y=Ie.x),Ie.y>Z&&(Z=Ie.y);const ue=Fe*4,he=Y-z+ue*2,fe=Z-X+ue*2,be=Math.min(A/he,$/fe,i.scale);i.scale=be;const Oe=(z+Y)/2,Me=(X+Z)/2;k={x:i.x,y:i.y,time:performance.now()},O={x:Oe-A/(2*i.scale),y:Me-$/(2*i.scale)}}me()},setEdges(h){U=h,w()},setEdgeLabels(h){Q=h,w()},setTypeHulls(h){W=h,w()},setMinimap(h){ne=h,w()},centerView(){Le()},panBy(h,b){i.x+=h/i.scale,i.y+=b/i.scale,w()},zoomBy(h){const b=c.clientWidth/2,A=c.clientHeight/2,[$,z]=se(b,A);i.scale=Math.max(y.zoomMin,Math.min(y.zoomMax,i.scale*h)),i.x=$-b/i.scale,i.y=z-A/i.scale,w()},reheat(){g&&m?m.postMessage({type:"params",params:rt()}):(I=.5,cancelAnimationFrame(F),xe())},exportImage(h){if(!a)return"";const b=c.width,A=c.height;if(h==="png"){const Y=document.createElement("canvas");Y.width=b,Y.height=A;const Z=Y.getContext("2d");return Z.fillStyle=Ce("--bg")||"#141414",Z.fillRect(0,0,b,A),Z.drawImage(c,0,0),xn(Z,b,A),Y.toDataURL("image/png")}const $=c.toDataURL("image/png"),z=Math.max(16,Math.round(b/80)),X=`<svg xmlns="http://www.w3.org/2000/svg" width="${b}" height="${A}">
2
+ <image href="${$}" width="${b}" height="${A}"/>
3
+ <text x="${b-20}" y="${A-16}" text-anchor="end" font-family="system-ui, sans-serif" font-size="${z}" fill="#ffffff" opacity="0.4">backpackontology.com</text>
4
+ </svg>`;return"data:image/svg+xml;charset=utf-8,"+encodeURIComponent(X)},enterFocus(h,b){if(!V||!a)return;B||(K=a,ce={...i}),B=h,ee=b;const A=sn(V,h,b);cancelAnimationFrame(F),m&&m.postMessage({type:"stop"}),a=Nt(A),H.rebuild(a.nodes),I=1,C=new Set(h),j=null,i={x:0,y:0,scale:1},g=A.nodes.length>=p;const $=g?v():null;$?$.postMessage({type:"start",data:A}):(g=!1,xe()),setTimeout(()=>{!a||!B||Le()},300),s==null||s({seedNodeIds:h,hops:b,totalNodes:A.nodes.length})},exitFocus(){!B||!K||(cancelAnimationFrame(F),m&&m.postMessage({type:"stop"}),a=K,H.rebuild(a.nodes),i=ce??{x:0,y:0,scale:1},B=null,K=null,ce=null,C=new Set,j=null,w(),s==null||s(null))},isFocused(){return B!==null},getFocusInfo(){return!B||!a?null:{seedNodeIds:B,hops:ee,totalNodes:a.nodes.length}},findPath(h,b){if(!a)return null;const A=new Set([h]),$=[{nodeId:h,path:[h],edges:[]}];for(;$.length>0;){const{nodeId:z,path:X,edges:Y}=$.shift();if(z===b)return{nodeIds:X,edgeIds:Y};for(const Z of a.edges){let ue=null;if(Z.sourceId===z?ue=Z.targetId:Z.targetId===z&&(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:[...X,ue],edges:[...Y,(he==null?void 0:he.id)??""]})}}}return null},setHighlightedPath(h,b){h&&b?J={nodeIds:new Set(h),edgeIds:new Set(b)}:J=null,w()},clearHighlightedPath(){J=null,w()},setWalkMode(h){ie=h,h?(te=B?[...B]:[...C],le||(le=requestAnimationFrame(ge))):(te=[],le&&(cancelAnimationFrame(le),le=0)),w()},getWalkMode(){return ie},getWalkTrail(){return[...te]},getFilteredNodeIds(){return j},removeFromWalkTrail(h){te=te.filter(b=>b!==h),w()},nodeAtScreen(h,b){return pe(h,b)},getNodeIds(){if(!a)return[];if(B){const h=new Set(B),b=a.nodes.filter($=>h.has($.id)).map($=>$.id),A=a.nodes.filter($=>!h.has($.id)).map($=>$.id);return[...b,...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,E=null,q.disconnect()}};function xn(h,b,A){const $=Math.max(16,Math.round(b/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",b-20,A-16),h.restore()}}function it(t){for(const e of Object.values(t.properties))if(typeof e=="string")return e;return t.id}const to="✎";function no(t,e,s,r){const l=document.createElement("div");l.id="info-panel",l.className="info-panel hidden",t.appendChild(l);let y=!1,x=[],c=-1,o=!1,P=null,i=[],a=!1,I=[],F=-1;function C(){l.classList.add("hidden"),l.classList.remove("info-panel-maximized"),l.innerHTML="",y=!1,x=[],c=-1}function j(B){!P||!s||(c<x.length-1&&(x=x.slice(0,c+1)),x.push(B),c=x.length-1,o=!0,s(B),o=!1)}function U(){if(c<=0||!P)return;c--,o=!0;const B=x[c];s==null||s(B),ne(B,P),o=!1}function Q(){if(c>=x.length-1||!P)return;c++,o=!0;const B=x[c];s==null||s(B),ne(B,P),o=!1}function W(){const B=document.createElement("div");B.className="info-panel-toolbar";const ee=document.createElement("button");ee.className="info-toolbar-btn",ee.textContent="←",ee.title="Back",ee.disabled=c<=0,ee.addEventListener("click",U),B.appendChild(ee);const K=document.createElement("button");if(K.className="info-toolbar-btn",K.textContent="→",K.title="Forward",K.disabled=c>=x.length-1,K.addEventListener("click",Q),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 ce=document.createElement("button");ce.className="info-toolbar-btn",ce.textContent=y?"⎘":"⛶",ce.title=y?"Restore":"Maximize",ce.addEventListener("click",()=>{y=!y,l.classList.toggle("info-panel-maximized",y),ce.textContent=y?"⎘":"⛶",ce.title=y?"Restore":"Maximize"}),B.appendChild(ce);const J=document.createElement("button");return J.className="info-toolbar-btn info-close-btn",J.textContent="×",J.title="Close",J.addEventListener("click",C),B.appendChild(J),B}function ne(B,ee){const K=ee.nodes.find(u=>u.id===B);if(!K)return;const ce=ee.edges.filter(u=>u.sourceId===B||u.targetId===B);I=ce.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(W());const ie=document.createElement("div");ie.className="info-header";const te=document.createElement("span");if(te.className="info-type-badge",te.textContent=K.type,te.style.backgroundColor=Ne(K.type),e){te.classList.add("info-editable");const u=document.createElement("button");u.className="info-inline-edit",u.textContent=to,u.addEventListener("click",E=>{E.stopPropagation();const R=document.createElement("input");R.type="text",R.className="info-edit-inline-input",R.value=K.type,te.textContent="",te.appendChild(R),R.focus(),R.select();const M=()=>{const O=R.value.trim();O&&O!==K.type?e.onChangeNodeType(B,O):(te.textContent=K.type,te.appendChild(u))};R.addEventListener("blur",M),R.addEventListener("keydown",O=>{O.key==="Enter"&&R.blur(),O.key==="Escape"&&(R.value=K.type,R.blur())})}),te.appendChild(u)}const ye=document.createElement("h3");ye.className="info-label",ye.textContent=it(K);const ke=document.createElement("span");ke.className="info-id",ke.textContent=K.id,ie.appendChild(te),ie.appendChild(ye),ie.appendChild(ke),J.appendChild(ie),l.appendChild(J);const _=document.createElement("div");_.className="info-panel-body";const H=Object.keys(K.properties),D=lt("Properties");if(H.length>0){const u=document.createElement("dl");u.className="info-props";for(const E of H){const R=document.createElement("dt");R.textContent=E;const M=document.createElement("dd");if(e){const O=Lt(K.properties[E]),k=document.createElement("textarea");k.className="info-edit-input",k.value=O,k.rows=1,k.addEventListener("input",()=>dn(k)),k.addEventListener("keydown",S=>{S.key==="Enter"&&!S.shiftKey&&(S.preventDefault(),k.blur())}),k.addEventListener("blur",()=>{const S=k.value;S!==O&&e.onUpdateNode(B,{[E]:so(S)})}),M.appendChild(k),requestAnimationFrame(()=>dn(k));const f=document.createElement("button");f.className="info-delete-prop",f.textContent="×",f.title=`Remove ${E}`,f.addEventListener("click",()=>{const S={...K.properties};delete S[E],e.onUpdateNode(B,S)}),M.appendChild(f)}else M.appendChild(oo(K.properties[E]));u.appendChild(R),u.appendChild(M)}D.appendChild(u)}if(e){const u=document.createElement("button");u.className="info-add-btn",u.textContent="+ Add property",u.addEventListener("click",()=>{const E=document.createElement("div");E.className="info-add-row";const R=document.createElement("input");R.type="text",R.className="info-edit-input",R.placeholder="key";const M=document.createElement("input");M.type="text",M.className="info-edit-input",M.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,M.value)}),E.appendChild(R),E.appendChild(M),E.appendChild(O),D.appendChild(E),R.focus()}),D.appendChild(u)}if(_.appendChild(D),ce.length>0){const u=lt(`Connections (${ce.length})`),E=document.createElement("ul");E.className="info-connections";for(const R of ce){const M=R.sourceId===B,O=M?R.targetId:R.sourceId,k=ee.nodes.find(d=>d.id===O),f=k?it(k):O,S=document.createElement("li");if(S.className="info-connection",s&&k&&(S.classList.add("info-connection-link"),S.addEventListener("click",d=>{d.target.closest(".info-delete-edge")||j(O)})),k){const d=document.createElement("span");d.className="info-target-dot",d.style.backgroundColor=Ne(k.type),S.appendChild(d)}const q=document.createElement("span");q.className="info-arrow",q.textContent=M?"→":"←";const se=document.createElement("span");se.className="info-edge-type",se.textContent=R.type;const pe=document.createElement("span");pe.className="info-target",pe.textContent=f,S.appendChild(q),S.appendChild(se),S.appendChild(pe);const n=Object.keys(R.properties);if(n.length>0){const d=document.createElement("div");d.className="info-edge-props";for(const T of n){const G=document.createElement("span");G.className="info-edge-prop",G.textContent=`${T}: ${Lt(R.properties[T])}`,d.appendChild(G)}S.appendChild(d)}if(e){const d=document.createElement("button");d.className="info-delete-edge",d.textContent="×",d.title="Remove connection",d.addEventListener("click",T=>{T.stopPropagation(),e.onDeleteEdge(R.id)}),S.appendChild(d)}E.appendChild(S)}u.appendChild(E),_.appendChild(u)}const w=lt("Timestamps"),p=document.createElement("dl");p.className="info-props";const m=document.createElement("dt");m.textContent="created";const g=document.createElement("dd");g.textContent=pn(K.createdAt);const v=document.createElement("dt");v.textContent="updated";const N=document.createElement("dd");if(N.textContent=pn(K.updatedAt),p.appendChild(m),p.appendChild(g),p.appendChild(v),p.appendChild(N),w.appendChild(p),_.appendChild(w),e){const u=document.createElement("div");u.className="info-section info-danger";const E=document.createElement("button");E.className="info-delete-node",E.textContent="Delete node",E.addEventListener("click",()=>{e.onDeleteNode(B),C()}),u.appendChild(E),_.appendChild(u)}l.appendChild(_)}function V(B,ee){const K=new Set(B),ce=ee.nodes.filter(w=>K.has(w.id));if(ce.length===0)return;const J=ee.edges.filter(w=>K.has(w.sourceId)&&K.has(w.targetId));l.innerHTML="",l.classList.remove("hidden"),y&&l.classList.add("info-panel-maximized"),l.appendChild(W());const ie=document.createElement("div");ie.className="info-header";const te=document.createElement("h3");te.className="info-label",te.textContent=`${ce.length} nodes selected`,ie.appendChild(te);const ye=document.createElement("div");ye.className="info-badge-row";const ke=new Map;for(const w of ce)ke.set(w.type,(ke.get(w.type)??0)+1);for(const[w,p]of ke){const m=document.createElement("span");m.className="info-type-badge",m.style.backgroundColor=Ne(w),m.textContent=p>1?`${w} (${p})`:w,ye.appendChild(m)}ie.appendChild(ye),l.appendChild(ie);const _=lt("Selected Nodes"),H=document.createElement("ul");H.className="info-connections";for(const w of ce){const p=document.createElement("li");p.className="info-connection",s&&(p.classList.add("info-connection-link"),p.addEventListener("click",()=>{j(w.id)}));const m=document.createElement("span");m.className="info-target-dot",m.style.backgroundColor=Ne(w.type);const g=document.createElement("span");g.className="info-target",g.textContent=it(w);const v=document.createElement("span");v.className="info-edge-type",v.textContent=w.type,p.appendChild(m),p.appendChild(g),p.appendChild(v),H.appendChild(p)}_.appendChild(H),l.appendChild(_);const D=lt(J.length>0?`Connections Between Selected (${J.length})`:"Connections Between Selected");if(J.length===0){const w=document.createElement("p");w.className="info-empty-message",w.textContent="No direct connections between selected nodes",D.appendChild(w)}else{const w=document.createElement("ul");w.className="info-connections";for(const p of J){const m=ee.nodes.find(S=>S.id===p.sourceId),g=ee.nodes.find(S=>S.id===p.targetId),v=m?it(m):p.sourceId,N=g?it(g):p.targetId,u=document.createElement("li");if(u.className="info-connection",m){const S=document.createElement("span");S.className="info-target-dot",S.style.backgroundColor=Ne(m.type),u.appendChild(S)}const E=document.createElement("span");E.className="info-target",E.textContent=v;const R=document.createElement("span");R.className="info-arrow",R.textContent="→";const M=document.createElement("span");M.className="info-edge-type",M.textContent=p.type;const O=document.createElement("span");if(O.className="info-arrow",O.textContent="→",u.appendChild(E),u.appendChild(R),u.appendChild(M),u.appendChild(O),g){const S=document.createElement("span");S.className="info-target-dot",S.style.backgroundColor=Ne(g.type),u.appendChild(S)}const k=document.createElement("span");k.className="info-target",k.textContent=N,u.appendChild(k);const f=Object.keys(p.properties);if(f.length>0){const S=document.createElement("div");S.className="info-edge-props";for(const q of f){const se=document.createElement("span");se.className="info-edge-prop",se.textContent=`${q}: ${Lt(p.properties[q])}`,S.appendChild(se)}u.appendChild(S)}w.appendChild(u)}D.appendChild(w)}l.appendChild(D)}return{show(B,ee){if(P=ee,i=B,B.length===1&&!o){const K=B[0];x[c]!==K&&(c<x.length-1&&(x=x.slice(0,c+1)),x.push(K),c=x.length-1)}B.length===1?ne(B[0],ee):B.length>1&&V(B,ee)},hide:C,goBack:U,goForward:Q,cycleConnection(B){if(I.length===0)return null;F===-1?F=B===1?0:I.length-1:(F+=B,F>=I.length&&(F=0),F<0&&(F=I.length-1));const ee=l.querySelectorAll(".info-connection");return ee.forEach((K,ce)=>{K.classList.toggle("info-connection-active",ce===F)}),F>=0&&ee[F]&&ee[F].scrollIntoView({block:"nearest"}),I[F]??null},setFocusDisabled(B){a=B;const ee=l.querySelector(".info-focus-btn");ee&&(ee.disabled=B,ee.style.opacity=B?"0.3":"")},get visible(){return!l.classList.contains("hidden")}}}function lt(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 oo(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 so(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 dn(t){t.style.height="auto",t.style.height=t.scrollHeight+"px"}function pn(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 un(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 ao(t,e){const s=(e==null?void 0:e.maxResults)??8,r=(e==null?void 0:e.debounceMs)??150;let l=null,y=null,x=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 I=document.createElement("ul");I.className="search-results hidden",o.appendChild(P),o.appendChild(I),t.appendChild(o);function F(){if(!l)return null;const W=i.value.trim();if(W.length===0)return null;const ne=new Set;for(const V of l.nodes)un(V,W)&&ne.add(V.id);return ne}function C(){const W=F();y==null||y(W),j()}function j(){I.innerHTML="",U=-1;const W=i.value.trim();if(!l||W.length===0){I.classList.add("hidden");return}const ne=[];for(const V of l.nodes)if(un(V,W)&&(ne.push(V),ne.length>=s))break;if(ne.length===0){I.classList.add("hidden");return}for(const V of ne){const B=document.createElement("li");B.className="search-result-item";const ee=document.createElement("span");ee.className="search-result-dot",ee.style.backgroundColor=Ne(V.type);const K=document.createElement("span");K.className="search-result-label";const ce=vn(V);K.textContent=ce.length>36?ce.slice(0,34)+"...":ce;const J=document.createElement("span");J.className="search-result-type",J.textContent=V.type,B.appendChild(ee),B.appendChild(K),B.appendChild(J),B.addEventListener("click",()=>{x==null||x(V.id),i.value="",I.classList.add("hidden"),C()}),I.appendChild(B)}I.classList.remove("hidden")}i.addEventListener("input",()=>{c&&clearTimeout(c),c=setTimeout(C,r)});let U=-1;function Q(){const W=I.querySelectorAll(".search-result-item");W.forEach((ne,V)=>{ne.classList.toggle("search-result-active",V===U)}),U>=0&&W[U]&&W[U].scrollIntoView({block:"nearest"})}return i.addEventListener("keydown",W=>{const ne=I.querySelectorAll(".search-result-item");W.key==="ArrowDown"?(W.preventDefault(),ne.length>0&&(U=Math.min(U+1,ne.length-1),Q())):W.key==="ArrowUp"?(W.preventDefault(),ne.length>0&&(U=Math.max(U-1,0),Q())):W.key==="Enter"?(W.preventDefault(),U>=0&&ne[U]?ne[U].click():ne.length>0&&ne[0].click(),i.blur()):W.key==="Escape"&&(i.value="",i.blur(),I.classList.add("hidden"),U=-1,C())}),document.addEventListener("click",W=>{o.contains(W.target)||I.classList.add("hidden")}),i.addEventListener("focus",()=>a.classList.add("hidden")),i.addEventListener("blur",()=>{i.value.length===0&&a.classList.remove("hidden")}),{setLearningGraphData(W){l=W,i.value="",I.classList.add("hidden"),l&&l.nodes.length>0?o.classList.remove("hidden"):o.classList.add("hidden")},onFilterChange(W){y=W},onNodeSelect(W){x=W},clear(){i.value="",I.classList.add("hidden"),y==null||y(null)},focus(){i.focus()}}}function co(t,e){let s=null,r=null,l=!0,y=null,x=!0,c=!0,o=!0,P="types",i="",a="",I=[],F=[];const C={types:new Set,nodeIds:new Set};function j(){if(!s)return[];const p=new Set;for(const m of s.nodes)C.types.has(m.type)&&p.add(m.id);for(const m of C.nodeIds)p.add(m);return[...p]}function U(p){if(C.nodeIds.has(p))return!0;const m=s==null?void 0:s.nodes.find(g=>g.id===p);return m?C.types.has(m.type):!1}function Q(){return C.types.size===0&&C.nodeIds.size===0}function W(){const p=j();e.onFocusChange(p.length>0?p:null)}const ne=document.createElement("button");ne.className="tools-pane-toggle hidden",ne.title="Graph Inspector",ne.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(ne),t.appendChild(V),ne.addEventListener("click",()=>{var p;l=!l,V.classList.toggle("hidden",l),ne.classList.toggle("active",!l),l||(p=e.onOpen)==null||p.call(e)});function B(){if(V.innerHTML="",!r)return;const p=document.createElement("div");if(p.className="tools-pane-summary",p.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(p),s&&r.nodeCount>0){const N=Math.ceil(JSON.stringify(s).length/4),u=Math.round(N/r.nodeCount),E=Math.max(10,Math.round(u*.3)*Math.min(5,r.nodeCount)),R=N>E?Math.round((1-E/N)*100):0,M=document.createElement("div");M.className="tools-pane-token-card";const O=Math.min(100,R),k=document.createElement("div");k.className="token-card-label",k.textContent="Token Efficiency",M.appendChild(k);const f=document.createElement("div");f.className="token-card-stat",f.textContent=`~${N.toLocaleString()} tokens stored`,M.appendChild(f);const S=document.createElement("div");S.className="token-card-bar";const q=document.createElement("div");q.className="token-card-bar-fill",q.style.width=`${O}%`,S.appendChild(q),M.appendChild(S);const se=document.createElement("div");se.className="token-card-stat",se.textContent=`A search returns ~${E} tokens instead of ~${N.toLocaleString()} (${R}% reduction)`,M.appendChild(se),V.appendChild(M)}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 N of g){const u=document.createElement("button");u.className="tools-pane-tab",P===N.id&&u.classList.add("tools-pane-tab-active"),u.textContent=N.label,u.addEventListener("click",()=>{P=N.id,B()}),m.appendChild(u)}V.appendChild(m),Q()||ce(),F.length>0&&K(),P==="types"&&r.types.length>5?V.appendChild(ie("Filter types...",i,N=>{i=N,ee()})):P==="insights"&&r.orphans.length+r.singletons.length+r.emptyNodes.length>5&&V.appendChild(ie("Filter issues...",a,u=>{a=u,ee()}));const v=document.createElement("div");v.className="tools-pane-tab-content",V.appendChild(v),ee()}function ee(){const p=V.querySelector(".tools-pane-tab-content");p&&(p.innerHTML="",P==="types"?te(p):P==="insights"?ye(p):P==="controls"&&ke(p))}function K(){V.appendChild(H(`Walk Trail (${F.length})`,p=>{for(let g=0;g<F.length;g++){const v=F[g],N=g===F.length-1;if(v.edgeType){const f=document.createElement("div");f.className="walk-trail-edge",f.textContent=`↓ ${v.edgeType}`,p.appendChild(f)}const u=document.createElement("div");u.className="tools-pane-row tools-pane-clickable",N&&(u.style.fontWeight="600");const E=document.createElement("span");E.className="tools-pane-count",E.style.minWidth="18px",E.textContent=`${g+1}`;const R=document.createElement("span");R.className="tools-pane-dot",R.style.backgroundColor=Ne(v.type);const M=document.createElement("span");M.className="tools-pane-name",M.textContent=v.label;const O=document.createElement("span");O.className="tools-pane-count",O.textContent=v.type;const k=document.createElement("button");k.className="tools-pane-edit",k.style.opacity="1",k.textContent="×",k.title="Remove from trail",k.addEventListener("click",f=>{var S;f.stopPropagation(),(S=e.onWalkTrailRemove)==null||S.call(e,v.id)}),u.appendChild(E),u.appendChild(R),u.appendChild(M),u.appendChild(O),u.appendChild(k),u.addEventListener("click",()=>{e.onNavigateToNode(v.id)}),p.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",()=>{gt("Save snippet","Name for this snippet").then(v=>{v&&e.onWalkSaveSnippet(v)})}),m.appendChild(g)}p.appendChild(m)}))}function ce(){if(!r||!s)return;const p=j();V.appendChild(H("Focused",m=>{for(const u of C.types){const E=r.types.find(S=>S.name===u);if(!E)continue;const R=document.createElement("div");R.className="tools-pane-row tools-pane-clickable";const M=document.createElement("span");M.className="tools-pane-dot",M.style.backgroundColor=Ne(E.name);const O=document.createElement("span");O.className="tools-pane-name",O.textContent=E.name;const k=document.createElement("span");k.className="tools-pane-count",k.textContent=`${E.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 ${E.name} from focus`,R.appendChild(M),R.appendChild(O),R.appendChild(k),R.appendChild(f),f.addEventListener("click",S=>{S.stopPropagation(),C.types.delete(E.name),W(),B()}),m.appendChild(R)}for(const u of C.nodeIds){const E=s.nodes.find(q=>q.id===u);if(!E)continue;const R=mn(E.properties)??E.id,M=document.createElement("div");M.className="tools-pane-row tools-pane-clickable";const O=document.createElement("span");O.className="tools-pane-dot",O.style.backgroundColor=Ne(E.type);const k=document.createElement("span");k.className="tools-pane-name",k.textContent=R;const f=document.createElement("span");f.className="tools-pane-count",f.textContent=E.type;const S=document.createElement("button");S.className="tools-pane-edit tools-pane-focus-active",S.style.opacity="1",S.textContent="×",S.title=`Remove ${R} from focus`,M.appendChild(O),M.appendChild(k),M.appendChild(f),M.appendChild(S),M.addEventListener("click",q=>{q.target.closest(".tools-pane-edit")||e.onNavigateToNode(u)}),S.addEventListener("click",q=>{q.stopPropagation(),C.nodeIds.delete(u),W(),B()}),m.appendChild(M)}const g=document.createElement("div");g.className="tools-pane-row tools-pane-clickable tools-pane-focus-clear";const v=document.createElement("span");v.className="tools-pane-name",v.style.color="var(--accent)",v.textContent=`${p.length} total`;const N=document.createElement("span");N.className="tools-pane-badge",N.textContent="clear all",g.appendChild(v),g.appendChild(N),g.addEventListener("click",()=>{C.types.clear(),C.nodeIds.clear(),W(),B()}),m.appendChild(g)}))}function J(p){const m=document.createElement("div");m.className="tools-pane-row tools-pane-clickable",y===p.name&&m.classList.add("active");const g=document.createElement("span");g.className="tools-pane-dot",g.style.backgroundColor=Ne(p.name);const v=document.createElement("span");v.className="tools-pane-name",v.textContent=p.name;const N=document.createElement("span");N.className="tools-pane-count",N.textContent=String(p.count);const u=document.createElement("button");u.className="tools-pane-edit tools-pane-focus-toggle",C.types.has(p.name)&&u.classList.add("tools-pane-focus-active"),u.textContent="◎",u.title=C.types.has(p.name)?`Remove ${p.name} from focus`:`Add ${p.name} to focus`;const E=document.createElement("button");return E.className="tools-pane-edit",E.textContent="✎",E.title=`Rename all ${p.name} nodes`,m.appendChild(g),m.appendChild(v),m.appendChild(N),m.appendChild(u),m.appendChild(E),m.addEventListener("click",R=>{R.target.closest(".tools-pane-edit")||(y===p.name?(y=null,e.onFilterByType(null)):(y=p.name,e.onFilterByType(p.name)),B())}),u.addEventListener("click",R=>{R.stopPropagation(),C.types.has(p.name)?C.types.delete(p.name):C.types.add(p.name),W(),B()}),E.addEventListener("click",R=>{R.stopPropagation(),D(m,p.name,M=>{M&&M!==p.name&&e.onRenameNodeType(p.name,M)})}),m}function ie(p,m,g){const v=document.createElement("input");return v.type="text",v.className="tools-pane-search",v.placeholder=p,v.value=m,v.addEventListener("input",()=>g(v.value)),v}function te(p){if(!r)return;const m=i.toLowerCase();if(r.types.length){const v=r.types.filter(N=>!C.types.has(N.name)).filter(N=>!m||N.name.toLowerCase().includes(m));v.length>0&&p.appendChild(H("Node Types",N=>{for(const u of v)N.appendChild(J(u))}))}const g=r.edgeTypes.filter(v=>!m||v.name.toLowerCase().includes(m));g.length&&p.appendChild(H("Edge Types",v=>{for(const N of g){const u=document.createElement("div");u.className="tools-pane-row tools-pane-clickable";const E=document.createElement("span");E.className="tools-pane-name",E.textContent=N.name;const R=document.createElement("span");R.className="tools-pane-count",R.textContent=String(N.count);const M=document.createElement("button");M.className="tools-pane-edit",M.textContent="✎",M.title=`Rename all ${N.name} edges`,u.appendChild(E),u.appendChild(R),u.appendChild(M),M.addEventListener("click",O=>{O.stopPropagation(),D(u,N.name,k=>{k&&k!==N.name&&e.onRenameEdgeType(N.name,k)})}),v.appendChild(u)}}))}function ye(p){if(!r)return;const m=a.toLowerCase(),g=r.starred.filter(k=>!m||k.label.toLowerCase().includes(m)||k.type.toLowerCase().includes(m));g.length&&p.appendChild(H("★ Starred",k=>{for(const q of g){const se=document.createElement("div");se.className="tools-pane-row tools-pane-clickable";const pe=document.createElement("span");pe.className="tools-pane-dot",pe.style.backgroundColor=Ne(q.type);const n=document.createElement("span");n.className="tools-pane-name",n.textContent=q.label;const d=document.createElement("button");d.className="tools-pane-edit tools-pane-focus-toggle",U(q.id)&&d.classList.add("tools-pane-focus-active"),d.textContent="◎",d.title=U(q.id)?`Remove ${q.label} from focus`:`Add ${q.label} to focus`,se.appendChild(pe),se.appendChild(n),se.appendChild(d),se.addEventListener("click",T=>{T.target.closest(".tools-pane-edit")||e.onNavigateToNode(q.id)}),d.addEventListener("click",T=>{T.stopPropagation(),C.nodeIds.has(q.id)?C.nodeIds.delete(q.id):C.nodeIds.add(q.id),W(),B()}),k.appendChild(se)}const f=document.createElement("div");f.className="tools-pane-row tools-pane-actions";const S=document.createElement("button");if(S.className="tools-pane-action-btn",S.textContent="Focus all",S.title="Enter focus mode with all starred nodes",S.addEventListener("click",()=>{C.nodeIds.clear(),C.types.clear();for(const q of r.starred)C.nodeIds.add(q.id);W(),B()}),f.appendChild(S),e.onStarredSaveSnippet){const q=document.createElement("button");q.className="tools-pane-action-btn",q.textContent="Save as snippet",q.title="Save starred nodes as a reusable snippet",q.addEventListener("click",async()=>{const se=await gt("Snippet name","starred");se&&e.onStarredSaveSnippet(se,r.starred.map(pe=>pe.id))}),f.appendChild(q)}k.appendChild(f)}));const v=r.mostConnected.filter(k=>!m||k.label.toLowerCase().includes(m)||k.type.toLowerCase().includes(m));v.length&&p.appendChild(H("Most Connected",k=>{for(const f of v){const S=document.createElement("div");S.className="tools-pane-row tools-pane-clickable";const q=document.createElement("span");q.className="tools-pane-dot",q.style.backgroundColor=Ne(f.type);const se=document.createElement("span");se.className="tools-pane-name",se.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",U(f.id)&&n.classList.add("tools-pane-focus-active"),n.textContent="◎",n.title=U(f.id)?`Remove ${f.label} from focus`:`Add ${f.label} to focus`,S.appendChild(q),S.appendChild(se),S.appendChild(pe),S.appendChild(n),S.addEventListener("click",d=>{d.target.closest(".tools-pane-edit")||e.onNavigateToNode(f.id)}),n.addEventListener("click",d=>{d.stopPropagation(),C.nodeIds.has(f.id)?C.nodeIds.delete(f.id):C.nodeIds.add(f.id),W(),B()}),k.appendChild(S)}}));const N=r.orphans.filter(k=>!m||k.label.toLowerCase().includes(m)||k.type.toLowerCase().includes(m)),u=r.singletons.filter(k=>!m||k.name.toLowerCase().includes(m)),E=r.emptyNodes.filter(k=>!m||k.label.toLowerCase().includes(m)||k.type.toLowerCase().includes(m)),R=N.length>0,M=u.length>0,O=E.length>0;if(!R&&!M&&!O){const k=document.createElement("div");k.className="tools-pane-empty-msg",k.textContent="No issues found",p.appendChild(k);return}R&&p.appendChild(H("Orphans",k=>{for(const f of N.slice(0,5)){const S=document.createElement("div");S.className="tools-pane-row tools-pane-clickable tools-pane-issue";const q=document.createElement("span");q.className="tools-pane-dot",q.style.backgroundColor=Ne(f.type);const se=document.createElement("span");se.className="tools-pane-name",se.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",U(f.id)&&n.classList.add("tools-pane-focus-active"),n.textContent="◎",n.title=U(f.id)?`Remove ${f.label} from focus`:`Add ${f.label} to focus`,S.appendChild(q),S.appendChild(se),S.appendChild(pe),S.appendChild(n),S.addEventListener("click",d=>{d.target.closest(".tools-pane-edit")||e.onNavigateToNode(f.id)}),n.addEventListener("click",d=>{d.stopPropagation(),C.nodeIds.has(f.id)?C.nodeIds.delete(f.id):C.nodeIds.add(f.id),W(),B()}),k.appendChild(S)}if(N.length>5){const f=document.createElement("div");f.className="tools-pane-more",f.textContent=`+ ${N.length-5} more orphans`,k.appendChild(f)}})),M&&p.appendChild(H("Singletons",k=>{for(const f of u.slice(0,5)){const S=document.createElement("div");S.className="tools-pane-row tools-pane-issue";const q=document.createElement("span");q.className="tools-pane-dot",q.style.backgroundColor=Ne(f.name);const se=document.createElement("span");se.className="tools-pane-name",se.textContent=f.name;const pe=document.createElement("span");pe.className="tools-pane-badge",pe.textContent="1 node",S.appendChild(q),S.appendChild(se),S.appendChild(pe),k.appendChild(S)}})),O&&p.appendChild(H("Empty Nodes",k=>{for(const f of E.slice(0,5)){const S=document.createElement("div");S.className="tools-pane-row tools-pane-issue";const q=document.createElement("span");q.className="tools-pane-dot",q.style.backgroundColor=Ne(f.type);const se=document.createElement("span");se.className="tools-pane-name",se.textContent=f.label;const pe=document.createElement("span");pe.className="tools-pane-badge",pe.textContent="empty",S.appendChild(q),S.appendChild(se),S.appendChild(pe),k.appendChild(S)}if(r.emptyNodes.length>5){const f=document.createElement("div");f.className="tools-pane-more",f.textContent=`+ ${r.emptyNodes.length-5} more empty nodes`,k.appendChild(f)}}))}function ke(p){p.appendChild(H("Display",m=>{const g=document.createElement("div");g.className="tools-pane-row tools-pane-clickable";const v=document.createElement("input");v.type="checkbox",v.checked=x,v.className="tools-pane-checkbox";const N=document.createElement("span");N.className="tools-pane-name",N.textContent="Edge labels",g.appendChild(v),g.appendChild(N),g.addEventListener("click",f=>{f.target!==v&&(v.checked=!v.checked),x=v.checked,e.onToggleEdgeLabels(x)}),m.appendChild(g);const u=document.createElement("div");u.className="tools-pane-row tools-pane-clickable";const E=document.createElement("input");E.type="checkbox",E.checked=c,E.className="tools-pane-checkbox";const R=document.createElement("span");R.className="tools-pane-name",R.textContent="Type regions",u.appendChild(E),u.appendChild(R),u.addEventListener("click",f=>{f.target!==E&&(E.checked=!E.checked),c=E.checked,e.onToggleTypeHulls(c)}),m.appendChild(u);const M=document.createElement("div");M.className="tools-pane-row tools-pane-clickable";const O=document.createElement("input");O.type="checkbox",O.checked=o,O.className="tools-pane-checkbox";const k=document.createElement("span");k.className="tools-pane-name",k.textContent="Minimap",M.appendChild(O),M.appendChild(k),M.addEventListener("click",f=>{f.target!==O&&(O.checked=!O.checked),o=O.checked,e.onToggleMinimap(o)}),m.appendChild(M)})),p.appendChild(H("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)}))})),p.appendChild(H("Export",m=>{const g=document.createElement("div");g.className="tools-pane-export-row";const v=document.createElement("button");v.className="tools-pane-export-btn",v.textContent="Export PNG",v.addEventListener("click",()=>e.onExport("png"));const N=document.createElement("button");N.className="tools-pane-export-btn",N.textContent="Export SVG",N.addEventListener("click",()=>e.onExport("svg")),g.appendChild(v),g.appendChild(N),m.appendChild(g)})),(e.onSnapshot||e.onRollback)&&p.appendChild(H("Versions",m=>{const g=document.createElement("div");g.className="tools-pane-export-row";const v=document.createElement("button");if(v.className="tools-pane-export-btn",v.textContent="Save snapshot",v.addEventListener("click",()=>{gt("Save snapshot","Label (optional)").then(N=>{var u;(u=e.onSnapshot)==null||u.call(e,N||void 0)})}),g.appendChild(v),m.appendChild(g),I.length>0)for(const N of I){const u=document.createElement("div");u.className="tools-pane-row";const E=document.createElement("span");E.className="tools-pane-name";const R=io(N.timestamp);E.textContent=N.label?`#${N.version} ${N.label}`:`#${N.version}`,E.title=`${R} — ${N.nodeCount} nodes, ${N.edgeCount} edges`;const M=document.createElement("span");M.className="tools-pane-count",M.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",()=>{hn("Restore snapshot",`Restore snapshot #${N.version}? Current state will be lost unless you save a snapshot first.`).then(k=>{var f;k&&((f=e.onRollback)==null||f.call(e,N.version))})}),u.appendChild(E),u.appendChild(M),u.appendChild(O),m.appendChild(u)}else{const N=document.createElement("div");N.className="tools-pane-empty-msg",N.textContent="No snapshots yet",m.appendChild(N)}}))}function _(p,m,g,v,N,u){const E=document.createElement("div");E.className="tools-pane-slider-row";const R=document.createElement("span");R.className="tools-pane-slider-label",R.textContent=p;const M=document.createElement("input");M.type="range",M.className="tools-pane-slider",M.min=String(m),M.max=String(g),M.step=String(v),M.value=String(N);const O=document.createElement("span");return O.className="tools-pane-slider-value",O.textContent=String(N),M.addEventListener("input",()=>{const k=parseFloat(M.value);O.textContent=k%1===0?String(k):k.toFixed(2),u(k)}),E.appendChild(R),E.appendChild(M),E.appendChild(O),E}function H(p,m){const g=document.createElement("div");g.className="tools-pane-section";const v=document.createElement("div");return v.className="tools-pane-heading",v.textContent=p,g.appendChild(v),m(g),g}function D(p,m,g){const v=document.createElement("input");v.className="tools-pane-inline-input",v.value=m,v.type="text";const N=p.innerHTML;p.innerHTML="",p.classList.add("tools-pane-editing"),p.appendChild(v),v.focus(),v.select();function u(){const E=v.value.trim();p.classList.remove("tools-pane-editing"),E&&E!==m?g(E):p.innerHTML=N}v.addEventListener("keydown",E=>{E.key==="Enter"&&(E.preventDefault(),u()),E.key==="Escape"&&(p.innerHTML=N,p.classList.remove("tools-pane-editing"))}),v.addEventListener("blur",u)}function w(p){const m=new Map,g=new Map,v=new Map,N=new Set;for(const f of p.nodes)m.set(f.type,(m.get(f.type)??0)+1);for(const f of p.edges)g.set(f.type,(g.get(f.type)??0)+1),v.set(f.sourceId,(v.get(f.sourceId)??0)+1),v.set(f.targetId,(v.get(f.targetId)??0)+1),N.add(f.sourceId),N.add(f.targetId);const u=f=>mn(f.properties)??f.id,E=p.nodes.filter(f=>f.properties._starred===!0).map(f=>({id:f.id,label:u(f),type:f.type})),R=p.nodes.filter(f=>!N.has(f.id)).map(f=>({id:f.id,label:u(f),type:f.type})),M=[...m.entries()].filter(([,f])=>f===1).map(([f])=>({name:f})),O=p.nodes.filter(f=>Object.keys(f.properties).length===0).map(f=>({id:f.id,label:f.id,type:f.type})),k=p.nodes.map(f=>({id:f.id,label:u(f),type:f.type,connections:v.get(f.id)??0})).filter(f=>f.connections>0).sort((f,S)=>S.connections-f.connections).slice(0,5);return{nodeCount:p.nodes.length,edgeCount:p.edges.length,types:[...m.entries()].sort((f,S)=>S[1]-f[1]).map(([f,S])=>({name:f,count:S})),edgeTypes:[...g.entries()].sort((f,S)=>S[1]-f[1]).map(([f,S])=>({name:f,count:S})),starred:E,orphans:R,singletons:M,emptyNodes:O,mostConnected:k}}return{collapse(){l=!0,V.classList.add("hidden"),ne.classList.remove("active")},addToFocusSet(p){for(const m of p)C.nodeIds.add(m);W(),B()},clearFocusSet(){C.types.clear(),C.nodeIds.clear(),W(),B()},setData(p){s=p,y=null,C.types.clear(),C.nodeIds.clear(),s&&s.nodes.length>0?(r=w(s),ne.classList.remove("hidden"),B()):(r=null,ne.classList.add("hidden"),V.classList.add("hidden"))},setSnapshots(p){I=p,P==="controls"&&ee()},setWalkTrail(p){F=p,B()}}}function io(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 mn(t){for(const e of Object.values(t))if(typeof e=="string")return e;return null}function lo(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"),x=l.includes("shift"),c=l.includes("alt");return y!==(t.ctrlKey||t.metaKey)||!y&&(t.ctrlKey||t.metaKey)||x&&!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 ro(){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 po=[{key:"Click",description:"Select node"},{key:"Ctrl+Click",description:"Multi-select nodes"},{key:"Drag",description:"Pan canvas"},{key:"Scroll",description:"Zoom in/out"}],uo=["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 mo(t){return t.split("+").map(e=>e.charAt(0).toUpperCase()+e.slice(1)).join("+")}function ho(t,e){const s=ro(),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 x=document.createElement("div");x.className="shortcuts-list";for(const a of uo){const I=e[a];if(!I)continue;const F=document.createElement("div");F.className="shortcuts-row";const C=document.createElement("div");C.className="shortcuts-keys";const j=document.createElement("kbd");j.textContent=mo(I),C.appendChild(j);const U=document.createElement("span");U.className="shortcuts-desc",U.textContent=s[a],F.appendChild(C),F.appendChild(U),x.appendChild(F)}for(const a of po){const I=document.createElement("div");I.className="shortcuts-row";const F=document.createElement("div");F.className="shortcuts-keys";const C=document.createElement("kbd");C.textContent=a.key,F.appendChild(C);const j=document.createElement("span");j.className="shortcuts-desc",j.textContent=a.description,I.appendChild(F),I.appendChild(j),x.appendChild(I)}const c=document.createElement("button");c.className="shortcuts-close",c.textContent="×",l.appendChild(c),l.appendChild(y),l.appendChild(x),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 fo(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 go=30;function yo(){let t=[],e=[];return{push(s){t.push(JSON.stringify(s)),t.length>go&&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 Co(t,e){let s=null;function r(x,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(x),premium:!1},{label:"◎ Focus on node",action:()=>e.onFocusNode(x),premium:!1},{label:"⑂ Explore in branch",action:()=>e.onExploreInBranch(x),premium:!1},{label:"⎘ Copy ID",action:()=>e.onCopyId(x),premium:!1}];e.onExpand&&a.push({label:"⊕ Expand node",action:()=>e.onExpand(x),premium:!0}),e.onExplainPath&&a.push({label:"↔ Explain path to…",action:()=>e.onExplainPath(x),premium:!0}),e.onEnrich&&a.push({label:"≡ Enrich from web",action:()=>e.onEnrich(x),premium:!0});let I=!1;for(const C of a){if(!I&&C.premium){const U=document.createElement("div");U.className="context-menu-separator",s.appendChild(U),I=!0}const j=document.createElement("div");j.className="context-menu-item",j.textContent=C.label,j.addEventListener("click",()=>{C.action(),l()}),s.appendChild(j)}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(x){x.key==="Escape"&&l()}return{show:r,hide:l}}const vo={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"},xo={host:"127.0.0.1",port:5173},bo={edges:!0,edgeLabels:!0,typeHulls:!0,minimap:!0,theme:"system"},Eo={spacing:1.5,clustering:.08},wo={panSpeed:60,panFastMultiplier:3,zoomFactor:1.3,zoomMin:.05,zoomMax:10,panAnimationMs:300},ko={hideBadges:.4,hideLabels:.25,hideEdgeLabels:.35,smallNodes:.2,hideArrows:.15},No={pulseSpeed:.02},So={maxSearchResults:8,maxQualityItems:5,maxMostConnected:5,searchDebounceMs:150},Lo={keybindings:vo,server:xo,display:bo,layout:Eo,navigation:wo,lod:ko,walk:No,limits:So};let de="",L=null,It=new Set,Mt=!1;async function Io(){const t=document.getElementById("canvas-container"),e={...Lo};try{const n=await fetch("/api/config");if(n.ok){const d=await n.json();Object.assign(e.keybindings,d.keybindings??{}),Object.assign(e.display,d.display??{}),Object.assign(e.layout,d.layout??{}),Object.assign(e.navigation,d.navigation??{}),Object.assign(e.lod,d.lod??{}),Object.assign(e.limits,d.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,x=localStorage.getItem("backpack-theme")??l;document.documentElement.setAttribute("data-theme",x);const c=document.createElement("button");c.className="theme-toggle",c.textContent=x==="light"?"☾":"☼",c.title="Toggle light/dark mode",c.addEventListener("click",()=>{const d=document.documentElement.getAttribute("data-theme")==="light"?"dark":"light";document.documentElement.setAttribute("data-theme",d),localStorage.setItem("backpack-theme",d),c.textContent=d==="light"?"☾":"☼"}),t.appendChild(c);const o=yo();async function P(){if(!de||!L)return;L.metadata.updatedAt=new Date().toISOString(),await wt(de,L),a.loadGraph(L),ce.setLearningGraphData(L),J.setData(L);const n=await at();w.setSummaries(n)}async function i(n){L=n,await wt(de,L),a.loadGraph(L),ce.setLearningGraphData(L),J.setData(L);const d=await at();w.setSummaries(d)}let a;const I=no(t,{onUpdateNode(n,d){if(!L)return;o.push(L);const T=L.nodes.find(G=>G.id===n);T&&(T.properties={...T.properties,...d},T.updatedAt=new Date().toISOString(),P().then(()=>I.show([n],L)))},onChangeNodeType(n,d){if(!L)return;o.push(L);const T=L.nodes.find(G=>G.id===n);T&&(T.type=d,T.updatedAt=new Date().toISOString(),P().then(()=>I.show([n],L)))},onDeleteNode(n){L&&(o.push(L),L.nodes=L.nodes.filter(d=>d.id!==n),L.edges=L.edges.filter(d=>d.sourceId!==n&&d.targetId!==n),P())},onDeleteEdge(n){var T;if(!L)return;o.push(L);const d=(T=L.edges.find(G=>G.id===n))==null?void 0:T.sourceId;L.edges=L.edges.filter(G=>G.id!==n),P().then(()=>{d&&L&&I.show([d],L)})},onAddProperty(n,d,T){if(!L)return;o.push(L);const G=L.nodes.find(re=>re.id===n);G&&(G.properties[d]=T,G.updatedAt=new Date().toISOString(),P().then(()=>I.show([n],L)))}},n=>{a.panToNode(n)},n=>{J.addToFocusSet(n)}),F=window.matchMedia("(max-width: 768px)");let C=[],j=e.display.edges,U=e.navigation.panSpeed,Q=-1,W=null;function ne(n){W&&W.remove(),W=document.createElement("div"),W.className="focus-indicator";const d=document.createElement("span");d.className="focus-indicator-label",d.textContent=`Focused: ${n.totalNodes} nodes`;const T=document.createElement("span");T.className="focus-indicator-hops",T.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())}),W.appendChild(d),W.appendChild(G),W.appendChild(T),W.appendChild(re),W.appendChild(le),W.appendChild(me)}function V(){W&&(W.remove(),W=null)}const B=document.createElement("div");B.className="path-bar hidden",t.appendChild(B);function ee(n){if(B.innerHTML="",!L)return;for(let T=0;T<n.nodeIds.length;T++){const G=n.nodeIds[T],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(T>0){const ge=n.edgeIds[T-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 d=document.createElement("button");d.className="path-bar-close",d.textContent="×",d.addEventListener("click",K),B.appendChild(d),B.classList.remove("hidden")}function K(){B.classList.add("hidden"),B.innerHTML="",a.clearHighlightedPath()}a=eo(t,n=>{if(C=n??[],!a.getWalkMode())if(n&&n.length===2){const d=a.findPath(n[0],n[1]);d&&d.nodeIds.length>0?(a.setHighlightedPath(d.nodeIds,d.edgeIds),ee(d)):K()}else K();n&&n.length>0&&L?(I.show(n,L),F.matches&&J.collapse(),M(de,n)):(I.hide(),de&&M(de))},n=>{if(n){ne(n);const d=t.querySelector(".canvas-top-left");d&&W&&d.appendChild(W),M(de,n.seedNodeIds),I.setFocusDisabled(n.hops===0),m()}else V(),I.setFocusDisabled(!1),de&&M(de),m()},{lod:e.lod,navigation:e.navigation,walk:e.walk});const ce=ao(t,{maxResults:e.limits.maxSearchResults,debounceMs:e.limits.searchDebounceMs}),J=co(t,{onFilterByType(n){if(L)if(n===null)a.setFilteredNodeIds(null);else{const d=new Set(((L==null?void 0:L.nodes)??[]).filter(T=>T.type===n).map(T=>T.id));a.setFilteredNodeIds(d)}},onNavigateToNode(n){a.panToNode(n),L&&I.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 d=a.getWalkTrail();if(d.length<2)return;const T=new Set(d),G=L.edges.filter(re=>T.has(re.sourceId)&&T.has(re.targetId)).map(re=>re.id);await Vt(de,n,d,G),await N(de)},async onStarredSaveSnippet(n,d){if(!de||!L)return;const T=new Set(d),G=L.edges.filter(re=>T.has(re.sourceId)&&T.has(re.targetId)).map(re=>re.id);await Vt(de,n,d,G),await N(de)},onFocusChange(n){n&&n.length>0?a.enterFocus(n,0):a.isFocused()&&a.exitFocus()},onRenameNodeType(n,d){if(L){o.push(L);for(const T of L.nodes)T.type===n&&(T.type=d,T.updatedAt=new Date().toISOString());P()}},onRenameEdgeType(n,d){if(L){o.push(L);for(const T of L.edges)T.type===n&&(T.type=d);P()}},onToggleEdgeLabels(n){a.setEdgeLabels(n)},onToggleTypeHulls(n){a.setTypeHulls(n)},onToggleMinimap(n){a.setMinimap(n)},onLayoutChange(n,d){Qe({[n]:d}),a.reheat()},onPanSpeedChange(n){U=n},onExport(n){const d=a.exportImage(n);if(!d)return;const T=document.createElement("a");T.download=`${de||"graph"}.${n}`,T.href=d,T.click()},onSnapshot:async n=>{de&&(await Tn(de,n),await v(de))},onRollback:async n=>{de&&(await An(de,n),L=await ht(de),a.loadGraph(L),ce.setLearningGraphData(L),J.setData(L),await v(de))},onOpen(){F.matches&&I.hide()}}),ie=document.createElement("div");ie.className="canvas-top-bar";const te=document.createElement("div");te.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");_&&te.appendChild(_);const H=t.querySelector(".search-overlay");H&&ye.appendChild(H);const D=t.querySelector(".zoom-controls");D&&ke.appendChild(D),ke.appendChild(c),ie.appendChild(te),ie.appendChild(ye),ie.appendChild(ke),t.appendChild(ie),ce.onFilterChange(n=>{a.setFilteredNodeIds(n)}),ce.onNodeSelect(n=>{a.isFocused()&&J.clearFocusSet(),a.panToNode(n),L&&I.show([n],L)});const w=$n(document.getElementById("sidebar"),{onSelect:n=>k(n),onRename:async(n,d)=>{await Sn(n,d),de===n&&(de=d);const T=await at();w.setSummaries(T),w.setActive(de),de===d&&(L=await ht(d),a.loadGraph(L),ce.setLearningGraphData(L),J.setData(L))},onBranchSwitch:async(n,d)=>{await Gt(n,d),await g(n),L=await ht(n),a.loadGraph(L),ce.setLearningGraphData(L),J.setData(L),await v(n)},onBranchCreate:async(n,d)=>{await _t(n,d),await g(n)},onBranchDelete:async(n,d)=>{await In(n,d),await g(n)},onSnippetLoad:async(n,d)=>{var G;const T=await Pn(n,d);((G=T==null?void 0:T.nodeIds)==null?void 0:G.length)>0&&a.enterFocus(T.nodeIds,0)},onSnippetDelete:async(n,d)=>{await Rn(n,d),await N(n)},onBackpackSwitch:async n=>{await fetch("/api/backpacks/switch",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:n})}),await p()},onBackpackRegister:async(n,d)=>{await fetch("/api/backpacks",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({path:n,activate:d})}),await p()}});async function p(){try{const d=await(await fetch("/api/backpacks")).json();w.setBackpacks(d)}catch{}try{const n=await at();w.setSummaries(n),de&&!n.some(d=>d.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 d=[],T=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&&d.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(T),n.length>=2?(a.setHighlightedPath(n,d),ee({nodeIds:n,edgeIds:d})):K()}async function g(n){const d=await Ln(n),T=d.find(G=>G.active);T&&w.setActiveBranch(n,T.name,d)}async function v(n){const d=await Mn(n);J.setSnapshots(d)}async function N(n){const d=await Bn(n);w.setSnippets(n,d)}te.insertBefore(w.expandBtn,te.firstChild);const u=ho(t,s),E=fo(t),R=Co(t,{onStar(n){if(!L)return;const d=L.nodes.find(G=>G.id===n);if(!d)return;const T=d.properties._starred===!0;d.properties._starred=!T,wt(de,L),a.loadGraph(L)},onFocusNode(n){J.addToFocusSet([n])},onExploreInBranch(n){if(de){const d=`explore-${n.slice(0,8)}`;_t(de,d).then(()=>{Gt(de,d).then(()=>{a.enterFocus([n],1)})})}},onCopyId(n){navigator.clipboard.writeText(n)}});t.addEventListener("contextmenu",n=>{n.preventDefault();const d=t.querySelector("canvas");if(!d||!L)return;const T=d.getBoundingClientRect(),G=n.clientX-T.left,re=n.clientY-T.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-T.left,n.clientY-T.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 M(n,d){const T=[];d!=null&&d.length&&T.push("node="+d.map(encodeURIComponent).join(","));const G=a.getFocusInfo();G&&(T.push("focus="+G.seedNodeIds.map(encodeURIComponent).join(",")),T.push("hops="+G.hops));const re="#"+encodeURIComponent(n)+(T.length?"?"+T.join("&"):"");history.replaceState(null,"",re)}function O(){const n=window.location.hash.slice(1);if(!n)return{graph:null,nodes:[],focus:[],hops:1};const[d,T]=n.split("?"),G=d?decodeURIComponent(d):null;let re=[],me=[],le=1;if(T){const ge=new URLSearchParams(T),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 k(n,d,T,G){de=n,Mt=It.has(n),w.setActive(n),I.hide(),V(),ce.clear(),o.clear(),L=Mt?await Nn(n):await ht(n);const re=zn(L.nodes.length);if(Qe({spacing:Math.max(e.layout.spacing,re.spacing),clusterStrength:Math.max(e.layout.clustering,re.clusterStrength)}),a.loadGraph(L),ce.setLearningGraphData(L),J.setData(L),E.hide(),M(n),Mt||(await g(n),await v(n),await N(n)),T!=null&&T.length&&L){const me=T.filter(le=>L.nodes.some(ge=>ge.id===le));if(me.length){setTimeout(()=>{a.enterFocus(me,G??1)},500);return}}if(d!=null&&d.length&&L){const me=d.filter(le=>L.nodes.some(ge=>ge.id===le));me.length&&setTimeout(()=>{a.panToNodes(me),L&&I.show(me,L),M(n,me)},500)}}try{const d=await(await fetch("/api/backpacks")).json();w.setBackpacks(d)}catch{}const[f,S]=await Promise.all([at(),kn().catch(()=>[])]);w.setSummaries(f),w.setRemotes(S),It=new Set(S.map(n=>n.name));const q=O(),se=q.graph&&f.some(n=>n.name===q.graph)||q.graph&&It.has(q.graph)?q.graph:f.length>0?f[0].name:S.length>0?S[0].name:null;se?await k(se,q.nodes.length?q.nodes:void 0,q.focus.length?q.focus:void 0,q.hops):E.show();const pe={search(){ce.focus()},searchAlt(){ce.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():C.length>0&&J.addToFocusSet(C)},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&&(Q=(Q+1)%n.length,a.panToNode(n[Q]),L&&I.show([n[Q]],L))},prevNode(){const n=a.getNodeIds();n.length>0&&(Q=Q<=0?n.length-1:Q-1,a.panToNode(n[Q]),L&&I.show([n[Q]],L))},nextConnection(){const n=I.cycleConnection(1);n&&a.panToNode(n)},prevConnection(){const n=I.cycleConnection(-1);n&&a.panToNode(n)},historyBack(){I.goBack()},historyForward(){I.goForward()},center(){a.centerView()},toggleEdges(){j=!j,a.setEdges(j)},panLeft(){a.panBy(-U,0)},panDown(){a.panBy(0,U)},panUp(){a.panBy(0,-U)},panRight(){a.panBy(U,0)},panFastLeft(){a.panBy(-U*e.navigation.panFastMultiplier,0)},zoomOut(){a.zoomBy(1/e.navigation.zoomFactor)},zoomIn(){a.zoomBy(e.navigation.zoomFactor)},panFastRight(){a.panBy(U*e.navigation.panFastMultiplier,0)},spacingDecrease(){const n=rt();Qe({spacing:Math.max(.5,n.spacing-.5)}),a.reheat()},spacingIncrease(){const n=rt();Qe({spacing:Math.min(20,n.spacing+.5)}),a.reheat()},clusteringDecrease(){const n=rt();Qe({clusterStrength:Math.max(0,n.clusterStrength-.03)}),a.reheat()},clusteringIncrease(){const n=rt();Qe({clusterStrength:Math.min(1,n.clusterStrength+.03)}),a.reheat()},help(){u.toggle()},toggleSidebar(){w.toggle()},walkIsolate(){if(!L)return;const n=a.getWalkTrail();n.length!==0&&a.enterFocus(n,0)},walkMode(){!a.isFocused()&&C.length>0&&J.addToFocusSet(C),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 d;if(!(n.target instanceof HTMLInputElement||n.target instanceof HTMLTextAreaElement)){for(const[T,G]of Object.entries(s))if(lo(n,G)){(T==="search"||T==="searchAlt"||T==="undo"||T==="redo"||T==="toggleSidebar")&&n.preventDefault(),(d=pe[T])==null||d.call(pe);return}}}),window.addEventListener("hashchange",()=>{const n=O();if(n.graph&&n.graph!==de)k(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 d=n.nodes.filter(T=>L.nodes.some(G=>G.id===T));d.length&&(a.panToNodes(d),I.show(d,L))}})}Io();
@@ -0,0 +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);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:6px 10px;background:var(--bg-base);border:1px solid var(--border);border-radius:6px;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-label{display:block;font-size:11px;color:var(--text-muted);margin-bottom:4px;margin-top:8px;text-transform:uppercase;letter-spacing:.04em}.bp-dialog-path-row{display:flex;gap:8px;margin-bottom:4px}.bp-dialog-path-input{flex:1;margin-bottom:0}.bp-dialog-browse-btn{flex-shrink:0;padding:8px 14px;font-size:12px}.bp-dialog-browse-btn:disabled{opacity:.4;cursor:not-allowed}.bp-dialog-path-input.bp-dialog-drag-over{border-color:var(--accent);background:var(--bg-hover)}.bp-dialog-picker-hint{font-size:11px;color:var(--text-muted);min-height:1em;margin-bottom:8px}.bp-dialog-activate-row{display:flex;align-items:center;gap:8px;margin-top:8px;margin-bottom:12px;font-size:12px;color:var(--text-muted)}.bp-dialog-activate-row input[type=checkbox]{margin:0}.bp-dialog-activate-row label{cursor:pointer}.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-DBZCyAjY.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-DlVz8Lz7.css">
7
+ <script type="module" crossorigin src="/assets/index-D7P8kwxI.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-dtYoeajN.css">
9
9
  </head>
10
10
  <body>
11
11
  <div id="app">
package/dist/config.js CHANGED
@@ -18,6 +18,7 @@ export function loadViewerConfig() {
18
18
  const raw = fs.readFileSync(filePath, "utf-8");
19
19
  const user = JSON.parse(raw);
20
20
  return {
21
+ server: { ...defaultConfig.server, ...(user.server ?? {}) },
21
22
  keybindings: { ...defaultConfig.keybindings, ...(user.keybindings ?? {}) },
22
23
  display: { ...defaultConfig.display, ...(user.display ?? {}) },
23
24
  layout: { ...defaultConfig.layout, ...(user.layout ?? {}) },
@@ -33,6 +33,10 @@
33
33
  "walkMode": "w",
34
34
  "walkIsolate": "i"
35
35
  },
36
+ "server": {
37
+ "host": "127.0.0.1",
38
+ "port": 5173
39
+ },
36
40
  "display": {
37
41
  "edges": true,
38
42
  "edgeLabels": true,
package/dist/dialog.d.ts CHANGED
@@ -3,6 +3,19 @@
3
3
  export declare function showConfirm(title: string, message: string): Promise<boolean>;
4
4
  /** Show a prompt dialog with an input field. Returns null if cancelled. */
5
5
  export declare function showPrompt(title: string, placeholder?: string, defaultValue?: string): Promise<string | null>;
6
+ export interface BackpackAddResult {
7
+ path: string;
8
+ activate: boolean;
9
+ }
10
+ /**
11
+ * Show the Add Backpack dialog. Single path field with an optional
12
+ * native folder picker (Chromium only) and drag-and-drop hint. No
13
+ * name field — the display name is derived from the path tail by
14
+ * the backend. No suggestion chips — user pastes any path.
15
+ *
16
+ * Returns null if the user cancels.
17
+ */
18
+ export declare function showBackpackAddDialog(): Promise<BackpackAddResult | null>;
6
19
  /** Show a danger confirmation (for destructive actions). */
7
20
  export declare function showDangerConfirm(title: string, message: string): Promise<boolean>;
8
21
  /** Show a brief toast notification. */
package/dist/dialog.js CHANGED
@@ -87,6 +87,122 @@ export function showPrompt(title, placeholder, defaultValue) {
87
87
  input.select();
88
88
  });
89
89
  }
90
+ /**
91
+ * Show the Add Backpack dialog. Single path field with an optional
92
+ * native folder picker (Chromium only) and drag-and-drop hint. No
93
+ * name field — the display name is derived from the path tail by
94
+ * the backend. No suggestion chips — user pastes any path.
95
+ *
96
+ * Returns null if the user cancels.
97
+ */
98
+ export function showBackpackAddDialog() {
99
+ return new Promise((resolve) => {
100
+ const overlay = createOverlay();
101
+ const modal = createModal(overlay, "Add Backpack");
102
+ const description = document.createElement("p");
103
+ description.className = "bp-dialog-message";
104
+ description.textContent =
105
+ "Enter the absolute path to a directory that should become a backpack. It will be shown in the sidebar using the last segment of the path as its display name.";
106
+ modal.appendChild(description);
107
+ const pathLabel = document.createElement("label");
108
+ pathLabel.className = "bp-dialog-label";
109
+ pathLabel.textContent = "Path";
110
+ modal.appendChild(pathLabel);
111
+ const pathRow = document.createElement("div");
112
+ pathRow.className = "bp-dialog-path-row";
113
+ modal.appendChild(pathRow);
114
+ const pathInput = document.createElement("input");
115
+ pathInput.type = "text";
116
+ pathInput.className = "bp-dialog-input bp-dialog-path-input";
117
+ pathInput.placeholder = "/Users/you/OneDrive/work";
118
+ pathRow.appendChild(pathInput);
119
+ const browseBtn = document.createElement("button");
120
+ browseBtn.type = "button";
121
+ browseBtn.className = "bp-dialog-btn bp-dialog-browse-btn";
122
+ browseBtn.textContent = "Browse...";
123
+ pathRow.appendChild(browseBtn);
124
+ // File System Access API is Chromium-only. On Safari/Firefox the
125
+ // picker doesn't exist; even on Chromium it returns a handle, not
126
+ // a filesystem path, so the user still types or pastes the
127
+ // absolute path. The picker is only a hint.
128
+ const hasNativePicker = typeof window.showDirectoryPicker === "function";
129
+ if (!hasNativePicker) {
130
+ browseBtn.disabled = true;
131
+ browseBtn.title =
132
+ "Browser doesn't support native folder picker — paste the path manually";
133
+ }
134
+ browseBtn.addEventListener("click", async (e) => {
135
+ e.preventDefault();
136
+ try {
137
+ const handle = await window.showDirectoryPicker({
138
+ mode: "read",
139
+ });
140
+ pickerHint.textContent = `Picked "${handle.name}" — paste the absolute path to it below.`;
141
+ pathInput.focus();
142
+ }
143
+ catch {
144
+ // User cancelled
145
+ }
146
+ });
147
+ const pickerHint = document.createElement("div");
148
+ pickerHint.className = "bp-dialog-picker-hint";
149
+ modal.appendChild(pickerHint);
150
+ // Activate checkbox
151
+ const activateRow = document.createElement("div");
152
+ activateRow.className = "bp-dialog-activate-row";
153
+ const activateCheckbox = document.createElement("input");
154
+ activateCheckbox.type = "checkbox";
155
+ activateCheckbox.id = "bp-dialog-activate";
156
+ activateCheckbox.checked = true;
157
+ const activateLabel = document.createElement("label");
158
+ activateLabel.htmlFor = "bp-dialog-activate";
159
+ activateLabel.textContent = "Switch to this backpack after registering";
160
+ activateRow.appendChild(activateCheckbox);
161
+ activateRow.appendChild(activateLabel);
162
+ modal.appendChild(activateRow);
163
+ // Drag-and-drop: dropping a folder gives us the folder name via
164
+ // webkitGetAsEntry but not the full OS path (sandboxed). Use it as
165
+ // a visual hint only.
166
+ pathInput.addEventListener("dragover", (e) => {
167
+ e.preventDefault();
168
+ pathInput.classList.add("bp-dialog-drag-over");
169
+ });
170
+ pathInput.addEventListener("dragleave", () => {
171
+ pathInput.classList.remove("bp-dialog-drag-over");
172
+ });
173
+ pathInput.addEventListener("drop", (e) => {
174
+ e.preventDefault();
175
+ pathInput.classList.remove("bp-dialog-drag-over");
176
+ const items = e.dataTransfer?.items;
177
+ if (!items || items.length === 0)
178
+ return;
179
+ const entry = items[0].webkitGetAsEntry?.();
180
+ if (entry?.isDirectory) {
181
+ pickerHint.textContent = `Dropped "${entry.name}" — paste the absolute path to it below.`;
182
+ }
183
+ });
184
+ const submit = () => {
185
+ const p = pathInput.value.trim();
186
+ if (!p)
187
+ return;
188
+ overlay.remove();
189
+ resolve({ path: p, activate: activateCheckbox.checked });
190
+ };
191
+ pathInput.addEventListener("keydown", (e) => {
192
+ if (e.key === "Enter")
193
+ submit();
194
+ if (e.key === "Escape") {
195
+ overlay.remove();
196
+ resolve(null);
197
+ }
198
+ });
199
+ addButtons(modal, [
200
+ { label: "Cancel", onClick: () => { overlay.remove(); resolve(null); } },
201
+ { label: "Register", accent: true, onClick: submit },
202
+ ]);
203
+ pathInput.focus();
204
+ });
205
+ }
90
206
  /** Show a danger confirmation (for destructive actions). */
91
207
  export function showDangerConfirm(title, message) {
92
208
  return new Promise((resolve) => {
package/dist/main.js CHANGED
@@ -528,11 +528,11 @@ async function main() {
528
528
  // live-reload channel, so we refresh immediately as fallback.
529
529
  await refreshBackpacksAndGraphs();
530
530
  },
531
- onBackpackRegister: async (name, p, activate) => {
531
+ onBackpackRegister: async (p, activate) => {
532
532
  await fetch("/api/backpacks", {
533
533
  method: "POST",
534
534
  headers: { "Content-Type": "application/json" },
535
- body: JSON.stringify({ name, path: p, activate }),
535
+ body: JSON.stringify({ path: p, activate }),
536
536
  });
537
537
  await refreshBackpacksAndGraphs();
538
538
  },
package/dist/sidebar.d.ts CHANGED
@@ -14,8 +14,8 @@ export interface SidebarCallbacks {
14
14
  onBranchDelete?: (graphName: string, branchName: string) => void;
15
15
  onSnippetLoad?: (graphName: string, snippetId: string) => void;
16
16
  onSnippetDelete?: (graphName: string, snippetId: string) => void;
17
- onBackpackSwitch?: (name: string) => void;
18
- onBackpackRegister?: (name: string, path: string, activate: boolean) => void;
17
+ onBackpackSwitch?: (pathOrName: string) => void;
18
+ onBackpackRegister?: (path: string, activate: boolean) => void;
19
19
  }
20
20
  export declare function initSidebar(container: HTMLElement, onSelectOrCallbacks: ((name: string) => void) | SidebarCallbacks): {
21
21
  setBackpacks(list: BackpackSummary[]): void;
package/dist/sidebar.js CHANGED
@@ -1,4 +1,4 @@
1
- import { showConfirm, showPrompt } from "./dialog";
1
+ import { showConfirm, showPrompt, showBackpackAddDialog } from "./dialog";
2
2
  function formatTokenCount(n) {
3
3
  if (n >= 1000)
4
4
  return `${(n / 1000).toFixed(1)}k tokens`;
@@ -146,14 +146,10 @@ export function initSidebar(container, onSelectOrCallbacks) {
146
146
  closePicker();
147
147
  if (!cbs.onBackpackRegister)
148
148
  return;
149
- const name = await showPrompt("Backpack name", "Short kebab-case name (e.g. work, family, project-alpha)", "");
150
- if (!name)
149
+ const result = await showBackpackAddDialog();
150
+ if (!result)
151
151
  return;
152
- const p = await showPrompt("Backpack path", "Absolute or tilde-expanded path to the graphs directory", "");
153
- if (!p)
154
- return;
155
- const activate = await showConfirm("Switch to new backpack?", `Make "${name}" the active backpack immediately?`);
156
- cbs.onBackpackRegister(name, p, activate);
152
+ cbs.onBackpackRegister(result.path, result.activate);
157
153
  });
158
154
  pickerDropdown.appendChild(addItem);
159
155
  }
package/dist/style.css CHANGED
@@ -330,10 +330,10 @@ body {
330
330
  align-items: center;
331
331
  gap: 6px;
332
332
  width: 100%;
333
- padding: 4px 8px;
333
+ padding: 6px 10px;
334
334
  background: var(--bg-base);
335
335
  border: 1px solid var(--border);
336
- border-radius: 999px;
336
+ border-radius: 6px;
337
337
  color: var(--fg);
338
338
  font-size: 11px;
339
339
  font-family: inherit;
@@ -2135,6 +2135,68 @@ body {
2135
2135
  border-color: var(--accent);
2136
2136
  }
2137
2137
 
2138
+ .bp-dialog-label {
2139
+ display: block;
2140
+ font-size: 11px;
2141
+ color: var(--text-muted);
2142
+ margin-bottom: 4px;
2143
+ margin-top: 8px;
2144
+ text-transform: uppercase;
2145
+ letter-spacing: 0.04em;
2146
+ }
2147
+
2148
+ .bp-dialog-path-row {
2149
+ display: flex;
2150
+ gap: 8px;
2151
+ margin-bottom: 4px;
2152
+ }
2153
+
2154
+ .bp-dialog-path-input {
2155
+ flex: 1;
2156
+ margin-bottom: 0;
2157
+ }
2158
+
2159
+ .bp-dialog-browse-btn {
2160
+ flex-shrink: 0;
2161
+ padding: 8px 14px;
2162
+ font-size: 12px;
2163
+ }
2164
+
2165
+ .bp-dialog-browse-btn:disabled {
2166
+ opacity: 0.4;
2167
+ cursor: not-allowed;
2168
+ }
2169
+
2170
+ .bp-dialog-path-input.bp-dialog-drag-over {
2171
+ border-color: var(--accent);
2172
+ background: var(--bg-hover);
2173
+ }
2174
+
2175
+ .bp-dialog-picker-hint {
2176
+ font-size: 11px;
2177
+ color: var(--text-muted);
2178
+ min-height: 1em;
2179
+ margin-bottom: 8px;
2180
+ }
2181
+
2182
+ .bp-dialog-activate-row {
2183
+ display: flex;
2184
+ align-items: center;
2185
+ gap: 8px;
2186
+ margin-top: 8px;
2187
+ margin-bottom: 12px;
2188
+ font-size: 12px;
2189
+ color: var(--text-muted);
2190
+ }
2191
+
2192
+ .bp-dialog-activate-row input[type="checkbox"] {
2193
+ margin: 0;
2194
+ }
2195
+
2196
+ .bp-dialog-activate-row label {
2197
+ cursor: pointer;
2198
+ }
2199
+
2138
2200
  .bp-dialog-buttons {
2139
2201
  display: flex;
2140
2202
  justify-content: flex-end;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backpack-viewer",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Web-based graph visualizer for backpack-ontology — Canvas 2D, force-directed layout, live reload",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Noah Irzinger",
@@ -23,7 +23,7 @@
23
23
  "release:major": "npm version major && git push && git push --tags"
24
24
  },
25
25
  "dependencies": {
26
- "backpack-ontology": "^0.4.0"
26
+ "backpack-ontology": "^0.5.0"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/node": "^25.5.0",
@@ -1,34 +0,0 @@
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 +0,0 @@
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}}