backpack-viewer 0.2.5 → 0.2.7
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
*{margin:0;padding:0;box-sizing:border-box}:root{--bg: #141414;--bg-surface: #1a1a1a;--bg-hover: #222222;--bg-active: #2a2a2a;--bg-elevated: #1e1e1e;--bg-inset: #111111;--border: #2a2a2a;--text: #d4d4d4;--text-strong: #e5e5e5;--text-muted: #737373;--text-dim: #525252;--accent: #d4a27f;--accent-hover: #e8b898;--badge-text: #141414;--glass-bg: rgba(20, 20, 20, .85);--glass-border: rgba(255, 255, 255, .08);--chip-bg: rgba(42, 42, 42, .7);--chip-bg-active: rgba(42, 42, 42, .9);--chip-bg-hover: rgba(50, 50, 50, .9);--chip-border-active: rgba(255, 255, 255, .06);--shadow: rgba(0, 0, 0, .6);--shadow-strong: rgba(0, 0, 0, .5);--canvas-edge: rgba(255, 255, 255, .08);--canvas-edge-highlight: rgba(212, 162, 127, .5);--canvas-edge-dim: rgba(255, 255, 255, .03);--canvas-edge-label: rgba(255, 255, 255, .2);--canvas-edge-label-highlight: rgba(212, 162, 127, .7);--canvas-edge-label-dim: rgba(255, 255, 255, .05);--canvas-arrow: rgba(255, 255, 255, .12);--canvas-arrow-highlight: rgba(212, 162, 127, .5);--canvas-node-label: #a3a3a3;--canvas-node-label-dim: rgba(212, 212, 212, .2);--canvas-type-badge: rgba(115, 115, 115, .5);--canvas-type-badge-dim: rgba(115, 115, 115, .15);--canvas-selection-border: #d4d4d4;--canvas-node-border: rgba(255, 255, 255, .15)}[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)}body{font-family:system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--text);overflow:hidden}#app{display:flex;height:100vh;width:100vw}#sidebar{width:280px;min-width:280px;background:var(--bg-surface);border-right:1px solid var(--border);display:flex;flex-direction:column;padding:16px;overflow-y:auto}#sidebar 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-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-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)}.theme-toggle{position:absolute;top:16px;right:16px;z-index:30;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)}#canvas-container{flex:1;position:relative;overflow:hidden}#graph-canvas{position:absolute;top:0;left:0;width:100%;height:100%;cursor:grab}#graph-canvas:active{cursor:grabbing}.search-overlay{position:absolute;top:16px;left:50%;transform:translate(-50%);z-index:20;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;width:380px;max-width:calc(100vw - 340px)}.search-input{width:100%;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{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}.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-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;width:360px;max-height:calc(100vh - 72px);background:var(--bg-surface);border:1px solid var(--border);border-radius:10px;overflow-y:auto;padding:20px;z-index:10;box-shadow:0 8px 32px var(--shadow)}.info-panel.hidden{display:none}.info-close{position:absolute;top:12px;right:14px;background:none;border:none;color:var(--text-muted);font-size:20px;cursor:pointer;line-height:1;padding:4px}.info-close:hover{color:var(--text)}.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-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-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;color:var(--text);flex:1;min-width:0}.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}
|
|
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)}[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)}body{font-family:system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--text);overflow:hidden}#app{display:flex;height:100vh;width:100vw}#sidebar{width:280px;min-width:280px;background:var(--bg-surface);border-right:1px solid var(--border);display:flex;flex-direction:column;padding:16px;overflow-y:auto}#sidebar 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-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-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)}.theme-toggle{position:absolute;top:16px;right:16px;z-index:30;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)}#canvas-container{flex:1;position:relative;overflow:hidden}#graph-canvas{position:absolute;top:0;left:0;width:100%;height:100%;cursor:grab}#graph-canvas:active{cursor:grabbing}.search-overlay{position:absolute;top:16px;left:50%;transform:translate(-50%);z-index:20;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;width:380px;max-width:calc(100vw - 340px)}.search-input{width:100%;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{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}.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-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;width:360px;max-height:calc(100vh - 72px);background:var(--bg-surface);border:1px solid var(--border);border-radius:10px;overflow-y:auto;padding:20px;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.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-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-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;color:var(--text);flex:1;min-width:0}.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}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(){const n=document.createElement("link").relList;if(n&&n.supports&&n.supports("modulepreload"))return;for(const d of document.querySelectorAll('link[rel="modulepreload"]'))t(d);new MutationObserver(d=>{for(const e of d)if(e.type==="childList")for(const i of e.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&t(i)}).observe(document,{childList:!0,subtree:!0});function o(d){const e={};return d.integrity&&(e.integrity=d.integrity),d.referrerPolicy&&(e.referrerPolicy=d.referrerPolicy),d.crossOrigin==="use-credentials"?e.credentials="include":d.crossOrigin==="anonymous"?e.credentials="omit":e.credentials="same-origin",e}function t(d){if(d.ep)return;d.ep=!0;const e=o(d);fetch(d.href,e)}})();async function ae(){const s=await fetch("/api/ontologies");return s.ok?s.json():[]}async function ce(s){const n=await fetch(`/api/ontologies/${encodeURIComponent(s)}`);if(!n.ok)throw new Error(`Failed to load ontology: ${s}`);return n.json()}async function be(s,n){if(!(await fetch(`/api/ontologies/${encodeURIComponent(s)}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)})).ok)throw new Error(`Failed to save ontology: ${s}`)}async function Le(s,n){if(!(await fetch(`/api/ontologies/${encodeURIComponent(s)}/rename`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:n})})).ok)throw new Error(`Failed to rename ontology: ${s}`)}function we(s,n){const o=typeof n=="function"?{onSelect:n}:n,t=document.createElement("h2");t.textContent="Backpack Ontology Viewer";const d=document.createElement("input");d.type="text",d.placeholder="Filter...",d.id="filter";const e=document.createElement("ul");e.id="ontology-list";const i=document.createElement("div");i.className="sidebar-footer",i.innerHTML='<a href="mailto:support@backpackontology.com">support@backpackontology.com</a><span>Feedback & support</span>',s.appendChild(t),s.appendChild(d),s.appendChild(e),s.appendChild(i);let v=[],h="";return d.addEventListener("input",()=>{const g=d.value.toLowerCase();for(const m of v){const a=m.dataset.name??"";m.style.display=a.includes(g)?"":"none"}}),{setSummaries(g){e.innerHTML="",v=g.map(m=>{const a=document.createElement("li");a.className="ontology-item",a.dataset.name=m.name;const y=document.createElement("span");y.className="name",y.textContent=m.name;const A=document.createElement("span");if(A.className="stats",A.textContent=`${m.nodeCount} nodes, ${m.edgeCount} edges`,a.appendChild(y),a.appendChild(A),o.onRename){const I=document.createElement("button");I.className="sidebar-edit-btn",I.textContent="✎",I.title="Rename";const K=o.onRename;I.addEventListener("click",f=>{f.stopPropagation();const x=document.createElement("input");x.type="text",x.className="sidebar-rename-input",x.value=m.name,y.textContent="",y.appendChild(x),I.style.display="none",x.focus(),x.select();const p=()=>{const C=x.value.trim();C&&C!==m.name?K(m.name,C):(y.textContent=m.name,I.style.display="")};x.addEventListener("blur",p),x.addEventListener("keydown",C=>{C.key==="Enter"&&x.blur(),C.key==="Escape"&&(x.value=m.name,x.blur())})}),a.appendChild(I)}return a.addEventListener("click",()=>o.onSelect(m.name)),e.appendChild(a),a}),h&&this.setActive(h)},setActive(g){h=g;for(const m of v)m.classList.toggle("active",m.dataset.name===g)}}}const Ne=5e3,Se=.005,Ce=150,pe=.9,ue=.01,me=30,ie=50;function Me(s,n){for(const o of Object.values(s))if(typeof o=="string")return o;return n}function Ae(s){const n=Math.sqrt(s.nodes.length)*Ce*.5,o=new Map,t=s.nodes.map((e,i)=>{const v=2*Math.PI*i/s.nodes.length,h={id:e.id,x:Math.cos(v)*n,y:Math.sin(v)*n,vx:0,vy:0,label:Me(e.properties,e.id),type:e.type};return o.set(e.id,h),h}),d=s.edges.map(e=>({sourceId:e.sourceId,targetId:e.targetId,type:e.type}));return{nodes:t,edges:d,nodeMap:o}}function Ie(s,n){const{nodes:o,edges:t,nodeMap:d}=s;for(let e=0;e<o.length;e++)for(let i=e+1;i<o.length;i++){const v=o[e],h=o[i];let g=h.x-v.x,m=h.y-v.y,a=Math.sqrt(g*g+m*m);a<me&&(a=me);const y=Ne*n/(a*a),A=g/a*y,I=m/a*y;v.vx-=A,v.vy-=I,h.vx+=A,h.vy+=I}for(const e of t){const i=d.get(e.sourceId),v=d.get(e.targetId);if(!i||!v)continue;const h=v.x-i.x,g=v.y-i.y,m=Math.sqrt(h*h+g*g);if(m===0)continue;const a=Se*(m-Ce)*n,y=h/m*a,A=g/m*a;i.vx+=y,i.vy+=A,v.vx-=y,v.vy-=A}for(const e of o)e.vx-=e.x*ue*n,e.vy-=e.y*ue*n;for(const e of o){e.vx*=pe,e.vy*=pe;const i=Math.sqrt(e.vx*e.vx+e.vy*e.vy);i>ie&&(e.vx=e.vx/i*ie,e.vy=e.vy/i*ie),e.x+=e.vx,e.y+=e.vy}return n*.995}const fe=["#d4a27f","#c17856","#b07a5e","#d4956b","#a67c5a","#cc9e7c","#c4866a","#cb8e6c","#b8956e","#a88a70","#d9b08c","#c4a882","#e8b898","#b5927a","#a8886e","#d1a990"],he=new Map;function J(s){const n=he.get(s);if(n)return n;let o=0;for(let d=0;d<s.length;d++)o=(o<<5)-o+s.charCodeAt(d)|0;const t=fe[Math.abs(o)%fe.length];return he.set(s,t),t}function q(s){return getComputedStyle(document.documentElement).getPropertyValue(s).trim()}const G=20,ke=.001;function Te(s,n){const o=s.querySelector("canvas"),t=o.getContext("2d"),d=window.devicePixelRatio||1;let e={x:0,y:0,scale:1},i=null,v=1,h=0,g=new Set,m=null,a=null,y=null;const A=300;function I(){o.width=o.clientWidth*d,o.height=o.clientHeight*d,p()}const K=new ResizeObserver(I);K.observe(s),I();function f(r,c){return[r/e.scale+e.x,c/e.scale+e.y]}function x(r,c){if(!i)return null;const[u,l]=f(r,c);for(let E=i.nodes.length-1;E>=0;E--){const b=i.nodes[E],w=u-b.x,T=l-b.y;if(w*w+T*T<=G*G)return b}return null}function p(){if(!i){t.clearRect(0,0,o.width,o.height);return}const r=q("--canvas-edge"),c=q("--canvas-edge-highlight"),u=q("--canvas-edge-dim"),l=q("--canvas-edge-label"),E=q("--canvas-edge-label-highlight"),b=q("--canvas-edge-label-dim"),w=q("--canvas-arrow"),T=q("--canvas-arrow-highlight"),S=q("--canvas-node-label"),B=q("--canvas-node-label-dim"),O=q("--canvas-type-badge"),Q=q("--canvas-type-badge-dim"),Z=q("--canvas-selection-border"),H=q("--canvas-node-border");t.save(),t.setTransform(d,0,0,d,0,0),t.clearRect(0,0,o.clientWidth,o.clientHeight),t.save(),t.translate(-e.x*e.scale,-e.y*e.scale),t.scale(e.scale,e.scale);for(const M of i.edges){const Y=i.nodeMap.get(M.sourceId),U=i.nodeMap.get(M.targetId);if(!Y||!U)continue;const se=m===null||m.has(M.sourceId),te=m===null||m.has(M.targetId),ee=se&&te;if(m!==null&&!se&&!te)continue;const V=g.size>0&&(g.has(M.sourceId)||g.has(M.targetId))||m!==null&&ee,re=m!==null&&!ee;if(M.sourceId===M.targetId){N(Y,M.type,V,r,c,l,E);continue}t.beginPath(),t.moveTo(Y.x,Y.y),t.lineTo(U.x,U.y),t.strokeStyle=V?c:re?u:r,t.lineWidth=V?2.5:1.5,t.stroke(),C(Y.x,Y.y,U.x,U.y,V,w,T);const ve=(Y.x+U.x)/2,Ee=(Y.y+U.y)/2;t.fillStyle=V?E:re?b:l,t.font="9px system-ui, sans-serif",t.textAlign="center",t.textBaseline="bottom",t.fillText(M.type,ve,Ee-4)}for(const M of i.nodes){const Y=J(M.type),U=g.has(M.id),se=g.size>0&&i.edges.some(V=>g.has(V.sourceId)&&V.targetId===M.id||g.has(V.targetId)&&V.sourceId===M.id),te=m!==null&&!m.has(M.id),ee=te||g.size>0&&!U&&!se;U&&(t.save(),t.shadowColor=Y,t.shadowBlur=20,t.beginPath(),t.arc(M.x,M.y,G+3,0,Math.PI*2),t.fillStyle=Y,t.globalAlpha=.3,t.fill(),t.restore()),t.beginPath(),t.arc(M.x,M.y,G,0,Math.PI*2),t.fillStyle=Y,t.globalAlpha=te?.1:ee?.3:1,t.fill(),t.strokeStyle=U?Z:H,t.lineWidth=U?3:1.5,t.stroke();const de=M.label.length>24?M.label.slice(0,22)+"...":M.label;t.fillStyle=ee?B:S,t.font="11px system-ui, sans-serif",t.textAlign="center",t.textBaseline="top",t.fillText(de,M.x,M.y+G+4),t.fillStyle=ee?Q:O,t.font="9px system-ui, sans-serif",t.textBaseline="bottom",t.fillText(M.type,M.x,M.y-G-3),t.globalAlpha=1}t.restore(),t.restore()}function C(r,c,u,l,E,b,w){const T=Math.atan2(l-c,u-r),S=u-Math.cos(T)*G,B=l-Math.sin(T)*G,O=8;t.beginPath(),t.moveTo(S,B),t.lineTo(S-O*Math.cos(T-.4),B-O*Math.sin(T-.4)),t.lineTo(S-O*Math.cos(T+.4),B-O*Math.sin(T+.4)),t.closePath(),t.fillStyle=E?w:b,t.fill()}function N(r,c,u,l,E,b,w){const T=r.x+G+15,S=r.y-G-15;t.beginPath(),t.arc(T,S,15,0,Math.PI*2),t.strokeStyle=u?E:l,t.lineWidth=u?2.5:1.5,t.stroke(),t.fillStyle=u?w:b,t.font="9px system-ui, sans-serif",t.textAlign="center",t.fillText(c,T,S-18)}function D(){if(!a||!y)return;const r=performance.now()-y.time,c=Math.min(r/A,1),u=1-Math.pow(1-c,3);e.x=y.x+(a.x-y.x)*u,e.y=y.y+(a.y-y.y)*u,p(),c<1?requestAnimationFrame(D):(a=null,y=null)}function z(){!i||v<ke||(v=Ie(i,v),p(),h=requestAnimationFrame(z))}let F=!1,j=!1,X=0,W=0;o.addEventListener("mousedown",r=>{F=!0,j=!1,X=r.clientX,W=r.clientY}),o.addEventListener("mousemove",r=>{if(!F)return;const c=r.clientX-X,u=r.clientY-W;(Math.abs(c)>2||Math.abs(u)>2)&&(j=!0),e.x-=c/e.scale,e.y-=u/e.scale,X=r.clientX,W=r.clientY,p()}),o.addEventListener("mouseup",r=>{if(F=!1,j)return;const c=o.getBoundingClientRect(),u=r.clientX-c.left,l=r.clientY-c.top,E=x(u,l),b=r.ctrlKey||r.metaKey;if(E){b?g.has(E.id)?g.delete(E.id):g.add(E.id):g.size===1&&g.has(E.id)?g.clear():(g.clear(),g.add(E.id));const w=[...g];n==null||n(w.length>0?w:null)}else g.clear(),n==null||n(null);p()}),o.addEventListener("mouseleave",()=>{F=!1}),o.addEventListener("wheel",r=>{r.preventDefault();const c=o.getBoundingClientRect(),u=r.clientX-c.left,l=r.clientY-c.top,[E,b]=f(u,l),w=r.ctrlKey?1-r.deltaY*.01:r.deltaY>0?.9:1.1;e.scale=Math.max(.05,Math.min(10,e.scale*w)),e.x=E-u/e.scale,e.y=b-l/e.scale,p()},{passive:!1});let R=[],k=0,P=1;o.addEventListener("touchstart",r=>{r.preventDefault(),R=Array.from(r.touches),R.length===2?(k=$(R[0],R[1]),P=e.scale):R.length===1&&(X=R[0].clientX,W=R[0].clientY)},{passive:!1}),o.addEventListener("touchmove",r=>{r.preventDefault();const c=Array.from(r.touches);if(c.length===2&&R.length===2){const l=$(c[0],c[1])/k;e.scale=Math.max(.05,Math.min(10,P*l)),p()}else if(c.length===1){const u=c[0].clientX-X,l=c[0].clientY-W;e.x-=u/e.scale,e.y-=l/e.scale,X=c[0].clientX,W=c[0].clientY,p()}R=c},{passive:!1});function $(r,c){const u=r.clientX-c.clientX,l=r.clientY-c.clientY;return Math.sqrt(u*u+l*l)}return{loadGraph(r){if(cancelAnimationFrame(h),i=Ae(r),v=1,g=new Set,m=null,e={x:0,y:0,scale:1},i.nodes.length>0){let c=1/0,u=1/0,l=-1/0,E=-1/0;for(const B of i.nodes)B.x<c&&(c=B.x),B.y<u&&(u=B.y),B.x>l&&(l=B.x),B.y>E&&(E=B.y);const b=(c+l)/2,w=(u+E)/2,T=o.clientWidth,S=o.clientHeight;e.x=b-T/2,e.y=w-S/2}z()},setFilteredNodeIds(r){m=r,p()},panToNode(r){if(!i)return;const c=i.nodeMap.get(r);if(!c)return;g=new Set([r]),n==null||n([r]);const u=o.clientWidth,l=o.clientHeight;y={x:e.x,y:e.y,time:performance.now()},a={x:c.x-u/(2*e.scale),y:c.y-l/(2*e.scale)},D()},destroy(){cancelAnimationFrame(h),K.disconnect()}}}function ne(s){for(const n of Object.values(s.properties))if(typeof n=="string")return n;return s.id}const Oe="✎";function De(s,n,o){const t=document.createElement("div");t.id="info-panel",t.className="info-panel hidden",s.appendChild(t);let d=!1,e=[],i=-1,v=!1,h=null;function g(){t.classList.add("hidden"),t.classList.remove("info-panel-maximized"),t.innerHTML="",d=!1,e=[],i=-1}function m(f){!h||!o||(i<e.length-1&&(e=e.slice(0,i+1)),e.push(f),i=e.length-1,v=!0,o(f),v=!1)}function a(){i<=0||!h||!o||(i--,v=!0,o(e[i]),v=!1)}function y(){i>=e.length-1||!h||!o||(i++,v=!0,o(e[i]),v=!1)}function A(){const f=document.createElement("div");f.className="info-panel-toolbar";const x=document.createElement("button");x.className="info-toolbar-btn",x.textContent="←",x.title="Back",x.disabled=i<=0,x.addEventListener("click",a),f.appendChild(x);const p=document.createElement("button");p.className="info-toolbar-btn",p.textContent="→",p.title="Forward",p.disabled=i>=e.length-1,p.addEventListener("click",y),f.appendChild(p);const C=document.createElement("button");C.className="info-toolbar-btn",C.textContent=d?"⎘":"⛶",C.title=d?"Restore":"Maximize",C.addEventListener("click",()=>{d=!d,t.classList.toggle("info-panel-maximized",d),C.textContent=d?"⎘":"⛶",C.title=d?"Restore":"Maximize"}),f.appendChild(C);const N=document.createElement("button");return N.className="info-toolbar-btn info-close-btn",N.textContent="×",N.title="Close",N.addEventListener("click",g),f.appendChild(N),f}function I(f,x){const p=x.nodes.find(c=>c.id===f);if(!p)return;const C=x.edges.filter(c=>c.sourceId===f||c.targetId===f);t.innerHTML="",t.classList.remove("hidden"),d&&t.classList.add("info-panel-maximized"),t.appendChild(A());const N=document.createElement("div");N.className="info-header";const D=document.createElement("span");if(D.className="info-type-badge",D.textContent=p.type,D.style.backgroundColor=J(p.type),n){D.classList.add("info-editable");const c=document.createElement("button");c.className="info-inline-edit",c.textContent=Oe,c.addEventListener("click",u=>{u.stopPropagation();const l=document.createElement("input");l.type="text",l.className="info-edit-inline-input",l.value=p.type,D.textContent="",D.appendChild(l),l.focus(),l.select();const E=()=>{const b=l.value.trim();b&&b!==p.type?n.onChangeNodeType(f,b):(D.textContent=p.type,D.appendChild(c))};l.addEventListener("blur",E),l.addEventListener("keydown",b=>{b.key==="Enter"&&l.blur(),b.key==="Escape"&&(l.value=p.type,l.blur())})}),D.appendChild(c)}const z=document.createElement("h3");z.className="info-label",z.textContent=ne(p);const F=document.createElement("span");F.className="info-id",F.textContent=p.id,N.appendChild(D),N.appendChild(z),N.appendChild(F),t.appendChild(N);const j=Object.keys(p.properties),X=oe("Properties");if(j.length>0){const c=document.createElement("dl");c.className="info-props";for(const u of j){const l=document.createElement("dt");l.textContent=u;const E=document.createElement("dd");if(n){const b=le(p.properties[u]),w=document.createElement("input");w.type="text",w.className="info-edit-input",w.value=b,w.addEventListener("keydown",S=>{S.key==="Enter"&&w.blur()}),w.addEventListener("blur",()=>{const S=w.value;S!==b&&n.onUpdateNode(f,{[u]:Be(S)})}),E.appendChild(w);const T=document.createElement("button");T.className="info-delete-prop",T.textContent="×",T.title=`Remove ${u}`,T.addEventListener("click",()=>{const S={...p.properties};delete S[u],n.onUpdateNode(f,S)}),E.appendChild(T)}else E.appendChild(Pe(p.properties[u]));c.appendChild(l),c.appendChild(E)}X.appendChild(c)}if(n){const c=document.createElement("button");c.className="info-add-btn",c.textContent="+ Add property",c.addEventListener("click",()=>{const u=document.createElement("div");u.className="info-add-row";const l=document.createElement("input");l.type="text",l.className="info-edit-input",l.placeholder="key";const E=document.createElement("input");E.type="text",E.className="info-edit-input",E.placeholder="value";const b=document.createElement("button");b.className="info-add-save",b.textContent="Add",b.addEventListener("click",()=>{l.value&&n.onAddProperty(f,l.value,E.value)}),u.appendChild(l),u.appendChild(E),u.appendChild(b),X.appendChild(u),l.focus()}),X.appendChild(c)}if(t.appendChild(X),C.length>0){const c=oe(`Connections (${C.length})`),u=document.createElement("ul");u.className="info-connections";for(const l of C){const E=l.sourceId===f,b=E?l.targetId:l.sourceId,w=x.nodes.find(H=>H.id===b),T=w?ne(w):b,S=document.createElement("li");if(S.className="info-connection",o&&w&&(S.classList.add("info-connection-link"),S.addEventListener("click",H=>{H.target.closest(".info-delete-edge")||m(b)})),w){const H=document.createElement("span");H.className="info-target-dot",H.style.backgroundColor=J(w.type),S.appendChild(H)}const B=document.createElement("span");B.className="info-arrow",B.textContent=E?"→":"←";const O=document.createElement("span");O.className="info-edge-type",O.textContent=l.type;const Q=document.createElement("span");Q.className="info-target",Q.textContent=T,S.appendChild(B),S.appendChild(O),S.appendChild(Q);const Z=Object.keys(l.properties);if(Z.length>0){const H=document.createElement("div");H.className="info-edge-props";for(const M of Z){const Y=document.createElement("span");Y.className="info-edge-prop",Y.textContent=`${M}: ${le(l.properties[M])}`,H.appendChild(Y)}S.appendChild(H)}if(n){const H=document.createElement("button");H.className="info-delete-edge",H.textContent="×",H.title="Remove connection",H.addEventListener("click",M=>{M.stopPropagation(),n.onDeleteEdge(l.id)}),S.appendChild(H)}u.appendChild(S)}c.appendChild(u),t.appendChild(c)}const W=oe("Timestamps"),R=document.createElement("dl");R.className="info-props";const k=document.createElement("dt");k.textContent="created";const P=document.createElement("dd");P.textContent=ge(p.createdAt);const $=document.createElement("dt");$.textContent="updated";const r=document.createElement("dd");if(r.textContent=ge(p.updatedAt),R.appendChild(k),R.appendChild(P),R.appendChild($),R.appendChild(r),W.appendChild(R),t.appendChild(W),n){const c=document.createElement("div");c.className="info-section info-danger";const u=document.createElement("button");u.className="info-delete-node",u.textContent="Delete node",u.addEventListener("click",()=>{n.onDeleteNode(f),g()}),c.appendChild(u),t.appendChild(c)}}function K(f,x){const p=new Set(f),C=x.nodes.filter(k=>p.has(k.id));if(C.length===0)return;const N=x.edges.filter(k=>p.has(k.sourceId)&&p.has(k.targetId));t.innerHTML="",t.classList.remove("hidden"),d&&t.classList.add("info-panel-maximized"),t.appendChild(A());const D=document.createElement("div");D.className="info-header";const z=document.createElement("h3");z.className="info-label",z.textContent=`${C.length} nodes selected`,D.appendChild(z);const F=document.createElement("div");F.style.cssText="display:flex;flex-wrap:wrap;gap:4px;margin-top:6px";const j=new Map;for(const k of C)j.set(k.type,(j.get(k.type)??0)+1);for(const[k,P]of j){const $=document.createElement("span");$.className="info-type-badge",$.style.backgroundColor=J(k),$.textContent=P>1?`${k} (${P})`:k,F.appendChild($)}D.appendChild(F),t.appendChild(D);const X=oe("Selected Nodes"),W=document.createElement("ul");W.className="info-connections";for(const k of C){const P=document.createElement("li");P.className="info-connection",o&&(P.classList.add("info-connection-link"),P.addEventListener("click",()=>{m(k.id)}));const $=document.createElement("span");$.className="info-target-dot",$.style.backgroundColor=J(k.type);const r=document.createElement("span");r.className="info-target",r.textContent=ne(k);const c=document.createElement("span");c.className="info-edge-type",c.textContent=k.type,P.appendChild($),P.appendChild(r),P.appendChild(c),W.appendChild(P)}X.appendChild(W),t.appendChild(X);const R=oe(N.length>0?`Connections Between Selected (${N.length})`:"Connections Between Selected");if(N.length===0){const k=document.createElement("p");k.style.cssText="font-size:12px;color:var(--text-dim)",k.textContent="No direct connections between selected nodes",R.appendChild(k)}else{const k=document.createElement("ul");k.className="info-connections";for(const P of N){const $=x.nodes.find(O=>O.id===P.sourceId),r=x.nodes.find(O=>O.id===P.targetId),c=$?ne($):P.sourceId,u=r?ne(r):P.targetId,l=document.createElement("li");if(l.className="info-connection",$){const O=document.createElement("span");O.className="info-target-dot",O.style.backgroundColor=J($.type),l.appendChild(O)}const E=document.createElement("span");E.className="info-target",E.textContent=c;const b=document.createElement("span");b.className="info-arrow",b.textContent="→";const w=document.createElement("span");w.className="info-edge-type",w.textContent=P.type;const T=document.createElement("span");if(T.className="info-arrow",T.textContent="→",l.appendChild(E),l.appendChild(b),l.appendChild(w),l.appendChild(T),r){const O=document.createElement("span");O.className="info-target-dot",O.style.backgroundColor=J(r.type),l.appendChild(O)}const S=document.createElement("span");S.className="info-target",S.textContent=u,l.appendChild(S);const B=Object.keys(P.properties);if(B.length>0){const O=document.createElement("div");O.className="info-edge-props";for(const Q of B){const Z=document.createElement("span");Z.className="info-edge-prop",Z.textContent=`${Q}: ${le(P.properties[Q])}`,O.appendChild(Z)}l.appendChild(O)}k.appendChild(l)}R.appendChild(k)}t.appendChild(R)}return{show(f,x){if(h=x,f.length===1&&!v){const p=f[0];e[i]!==p&&(i<e.length-1&&(e=e.slice(0,i+1)),e.push(p),i=e.length-1)}f.length===1?I(f[0],x):f.length>1&&K(f,x)},hide:g,get visible(){return!t.classList.contains("hidden")}}}function oe(s){const n=document.createElement("div");n.className="info-section";const o=document.createElement("h4");return o.className="info-section-title",o.textContent=s,n.appendChild(o),n}function Pe(s){if(Array.isArray(s)){const o=document.createElement("div");o.className="info-array";for(const t of s){const d=document.createElement("span");d.className="info-tag",d.textContent=String(t),o.appendChild(d)}return o}if(s!==null&&typeof s=="object"){const o=document.createElement("pre");return o.className="info-json",o.textContent=JSON.stringify(s,null,2),o}const n=document.createElement("span");return n.className="info-value",n.textContent=String(s??""),n}function le(s){return Array.isArray(s)?s.map(String).join(", "):s!==null&&typeof s=="object"?JSON.stringify(s):String(s??"")}function Be(s){const n=s.trim();if(n==="true")return!0;if(n==="false")return!1;if(n!==""&&!isNaN(Number(n)))return Number(n);if(n.startsWith("[")&&n.endsWith("]")||n.startsWith("{")&&n.endsWith("}"))try{return JSON.parse(n)}catch{return s}return s}function ge(s){try{return new Date(s).toLocaleString()}catch{return s}}function xe(s){for(const n of Object.values(s.properties))if(typeof n=="string")return n;return s.id}function ye(s,n){const o=n.toLowerCase();if(xe(s).toLowerCase().includes(o)||s.type.toLowerCase().includes(o))return!0;for(const t of Object.values(s.properties))if(typeof t=="string"&&t.toLowerCase().includes(o))return!0;return!1}function Re(s){let n=null,o=null,t=null,d=new Set,e=null;const i=document.createElement("div");i.className="search-overlay hidden";const v=document.createElement("div");v.className="search-input-wrap";const h=document.createElement("input");h.className="search-input",h.type="text",h.placeholder="Search nodes...",h.setAttribute("autocomplete","off"),h.setAttribute("spellcheck","false");const g=document.createElement("kbd");g.className="search-kbd",g.textContent="/",v.appendChild(h),v.appendChild(g);const m=document.createElement("ul");m.className="search-results hidden";const a=document.createElement("div");a.className="type-chips",i.appendChild(v),i.appendChild(m),i.appendChild(a),s.appendChild(i);function y(){if(a.innerHTML="",!n)return;const f=new Map;for(const p of n.nodes)f.set(p.type,(f.get(p.type)??0)+1);const x=[...f.keys()].sort();d=new Set;for(const p of x){const C=document.createElement("button");C.className="type-chip",C.dataset.type=p;const N=document.createElement("span");N.className="type-chip-dot",N.style.backgroundColor=J(p);const D=document.createElement("span");D.textContent=`${p} (${f.get(p)})`,C.appendChild(N),C.appendChild(D),C.addEventListener("click",()=>{d.has(p)?(d.delete(p),C.classList.remove("active")):(d.add(p),C.classList.add("active")),I()}),a.appendChild(C)}}function A(){if(!n)return null;const f=h.value.trim(),x=d.size===0,p=f.length===0;if(p&&x)return null;const C=new Set;for(const N of n.nodes)!x&&!d.has(N.type)||(p||ye(N,f))&&C.add(N.id);return C}function I(){const f=A();o==null||o(f),K()}function K(){m.innerHTML="";const f=h.value.trim();if(!n||f.length===0){m.classList.add("hidden");return}const x=d.size===0,p=[];for(const C of n.nodes)if(!(!x&&!d.has(C.type))&&ye(C,f)&&(p.push(C),p.length>=8))break;if(p.length===0){m.classList.add("hidden");return}for(const C of p){const N=document.createElement("li");N.className="search-result-item";const D=document.createElement("span");D.className="search-result-dot",D.style.backgroundColor=J(C.type);const z=document.createElement("span");z.className="search-result-label";const F=xe(C);z.textContent=F.length>36?F.slice(0,34)+"...":F;const j=document.createElement("span");j.className="search-result-type",j.textContent=C.type,N.appendChild(D),N.appendChild(z),N.appendChild(j),N.addEventListener("click",()=>{t==null||t(C.id),h.value="",m.classList.add("hidden"),I()}),m.appendChild(N)}m.classList.remove("hidden")}return h.addEventListener("input",()=>{e&&clearTimeout(e),e=setTimeout(I,150)}),h.addEventListener("keydown",f=>{if(f.key==="Escape")h.value="",h.blur(),m.classList.add("hidden"),I();else if(f.key==="Enter"){const x=m.querySelector(".search-result-item");x==null||x.click()}}),document.addEventListener("click",f=>{i.contains(f.target)||m.classList.add("hidden")}),h.addEventListener("focus",()=>g.classList.add("hidden")),h.addEventListener("blur",()=>{h.value.length===0&&g.classList.remove("hidden")}),{setOntologyData(f){n=f,h.value="",m.classList.add("hidden"),n&&n.nodes.length>0?(i.classList.remove("hidden"),y()):i.classList.add("hidden")},onFilterChange(f){o=f},onNodeSelect(f){t=f},clear(){h.value="",m.classList.add("hidden"),d.clear(),o==null||o(null)},focus(){h.focus()}}}let _="",L=null;async function $e(){const s=document.getElementById("canvas-container"),n=window.matchMedia("(prefers-color-scheme: dark)"),t=localStorage.getItem("backpack-theme")??(n.matches?"dark":"light");document.documentElement.setAttribute("data-theme",t);const d=document.createElement("button");d.className="theme-toggle",d.textContent=t==="light"?"☾":"☼",d.title="Toggle light/dark mode",d.addEventListener("click",()=>{const y=document.documentElement.getAttribute("data-theme")==="light"?"dark":"light";document.documentElement.setAttribute("data-theme",y),localStorage.setItem("backpack-theme",y),d.textContent=y==="light"?"☾":"☼"}),s.appendChild(d);async function e(){if(!_||!L)return;L.metadata.updatedAt=new Date().toISOString(),await be(_,L),i.loadGraph(L),h.setOntologyData(L);const a=await ae();g.setSummaries(a)}let i;const v=De(s,{onUpdateNode(a,y){if(!L)return;const A=L.nodes.find(I=>I.id===a);A&&(A.properties={...A.properties,...y},A.updatedAt=new Date().toISOString(),e().then(()=>v.show([a],L)))},onChangeNodeType(a,y){if(!L)return;const A=L.nodes.find(I=>I.id===a);A&&(A.type=y,A.updatedAt=new Date().toISOString(),e().then(()=>v.show([a],L)))},onDeleteNode(a){L&&(L.nodes=L.nodes.filter(y=>y.id!==a),L.edges=L.edges.filter(y=>y.sourceId!==a&&y.targetId!==a),e())},onDeleteEdge(a){var A;if(!L)return;const y=(A=L.edges.find(I=>I.id===a))==null?void 0:A.sourceId;L.edges=L.edges.filter(I=>I.id!==a),e().then(()=>{y&&L&&v.show([y],L)})},onAddProperty(a,y,A){if(!L)return;const I=L.nodes.find(K=>K.id===a);I&&(I.properties[y]=A,I.updatedAt=new Date().toISOString(),e().then(()=>v.show([a],L)))}},a=>{i.panToNode(a)});i=Te(s,a=>{a&&a.length>0&&L?v.show(a,L):v.hide()});const h=Re(s);h.onFilterChange(a=>{i.setFilteredNodeIds(a)}),h.onNodeSelect(a=>{i.panToNode(a),L&&v.show([a],L)});const g=we(document.getElementById("sidebar"),{onSelect:async a=>{_=a,g.setActive(a),v.hide(),h.clear(),L=await ce(a),i.loadGraph(L),h.setOntologyData(L)},onRename:async(a,y)=>{await Le(a,y),_===a&&(_=y);const A=await ae();g.setSummaries(A),g.setActive(_),_===y&&(L=await ce(y),i.loadGraph(L),h.setOntologyData(L))}}),m=await ae();g.setSummaries(m),m.length>0&&(_=m[0].name,g.setActive(_),L=await ce(_),i.loadGraph(L),h.setOntologyData(L)),document.addEventListener("keydown",a=>{a.target instanceof HTMLInputElement||a.target instanceof HTMLTextAreaElement||(a.key==="/"||a.key==="k"&&(a.metaKey||a.ctrlKey))&&(a.preventDefault(),h.focus())})}$e();
|
package/dist/index.html
CHANGED
|
@@ -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 Ontology Viewer</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-CCGK0wTJ.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Bm7EACmJ.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="app">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backpack-viewer",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
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.
|
|
26
|
+
"backpack-ontology": "^0.2.8"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/node": "^25.5.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(function(){const o=document.createElement("link").relList;if(o&&o.supports&&o.supports("modulepreload"))return;for(const p of document.querySelectorAll('link[rel="modulepreload"]'))n(p);new MutationObserver(p=>{for(const e of p)if(e.type==="childList")for(const c of e.addedNodes)c.tagName==="LINK"&&c.rel==="modulepreload"&&n(c)}).observe(document,{childList:!0,subtree:!0});function t(p){const e={};return p.integrity&&(e.integrity=p.integrity),p.referrerPolicy&&(e.referrerPolicy=p.referrerPolicy),p.crossOrigin==="use-credentials"?e.credentials="include":p.crossOrigin==="anonymous"?e.credentials="omit":e.credentials="same-origin",e}function n(p){if(p.ep)return;p.ep=!0;const e=t(p);fetch(p.href,e)}})();async function ne(){const a=await fetch("/api/ontologies");return a.ok?a.json():[]}async function oe(a){const o=await fetch(`/api/ontologies/${encodeURIComponent(a)}`);if(!o.ok)throw new Error(`Failed to load ontology: ${a}`);return o.json()}async function be(a,o){if(!(await fetch(`/api/ontologies/${encodeURIComponent(a)}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)})).ok)throw new Error(`Failed to save ontology: ${a}`)}async function Ne(a,o){if(!(await fetch(`/api/ontologies/${encodeURIComponent(a)}/rename`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:o})})).ok)throw new Error(`Failed to rename ontology: ${a}`)}function we(a,o){const t=typeof o=="function"?{onSelect:o}:o,n=document.createElement("h2");n.textContent="Backpack Ontology Viewer";const p=document.createElement("input");p.type="text",p.placeholder="Filter...",p.id="filter";const e=document.createElement("ul");e.id="ontology-list";const c=document.createElement("div");c.className="sidebar-footer",c.innerHTML='<a href="mailto:support@backpackontology.com">support@backpackontology.com</a><span>Feedback & support</span>',a.appendChild(n),a.appendChild(p),a.appendChild(e),a.appendChild(c);let y=[],i="";return p.addEventListener("input",()=>{const u=p.value.toLowerCase();for(const d of y){const s=d.dataset.name??"";d.style.display=s.includes(u)?"":"none"}}),{setSummaries(u){e.innerHTML="",y=u.map(d=>{const s=document.createElement("li");s.className="ontology-item",s.dataset.name=d.name;const r=document.createElement("span");r.className="name",r.textContent=d.name;const N=document.createElement("span");if(N.className="stats",N.textContent=`${d.nodeCount} nodes, ${d.edgeCount} edges`,s.appendChild(r),s.appendChild(N),t.onRename){const x=document.createElement("button");x.className="sidebar-edit-btn",x.textContent="✎",x.title="Rename";const Y=t.onRename;x.addEventListener("click",w=>{w.stopPropagation();const I=document.createElement("input");I.type="text",I.className="sidebar-rename-input",I.value=d.name,r.textContent="",r.appendChild(I),x.style.display="none",I.focus(),I.select();const C=()=>{const f=I.value.trim();f&&f!==d.name?Y(d.name,f):(r.textContent=d.name,x.style.display="")};I.addEventListener("blur",C),I.addEventListener("keydown",f=>{f.key==="Enter"&&I.blur(),f.key==="Escape"&&(I.value=d.name,I.blur())})}),s.appendChild(x)}return s.addEventListener("click",()=>t.onSelect(d.name)),e.appendChild(s),s}),i&&this.setActive(i)},setActive(u){i=u;for(const d of y)d.classList.toggle("active",d.dataset.name===u)}}}const Le=5e3,Se=.005,he=150,de=.9,le=.01,re=30,se=50;function Me(a,o){for(const t of Object.values(a))if(typeof t=="string")return t;return o}function Ie(a){const o=Math.sqrt(a.nodes.length)*he*.5,t=new Map,n=a.nodes.map((e,c)=>{const y=2*Math.PI*c/a.nodes.length,i={id:e.id,x:Math.cos(y)*o,y:Math.sin(y)*o,vx:0,vy:0,label:Me(e.properties,e.id),type:e.type};return t.set(e.id,i),i}),p=a.edges.map(e=>({sourceId:e.sourceId,targetId:e.targetId,type:e.type}));return{nodes:n,edges:p,nodeMap:t}}function Te(a,o){const{nodes:t,edges:n,nodeMap:p}=a;for(let e=0;e<t.length;e++)for(let c=e+1;c<t.length;c++){const y=t[e],i=t[c];let u=i.x-y.x,d=i.y-y.y,s=Math.sqrt(u*u+d*d);s<re&&(s=re);const r=Le*o/(s*s),N=u/s*r,x=d/s*r;y.vx-=N,y.vy-=x,i.vx+=N,i.vy+=x}for(const e of n){const c=p.get(e.sourceId),y=p.get(e.targetId);if(!c||!y)continue;const i=y.x-c.x,u=y.y-c.y,d=Math.sqrt(i*i+u*u);if(d===0)continue;const s=Se*(d-he)*o,r=i/d*s,N=u/d*s;c.vx+=r,c.vy+=N,y.vx-=r,y.vy-=N}for(const e of t)e.vx-=e.x*le*o,e.vy-=e.y*le*o;for(const e of t){e.vx*=de,e.vy*=de;const c=Math.sqrt(e.vx*e.vx+e.vy*e.vy);c>se&&(e.vx=e.vx/c*se,e.vy=e.vy/c*se),e.x+=e.vx,e.y+=e.vy}return o*.995}const pe=["#d4a27f","#c17856","#b07a5e","#d4956b","#a67c5a","#cc9e7c","#c4866a","#cb8e6c","#b8956e","#a88a70","#d9b08c","#c4a882","#e8b898","#b5927a","#a8886e","#d1a990"],ue=new Map;function G(a){const o=ue.get(a);if(o)return o;let t=0;for(let p=0;p<a.length;p++)t=(t<<5)-t+a.charCodeAt(p)|0;const n=pe[Math.abs(t)%pe.length];return ue.set(a,n),n}function X(a){return getComputedStyle(document.documentElement).getPropertyValue(a).trim()}const K=20,Ae=.001;function ke(a,o){const t=a.querySelector("canvas"),n=t.getContext("2d"),p=window.devicePixelRatio||1;let e={x:0,y:0,scale:1},c=null,y=1,i=0,u=new Set,d=null,s=null,r=null;const N=300;function x(){t.width=t.clientWidth*p,t.height=t.clientHeight*p,C()}const Y=new ResizeObserver(x);Y.observe(a),x();function w(m,l){return[m/e.scale+e.x,l/e.scale+e.y]}function I(m,l){if(!c)return null;const[v,S]=w(m,l);for(let E=c.nodes.length-1;E>=0;E--){const B=c.nodes[E],R=v-B.x,H=S-B.y;if(R*R+H*H<=K*K)return B}return null}function C(){if(!c){n.clearRect(0,0,t.width,t.height);return}const m=X("--canvas-edge"),l=X("--canvas-edge-highlight"),v=X("--canvas-edge-dim"),S=X("--canvas-edge-label"),E=X("--canvas-edge-label-highlight"),B=X("--canvas-edge-label-dim"),R=X("--canvas-arrow"),H=X("--canvas-arrow-highlight"),z=X("--canvas-node-label"),F=X("--canvas-node-label-dim"),_=X("--canvas-type-badge"),ge=X("--canvas-type-badge-dim"),Ce=X("--canvas-selection-border"),ve=X("--canvas-node-border");n.save(),n.setTransform(p,0,0,p,0,0),n.clearRect(0,0,t.clientWidth,t.clientHeight),n.save(),n.translate(-e.x*e.scale,-e.y*e.scale),n.scale(e.scale,e.scale);for(const A of c.edges){const q=c.nodeMap.get(A.sourceId),W=c.nodeMap.get(A.targetId);if(!q||!W)continue;const te=d===null||d.has(A.sourceId),Q=d===null||d.has(A.targetId),J=te&&Q;if(d!==null&&!te&&!Q)continue;const U=u.size>0&&(u.has(A.sourceId)||u.has(A.targetId))||d!==null&&J,ie=d!==null&&!J;if(A.sourceId===A.targetId){L(q,A.type,U,m,l,S,E);continue}n.beginPath(),n.moveTo(q.x,q.y),n.lineTo(W.x,W.y),n.strokeStyle=U?l:ie?v:m,n.lineWidth=U?2.5:1.5,n.stroke(),f(q.x,q.y,W.x,W.y,U,R,H);const Ee=(q.x+W.x)/2,xe=(q.y+W.y)/2;n.fillStyle=U?E:ie?B:S,n.font="9px system-ui, sans-serif",n.textAlign="center",n.textBaseline="bottom",n.fillText(A.type,Ee,xe-4)}for(const A of c.nodes){const q=G(A.type),W=u.has(A.id),te=u.size>0&&c.edges.some(U=>u.has(U.sourceId)&&U.targetId===A.id||u.has(U.targetId)&&U.sourceId===A.id),Q=d!==null&&!d.has(A.id),J=Q||u.size>0&&!W&&!te;W&&(n.save(),n.shadowColor=q,n.shadowBlur=20,n.beginPath(),n.arc(A.x,A.y,K+3,0,Math.PI*2),n.fillStyle=q,n.globalAlpha=.3,n.fill(),n.restore()),n.beginPath(),n.arc(A.x,A.y,K,0,Math.PI*2),n.fillStyle=q,n.globalAlpha=Q?.1:J?.3:1,n.fill(),n.strokeStyle=W?Ce:ve,n.lineWidth=W?3:1.5,n.stroke();const ce=A.label.length>24?A.label.slice(0,22)+"...":A.label;n.fillStyle=J?F:z,n.font="11px system-ui, sans-serif",n.textAlign="center",n.textBaseline="top",n.fillText(ce,A.x,A.y+K+4),n.fillStyle=J?ge:_,n.font="9px system-ui, sans-serif",n.textBaseline="bottom",n.fillText(A.type,A.x,A.y-K-3),n.globalAlpha=1}n.restore(),n.restore()}function f(m,l,v,S,E,B,R){const H=Math.atan2(S-l,v-m),z=v-Math.cos(H)*K,F=S-Math.sin(H)*K,_=8;n.beginPath(),n.moveTo(z,F),n.lineTo(z-_*Math.cos(H-.4),F-_*Math.sin(H-.4)),n.lineTo(z-_*Math.cos(H+.4),F-_*Math.sin(H+.4)),n.closePath(),n.fillStyle=E?R:B,n.fill()}function L(m,l,v,S,E,B,R){const H=m.x+K+15,z=m.y-K-15;n.beginPath(),n.arc(H,z,15,0,Math.PI*2),n.strokeStyle=v?E:S,n.lineWidth=v?2.5:1.5,n.stroke(),n.fillStyle=v?R:B,n.font="9px system-ui, sans-serif",n.textAlign="center",n.fillText(l,H,z-18)}function k(){if(!s||!r)return;const m=performance.now()-r.time,l=Math.min(m/N,1),v=1-Math.pow(1-l,3);e.x=r.x+(s.x-r.x)*v,e.y=r.y+(s.y-r.y)*v,C(),l<1?requestAnimationFrame(k):(s=null,r=null)}function $(){!c||y<Ae||(y=Te(c,y),C(),i=requestAnimationFrame($))}let g=!1,M=!1,h=0,O=0;t.addEventListener("mousedown",m=>{g=!0,M=!1,h=m.clientX,O=m.clientY}),t.addEventListener("mousemove",m=>{if(!g)return;const l=m.clientX-h,v=m.clientY-O;(Math.abs(l)>2||Math.abs(v)>2)&&(M=!0),e.x-=l/e.scale,e.y-=v/e.scale,h=m.clientX,O=m.clientY,C()}),t.addEventListener("mouseup",m=>{if(g=!1,M)return;const l=t.getBoundingClientRect(),v=m.clientX-l.left,S=m.clientY-l.top,E=I(v,S),B=m.ctrlKey||m.metaKey;if(E){B?u.has(E.id)?u.delete(E.id):u.add(E.id):u.size===1&&u.has(E.id)?u.clear():(u.clear(),u.add(E.id));const R=[...u];o==null||o(R.length>0?R:null)}else u.clear(),o==null||o(null);C()}),t.addEventListener("mouseleave",()=>{g=!1}),t.addEventListener("wheel",m=>{m.preventDefault();const l=t.getBoundingClientRect(),v=m.clientX-l.left,S=m.clientY-l.top,[E,B]=w(v,S),R=m.ctrlKey?1-m.deltaY*.01:m.deltaY>0?.9:1.1;e.scale=Math.max(.05,Math.min(10,e.scale*R)),e.x=E-v/e.scale,e.y=B-S/e.scale,C()},{passive:!1});let T=[],D=0,j=1;t.addEventListener("touchstart",m=>{m.preventDefault(),T=Array.from(m.touches),T.length===2?(D=P(T[0],T[1]),j=e.scale):T.length===1&&(h=T[0].clientX,O=T[0].clientY)},{passive:!1}),t.addEventListener("touchmove",m=>{m.preventDefault();const l=Array.from(m.touches);if(l.length===2&&T.length===2){const S=P(l[0],l[1])/D;e.scale=Math.max(.05,Math.min(10,j*S)),C()}else if(l.length===1){const v=l[0].clientX-h,S=l[0].clientY-O;e.x-=v/e.scale,e.y-=S/e.scale,h=l[0].clientX,O=l[0].clientY,C()}T=l},{passive:!1});function P(m,l){const v=m.clientX-l.clientX,S=m.clientY-l.clientY;return Math.sqrt(v*v+S*S)}return{loadGraph(m){if(cancelAnimationFrame(i),c=Ie(m),y=1,u=new Set,d=null,e={x:0,y:0,scale:1},c.nodes.length>0){let l=1/0,v=1/0,S=-1/0,E=-1/0;for(const F of c.nodes)F.x<l&&(l=F.x),F.y<v&&(v=F.y),F.x>S&&(S=F.x),F.y>E&&(E=F.y);const B=(l+S)/2,R=(v+E)/2,H=t.clientWidth,z=t.clientHeight;e.x=B-H/2,e.y=R-z/2}$()},setFilteredNodeIds(m){d=m,C()},panToNode(m){if(!c)return;const l=c.nodeMap.get(m);if(!l)return;u=new Set([m]),o==null||o([m]);const v=t.clientWidth,S=t.clientHeight;r={x:e.x,y:e.y,time:performance.now()},s={x:l.x-v/(2*e.scale),y:l.y-S/(2*e.scale)},k()},destroy(){cancelAnimationFrame(i),Y.disconnect()}}}function Z(a){for(const o of Object.values(a.properties))if(typeof o=="string")return o;return a.id}const Oe="✎";function Pe(a,o){const t=document.createElement("div");t.id="info-panel",t.className="info-panel hidden",a.appendChild(t);function n(){t.classList.add("hidden"),t.innerHTML=""}function p(c,y){const i=y.nodes.find(g=>g.id===c);if(!i)return;const u=y.edges.filter(g=>g.sourceId===c||g.targetId===c);t.innerHTML="",t.classList.remove("hidden");const d=document.createElement("button");d.className="info-close",d.textContent="×",d.addEventListener("click",n),t.appendChild(d);const s=document.createElement("div");s.className="info-header";const r=document.createElement("span");if(r.className="info-type-badge",r.textContent=i.type,r.style.backgroundColor=G(i.type),o){r.classList.add("info-editable");const g=document.createElement("button");g.className="info-inline-edit",g.textContent=Oe,g.addEventListener("click",M=>{M.stopPropagation();const h=document.createElement("input");h.type="text",h.className="info-edit-inline-input",h.value=i.type,r.textContent="",r.appendChild(h),h.focus(),h.select();const O=()=>{const T=h.value.trim();T&&T!==i.type?o.onChangeNodeType(c,T):(r.textContent=i.type,r.appendChild(g))};h.addEventListener("blur",O),h.addEventListener("keydown",T=>{T.key==="Enter"&&h.blur(),T.key==="Escape"&&(h.value=i.type,h.blur())})}),r.appendChild(g)}const N=document.createElement("h3");N.className="info-label",N.textContent=Z(i);const x=document.createElement("span");x.className="info-id",x.textContent=i.id,s.appendChild(r),s.appendChild(N),s.appendChild(x),t.appendChild(s);const Y=Object.keys(i.properties),w=ee("Properties");if(Y.length>0){const g=document.createElement("dl");g.className="info-props";for(const M of Y){const h=document.createElement("dt");h.textContent=M;const O=document.createElement("dd");if(o){const T=ae(i.properties[M]),D=document.createElement("input");D.type="text",D.className="info-edit-input",D.value=T,D.addEventListener("keydown",P=>{P.key==="Enter"&&D.blur()}),D.addEventListener("blur",()=>{const P=D.value;P!==T&&o.onUpdateNode(c,{[M]:Be(P)})}),O.appendChild(D);const j=document.createElement("button");j.className="info-delete-prop",j.textContent="×",j.title=`Remove ${M}`,j.addEventListener("click",()=>{const P={...i.properties};delete P[M],o.onUpdateNode(c,P)}),O.appendChild(j)}else O.appendChild(De(i.properties[M]));g.appendChild(h),g.appendChild(O)}w.appendChild(g)}if(o){const g=document.createElement("button");g.className="info-add-btn",g.textContent="+ Add property",g.addEventListener("click",()=>{const M=document.createElement("div");M.className="info-add-row";const h=document.createElement("input");h.type="text",h.className="info-edit-input",h.placeholder="key";const O=document.createElement("input");O.type="text",O.className="info-edit-input",O.placeholder="value";const T=document.createElement("button");T.className="info-add-save",T.textContent="Add",T.addEventListener("click",()=>{h.value&&o.onAddProperty(c,h.value,O.value)}),M.appendChild(h),M.appendChild(O),M.appendChild(T),w.appendChild(M),h.focus()}),w.appendChild(g)}if(t.appendChild(w),u.length>0){const g=ee(`Connections (${u.length})`),M=document.createElement("ul");M.className="info-connections";for(const h of u){const O=h.sourceId===c,T=O?h.targetId:h.sourceId,D=y.nodes.find(E=>E.id===T),j=D?Z(D):T,P=document.createElement("li");if(P.className="info-connection",D){const E=document.createElement("span");E.className="info-target-dot",E.style.backgroundColor=G(D.type),P.appendChild(E)}const m=document.createElement("span");m.className="info-arrow",m.textContent=O?"→":"←";const l=document.createElement("span");l.className="info-edge-type",l.textContent=h.type;const v=document.createElement("span");v.className="info-target",v.textContent=j,P.appendChild(m),P.appendChild(l),P.appendChild(v);const S=Object.keys(h.properties);if(S.length>0){const E=document.createElement("div");E.className="info-edge-props";for(const B of S){const R=document.createElement("span");R.className="info-edge-prop",R.textContent=`${B}: ${ae(h.properties[B])}`,E.appendChild(R)}P.appendChild(E)}if(o){const E=document.createElement("button");E.className="info-delete-edge",E.textContent="×",E.title="Remove connection",E.addEventListener("click",B=>{B.stopPropagation(),o.onDeleteEdge(h.id)}),P.appendChild(E)}M.appendChild(P)}g.appendChild(M),t.appendChild(g)}const I=ee("Timestamps"),C=document.createElement("dl");C.className="info-props";const f=document.createElement("dt");f.textContent="created";const L=document.createElement("dd");L.textContent=me(i.createdAt);const k=document.createElement("dt");k.textContent="updated";const $=document.createElement("dd");if($.textContent=me(i.updatedAt),C.appendChild(f),C.appendChild(L),C.appendChild(k),C.appendChild($),I.appendChild(C),t.appendChild(I),o){const g=document.createElement("div");g.className="info-section info-danger";const M=document.createElement("button");M.className="info-delete-node",M.textContent="Delete node",M.addEventListener("click",()=>{o.onDeleteNode(c),n()}),g.appendChild(M),t.appendChild(g)}}function e(c,y){const i=new Set(c),u=y.nodes.filter(f=>i.has(f.id));if(u.length===0)return;const d=y.edges.filter(f=>i.has(f.sourceId)&&i.has(f.targetId));t.innerHTML="",t.classList.remove("hidden");const s=document.createElement("button");s.className="info-close",s.textContent="×",s.addEventListener("click",n),t.appendChild(s);const r=document.createElement("div");r.className="info-header";const N=document.createElement("h3");N.className="info-label",N.textContent=`${u.length} nodes selected`,r.appendChild(N);const x=document.createElement("div");x.style.cssText="display:flex;flex-wrap:wrap;gap:4px;margin-top:6px";const Y=new Map;for(const f of u)Y.set(f.type,(Y.get(f.type)??0)+1);for(const[f,L]of Y){const k=document.createElement("span");k.className="info-type-badge",k.style.backgroundColor=G(f),k.textContent=L>1?`${f} (${L})`:f,x.appendChild(k)}r.appendChild(x),t.appendChild(r);const w=ee("Selected Nodes"),I=document.createElement("ul");I.className="info-connections";for(const f of u){const L=document.createElement("li");L.className="info-connection";const k=document.createElement("span");k.className="info-target-dot",k.style.backgroundColor=G(f.type);const $=document.createElement("span");$.className="info-target",$.textContent=Z(f);const g=document.createElement("span");g.className="info-edge-type",g.textContent=f.type,L.appendChild(k),L.appendChild($),L.appendChild(g),I.appendChild(L)}w.appendChild(I),t.appendChild(w);const C=ee(d.length>0?`Connections Between Selected (${d.length})`:"Connections Between Selected");if(d.length===0){const f=document.createElement("p");f.style.cssText="font-size:12px;color:var(--text-dim)",f.textContent="No direct connections between selected nodes",C.appendChild(f)}else{const f=document.createElement("ul");f.className="info-connections";for(const L of d){const k=y.nodes.find(l=>l.id===L.sourceId),$=y.nodes.find(l=>l.id===L.targetId),g=k?Z(k):L.sourceId,M=$?Z($):L.targetId,h=document.createElement("li");if(h.className="info-connection",k){const l=document.createElement("span");l.className="info-target-dot",l.style.backgroundColor=G(k.type),h.appendChild(l)}const O=document.createElement("span");O.className="info-target",O.textContent=g;const T=document.createElement("span");T.className="info-arrow",T.textContent="→";const D=document.createElement("span");D.className="info-edge-type",D.textContent=L.type;const j=document.createElement("span");if(j.className="info-arrow",j.textContent="→",h.appendChild(O),h.appendChild(T),h.appendChild(D),h.appendChild(j),$){const l=document.createElement("span");l.className="info-target-dot",l.style.backgroundColor=G($.type),h.appendChild(l)}const P=document.createElement("span");P.className="info-target",P.textContent=M,h.appendChild(P);const m=Object.keys(L.properties);if(m.length>0){const l=document.createElement("div");l.className="info-edge-props";for(const v of m){const S=document.createElement("span");S.className="info-edge-prop",S.textContent=`${v}: ${ae(L.properties[v])}`,l.appendChild(S)}h.appendChild(l)}f.appendChild(h)}C.appendChild(f)}t.appendChild(C)}return{show(c,y){c.length===1?p(c[0],y):c.length>1&&e(c,y)},hide:n,get visible(){return!t.classList.contains("hidden")}}}function ee(a){const o=document.createElement("div");o.className="info-section";const t=document.createElement("h4");return t.className="info-section-title",t.textContent=a,o.appendChild(t),o}function De(a){if(Array.isArray(a)){const t=document.createElement("div");t.className="info-array";for(const n of a){const p=document.createElement("span");p.className="info-tag",p.textContent=String(n),t.appendChild(p)}return t}if(a!==null&&typeof a=="object"){const t=document.createElement("pre");return t.className="info-json",t.textContent=JSON.stringify(a,null,2),t}const o=document.createElement("span");return o.className="info-value",o.textContent=String(a??""),o}function ae(a){return Array.isArray(a)?a.map(String).join(", "):a!==null&&typeof a=="object"?JSON.stringify(a):String(a??"")}function Be(a){const o=a.trim();if(o==="true")return!0;if(o==="false")return!1;if(o!==""&&!isNaN(Number(o)))return Number(o);if(o.startsWith("[")&&o.endsWith("]")||o.startsWith("{")&&o.endsWith("}"))try{return JSON.parse(o)}catch{return a}return a}function me(a){try{return new Date(a).toLocaleString()}catch{return a}}function ye(a){for(const o of Object.values(a.properties))if(typeof o=="string")return o;return a.id}function fe(a,o){const t=o.toLowerCase();if(ye(a).toLowerCase().includes(t)||a.type.toLowerCase().includes(t))return!0;for(const n of Object.values(a.properties))if(typeof n=="string"&&n.toLowerCase().includes(t))return!0;return!1}function Re(a){let o=null,t=null,n=null,p=new Set,e=null;const c=document.createElement("div");c.className="search-overlay hidden";const y=document.createElement("div");y.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 u=document.createElement("kbd");u.className="search-kbd",u.textContent="/",y.appendChild(i),y.appendChild(u);const d=document.createElement("ul");d.className="search-results hidden";const s=document.createElement("div");s.className="type-chips",c.appendChild(y),c.appendChild(d),c.appendChild(s),a.appendChild(c);function r(){if(s.innerHTML="",!o)return;const w=new Map;for(const C of o.nodes)w.set(C.type,(w.get(C.type)??0)+1);const I=[...w.keys()].sort();p=new Set;for(const C of I){const f=document.createElement("button");f.className="type-chip",f.dataset.type=C;const L=document.createElement("span");L.className="type-chip-dot",L.style.backgroundColor=G(C);const k=document.createElement("span");k.textContent=`${C} (${w.get(C)})`,f.appendChild(L),f.appendChild(k),f.addEventListener("click",()=>{p.has(C)?(p.delete(C),f.classList.remove("active")):(p.add(C),f.classList.add("active")),x()}),s.appendChild(f)}}function N(){if(!o)return null;const w=i.value.trim(),I=p.size===0,C=w.length===0;if(C&&I)return null;const f=new Set;for(const L of o.nodes)!I&&!p.has(L.type)||(C||fe(L,w))&&f.add(L.id);return f}function x(){const w=N();t==null||t(w),Y()}function Y(){d.innerHTML="";const w=i.value.trim();if(!o||w.length===0){d.classList.add("hidden");return}const I=p.size===0,C=[];for(const f of o.nodes)if(!(!I&&!p.has(f.type))&&fe(f,w)&&(C.push(f),C.length>=8))break;if(C.length===0){d.classList.add("hidden");return}for(const f of C){const L=document.createElement("li");L.className="search-result-item";const k=document.createElement("span");k.className="search-result-dot",k.style.backgroundColor=G(f.type);const $=document.createElement("span");$.className="search-result-label";const g=ye(f);$.textContent=g.length>36?g.slice(0,34)+"...":g;const M=document.createElement("span");M.className="search-result-type",M.textContent=f.type,L.appendChild(k),L.appendChild($),L.appendChild(M),L.addEventListener("click",()=>{n==null||n(f.id),i.value="",d.classList.add("hidden"),x()}),d.appendChild(L)}d.classList.remove("hidden")}return i.addEventListener("input",()=>{e&&clearTimeout(e),e=setTimeout(x,150)}),i.addEventListener("keydown",w=>{if(w.key==="Escape")i.value="",i.blur(),d.classList.add("hidden"),x();else if(w.key==="Enter"){const I=d.querySelector(".search-result-item");I==null||I.click()}}),document.addEventListener("click",w=>{c.contains(w.target)||d.classList.add("hidden")}),i.addEventListener("focus",()=>u.classList.add("hidden")),i.addEventListener("blur",()=>{i.value.length===0&&u.classList.remove("hidden")}),{setOntologyData(w){o=w,i.value="",d.classList.add("hidden"),o&&o.nodes.length>0?(c.classList.remove("hidden"),r()):c.classList.add("hidden")},onFilterChange(w){t=w},onNodeSelect(w){n=w},clear(){i.value="",d.classList.add("hidden"),p.clear(),t==null||t(null)},focus(){i.focus()}}}let V="",b=null;async function $e(){const a=document.getElementById("canvas-container"),o=window.matchMedia("(prefers-color-scheme: dark)"),n=localStorage.getItem("backpack-theme")??(o.matches?"dark":"light");document.documentElement.setAttribute("data-theme",n);const p=document.createElement("button");p.className="theme-toggle",p.textContent=n==="light"?"☾":"☼",p.title="Toggle light/dark mode",p.addEventListener("click",()=>{const r=document.documentElement.getAttribute("data-theme")==="light"?"dark":"light";document.documentElement.setAttribute("data-theme",r),localStorage.setItem("backpack-theme",r),p.textContent=r==="light"?"☾":"☼"}),a.appendChild(p);async function e(){if(!V||!b)return;b.metadata.updatedAt=new Date().toISOString(),await be(V,b),y.loadGraph(b),i.setOntologyData(b);const s=await ne();u.setSummaries(s)}const c=Pe(a,{onUpdateNode(s,r){if(!b)return;const N=b.nodes.find(x=>x.id===s);N&&(N.properties={...N.properties,...r},N.updatedAt=new Date().toISOString(),e().then(()=>c.show([s],b)))},onChangeNodeType(s,r){if(!b)return;const N=b.nodes.find(x=>x.id===s);N&&(N.type=r,N.updatedAt=new Date().toISOString(),e().then(()=>c.show([s],b)))},onDeleteNode(s){b&&(b.nodes=b.nodes.filter(r=>r.id!==s),b.edges=b.edges.filter(r=>r.sourceId!==s&&r.targetId!==s),e())},onDeleteEdge(s){var N;if(!b)return;const r=(N=b.edges.find(x=>x.id===s))==null?void 0:N.sourceId;b.edges=b.edges.filter(x=>x.id!==s),e().then(()=>{r&&b&&c.show([r],b)})},onAddProperty(s,r,N){if(!b)return;const x=b.nodes.find(Y=>Y.id===s);x&&(x.properties[r]=N,x.updatedAt=new Date().toISOString(),e().then(()=>c.show([s],b)))}}),y=ke(a,s=>{s&&s.length>0&&b?c.show(s,b):c.hide()}),i=Re(a);i.onFilterChange(s=>{y.setFilteredNodeIds(s)}),i.onNodeSelect(s=>{y.panToNode(s),b&&c.show([s],b)});const u=we(document.getElementById("sidebar"),{onSelect:async s=>{V=s,u.setActive(s),c.hide(),i.clear(),b=await oe(s),y.loadGraph(b),i.setOntologyData(b)},onRename:async(s,r)=>{await Ne(s,r),V===s&&(V=r);const N=await ne();u.setSummaries(N),u.setActive(V),V===r&&(b=await oe(r),y.loadGraph(b),i.setOntologyData(b))}}),d=await ne();u.setSummaries(d),d.length>0&&(V=d[0].name,u.setActive(V),b=await oe(V),y.loadGraph(b),i.setOntologyData(b)),document.addEventListener("keydown",s=>{s.target instanceof HTMLInputElement||s.target instanceof HTMLTextAreaElement||(s.key==="/"||s.key==="k"&&(s.metaKey||s.ctrlKey))&&(s.preventDefault(),i.focus())})}$e();
|