superlocalmemory 3.3.29 → 3.4.1

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.
Files changed (69) hide show
  1. package/ATTRIBUTION.md +1 -1
  2. package/CHANGELOG.md +3 -0
  3. package/LICENSE +633 -70
  4. package/README.md +14 -11
  5. package/docs/screenshots/01-dashboard-main.png +0 -0
  6. package/docs/screenshots/02-knowledge-graph.png +0 -0
  7. package/docs/screenshots/03-patterns-learning.png +0 -0
  8. package/docs/screenshots/04-learning-dashboard.png +0 -0
  9. package/docs/screenshots/05-behavioral-analysis.png +0 -0
  10. package/docs/screenshots/06-graph-communities.png +0 -0
  11. package/docs/v2-archive/ACCESSIBILITY.md +1 -1
  12. package/docs/v2-archive/FRAMEWORK-INTEGRATIONS.md +1 -1
  13. package/docs/v2-archive/MCP-MANUAL-SETUP.md +1 -1
  14. package/docs/v2-archive/SEARCH-ENGINE-V2.2.0.md +2 -2
  15. package/docs/v2-archive/SEARCH-INTEGRATION-GUIDE.md +1 -1
  16. package/docs/v2-archive/UNIVERSAL-INTEGRATION.md +1 -1
  17. package/docs/v2-archive/V2.2.0-OPTIONAL-SEARCH.md +1 -1
  18. package/docs/v2-archive/example_graph_usage.py +1 -1
  19. package/ide/configs/codex-mcp.toml +1 -1
  20. package/ide/integrations/langchain/README.md +1 -1
  21. package/ide/integrations/langchain/langchain_superlocalmemory/__init__.py +1 -1
  22. package/ide/integrations/langchain/langchain_superlocalmemory/chat_message_history.py +1 -1
  23. package/ide/integrations/langchain/pyproject.toml +2 -2
  24. package/ide/integrations/langchain/tests/__init__.py +1 -1
  25. package/ide/integrations/langchain/tests/test_chat_message_history.py +1 -1
  26. package/ide/integrations/langchain/tests/test_security.py +1 -1
  27. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/__init__.py +1 -1
  28. package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/base.py +1 -1
  29. package/ide/integrations/llamaindex/pyproject.toml +2 -2
  30. package/ide/integrations/llamaindex/tests/__init__.py +1 -1
  31. package/ide/integrations/llamaindex/tests/test_chat_store.py +1 -1
  32. package/ide/integrations/llamaindex/tests/test_security.py +1 -1
  33. package/ide/skills/slm-build-graph/SKILL.md +3 -3
  34. package/ide/skills/slm-list-recent/SKILL.md +3 -3
  35. package/ide/skills/slm-recall/SKILL.md +3 -3
  36. package/ide/skills/slm-remember/SKILL.md +3 -3
  37. package/ide/skills/slm-show-patterns/SKILL.md +3 -3
  38. package/ide/skills/slm-status/SKILL.md +3 -3
  39. package/ide/skills/slm-switch-profile/SKILL.md +3 -3
  40. package/package.json +3 -3
  41. package/pyproject.toml +3 -3
  42. package/src/superlocalmemory/core/engine_wiring.py +5 -1
  43. package/src/superlocalmemory/core/graph_analyzer.py +254 -12
  44. package/src/superlocalmemory/learning/consolidation_worker.py +240 -52
  45. package/src/superlocalmemory/retrieval/entity_channel.py +135 -4
  46. package/src/superlocalmemory/retrieval/spreading_activation.py +45 -0
  47. package/src/superlocalmemory/server/api.py +9 -1
  48. package/src/superlocalmemory/server/routes/behavioral.py +8 -4
  49. package/src/superlocalmemory/server/routes/chat.py +320 -0
  50. package/src/superlocalmemory/server/routes/insights.py +368 -0
  51. package/src/superlocalmemory/server/routes/learning.py +106 -6
  52. package/src/superlocalmemory/server/routes/memories.py +20 -9
  53. package/src/superlocalmemory/server/routes/stats.py +25 -3
  54. package/src/superlocalmemory/server/routes/timeline.py +252 -0
  55. package/src/superlocalmemory/server/routes/v3_api.py +161 -0
  56. package/src/superlocalmemory/server/ui.py +8 -0
  57. package/src/superlocalmemory/ui/index.html +168 -58
  58. package/src/superlocalmemory/ui/js/graph-event-bus.js +83 -0
  59. package/src/superlocalmemory/ui/js/graph-filters.js +1 -1
  60. package/src/superlocalmemory/ui/js/knowledge-graph.js +942 -0
  61. package/src/superlocalmemory/ui/js/memory-chat.js +344 -0
  62. package/src/superlocalmemory/ui/js/memory-timeline.js +265 -0
  63. package/src/superlocalmemory/ui/js/quick-actions.js +334 -0
  64. package/src/superlocalmemory.egg-info/PKG-INFO +597 -0
  65. package/src/superlocalmemory.egg-info/SOURCES.txt +287 -0
  66. package/src/superlocalmemory.egg-info/dependency_links.txt +1 -0
  67. package/src/superlocalmemory.egg-info/entry_points.txt +2 -0
  68. package/src/superlocalmemory.egg-info/requires.txt +47 -0
  69. package/src/superlocalmemory.egg-info/top_level.txt +1 -0
