superlocalmemory 3.4.18 → 3.4.21

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 (172) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +42 -34
  3. package/bin/slm +11 -0
  4. package/bin/slm.bat +12 -0
  5. package/package.json +4 -3
  6. package/pyproject.toml +3 -2
  7. package/scripts/build-slm-hook.ps1 +40 -0
  8. package/scripts/build-slm-hook.sh +45 -0
  9. package/scripts/build_entry.py +452 -0
  10. package/scripts/ci/stage5b_gate.sh +50 -0
  11. package/scripts/postinstall/validation.js +187 -0
  12. package/scripts/postinstall-interactive.js +756 -0
  13. package/scripts/postinstall_binary.js +287 -0
  14. package/scripts/release_manifest.py +273 -0
  15. package/scripts/slm-hook.spec +56 -0
  16. package/skills/slm-build-graph/SKILL.md +423 -0
  17. package/skills/slm-list-recent/SKILL.md +348 -0
  18. package/skills/slm-recall/SKILL.md +343 -0
  19. package/skills/slm-remember/SKILL.md +194 -0
  20. package/skills/slm-show-patterns/SKILL.md +224 -0
  21. package/skills/slm-status/SKILL.md +363 -0
  22. package/skills/slm-switch-profile/SKILL.md +442 -0
  23. package/src/superlocalmemory/cli/commands.py +219 -79
  24. package/src/superlocalmemory/cli/context_commands.py +192 -0
  25. package/src/superlocalmemory/cli/daemon.py +15 -1
  26. package/src/superlocalmemory/cli/db_migrate.py +80 -0
  27. package/src/superlocalmemory/cli/escape_hatch.py +220 -0
  28. package/src/superlocalmemory/cli/main.py +72 -1
  29. package/src/superlocalmemory/core/context_cache.py +397 -0
  30. package/src/superlocalmemory/core/embeddings.py +8 -2
  31. package/src/superlocalmemory/core/engine.py +38 -2
  32. package/src/superlocalmemory/core/engine_wiring.py +1 -1
  33. package/src/superlocalmemory/core/ram_lock.py +111 -0
  34. package/src/superlocalmemory/core/recall_pipeline.py +433 -3
  35. package/src/superlocalmemory/core/recall_worker.py +8 -3
  36. package/src/superlocalmemory/core/security_primitives.py +635 -0
  37. package/src/superlocalmemory/core/shadow_router.py +319 -0
  38. package/src/superlocalmemory/core/slm_disabled.py +87 -0
  39. package/src/superlocalmemory/core/slmignore.py +125 -0
  40. package/src/superlocalmemory/core/topic_signature.py +143 -0
  41. package/src/superlocalmemory/core/worker_pool.py +14 -3
  42. package/src/superlocalmemory/encoding/cognitive_consolidator.py +2 -2
  43. package/src/superlocalmemory/evolution/budget.py +321 -0
  44. package/src/superlocalmemory/evolution/llm_dispatch.py +508 -0
  45. package/src/superlocalmemory/evolution/skill_evolver.py +144 -94
  46. package/src/superlocalmemory/hooks/_outcome_common.py +506 -0
  47. package/src/superlocalmemory/hooks/adapter_base.py +317 -0
  48. package/src/superlocalmemory/hooks/antigravity_adapter.py +192 -0
  49. package/src/superlocalmemory/hooks/claude_code_hooks.py +33 -1
  50. package/src/superlocalmemory/hooks/context_payload.py +312 -0
  51. package/src/superlocalmemory/hooks/copilot_adapter.py +154 -0
  52. package/src/superlocalmemory/hooks/cross_platform_connector.py +90 -0
  53. package/src/superlocalmemory/hooks/cursor_adapter.py +195 -0
  54. package/src/superlocalmemory/hooks/hook_handlers.py +109 -8
  55. package/src/superlocalmemory/hooks/ide_connector.py +25 -2
  56. package/src/superlocalmemory/hooks/post_tool_async_hook.py +165 -0
  57. package/src/superlocalmemory/hooks/post_tool_outcome_hook.py +223 -0
  58. package/src/superlocalmemory/hooks/prewarm_auth.py +170 -0
  59. package/src/superlocalmemory/hooks/session_registry.py +186 -0
  60. package/src/superlocalmemory/hooks/stop_outcome_hook.py +134 -0
  61. package/src/superlocalmemory/hooks/sync_loop.py +114 -0
  62. package/src/superlocalmemory/hooks/user_prompt_hook.py +128 -0
  63. package/src/superlocalmemory/hooks/user_prompt_rehash_hook.py +202 -0
  64. package/src/superlocalmemory/infra/backup.py +3 -3
  65. package/src/superlocalmemory/infra/cloud_backup.py +2 -2
  66. package/src/superlocalmemory/infra/event_bus.py +2 -2
  67. package/src/superlocalmemory/infra/webhook_dispatcher.py +3 -3
  68. package/src/superlocalmemory/learning/arm_catalog.py +99 -0
  69. package/src/superlocalmemory/learning/bandit.py +526 -0
  70. package/src/superlocalmemory/learning/bandit_cache.py +133 -0
  71. package/src/superlocalmemory/learning/behavioral.py +53 -1
  72. package/src/superlocalmemory/learning/consolidation_cycle.py +381 -0
  73. package/src/superlocalmemory/learning/consolidation_worker.py +188 -520
  74. package/src/superlocalmemory/learning/database.py +256 -0
  75. package/src/superlocalmemory/learning/dedup_hnsw.py +413 -0
  76. package/src/superlocalmemory/learning/ensemble.py +300 -0
  77. package/src/superlocalmemory/learning/fact_outcome_joins.py +207 -0
  78. package/src/superlocalmemory/learning/forgetting_scheduler.py +55 -0
  79. package/src/superlocalmemory/learning/hnsw_dedup.py +69 -0
  80. package/src/superlocalmemory/learning/labeler.py +87 -0
  81. package/src/superlocalmemory/learning/legacy_migration.py +277 -0
  82. package/src/superlocalmemory/learning/memory_merge.py +160 -0
  83. package/src/superlocalmemory/learning/model_cache.py +269 -0
  84. package/src/superlocalmemory/learning/model_rollback.py +278 -0
  85. package/src/superlocalmemory/learning/outcome_queue.py +284 -0
  86. package/src/superlocalmemory/learning/pattern_miner.py +415 -0
  87. package/src/superlocalmemory/learning/pattern_miner_constants.py +47 -0
  88. package/src/superlocalmemory/learning/ranker.py +225 -81
  89. package/src/superlocalmemory/learning/ranker_common.py +163 -0
  90. package/src/superlocalmemory/learning/ranker_retrain_legacy.py +202 -0
  91. package/src/superlocalmemory/learning/ranker_retrain_online.py +411 -0
  92. package/src/superlocalmemory/learning/reward.py +777 -0
  93. package/src/superlocalmemory/learning/reward_archive.py +210 -0
  94. package/src/superlocalmemory/learning/reward_boost.py +201 -0
  95. package/src/superlocalmemory/learning/reward_proxy.py +326 -0
  96. package/src/superlocalmemory/learning/shadow_test.py +524 -0
  97. package/src/superlocalmemory/learning/signal_worker.py +270 -0
  98. package/src/superlocalmemory/learning/signals.py +314 -0
  99. package/src/superlocalmemory/learning/trigram_index.py +547 -0
  100. package/src/superlocalmemory/mcp/server.py +5 -5
  101. package/src/superlocalmemory/mcp/tools_context.py +183 -0
  102. package/src/superlocalmemory/mcp/tools_core.py +92 -27
  103. package/src/superlocalmemory/parameterization/soft_prompt_generator.py +13 -0
  104. package/src/superlocalmemory/retrieval/engine.py +52 -0
  105. package/src/superlocalmemory/retrieval/reranker.py +4 -2
  106. package/src/superlocalmemory/server/api.py +2 -2
  107. package/src/superlocalmemory/server/bandit_loops.py +140 -0
  108. package/src/superlocalmemory/server/middleware/__init__.py +11 -0
  109. package/src/superlocalmemory/server/middleware/security_headers.py +144 -0
  110. package/src/superlocalmemory/server/routes/backup.py +36 -13
  111. package/src/superlocalmemory/server/routes/behavioral.py +50 -19
  112. package/src/superlocalmemory/server/routes/brain.py +1234 -0
  113. package/src/superlocalmemory/server/routes/data_io.py +4 -4
  114. package/src/superlocalmemory/server/routes/events.py +2 -2
  115. package/src/superlocalmemory/server/routes/helpers.py +1 -1
  116. package/src/superlocalmemory/server/routes/learning.py +192 -7
  117. package/src/superlocalmemory/server/routes/memories.py +189 -1
  118. package/src/superlocalmemory/server/routes/prewarm.py +171 -0
  119. package/src/superlocalmemory/server/routes/profiles.py +3 -3
  120. package/src/superlocalmemory/server/routes/token.py +88 -0
  121. package/src/superlocalmemory/server/routes/ws.py +5 -5
  122. package/src/superlocalmemory/server/security_middleware.py +13 -7
  123. package/src/superlocalmemory/server/ui.py +2 -2
  124. package/src/superlocalmemory/server/unified_daemon.py +335 -3
  125. package/src/superlocalmemory/storage/migration_runner.py +545 -0
  126. package/src/superlocalmemory/storage/migrations/M001_add_signal_features_columns.py +67 -0
  127. package/src/superlocalmemory/storage/migrations/M002_model_state_history.py +132 -0
  128. package/src/superlocalmemory/storage/migrations/M003_migration_log.py +38 -0
  129. package/src/superlocalmemory/storage/migrations/M004_cross_platform_sync_log.py +46 -0
  130. package/src/superlocalmemory/storage/migrations/M005_bandit_tables.py +75 -0
  131. package/src/superlocalmemory/storage/migrations/M006_action_outcomes_reward.py +75 -0
  132. package/src/superlocalmemory/storage/migrations/M007_pending_outcomes.py +63 -0
  133. package/src/superlocalmemory/storage/migrations/M009_model_lineage.py +54 -0
  134. package/src/superlocalmemory/storage/migrations/M010_evolution_config.py +75 -0
  135. package/src/superlocalmemory/storage/migrations/M011_archive_and_merge.py +87 -0
  136. package/src/superlocalmemory/storage/migrations/M012_shadow_observations.py +72 -0
  137. package/src/superlocalmemory/storage/migrations/M013_bi_temporal_columns.py +55 -0
  138. package/src/superlocalmemory/storage/migrations/__init__.py +81 -0
  139. package/src/superlocalmemory/storage/models.py +4 -0
  140. package/src/superlocalmemory/ui/css/brain.css +409 -0
  141. package/src/superlocalmemory/ui/css/legacy-dashboard.css +645 -0
  142. package/src/superlocalmemory/ui/index.html +459 -1345
  143. package/src/superlocalmemory/ui/js/brain.js +1321 -0
  144. package/src/superlocalmemory/ui/js/clusters.js +123 -4
  145. package/src/superlocalmemory/ui/js/init.js +48 -39
  146. package/src/superlocalmemory/ui/js/memories.js +88 -2
  147. package/src/superlocalmemory/ui/js/modal.js +71 -1
  148. package/src/superlocalmemory/ui/js/ng-shell.js +101 -88
  149. package/src/superlocalmemory/ui/js/trust-dashboard.js +168 -25
  150. package/src/superlocalmemory/ui/vendor/bootstrap-icons/bootstrap-icons.css +2018 -0
  151. package/src/superlocalmemory/ui/vendor/bootstrap-icons/fonts/bootstrap-icons.woff +0 -0
  152. package/src/superlocalmemory/ui/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2 +0 -0
  153. package/src/superlocalmemory/ui/vendor/bootstrap.bundle.min.js +7 -0
  154. package/src/superlocalmemory/ui/vendor/bootstrap.min.css +6 -0
  155. package/src/superlocalmemory/ui/vendor/d3.v7.min.js +2 -0
  156. package/src/superlocalmemory/ui/vendor/graphology-library.min.js +2 -0
  157. package/src/superlocalmemory/ui/vendor/graphology.umd.min.js +2 -0
  158. package/src/superlocalmemory/ui/vendor/inter-ui/inter-variable.min.css +8 -0
  159. package/src/superlocalmemory/ui/vendor/inter-ui/variable/InterVariable-Italic.woff2 +0 -0
  160. package/src/superlocalmemory/ui/vendor/inter-ui/variable/InterVariable.woff2 +0 -0
  161. package/src/superlocalmemory/ui/vendor/sigma.min.js +1 -0
  162. package/src/superlocalmemory/ui/js/behavioral.js +0 -447
  163. package/src/superlocalmemory/ui/js/graph-core.js +0 -447
  164. package/src/superlocalmemory/ui/js/graph-interactions.js +0 -351
  165. package/src/superlocalmemory/ui/js/learning.js +0 -435
  166. package/src/superlocalmemory/ui/js/patterns.js +0 -93
  167. package/src/superlocalmemory.egg-info/PKG-INFO +0 -647
  168. package/src/superlocalmemory.egg-info/SOURCES.txt +0 -335
  169. package/src/superlocalmemory.egg-info/dependency_links.txt +0 -1
  170. package/src/superlocalmemory.egg-info/entry_points.txt +0 -2
  171. package/src/superlocalmemory.egg-info/requires.txt +0 -58
  172. package/src/superlocalmemory.egg-info/top_level.txt +0 -1
@@ -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
- }