superlocalmemory 3.4.16 → 3.4.18

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 (83) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/package.json +1 -3
  3. package/pyproject.toml +10 -1
  4. package/src/superlocalmemory/cli/setup_wizard.py +30 -0
  5. package/src/superlocalmemory/server/routes/entity.py +5 -9
  6. package/src/superlocalmemory/server/routes/helpers.py +120 -15
  7. package/src/superlocalmemory/server/routes/ingest.py +2 -3
  8. package/src/superlocalmemory/server/routes/v3_api.py +42 -2
  9. package/src/superlocalmemory/server/unified_daemon.py +21 -11
  10. package/src/superlocalmemory.egg-info/PKG-INFO +5 -2
  11. package/src/superlocalmemory.egg-info/requires.txt +3 -0
  12. package/docs/ARCHITECTURE.md +0 -149
  13. package/docs/api-reference.md +0 -284
  14. package/docs/auto-memory.md +0 -150
  15. package/docs/cli-reference.md +0 -327
  16. package/docs/cloud-backup.md +0 -174
  17. package/docs/compliance.md +0 -191
  18. package/docs/configuration.md +0 -182
  19. package/docs/getting-started.md +0 -102
  20. package/docs/ide-setup.md +0 -261
  21. package/docs/mcp-tools.md +0 -220
  22. package/docs/migration-from-v2.md +0 -170
  23. package/docs/profiles.md +0 -173
  24. package/docs/screenshots/01-dashboard-main.png +0 -0
  25. package/docs/screenshots/02-knowledge-graph.png +0 -0
  26. package/docs/screenshots/03-math-health.png +0 -0
  27. package/docs/screenshots/03-patterns-learning.png +0 -0
  28. package/docs/screenshots/04-learning-dashboard.png +0 -0
  29. package/docs/screenshots/04-recall-lab.png +0 -0
  30. package/docs/screenshots/05-behavioral-analysis.png +0 -0
  31. package/docs/screenshots/05-trust-dashboard.png +0 -0
  32. package/docs/screenshots/06-graph-communities.png +0 -0
  33. package/docs/screenshots/06-settings.png +0 -0
  34. package/docs/screenshots/07-memories-blurred.png +0 -0
  35. package/docs/skill-evolution.md +0 -256
  36. package/docs/troubleshooting.md +0 -310
  37. package/docs/v2-archive/ACCESSIBILITY.md +0 -291
  38. package/docs/v2-archive/ARCHITECTURE.md +0 -886
  39. package/docs/v2-archive/CLI-COMMANDS-REFERENCE.md +0 -425
  40. package/docs/v2-archive/COMPRESSION-README.md +0 -390
  41. package/docs/v2-archive/FRAMEWORK-INTEGRATIONS.md +0 -300
  42. package/docs/v2-archive/MCP-MANUAL-SETUP.md +0 -775
  43. package/docs/v2-archive/MCP-TROUBLESHOOTING.md +0 -787
  44. package/docs/v2-archive/PATTERN-LEARNING.md +0 -228
  45. package/docs/v2-archive/PROFILES-GUIDE.md +0 -453
  46. package/docs/v2-archive/RESET-GUIDE.md +0 -353
  47. package/docs/v2-archive/SEARCH-ENGINE-V2.2.0.md +0 -749
  48. package/docs/v2-archive/SEARCH-INTEGRATION-GUIDE.md +0 -502
  49. package/docs/v2-archive/UI-SERVER.md +0 -262
  50. package/docs/v2-archive/UNIVERSAL-INTEGRATION.md +0 -488
  51. package/docs/v2-archive/V2.2.0-OPTIONAL-SEARCH.md +0 -666
  52. package/docs/v2-archive/WINDOWS-INSTALL-README.txt +0 -34
  53. package/docs/v2-archive/WINDOWS-POST-INSTALL.txt +0 -45
  54. package/docs/v2-archive/example_graph_usage.py +0 -146
  55. package/ui/index.html +0 -1879
  56. package/ui/js/agents.js +0 -192
  57. package/ui/js/auto-settings.js +0 -399
  58. package/ui/js/behavioral.js +0 -276
  59. package/ui/js/clusters.js +0 -206
  60. package/ui/js/compliance.js +0 -252
  61. package/ui/js/core.js +0 -246
  62. package/ui/js/dashboard.js +0 -110
  63. package/ui/js/events.js +0 -178
  64. package/ui/js/fact-detail.js +0 -92
  65. package/ui/js/feedback.js +0 -333
  66. package/ui/js/graph-core.js +0 -447
  67. package/ui/js/graph-filters.js +0 -220
  68. package/ui/js/graph-interactions.js +0 -351
  69. package/ui/js/graph-ui.js +0 -214
  70. package/ui/js/ide-status.js +0 -102
  71. package/ui/js/init.js +0 -45
  72. package/ui/js/learning.js +0 -435
  73. package/ui/js/lifecycle.js +0 -298
  74. package/ui/js/math-health.js +0 -98
  75. package/ui/js/memories.js +0 -264
  76. package/ui/js/modal.js +0 -357
  77. package/ui/js/patterns.js +0 -93
  78. package/ui/js/profiles.js +0 -236
  79. package/ui/js/recall-lab.js +0 -292
  80. package/ui/js/search.js +0 -59
  81. package/ui/js/settings.js +0 -224
  82. package/ui/js/timeline.js +0 -32
  83. package/ui/js/trust-dashboard.js +0 -73