@@ -654,6 +654,9 @@
654
654
  </button>
655
655
  </div>
656
656
  <span class="text-white-50 d-none d-md-inline" id="navbar-subtitle">Knowledge Graph Explorer</span>
657
+ <a href="https://github.com/qualixar/superlocalmemory" target="_blank" class="btn btn-sm btn-outline-light ms-2 d-none d-md-inline-flex align-items-center" style="font-size:0.75rem; gap:4px;" title="Star us on GitHub">
658
+ <i class="bi bi-github"></i> <i class="bi bi-star"></i> Star
659
+ </a>
657
660
  <button class="btn btn-sm theme-toggle" id="refresh-dashboard-btn" onclick="refreshDashboard()" title="Refresh Dashboard" style="padding:4px 8px;">
658
661
  <i class="bi bi-arrow-clockwise" aria-hidden="true"></i>
659
662
  </button>
@@ -894,7 +897,7 @@
894
897
  <div class="row align-items-center mb-2">
895
898
  <div class="col-md-8">
896
899
  <h5 class="mb-0">Interactive Knowledge Graph</h5>
897
- <small class="text-muted">Zoom, pan, click nodes to explore • Powered by Cytoscape.js</small>
900
+ <small class="text-muted" id="graph-renderer-label">Zoom, pan, click nodes to explore • <span id="graph-engine-name">Sigma.js WebGL</span></small>
898
901
  <div id="graph-stats" class="mt-2"></div>
899
902
  </div>
900
903
  <div class="col-md-4 text-end">
@@ -902,62 +905,161 @@
902
905
  <i class="bi bi-arrow-clockwise"></i> Refresh
903
906
  </button>
904
907
  <select class="form-select form-select-sm d-inline-block w-auto" id="graph-max-nodes" onchange="loadGraph()" aria-label="Select maximum number of nodes to display">
905
- <option value="50" selected>50 nodes</option>
908
+ <option value="50">50 nodes</option>
906
909
  <option value="100">100 nodes</option>
907
- <option value="200">200 nodes</option>
910
+ <option value="200" selected>200 nodes</option>
908
911
  <option value="500">500 nodes</option>
912
+ <option value="1000">1,000 nodes</option>
913
+ <option value="2000">2,000 nodes</option>
914
+ <option value="5000">All (5K+)</option>
909
915
  </select>
910
916
  </div>
911
917
  </div>
912
- <div class="row align-items-center">
913
- <div class="col-md-6">
914
- <label class="form-label mb-1 small" for="graph-layout-selector">Layout Algorithm:</label>
915
- <select class="form-select form-select-sm" id="graph-layout-selector" onchange="changeGraphLayout(this.value)" aria-label="Select graph layout algorithm">
916
- <option value="fcose" selected>Force-Directed (Fast)</option>
917
- <option value="cose">Force-Directed (Classic)</option>
918
- <option value="circle">Circular</option>
919
- <option value="grid">Grid</option>
920
- <option value="breadthfirst">Hierarchical</option>
921
- <option value="concentric">Concentric (by Importance)</option>
922
- </select>
918
+ <!-- Filter status (kept for cluster/modal integration) -->
919
+ <div id="graph-status-container" style="display:none;">
920
+ <div id="graph-status-full"></div>
921
+ <div id="graph-status-filtered"></div>
922
+ </div>
923
+ </div>
924
+ <!-- v3.4.1: Sigma.js controls bar -->
925
+ <div class="row g-2 mb-2" id="sigma-controls">
926
+ <div class="col-12">
927
+ <div class="d-flex align-items-center gap-2">
928
+ <div class="input-group input-group-sm" style="max-width:400px;">
929
+ <span class="input-group-text"><i class="bi bi-search"></i></span>
930
+ <input type="text" class="form-control" id="sigma-search-input"
931
+ placeholder="Search memories in graph..." aria-label="Search graph nodes"
932
+ oninput="sigmaSearch(this.value)">
933
+ <button class="btn btn-outline-secondary" type="button"
934
+ onclick="sigmaClearSearch(); document.getElementById('sigma-search-input').value='';">
935
+ <i class="bi bi-x-lg"></i>
936
+ </button>
937
+ </div>
938
+ <div class="btn-group btn-group-sm">
939
+ <button class="btn btn-outline-secondary" onclick="sigmaZoomIn()" title="Zoom In"><i class="bi bi-zoom-in"></i></button>
940
+ <button class="btn btn-outline-secondary" onclick="sigmaZoomOut()" title="Zoom Out"><i class="bi bi-zoom-out"></i></button>
941
+ <button class="btn btn-outline-secondary" onclick="sigmaResetView()" title="Reset View"><i class="bi bi-arrows-angle-expand"></i></button>
942
+ </div>
923
943
  </div>
924
- <div class="col-md-6">
925
- <!-- Clear status indicator - shows what user is viewing -->
926
- <div id="graph-status-container">
927
- <!-- When viewing FULL graph (default state) -->
928
- <div id="graph-status-full" style="display:block;" role="status" aria-live="polite">
929
- <div class="d-flex align-items-center">
930
- <span class="text-muted small">
931
- <i class="bi bi-diagram-3"></i> <span id="graph-status-full-text">Showing all memories</span>
932
- </span>
933
- <button class="btn btn-sm btn-outline-secondary ms-2" onclick="loadGraph()" aria-label="Refresh current graph view">
934
- <i class="bi bi-arrow-clockwise"></i> Refresh
935
- </button>
936
- </div>
937
- </div>
944
+ </div>
945
+ </div>
938
946
 