@@ -1,351 +0,0 @@
1
- // SuperLocalMemory V2.6.5 - Interactive Knowledge Graph - Interactions Module
2
- // Copyright (c) 2026 Varun Pratap Bhardwaj — Elastic License 2.0
3
- // Part of modular graph visualization system (split from monolithic graph-cytoscape.js)
4
-
5
- // ============================================================================
6
- // CYTOSCAPE INTERACTIONS
7
- // ============================================================================
8
-
9
- function addCytoscapeInteractions() {
10
- if (!cy) return;
11
-
12
- // Hover: Show tooltip
13
- cy.on('mouseover', 'node', function(evt) {
14
- const node = evt.target;
15
- const pos = evt.renderedPosition;
16
- showTooltip(node, pos.x, pos.y);
17
-
18
- // Highlight connected nodes
19
- node.addClass('highlighted');
20
- node.connectedEdges().addClass('highlighted');
21
- node.neighborhood('node').addClass('highlighted');
22
-
23
- // Dim others
24
- cy.nodes().not(node).not(node.neighborhood('node')).addClass('dimmed');
25
- cy.edges().not(node.connectedEdges()).addClass('dimmed');
26
- });
27
-
28
- cy.on('mouseout', 'node', function(evt) {
29
- hideTooltip();
30
-
31
- // Remove highlighting
32
- cy.elements().removeClass('highlighted').removeClass('dimmed');
33
- });
34
-
35
- // Single click: Open modal preview (source='graph' for context-aware buttons)
36
- cy.on('tap', 'node', function(evt) {
37
- const node = evt.target;
38
- openMemoryModal(node);
39
- });
40
-
41
- // Double click: Navigate to Memories tab
42
- cy.on('dbltap', 'node', function(evt) {
43
- const node = evt.target;
44
- navigateToMemoryTab(node.data('id'));
45
- });
46
-
47
- // Enable node dragging
48
- cy.on('drag', 'node', function(evt) {
49
- // Node position is automatically updated by Cytoscape
50
- });
51
-
52
- // Save layout when drag ends
53
- cy.on('dragfree', 'node', function(evt) {
54
- saveLayoutPositions();
55
- });
56
-
57
- // Pan & zoom events (for performance monitoring)
58
- let panZoomTimeout;
59
- cy.on('pan zoom', function() {
60
- clearTimeout(panZoomTimeout);
61
- panZoomTimeout = setTimeout(() => {
62
- saveLayoutPositions();
63
- }, 1000);
64
- });
65
-
66
- // Add keyboard navigation
67
- setupKeyboardNavigation();
68
- }
69
-
70
- // ============================================================================
71
- // TOOLTIP
72
- // ============================================================================
73
-
74
- let tooltipTimeout;
75
- function showTooltip(node, x, y) {
76
- clearTimeout(tooltipTimeout);
77
- tooltipTimeout = setTimeout(() => {
78
- let tooltip = document.getElementById('graph-tooltip');
79
- if (!tooltip) {
80
- tooltip = document.createElement('div');
81
- tooltip.id = 'graph-tooltip';
82
- tooltip.style.cssText = 'position:fixed; background:#333; color:#fff; padding:10px; border-radius:6px; font-size:12px; max-width:300px; z-index:10000; pointer-events:none; box-shadow: 0 4px 12px rgba(0,0,0,0.3);';
83
- document.body.appendChild(tooltip);
84
- }
85
-
86
- // Build tooltip content safely (no innerHTML)
87
- tooltip.textContent = ''; // Clear
88
- const data = node.data();
89
-
90
- const title = document.createElement('strong');
91
- title.textContent = data.label;
92
- tooltip.appendChild(title);
93
-
94
- tooltip.appendChild(document.createElement('br'));
95
-
96
- const meta = document.createElement('span');
97
- meta.style.color = '#aaa';
98
- meta.textContent = `Cluster ${data.cluster_id} • Importance ${data.importance}`;
99
- tooltip.appendChild(meta);
100
-
101
- tooltip.appendChild(document.createElement('br'));
102
-
103
- const preview = document.createElement('span');
104
- preview.style.cssText = 'font-size:11px; color:#ccc;';
105
- preview.textContent = data.content_preview;
106
- tooltip.appendChild(preview);
107
-
108
- tooltip.style.display = 'block';
109
- tooltip.style.left = (x + 20) + 'px';
110
- tooltip.style.top = (y - 20) + 'px';
111
- }, 200);
112
- }
113
-
114
- function hideTooltip() {
115
- clearTimeout(tooltipTimeout);
116
- const tooltip = document.getElementById('graph-tooltip');
117
- if (tooltip) {
118
- tooltip.style.display = 'none';
119
- }
120
- }
121
-
122
- // ============================================================================
123
- // MODAL INTEGRATION
124
- // ============================================================================
125
-
126
- function openMemoryModal(node) {
127
- const memoryData = {
128
- id: node.data('id'),
129
- content: node.data('content'),
130
- summary: node.data('summary'),
131
- category: node.data('category'),
132
- project_name: node.data('project_name'),
133
- cluster_id: node.data('cluster_id'),
134
- importance: node.data('importance'),
135
- tags: node.data('tags'),
136
- created_at: node.data('created_at')
137
- };
138
-
139
- if (typeof openMemoryDetail === 'function') {
140
- openMemoryDetail(memoryData, 'graph'); // source='graph': show Expand Neighbors, hide View Original
141
- }
142
- }
143
-
144
- function navigateToMemoryTab(memoryId) {
145
- // Switch to Memories tab
146
- const memoriesTab = document.querySelector('a[href="#memories"]');
147
- if (memoriesTab) {
148
- memoriesTab.click();
149
- }
150
-
151
- // Scroll to memory after a short delay (for tab to load)
152
- setTimeout(() => {
153
- if (typeof scrollToMemory === 'function') {
154
- scrollToMemory(memoryId);
155
- } else {
156
- console.warn('scrollToMemory function not found in memories.js');
157
- }
158
- }, 300);
159
- }
160
-
161
- // ============================================================================
162
- // KEYBOARD NAVIGATION
163
- // ============================================================================
164
-
165
- function setupKeyboardNavigation() {
166
- if (!cy) return;
167
-
168
- const container = document.getElementById('graph-container');
169
- if (!container) return;
170
-
171
- // Make container focusable
172
- container.setAttribute('tabindex', '0');
173
-
174
- // Focus handler - enable keyboard nav when container is focused
175
- container.addEventListener('focus', function() {
176
- keyboardNavigationEnabled = true;
177
- if (cy.nodes().length > 0) {
178
- focusNodeAtIndex(0);
179
- }
180
- });
181
-
182
- container.addEventListener('blur', function() {
183
- keyboardNavigationEnabled = false;
184
- cy.nodes().removeClass('keyboard-focused');
185
- });
186
-
187
- // Keyboard event handler
188
- container.addEventListener('keydown', function(e) {
189
- if (!keyboardNavigationEnabled || !cy) return;
190
-
191
- const nodes = cy.nodes();
192
- if (nodes.length === 0) return;
193
-
194
- const currentNode = nodes[focusedNodeIndex];
195
-
196
- switch(e.key) {
197
- case 'Tab':
198
- e.preventDefault();
199
- if (e.shiftKey) {
200
- // Shift+Tab: previous node
201
- focusedNodeIndex = (focusedNodeIndex - 1 + nodes.length) % nodes.length;
202
- } else {
203
- // Tab: next node
204
- focusedNodeIndex = (focusedNodeIndex + 1) % nodes.length;
205
- }
206
- focusNodeAtIndex(focusedNodeIndex);
207
- announceNode(nodes[focusedNodeIndex]);
208
- break;
209
-
210
- case 'Enter':
211
- case ' ':
212
- e.preventDefault();
213
- if (currentNode) {
214
- lastFocusedElement = container;
215
- openMemoryModal(currentNode);
216
- }
217
- break;
218
-
219
- case 'ArrowRight':
220
- e.preventDefault();
221
- moveToAdjacentNode('right', currentNode);
222
- break;
223
-
224
- case 'ArrowLeft':
225
- e.preventDefault();
226
- moveToAdjacentNode('left', currentNode);
227
- break;
228
-
229
- case 'ArrowDown':
230
- e.preventDefault();
231
- moveToAdjacentNode('down', currentNode);
232
- break;
233
-
234
- case 'ArrowUp':
235
- e.preventDefault();
236
- moveToAdjacentNode('up', currentNode);
237
- break;
238
-
239
- case 'Escape':
240
- e.preventDefault();
241
- if (filterState.cluster_id || filterState.entity) {
242
- clearGraphFilters();
243
- updateScreenReaderStatus('Filters cleared, showing all memories');
244
- } else {
245
- container.blur();
246
- keyboardNavigationEnabled = false;
247
- }
248
- break;
249
-
250
- case 'Home':
251
- e.preventDefault();
252
- focusedNodeIndex = 0;
253
- focusNodeAtIndex(0);
254
- announceNode(nodes[0]);
255
- break;
256
-
257
- case 'End':
258
- e.preventDefault();
259
- focusedNodeIndex = nodes.length - 1;
260
- focusNodeAtIndex(focusedNodeIndex);
261
- announceNode(nodes[focusedNodeIndex]);
262
- break;
263
- }
264
- });
265
- }
266
-
267
- function focusNodeAtIndex(index) {
268
- if (!cy) return;
269
-
270
- const nodes = cy.nodes();
271
- if (index < 0 || index >= nodes.length) return;
272
-
273
- // Remove focus from all nodes
274
- cy.nodes().removeClass('keyboard-focused');
275
-
276
- // Add focus to target node
277
- const node = nodes[index];
278
- node.addClass('keyboard-focused');
279
-
280
- // Center node in viewport with smooth animation
281
- cy.animate({
282
- center: { eles: node },
283
- zoom: Math.max(cy.zoom(), 1.0),
284
- duration: 300,
285
- easing: 'ease-in-out'
286
- });
287
- }
288
-
289
- function moveToAdjacentNode(direction, currentNode) {
290
- if (!currentNode) return;
291
-
292
- const nodes = cy.nodes();
293
- const currentPos = currentNode.position();
294
- let bestNode = null;
295
- let bestScore = Infinity;
296
-
297
- // Find adjacent nodes based on direction
298
- nodes.forEach((node, index) => {
299
- if (node.id() === currentNode.id()) return;
300
-
301
- const pos = node.position();
302
- const dx = pos.x - currentPos.x;
303
- const dy = pos.y - currentPos.y;
304
- const distance = Math.sqrt(dx * dx + dy * dy);
305
-
306
- let isCorrectDirection = false;
307
- let directionScore = 0;
308
-
309
- switch(direction) {
310
- case 'right':
311
- isCorrectDirection = dx > 0;
312
- directionScore = dx;
313
- break;
314
- case 'left':
315
- isCorrectDirection = dx < 0;
316
- directionScore = -dx;
317
- break;
318
- case 'down':
319
- isCorrectDirection = dy > 0;
320
- directionScore = dy;
321
- break;
322
- case 'up':
323
- isCorrectDirection = dy < 0;
324
- directionScore = -dy;
325
- break;
326
- }
327
-
328
- // Combine distance with direction preference
329
- if (isCorrectDirection) {
330
- const score = distance - (directionScore * 0.5);
331
- if (score < bestScore) {
332
- bestScore = score;
333
- bestNode = node;
334
- focusedNodeIndex = index;
335
- }
336
- }
337
- });
338
-
339
- if (bestNode) {
340
- focusNodeAtIndex(focusedNodeIndex);
341
- announceNode(bestNode);
342
- }
343
- }
344
-
345
- function announceNode(node) {
346
- if (!node) return;
347
-
348
- const data = node.data();
349
- const message = `Memory ${data.id}: ${data.label}, Cluster ${data.cluster_id}, Importance ${data.importance} out of 10`;
350
- updateScreenReaderStatus(message);
351
- }
package/ui/js/graph-ui.js DELETED
@@ -1,214 +0,0 @@
1
- // SuperLocalMemory V2.6.5 - Interactive Knowledge Graph - UI Elements Module
2
- // Copyright (c) 2026 Varun Pratap Bhardwaj — Elastic License 2.0
3
- // Part of modular graph visualization system (split from monolithic graph-cytoscape.js)
4
-
5
- // ============================================================================
6
- // FILTER BADGE UI
7
- // ============================================================================
8
-
9
- function updateFilterBadge() {
10
- const statusFull = document.getElementById('graph-status-full');
11
- const statusFiltered = document.getElementById('graph-status-filtered');
12
- const filterDescription = document.getElementById('graph-filter-description');
13
- const filterCount = document.getElementById('graph-filter-count');
14
- const statusFullText = document.getElementById('graph-status-full-text');
15
-
16
- const hasFilter = filterState.cluster_id || filterState.entity;
17
-
18
- if (hasFilter) {
19
- // FILTERED STATE - Show prominent alert with "Show All Memories" button
20
- if (statusFull) statusFull.style.display = 'none';
21
- if (statusFiltered) statusFiltered.style.display = 'block';
22
-
23
- // Update filter description
24
- if (filterDescription) {
25
- const text = filterState.cluster_id
26
- ? `Viewing Cluster ${filterState.cluster_id}`
27
- : `Viewing: ${filterState.entity}`;
28
- filterDescription.textContent = text;
29
- }
30
-
31
- // Update count (will be set after graph renders)
32
- if (filterCount && graphData && graphData.nodes) {
33
- filterCount.textContent = `(${graphData.nodes.length} ${graphData.nodes.length === 1 ? 'memory' : 'memories'})`;
34
- }
35
-
36
- console.log('[updateFilterBadge] Showing FILTERED state');
37
- } else {
38
- // FULL GRAPH STATE - Show normal status with refresh button
39
- if (statusFull) statusFull.style.display = 'block';
40
- if (statusFiltered) statusFiltered.style.display = 'none';
41
-
42
- // Update count
43
- if (statusFullText && graphData && graphData.nodes) {
44
- const maxNodes = document.getElementById('graph-max-nodes')?.value || 50;
45
- statusFullText.textContent = `Showing ${graphData.nodes.length} of ${maxNodes} memories`;
46
- }
47
-
48
- console.log('[updateFilterBadge] Showing FULL state');
49
- }
50
- }
51
-
52
- // ============================================================================
53
- // GRAPH STATS
54
- // ============================================================================
55
-
56
- function updateGraphStats(data) {
57
- const statsEl = document.getElementById('graph-stats');
58
- if (statsEl) {
59
- // Clear and rebuild safely
60
- statsEl.textContent = '';
61
-
62
- const nodeBadge = document.createElement('span');
63
- nodeBadge.className = 'badge bg-primary';
64
- nodeBadge.textContent = `${data.nodes.length} nodes`;
65
- statsEl.appendChild(nodeBadge);
66
-
67
- const edgeBadge = document.createElement('span');
68
- edgeBadge.className = 'badge bg-secondary';
69
- edgeBadge.textContent = `${data.links.length} edges`;
70
- statsEl.appendChild(document.createTextNode(' '));
71
- statsEl.appendChild(edgeBadge);
72
-
73
- const clusterBadge = document.createElement('span');
74
- clusterBadge.className = 'badge bg-info';
75
- clusterBadge.textContent = `${data.clusters?.length || 0} clusters`;
76
- statsEl.appendChild(document.createTextNode(' '));
77
- statsEl.appendChild(clusterBadge);
78
- }
79
- }
80
-
81
- // ============================================================================
82
- // LOADING SPINNER
83
- // ============================================================================
84
-
85
- function showLoadingSpinner() {
86
- const container = document.getElementById('graph-container');
87
- if (container) {
88
- container.textContent = ''; // Clear safely
89
-
90
- const wrapper = document.createElement('div');
91
- wrapper.style.cssText = 'text-align:center; padding:100px;';
92
-
93
- const spinner = document.createElement('div');
94
- spinner.className = 'spinner-border text-primary';
95
- spinner.setAttribute('role', 'status');
96
- wrapper.appendChild(spinner);
97
-
98
- const text = document.createElement('p');
99
- text.style.marginTop = '20px';
100
- text.textContent = 'Loading graph...';
101
- wrapper.appendChild(text);
102
-
103
- container.appendChild(wrapper);
104
- }
105
- }
106
-
107
- function hideLoadingSpinner() {
108
- // Do nothing - renderGraph() already cleared the spinner
109
- // If we clear here, we destroy the Cytoscape canvas!
110
- console.log('[hideLoadingSpinner] Graph already rendered, spinner cleared by renderGraph()');
111
- }
112
-
113
- function showError(message) {
114
- const container = document.getElementById('graph-container');
115
- if (container) {
116
- container.textContent = ''; // Clear safely
117
-
118
- const alert = document.createElement('div');
119
- alert.className = 'alert alert-danger';
120
- alert.setAttribute('role', 'alert');
121
- alert.style.margin = '50px';
122
- alert.textContent = message;
123
- container.appendChild(alert);
124
- }
125
- }
126
-
127
- // ============================================================================
128
- // LAYOUT MANAGEMENT
129
- // ============================================================================
130
-
131
- function saveLayoutPositions() {
132
- if (!cy) return;
133
-
134
- const positions = {};
135
- cy.nodes().forEach(node => {
136
- positions[node.id()] = node.position();
137
- });
138
-
139
- try {
140
- localStorage.setItem('slm_graph_layout', JSON.stringify(positions));
141
- } catch (e) {
142
- console.warn('Failed to save graph layout:', e);
143
- }
144
- }
145
-
146
- function restoreSavedLayout() {
147
- if (!cy) return;
148
-
149
- try {
150
- const saved = localStorage.getItem('slm_graph_layout');
151
- if (saved) {
152
- const positions = JSON.parse(saved);
153
- cy.nodes().forEach(node => {
154
- const pos = positions[node.id()];
155
- if (pos) {
156
- node.position(pos);
157
- }
158
- });
159
- }
160
- } catch (e) {
161
- console.warn('Failed to restore graph layout:', e);
162
- }
163
- }
164
-
165
- function changeGraphLayout(layoutName) {
166
- if (!cy) return;
167
-
168
- currentLayout = layoutName;
169
- const layout = cy.layout(getLayoutConfig(layoutName));
170
- layout.run();
171
-
172
- // Save preference
173
- localStorage.setItem('slm_graph_layout_preference', layoutName);
174
- }
175
-
176
- // ============================================================================
177
- // EXPAND NEIGHBORS
178
- // ============================================================================
179
-
180
- function expandNeighbors(memoryId) {
181
- if (!cy) return;
182
-
183
- const node = cy.getElementById(String(memoryId));
184
- if (!node || node.length === 0) return;
185
-
186
- // Hide all nodes and edges
187
- cy.elements().addClass('dimmed');
188
-
189
- // Show target node + neighbors + connecting edges
190
- node.removeClass('dimmed');
191
- node.neighborhood().removeClass('dimmed');
192
- node.connectedEdges().removeClass('dimmed');
193
-
194
- // Fit view to visible elements
195
- cy.fit(node.neighborhood().union(node), 50);
196
- }
197
-
198
- // ============================================================================
199
- // SCREEN READER STATUS
200
- // ============================================================================
201
-
202
- function updateScreenReaderStatus(message) {
203
- let statusRegion = document.getElementById('graph-sr-status');
204
- if (!statusRegion) {
205
- statusRegion = document.createElement('div');
206
- statusRegion.id = 'graph-sr-status';
207
- statusRegion.setAttribute('role', 'status');
208
- statusRegion.setAttribute('aria-live', 'polite');
209
- statusRegion.setAttribute('aria-atomic', 'true');
210
- statusRegion.style.cssText = 'position:absolute; left:-10000px; width:1px; height:1px; overflow:hidden;';
211
- document.body.appendChild(statusRegion);
212
- }
213
- statusRegion.textContent = message;
214
- }
@@ -1,102 +0,0 @@
1
- // SuperLocalMemory V3 — IDE Connections
2
- // Displays detected IDEs and allows connecting them to SLM.
3
-
4
- async function loadIDEStatus() {
5
- try {
6
- var response = await fetch('/api/v3/ide/status');
7
- if (!response.ok) return;
8
- var data = await response.json();
9
-
10
- var tbody = document.getElementById('ide-list-body');
11
- tbody.textContent = '';
12
- (data.ides || []).forEach(function(ide) {
13
- var tr = document.createElement('tr');
14
-
15
- // IDE name cell
16
- var tdName = document.createElement('td');
17
- var strong = document.createElement('strong');
18
- strong.textContent = ide.name;
19
- tdName.appendChild(strong);
20
- tr.appendChild(tdName);
21
-
22
- // Installed status cell
23
- var tdInstalled = document.createElement('td');
24
- var badge = document.createElement('span');
25
- if (ide.installed) {
26
- badge.className = 'badge bg-success';
27
- badge.textContent = 'Installed';
28
- } else {
29
- badge.className = 'badge bg-secondary';
30
- badge.textContent = 'Not Found';
31
- }
32
- tdInstalled.appendChild(badge);
33
- tr.appendChild(tdInstalled);
34
-
35
- // Config path cell
36
- var tdPath = document.createElement('td');
37
- tdPath.className = 'text-muted small';
38
- tdPath.textContent = ide.config_path || '';
39
- tr.appendChild(tdPath);
40
-
41
- // Action cell
42
- var tdAction = document.createElement('td');
43
- if (ide.installed) {
44
- var btn = document.createElement('button');
45
- btn.className = 'btn btn-sm btn-outline-primary ide-connect-btn';
46
- btn.dataset.ide = ide.id;
47
- btn.textContent = 'Connect';
48
- tdAction.appendChild(btn);
49
- }
50
- tr.appendChild(tdAction);
51
-
52
- tbody.appendChild(tr);
53
- });
54
- } catch (e) {
55
- console.log('IDE status error:', e);
56
- }
57
- }
58
-
59
- // Delegate click handler for individual IDE connect buttons
60
- document.addEventListener('click', function(e) {
61
- var btn = e.target.closest('.ide-connect-btn');
62
- if (!btn) return;
63
-
64
- var ideId = btn.dataset.ide;
65
- fetch('/api/v3/ide/connect', {
66
- method: 'POST',
67
- headers: { 'Content-Type': 'application/json' },
68
- body: JSON.stringify({ ide: ideId })
69
- }).then(function(r) {
70
- return r.json();
71
- }).then(function(data) {
72
- alert(data.success ? 'Connected: ' + ideId : 'Failed to connect');
73
- loadIDEStatus();
74
- }).catch(function(e) {
75
- console.log('IDE connect error:', e);
76
- });
77
- });
78
-
79
- // Connect all detected IDEs
80
- var connectAllBtn = document.getElementById('ide-connect-all-btn');
81
- if (connectAllBtn) {
82
- connectAllBtn.addEventListener('click', function() {
83
- fetch('/api/v3/ide/connect', {
84
- method: 'POST',
85
- headers: { 'Content-Type': 'application/json' },
86
- body: JSON.stringify({})
87
- }).then(function(r) {
88
- return r.json();
89
- }).then(function(data) {
90
- var results = data.results || {};
91
- var count = Object.values(results).filter(function(s) {
92
- return s === 'connected';
93
- }).length;
94
- alert('Connected ' + count + ' IDEs');
95
- loadIDEStatus();
96
- }).catch(function(e) {
97
- console.log('IDE connect-all error:', e);
98
- });
99
- });
100
- }
101
-
102
- document.getElementById('ide-tab')?.addEventListener('shown.bs.tab', loadIDEStatus);