939
- <!-- When viewing FILTERED by cluster (show BIG clear button) -->
940
- <div id="graph-status-filtered" style="display:none;" role="status" aria-live="polite">
941
- <div class="alert alert-info py-2 px-3 mb-0 d-flex align-items-center justify-content-between">
942
- <span>
943
- <i class="bi bi-funnel-fill"></i>
944
- <strong id="graph-filter-description">Viewing Cluster X</strong>
945
- <span class="small text-muted ms-2" id="graph-filter-count">(X memories)</span>
946
- </span>
947
- <button class="btn btn-primary btn-sm ms-3" onclick="clearGraphFilters()" aria-label="Clear filter and show all memories">
948
- <i class="bi bi-grid-3x3"></i> Show All Memories
949
- </button>
950
- </div>
947
+ <div class="row g-2">
948
+ <!-- LEFT PANEL: Legend + Communities + Stats -->
949
+ <div class="col-md-2" id="graph-left-panel">
950
+ <div class="card p-2" style="font-size:0.8rem; max-height:550px; overflow-y:auto;">
951
+ <h6 class="mb-2"><i class="bi bi-palette"></i> Legend</h6>
952
+ <div class="mb-1"><span style="display:inline-block;width:12px;height:12px;border-radius:50%;background:#667eea;"></span> Semantic</div>
953
+ <div class="mb-1"><span style="display:inline-block;width:12px;height:12px;border-radius:50%;background:#43e97b;"></span> Episodic</div>
954
+ <div class="mb-1"><span style="display:inline-block;width:12px;height:12px;border-radius:50%;background:#f093fb;"></span> Opinion</div>
955
+ <div class="mb-1"><span style="display:inline-block;width:12px;height:12px;border-radius:50%;background:#4facfe;"></span> Temporal</div>
956
+ <hr class="my-2">
957
+ <h6 class="mb-2 d-flex justify-content-between align-items-center">
958
+ <span><i class="bi bi-collection"></i> Communities</span>
959
+ <div class="btn-group btn-group-sm" role="group" aria-label="Community source">
960
+ <input type="radio" class="btn-check" name="community-source" id="community-live" checked onchange="handleCommunitySourceToggle('live')">
961
+ <label class="btn btn-outline-secondary py-0 px-1" style="font-size:0.65rem;" for="community-live">Live</label>
962
+ <input type="radio" class="btn-check" name="community-source" id="community-backend" onchange="handleCommunitySourceToggle('backend')">
963
+ <label class="btn btn-outline-secondary py-0 px-1" style="font-size:0.65rem;" for="community-backend">Backend</label>
951
964
  </div>
965
+ </h6>
966
+ <div class="mb-1" id="community-resolution-container">
967
+ <label class="form-label small mb-0">Resolution <span id="community-resolution-value" class="text-muted">1.0</span></label>
968
+ <input type="range" class="form-range" id="community-resolution" min="0.1" max="3.0" step="0.1" value="1.0"
969
+ oninput="handleResolutionChange(this.value)">
970
+ </div>
971
+ <div id="community-list-panel">
972
+ <div class="text-muted small">No communities detected yet.</div>
973
+ <button class="btn btn-sm btn-outline-primary w-100 mt-1" onclick="runCommunityDetection()">
974
+ <i class="bi bi-cpu"></i> Detect
975
+ </button>
976
+ </div>
977
+ <hr class="my-2">
978
+ <h6 class="mb-2"><i class="bi bi-graph-up"></i> Stats</h6>
979
+ <div id="sigma-stats-panel">
980
+ <div class="text-muted">Loading...</div>
981
+ </div>
982
+ <hr class="my-2">
983
+ <h6 class="mb-2"><i class="bi bi-funnel"></i> Filter</h6>
984
+ <select class="form-select form-select-sm mb-1" id="sigma-category-filter"
985
+ onchange="sigmaFilterByCategory(this.value)">
986
+ <option value="">All types</option>
987
+ <option value="semantic">Semantic</option>
988
+ <option value="episodic">Episodic</option>
989
+ <option value="opinion">Opinion</option>
990
+ <option value="temporal">Temporal</option>
991
+ </select>
992
+ </div>
993
+ </div>
994
+
995
+ <!-- CENTER: Graph canvas -->
996
+ <div class="col-md-7">
997
+ <div id="graph-container"
998
+ role="application"
999
+ aria-label="Interactive knowledge graph"
1000
+ aria-describedby="graph-stats"
1001
+ style="width:100%; height:550px; border:1px solid #dee2e6; border-radius:8px; background:#fafbfc;">
1002
+ </div>
1003
+ </div>
1004
+
1005
+ <!-- RIGHT PANEL: Node detail -->
1006
+ <div class="col-md-3" id="graph-right-panel">
1007
+ <div class="card p-2" id="sigma-detail-panel" style="font-size:0.85rem; max-height:550px; overflow-y:auto;">
1008
+ <h6 class="mb-2"><i class="bi bi-info-circle"></i> Node Detail</h6>
1009
+ <div id="sigma-detail-content" class="text-muted">
1010
+ Click a node to see its details, connected memories, and trust score.
952
1011
  </div>
953
1012
  </div>
954
1013
  </div>
955
1014
  </div>
956
- <div id="graph-container"
957
- role="application"
958
- aria-label="Interactive knowledge graph - use Tab to navigate nodes, Enter to view details, Arrow keys to move between adjacent nodes, Escape to clear filters"
959
- aria-describedby="graph-stats"
960
- style="width:100%; height:600px; border:1px solid #dee2e6; border-radius:8px; background:#f8f9fa;">
1015
+
1016
+ <!-- v3.4.1: Quick Insight Actions -->
1017
+ <div class="row g-2 mt-2">
1018
+ <div class="col-12">
1019
+ <div class="card p-2">
1020
+ <div class="d-flex flex-wrap gap-1 mb-2">
1021
+ <button class="btn btn-sm btn-outline-info" data-insight-action="changed_this_week">
1022
+ <i class="bi bi-clock-history"></i> Changed This Week
1023
+ </button>
1024
+ <button class="btn btn-sm btn-outline-warning" data-insight-action="opinions">
1025
+ <i class="bi bi-lightbulb"></i> Opinions & Rationale
1026
+ </button>
1027
+ <button class="btn btn-sm btn-outline-danger" data-insight-action="contradictions">
1028
+ <i class="bi bi-exclamation-triangle"></i> Contradictions
1029
+ </button>
1030
+ <button class="btn btn-sm btn-outline-success" data-insight-action="health">
1031
+ <i class="bi bi-heart-pulse"></i> Memory Health
1032
+ </button>
1033
+ <button class="btn btn-sm btn-outline-primary" data-insight-action="cross_project">
1034
+ <i class="bi bi-diagram-3"></i> Cross-Project Links
1035
+ </button>
1036
+ </div>
1037
+ <div id="insight-results" style="max-height:300px; overflow-y:auto;"></div>
1038
+ </div>
1039
+ </div>
1040
+ </div>
1041
+
1042
+ <!-- v3.4.1: Memory Timeline (D3.js) -->
1043
+ <div id="memory-timeline-panel" class="mt-3">
1044
+ <div class="d-flex justify-content-between align-items-center mb-2">
1045
+ <h6 class="mb-0"><i class="bi bi-clock-history"></i> Memory Timeline</h6>
1046
+ <div class="btn-group btn-group-sm" role="group" id="timeline-zoom-group">
1047
+ <button type="button" class="btn btn-outline-secondary active" data-zoom="week">Week</button>
1048
+ <button type="button" class="btn btn-outline-secondary" data-zoom="day">Day</button>
1049
+ <button type="button" class="btn btn-outline-secondary" data-zoom="month">Month</button>
1050
+ </div>
1051
+ <div class="btn-group btn-group-sm ms-2" role="group" id="timeline-group-by-group">
1052
+ <button type="button" class="btn btn-outline-info active" data-groupby="category">By Type</button>
1053
+ <button type="button" class="btn btn-outline-info" data-groupby="community">By Community</button>
1054
+ </div>
1055
+ </div>
1056
+ <div id="memory-timeline-chart" style="width:100%;height:280px;overflow:hidden;border:1px solid #dee2e6;border-radius:8px;background:#fafbfc;"></div>
1057
+ <div class="d-flex gap-3 mt-1 small text-muted">
1058
+ <span><span class="badge" style="background:#198754">&nbsp;</span> High trust (>=0.7)</span>
1059
+ <span><span class="badge" style="background:#ffc107">&nbsp;</span> Medium (0.4-0.7)</span>
1060
+ <span><span class="badge" style="background:#dc3545">&nbsp;</span> Low (<0.4)</span>
1061
+ <span><span class="badge" style="background:#6c757d">&nbsp;</span> Unknown</span>
1062
+ </div>
961
1063
  </div>
962
1064
 
963
1065
  <!-- Skip link for keyboard users -->
@@ -1830,22 +1932,30 @@
1830
1932
  <!-- D3.js (kept for backward compatibility) -->
1831
1933
  <script src="https://d3js.org/d3.v7.min.js"></script>
1832
1934
 
1833
- <!-- Cytoscape.js + Extensions (v2.6.5interactive graph) -->
1834
- <script src="https://unpkg.com/cytoscape@3.30.4/dist/cytoscape.min.js"></script>
1835
- <script src="https://unpkg.com/layout-base@2.0.1/layout-base.js"></script>
1836
- <script src="https://unpkg.com/cose-base@2.2.0/cose-base.js"></script>
1837
- <script src="https://unpkg.com/cytoscape-fcose@2.2.0/cytoscape-fcose.js"></script>
1838
- <link href="https://unpkg.com/cytoscape-navigator@1.3.3/cytoscape.js-navigator.css" rel="stylesheet" type="text/css" />
1839
- <script src="https://unpkg.com/cytoscape-navigator@1.3.3/cytoscape-navigator.js"></script>
1935
+ <!-- Cytoscape.js REMOVED in v3.4.1replaced by Sigma.js WebGL -->
1936
+
1937
+ <!-- Sigma.js v3 + graphology (v3.4.1 — WebGL graph) -->
1938
+ <script src="https://cdn.jsdelivr.net/npm/graphology@0.25.4/dist/graphology.umd.min.js"></script>
1939
+ <script src="https://cdn.jsdelivr.net/npm/graphology-library@0.8.0/dist/graphology-library.min.js"></script>
1940
+ <script src="https://cdn.jsdelivr.net/npm/sigma@2.4.0/build/sigma.min.js"></script>
1840
1941
 
1841
1942
  <!-- Modular JS (v2.5 — split from monolith app.js) -->
1842
1943
  <script src="static/js/core.js"></script>
1843
1944
 
1844
- <!-- Graph Visualization Scripts (v2.6.5 - Modular) -->
1845
- <script src="static/js/graph-core.js"></script>
1846
- <script src="static/js/graph-filters.js"></script>
1945
+ <!-- v3.4.1: Event bus (must load before graph + chat) -->
1946
+ <script src="static/js/graph-event-bus.js"></script>
1947
+ <!-- v3.4.1: Sigma.js Knowledge Graph (defines globals: filterState, loadGraph, renderGraph) -->
1948
+ <script src="static/js/knowledge-graph.js"></script>
1949
+ <!-- Shared UI utilities (showLoadingSpinner, updateGraphStats, etc.) -->
1847
1950
  <script src="static/js/graph-ui.js"></script>
1848
- <script src="static/js/graph-interactions.js"></script>
1951
+ <!-- Filter functions (filterByCluster, filterByEntity — used by clusters.js, modal.js) -->
1952
+ <script src="static/js/graph-filters.js"></script>
1953
+ <!-- v3.4.1: Ask My Memory chat interface -->
1954
+ <script src="static/js/memory-chat.js"></script>
1955
+ <!-- v3.4.1: Quick Insight Actions (5 one-click intelligence buttons) -->
1956
+ <script src="static/js/quick-actions.js"></script>
1957
+ <!-- v3.4.1: Memory Timeline (D3 time axis, zoom, trust colors) -->
1958
+ <script src="static/js/memory-timeline.js"></script>
1849
1959
 
1850
1960
  <script src="static/js/memories.js"></script>
1851
1961
  <script src="static/js/search.js"></script>
@@ -1872,8 +1982,8 @@
1872
1982
  <script src="static/js/fact-detail.js"></script>
1873
1983
 
1874
1984
  <footer>
1875
- <p>SuperLocalMemory V3 by <a href="https://github.com/varun369">Varun Pratap Bhardwaj</a></p>
1876
- <p><a href="https://github.com/qualixar/superlocalmemory">GitHub Repository</a> | Elastic License 2.0</p>
1985
+ <p>SuperLocalMemory V3 by <a href="https://github.com/varun369">Varun Pratap Bhardwaj</a> · <a href="https://qualixar.com">Qualixar</a></p>
1986
+ <p><a href="https://github.com/qualixar/superlocalmemory">GitHub</a> · AGPL-3.0-or-later · <a href="https://github.com/qualixar/superlocalmemory/stargazers">⭐ Star us on GitHub</a></p>
1877
1987
  </footer>
1878
1988
  </body>
1879
1989
  </html>
@@ -0,0 +1,83 @@
1
+ // SuperLocalMemory v3.4.1 — Graph Event Bus
2
+ // Copyright (c) 2026 Varun Pratap Bhardwaj — AGPL-3.0-or-later
3
+ // CustomEvent-based pub/sub for graph ↔ chat bidirectional linking.
4
+ // Renderer-agnostic — works regardless of which graph engine is active.
5
+
6
+ var SLMEventBus = (function() {
7
+ var _debounceTimers = {};
8
+
9
+ function publish(eventName, detail) {
10
+ window.dispatchEvent(new CustomEvent(eventName, {
11
+ detail: Object.freeze(detail || {}),
12
+ }));
13
+ }
14
+
15
+ function publishDebounced(eventName, detail, delayMs) {
16
+ if (_debounceTimers[eventName]) {
17
+ clearTimeout(_debounceTimers[eventName]);
18
+ }
19
+ _debounceTimers[eventName] = setTimeout(function() {
20
+ publish(eventName, detail);
21
+ _debounceTimers[eventName] = null;
22
+ }, delayMs || 200);
23
+ }
24
+
25
+ function subscribe(eventName, callback) {
26
+ window.addEventListener(eventName, function(e) {
27
+ callback(e.detail);
28
+ });
29
+ }
30
+
31
+ return { publish: publish, publishDebounced: publishDebounced, subscribe: subscribe };
32
+ })();
33
+
34
+ // Expose globally
35
+ window.SLMEventBus = SLMEventBus;
36
+
37
+ // ============================================================================
38
+ // EVENT DEFINITIONS
39
+ // ============================================================================
40
+ // slm:graph:nodeClicked — { factId, label } — graph node was clicked
41
+ // slm:graph:highlightNode — { factId } — highlight a node in graph
42
+ // slm:chat:citationClicked — { factId } — citation badge clicked in chat
43
+ // slm:chat:queryAbout — { query } — fill chat input and send
44
+
45
+ // ============================================================================
46
+ // WIRING: Graph → Chat
47
+ // ============================================================================
48
+
49
+ SLMEventBus.subscribe('slm:graph:nodeClicked', function(detail) {
50
+ // Double-click on graph node → fill chat with query about that node
51
+ // (Single click handled by openSigmaNodeDetail in knowledge-graph.js)
52
+ });
53
+
54
+ SLMEventBus.subscribe('slm:chat:queryAbout', function(detail) {
55
+ if (!detail || !detail.query) return;
56
+ var input = document.getElementById('chat-input');
57
+ if (input) {
58
+ input.value = detail.query;
59
+ // Switch to chat panel if on detail panel
60
+ if (typeof showChatPanel === 'function') showChatPanel();
61
+ // Auto-send
62
+ if (typeof sendChatFromInput === 'function') sendChatFromInput();
63
+ }
64
+ });
65
+
66
+ // ============================================================================
67
+ // WIRING: Chat → Graph
68
+ // ============================================================================
69
+
70
+ SLMEventBus.subscribe('slm:chat:citationClicked', function(detail) {
71
+ if (!detail || !detail.factId) return;
72
+ // Highlight the cited node in the graph
73
+ if (typeof sigmaHighlightNode === 'function') {
74
+ sigmaHighlightNode(detail.factId);
75
+ }
76
+ });
77
+
78
+ SLMEventBus.subscribe('slm:graph:highlightNode', function(detail) {
79
+ if (!detail || !detail.factId) return;
80
+ if (typeof sigmaHighlightNode === 'function') {
81
+ sigmaHighlightNode(detail.factId);
82
+ }
83
+ });
@@ -123,7 +123,7 @@ function setupGraphEventListeners() {
123
123
  graphTab.addEventListener('shown.bs.tab', function(event) {
124
124
  console.log('[Event] Knowledge Graph tab SHOWN (tab switch)');
125
125
 
126
- if (cy) {
126
+ if (cy || sigmaInstance) {
127
127
  // Graph already exists - user is returning to KG tab from another tab
128
128
  // Clear filter and reload to show full graph
129
129
  console.log('[Event] Returning to KG tab from another tab - clearing filter');