superlocalmemory 3.4.17 → 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 (78) hide show
  1. package/CHANGELOG.md +8 -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.egg-info/PKG-INFO +4 -1
  6. package/src/superlocalmemory.egg-info/requires.txt +3 -0
  7. package/docs/ARCHITECTURE.md +0 -149
  8. package/docs/api-reference.md +0 -284
  9. package/docs/auto-memory.md +0 -150
  10. package/docs/cli-reference.md +0 -327
  11. package/docs/cloud-backup.md +0 -174
  12. package/docs/compliance.md +0 -191
  13. package/docs/configuration.md +0 -182
  14. package/docs/getting-started.md +0 -102
  15. package/docs/ide-setup.md +0 -261
  16. package/docs/mcp-tools.md +0 -220
  17. package/docs/migration-from-v2.md +0 -170
  18. package/docs/profiles.md +0 -173
  19. package/docs/screenshots/01-dashboard-main.png +0 -0
  20. package/docs/screenshots/02-knowledge-graph.png +0 -0
  21. package/docs/screenshots/03-math-health.png +0 -0
  22. package/docs/screenshots/03-patterns-learning.png +0 -0
  23. package/docs/screenshots/04-learning-dashboard.png +0 -0
  24. package/docs/screenshots/04-recall-lab.png +0 -0
  25. package/docs/screenshots/05-behavioral-analysis.png +0 -0
  26. package/docs/screenshots/05-trust-dashboard.png +0 -0
  27. package/docs/screenshots/06-graph-communities.png +0 -0
  28. package/docs/screenshots/06-settings.png +0 -0
  29. package/docs/screenshots/07-memories-blurred.png +0 -0
  30. package/docs/skill-evolution.md +0 -256
  31. package/docs/troubleshooting.md +0 -310
  32. package/docs/v2-archive/ACCESSIBILITY.md +0 -291
  33. package/docs/v2-archive/ARCHITECTURE.md +0 -886
  34. package/docs/v2-archive/CLI-COMMANDS-REFERENCE.md +0 -425
  35. package/docs/v2-archive/COMPRESSION-README.md +0 -390
  36. package/docs/v2-archive/FRAMEWORK-INTEGRATIONS.md +0 -300
  37. package/docs/v2-archive/MCP-MANUAL-SETUP.md +0 -775
  38. package/docs/v2-archive/MCP-TROUBLESHOOTING.md +0 -787
  39. package/docs/v2-archive/PATTERN-LEARNING.md +0 -228
  40. package/docs/v2-archive/PROFILES-GUIDE.md +0 -453
  41. package/docs/v2-archive/RESET-GUIDE.md +0 -353
  42. package/docs/v2-archive/SEARCH-ENGINE-V2.2.0.md +0 -749
  43. package/docs/v2-archive/SEARCH-INTEGRATION-GUIDE.md +0 -502
  44. package/docs/v2-archive/UI-SERVER.md +0 -262
  45. package/docs/v2-archive/UNIVERSAL-INTEGRATION.md +0 -488
  46. package/docs/v2-archive/V2.2.0-OPTIONAL-SEARCH.md +0 -666
  47. package/docs/v2-archive/WINDOWS-INSTALL-README.txt +0 -34
  48. package/docs/v2-archive/WINDOWS-POST-INSTALL.txt +0 -45
  49. package/docs/v2-archive/example_graph_usage.py +0 -146
  50. package/ui/index.html +0 -1879
  51. package/ui/js/agents.js +0 -192
  52. package/ui/js/auto-settings.js +0 -399
  53. package/ui/js/behavioral.js +0 -276
  54. package/ui/js/clusters.js +0 -206
  55. package/ui/js/compliance.js +0 -252
  56. package/ui/js/core.js +0 -246
  57. package/ui/js/dashboard.js +0 -110
  58. package/ui/js/events.js +0 -178
  59. package/ui/js/fact-detail.js +0 -92
  60. package/ui/js/feedback.js +0 -333
  61. package/ui/js/graph-core.js +0 -447
  62. package/ui/js/graph-filters.js +0 -220
  63. package/ui/js/graph-interactions.js +0 -351
  64. package/ui/js/graph-ui.js +0 -214
  65. package/ui/js/ide-status.js +0 -102
  66. package/ui/js/init.js +0 -45
  67. package/ui/js/learning.js +0 -435
  68. package/ui/js/lifecycle.js +0 -298
  69. package/ui/js/math-health.js +0 -98
  70. package/ui/js/memories.js +0 -264
  71. package/ui/js/modal.js +0 -357
  72. package/ui/js/patterns.js +0 -93
  73. package/ui/js/profiles.js +0 -236
  74. package/ui/js/recall-lab.js +0 -292
  75. package/ui/js/search.js +0 -59
  76. package/ui/js/settings.js +0 -224
  77. package/ui/js/timeline.js +0 -32
  78. package/ui/js/trust-dashboard.js +0 -73
@@ -1,298 +0,0 @@
1
- // SPDX-License-Identifier: Elastic-2.0
2
- // Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
3
- // Lifecycle tab — state distribution, compaction, transitions (v2.8)
4
- // NOTE: All dynamic values pass through escapeHtml() or textContent for DOM insertion.
5
-
6
- var _lifecycleData = null;
7
-
8
- async function loadLifecycle() {
9
- try {
10
- var response = await fetch('/api/lifecycle/status');
11
- var data = await response.json();
12
- _lifecycleData = data;
13
-
14
- if (!data.available) {
15
- showEmpty('lifecycle-states-row', 'hourglass-split', 'Lifecycle engine not available. Upgrade to v2.8.');
16
- return;
17
- }
18
-
19
- renderLifecycleStates(data);
20
- renderLifecycleProgress(data);
21
- renderLifecycleAgeStats(data);
22
- renderLifecycleTransitions(data);
23
-
24
- var badge = document.getElementById('lifecycle-profile-badge');
25
- if (badge) badge.textContent = data.active_profile || 'default';
26
- } catch (error) {
27
- console.error('Error loading lifecycle:', error);
28
- }
29
- }
30
-
31
- function renderLifecycleStates(data) {
32
- var states = data.states || {};
33
- var mapping = {
34
- active: 'lc-active-count',
35
- warm: 'lc-warm-count',
36
- cold: 'lc-cold-count',
37
- archived: 'lc-archived-count',
38
- tombstoned: 'lc-tombstoned-count'
39
- };
40
- for (var state in mapping) {
41
- animateCounter(mapping[state], states[state] || 0);
42
- }
43
- animateCounter('lc-total-count', data.total_memories || 0);
44
- }
45
-
46
- function renderLifecycleProgress(data) {
47
- var bar = document.getElementById('lifecycle-progress-bar');
48
- if (!bar) return;
49
- var states = data.states || {};
50
- var total = data.total_memories || 1;
51
- var colors = {
52
- active: '#198754',
53
- warm: '#ffc107',
54
- cold: '#0dcaf0',
55
- archived: '#6c757d',
56
- tombstoned: '#dc3545'
57
- };
58
-
59
- bar.textContent = '';
60
- var hasSegments = false;
61
-
62
- var stateKeys = ['active', 'warm', 'cold', 'archived', 'tombstoned'];
63
- for (var i = 0; i < stateKeys.length; i++) {
64
- var state = stateKeys[i];
65
- var count = states[state] || 0;
66
- if (count > 0) {
67
- hasSegments = true;
68
- var pct = ((count / total) * 100).toFixed(1);
69
- var segment = document.createElement('div');
70
- segment.className = 'progress-bar';
71
- segment.setAttribute('role', 'progressbar');
72
- segment.style.width = pct + '%';
73
- segment.style.backgroundColor = colors[state];
74
- segment.title = state + ': ' + count + ' (' + pct + '%)';
75
- segment.textContent = state;
76
- bar.appendChild(segment);
77
- }
78
- }
79
-
80
- if (!hasSegments) {
81
- var fallback = document.createElement('div');
82
- fallback.className = 'progress-bar bg-success';
83
- fallback.style.width = '100%';
84
- fallback.textContent = 'All Active';
85
- bar.appendChild(fallback);
86
- }
87
- }
88
-
89
- function renderLifecycleAgeStats(data) {
90
- var container = document.getElementById('lifecycle-age-content');
91
- if (!container) return;
92
- var stats = data.age_stats || {};
93
- if (Object.keys(stats).length === 0) {
94
- container.textContent = '';
95
- var empty = document.createElement('span');
96
- empty.className = 'text-muted';
97
- empty.textContent = 'No age data available yet.';
98
- container.appendChild(empty);
99
- return;
100
- }
101
-
102
- var table = document.createElement('table');
103
- table.className = 'table table-sm table-hover mb-0';
104
- var thead = document.createElement('thead');
105
- var headRow = document.createElement('tr');
106
- ['State', 'Avg Age', 'Newest', 'Oldest'].forEach(function(h) {
107
- var th = document.createElement('th');
108
- th.textContent = h;
109
- headRow.appendChild(th);
110
- });
111
- thead.appendChild(headRow);
112
- table.appendChild(thead);
113
-
114
- var tbody = document.createElement('tbody');
115
- var stateOrder = ['active', 'warm', 'cold', 'archived'];
116
- var badgeColors = { active: 'success', warm: 'warning', cold: 'info', archived: 'secondary' };
117
-
118
- for (var i = 0; i < stateOrder.length; i++) {
119
- var s = stateOrder[i];
120
- if (stats[s]) {
121
- var row = document.createElement('tr');
122
-
123
- var stateCell = document.createElement('td');
124
- var stateBadge = document.createElement('span');
125
- stateBadge.className = 'badge bg-' + (badgeColors[s] || 'secondary');
126
- stateBadge.textContent = s;
127
- stateCell.appendChild(stateBadge);
128
- row.appendChild(stateCell);
129
-
130
- var avgCell = document.createElement('td');
131
- avgCell.textContent = (stats[s].avg_days || 0) + 'd';
132
- row.appendChild(avgCell);
133
-
134
- var minCell = document.createElement('td');
135
- minCell.textContent = (stats[s].min_days || 0) + 'd';
136
- row.appendChild(minCell);
137
-
138
- var maxCell = document.createElement('td');
139
- maxCell.textContent = (stats[s].max_days || 0) + 'd';
140
- row.appendChild(maxCell);
141
-
142
- tbody.appendChild(row);
143
- }
144
- }
145
- table.appendChild(tbody);
146
-
147
- container.textContent = '';
148
- container.appendChild(table);
149
- }
150
-
151
- function renderLifecycleTransitions(data) {
152
- var container = document.getElementById('lifecycle-transitions-content');
153
- if (!container) return;
154
- var transitions = data.recent_transitions || [];
155
- if (transitions.length === 0) {
156
- container.textContent = '';
157
- var empty = document.createElement('span');
158
- empty.className = 'text-muted';
159
- empty.textContent = 'No transitions yet. Memories start as Active and transition based on usage.';
160
- container.appendChild(empty);
161
- return;
162
- }
163
-
164
- var table = document.createElement('table');
165
- table.className = 'table table-sm table-hover mb-0';
166
- var thead = document.createElement('thead');
167
- var headRow = document.createElement('tr');
168
- ['Memory', 'State', 'Last Transition'].forEach(function(h) {
169
- var th = document.createElement('th');
170
- th.textContent = h;
171
- headRow.appendChild(th);
172
- });
173
- thead.appendChild(headRow);
174
- table.appendChild(thead);
175
-
176
- var tbody = document.createElement('tbody');
177
- for (var i = 0; i < transitions.length; i++) {
178
- var t = transitions[i];
179
- var row = document.createElement('tr');
180
-
181
- var memCell = document.createElement('td');
182
- var preview = (t.content_preview || '').substring(0, 40);
183
- memCell.textContent = '#' + t.memory_id + ' ' + preview + (preview.length >= 40 ? '...' : '');
184
- memCell.title = t.content_preview || '';
185
- row.appendChild(memCell);
186
-
187
- var stateCell = document.createElement('td');
188
- var badge = document.createElement('span');
189
- badge.className = 'badge bg-secondary';
190
- badge.textContent = t.current_state || '';
191
- stateCell.appendChild(badge);
192
- row.appendChild(stateCell);
193
-
194
- var transCell = document.createElement('td');
195
- transCell.className = 'small text-muted';
196
- transCell.textContent = JSON.stringify(t.last_transition || {});
197
- row.appendChild(transCell);
198
-
199
- tbody.appendChild(row);
200
- }
201
- table.appendChild(tbody);
202
-
203
- container.textContent = '';
204
- container.appendChild(table);
205
- }
206
-
207
- async function compactDryRun() {
208
- try {
209
- var response = await fetch('/api/lifecycle/compact', {
210
- method: 'POST',
211
- headers: { 'Content-Type': 'application/json' },
212
- body: JSON.stringify({ dry_run: true })
213
- });
214
- var data = await response.json();
215
- var resultsDiv = document.getElementById('compaction-results');
216
- var titleEl = document.getElementById('compaction-results-title');
217
- var contentEl = document.getElementById('compaction-results-content');
218
- resultsDiv.classList.remove('d-none');
219
- titleEl.textContent = 'Compaction Preview (Dry Run)';
220
- contentEl.textContent = '';
221
-
222
- if (data.recommendations === 0) {
223
- var ok = document.createElement('span');
224
- ok.className = 'text-success';
225
- var icon = document.createElement('i');
226
- icon.className = 'bi bi-check-circle';
227
- ok.appendChild(icon);
228
- ok.appendChild(document.createTextNode(' No compaction needed. All memories are in optimal states.'));
229
- contentEl.appendChild(ok);
230
- } else {
231
- var p = document.createElement('p');
232
- p.className = 'mb-2';
233
- p.textContent = data.recommendations + ' memories would be transitioned:';
234
- contentEl.appendChild(p);
235
-
236
- var table = document.createElement('table');
237
- table.className = 'table table-sm mb-0';
238
- var thead = document.createElement('thead');
239
- var headRow = document.createElement('tr');
240
- ['Memory ID', 'From', 'To'].forEach(function(h) {
241
- var th = document.createElement('th');
242
- th.textContent = h;
243
- headRow.appendChild(th);
244
- });
245
- thead.appendChild(headRow);
246
- table.appendChild(thead);
247
-
248
- var tbody = document.createElement('tbody');
249
- var details = data.details || [];
250
- for (var i = 0; i < details.length; i++) {
251
- var row = document.createElement('tr');
252
- var idCell = document.createElement('td');
253
- idCell.textContent = '#' + details[i].memory_id;
254
- row.appendChild(idCell);
255
- var fromCell = document.createElement('td');
256
- fromCell.textContent = details[i].from || '';
257
- row.appendChild(fromCell);
258
- var toCell = document.createElement('td');
259
- toCell.textContent = details[i].to || '';
260
- row.appendChild(toCell);
261
- tbody.appendChild(row);
262
- }
263
- table.appendChild(tbody);
264
- contentEl.appendChild(table);
265
- }
266
- } catch (e) {
267
- console.error('Compaction preview error:', e);
268
- }
269
- }
270
-
271
- async function compactExecute() {
272
- if (!confirm('This will transition memories to lower lifecycle states. Continue?')) return;
273
- try {
274
- var response = await fetch('/api/lifecycle/compact', {
275
- method: 'POST',
276
- headers: { 'Content-Type': 'application/json' },
277
- body: JSON.stringify({ dry_run: false })
278
- });
279
- var data = await response.json();
280
- var resultsDiv = document.getElementById('compaction-results');
281
- var contentEl = document.getElementById('compaction-results-content');
282
- resultsDiv.classList.remove('d-none');
283
- document.getElementById('compaction-results-title').textContent = 'Compaction Results';
284
-
285
- contentEl.textContent = '';
286
- var ok = document.createElement('span');
287
- ok.className = 'text-success';
288
- var icon = document.createElement('i');
289
- icon.className = 'bi bi-check-circle';
290
- ok.appendChild(icon);
291
- ok.appendChild(document.createTextNode(' ' + (data.transitioned || 0) + ' memories transitioned successfully.'));
292
- contentEl.appendChild(ok);
293
-
294
- loadLifecycle(); // Refresh
295
- } catch (e) {
296
- console.error('Compaction error:', e);
297
- }
298
- }
@@ -1,98 +0,0 @@
1
- // SuperLocalMemory V3 — Math Health
2
- // Displays status of Fisher-Rao, sheaf cohomology, and Langevin dynamics layers.
3
-
4
- async function loadMathHealth() {
5
- try {
6
- var response = await fetch('/api/v3/math/health');
7
- if (!response.ok) return;
8
- var data = await response.json();
9
-
10
- var container = document.getElementById('math-health-cards');
11
- container.textContent = '';
12
-
13
- var layers = data.health || {};
14
- var colors = { fisher: 'primary', sheaf: 'success', langevin: 'info' };
15
- var icons = { fisher: 'bi-graph-up', sheaf: 'bi-diagram-3', langevin: 'bi-activity' };
16
-
17
- Object.keys(layers).forEach(function(key) {
18
- var layer = layers[key];
19
- var col = document.createElement('div');
20
- col.className = 'col-md-4';
21
-
22
- var card = document.createElement('div');
23
- card.className = 'card h-100';
24
-
25
- // Card header
26
- var header = document.createElement('div');
27
- header.className = 'card-header bg-' + (colors[key] || 'secondary') + ' text-white';
28
- var h6 = document.createElement('h6');
29
- h6.className = 'mb-0';
30
- var icon = document.createElement('i');
31
- icon.className = 'bi ' + (icons[key] || 'bi-gear');
32
- h6.appendChild(icon);
33
- h6.appendChild(document.createTextNode(' ' + key.charAt(0).toUpperCase() + key.slice(1)));
34
- header.appendChild(h6);
35
- card.appendChild(header);
36
-
37
- // Card body
38
- var body = document.createElement('div');
39
- body.className = 'card-body';
40
-
41
- var desc = document.createElement('p');
42
- desc.className = 'text-muted';
43
- desc.textContent = layer.description || '';
44
- body.appendChild(desc);
45
-
46
- var ul = document.createElement('ul');
47
- ul.className = 'list-unstyled mb-0';
48
-
49
- // Status item
50
- var liStatus = document.createElement('li');
51
- liStatus.appendChild(document.createTextNode('Status: '));
52
- var statusBadge = document.createElement('span');
53
- statusBadge.className = 'badge bg-success';
54
- statusBadge.textContent = layer.status || 'active';
55
- liStatus.appendChild(statusBadge);
56
- ul.appendChild(liStatus);
57
-
58
- // Mode item (if present)
59
- if (layer.mode) {
60
- var liMode = document.createElement('li');
61
- liMode.appendChild(document.createTextNode('Mode: '));
62
- var modeStrong = document.createElement('strong');
63
- modeStrong.textContent = layer.mode;
64
- liMode.appendChild(modeStrong);
65
- ul.appendChild(liMode);
66
- }
67
-
68
- // Threshold item (if present)
69
- if (layer.threshold) {
70
- var liThresh = document.createElement('li');
71
- liThresh.appendChild(document.createTextNode('Threshold: '));
72
- var threshStrong = document.createElement('strong');
73
- threshStrong.textContent = layer.threshold;
74
- liThresh.appendChild(threshStrong);
75
- ul.appendChild(liThresh);
76
- }
77
-
78
- // Temperature item (if present)
79
- if (layer.temperature) {
80
- var liTemp = document.createElement('li');
81
- liTemp.appendChild(document.createTextNode('Temperature: '));
82
- var tempStrong = document.createElement('strong');
83
- tempStrong.textContent = layer.temperature;
84
- liTemp.appendChild(tempStrong);
85
- ul.appendChild(liTemp);
86
- }
87
-
88
- body.appendChild(ul);
89
- card.appendChild(body);
90
- col.appendChild(card);
91
- container.appendChild(col);
92
- });
93
- } catch (e) {
94
- console.log('Math health error:', e);
95
- }
96
- }
97
-
98
- document.getElementById('math-health-tab')?.addEventListener('shown.bs.tab', loadMathHealth);
package/ui/js/memories.js DELETED
@@ -1,264 +0,0 @@
1
- // SuperLocalMemory V2 - Memories List + Sorting
2
- // Depends on: core.js, modal.js (openMemoryDetail)
3
- //
4
- // Security: All dynamic values are escaped via escapeHtml() before DOM insertion.
5
- // The innerHTML usage in renderMemoriesTable is safe because every interpolated
6
- // value passes through escapeHtml(). Data comes from our own local SQLite DB only.
7
- // nosemgrep: innerHTML-xss — all dynamic values escaped via escapeHtml()
8
-
9
- async function loadMemories() {
10
- var category = document.getElementById('filter-category').value;
11
- var project = document.getElementById('filter-project').value;
12
- var url = '/api/memories?limit=50';
13
- if (category) url += '&category=' + encodeURIComponent(category);
14
- if (project) url += '&project_name=' + encodeURIComponent(project);
15
-
16
- showLoading('memories-list', 'Loading memories...');
17
- try {
18
- var response = await fetch(url);
19
- var data = await response.json();
20
- lastSearchResults = null;
21
- var exportBtn = document.getElementById('export-search-btn');
22
- if (exportBtn) exportBtn.style.display = 'none';
23
- renderMemoriesTable(data.memories, false);
24
- } catch (error) {
25
- console.error('Error loading memories:', error);
26
- showEmpty('memories-list', 'exclamation-triangle', 'Failed to load memories');
27
- }
28
- }
29
-
30
- function renderMemoriesTable(memories, showScores) {
31
- var container = document.getElementById('memories-list');
32
- if (!memories || memories.length === 0) {
33
- showEmpty('memories-list', 'journal-x', 'No memories found. Try a different search or filter.');
34
- return;
35
- }
36
-
37
- window._slmMemories = memories;
38
- var scoreHeader = showScores ? '<th>Score</th>' : '';
39
-
40
- var rows = '';
41
- memories.forEach(function(mem, idx) {
42
- var content = mem.summary || mem.content || '';
43
- var contentPreview = content.length > 100 ? content.substring(0, 100) + '...' : content;
44
- var importance = mem.importance || 5;
45
- var importanceClass = importance >= 8 ? 'success' : importance >= 5 ? 'warning' : 'secondary';
46
-
47
- var scoreCell = '';
48
- if (showScores) {
49
- var score = mem.score || 0;
50
- var pct = Math.round(score * 100);
51
- var barColor = pct >= 70 ? '#43e97b' : pct >= 40 ? '#f9c74f' : '#f94144';
52
- scoreCell = '<td><span class="score-label">' + escapeHtml(String(pct)) + '%</span>'
53
- + '<div class="score-bar-container"><div class="score-bar">'
54
- + '<div class="score-bar-fill" style="width:' + pct + '%;background:' + barColor + '"></div>'
55
- + '</div></div></td>';
56
- }
57
-
58
- var memId = mem.memory_id || mem.id;
59
- var expandBtnHtml = '<button class="btn btn-sm btn-outline-secondary expand-facts-btn ms-1" data-memory-id="' + escapeHtml(String(memId)) + '" title="View atomic facts">&#9660;</button>';
60
-
61
- rows += '<tr data-mem-idx="' + idx + '">'
62
- + '<td>' + escapeHtml(String(mem.id)) + '</td>'
63
- + '<td><span class="badge bg-primary">' + escapeHtml(mem.category || 'None') + '</span></td>'
64
- + '<td><small>' + escapeHtml(mem.project_name || '-') + '</small></td>'
65
- + '<td class="memory-content" title="' + escapeHtml(content) + '">' + escapeHtml(contentPreview) + expandBtnHtml + '</td>'
66
- + scoreCell
67
- + '<td><span class="badge bg-' + importanceClass + ' badge-importance">' + escapeHtml(String(importance)) + '</span></td>'
68
- + '<td>' + escapeHtml(String(mem.cluster_id || '-')) + '</td>'
69
- + '<td><small>' + escapeHtml(formatDate(mem.created_at)) + '</small></td>'
70
- + '</tr>';
71
- });
72
-
73
- var tableHtml = '<table class="table table-hover memory-table"><thead><tr>'
74
- + '<th class="sortable" data-sort="id">ID</th>'
75
- + '<th class="sortable" data-sort="category">Category</th>'
76
- + '<th class="sortable" data-sort="project">Project</th>'
77
- + '<th>Content</th>'
78
- + scoreHeader
79
- + '<th class="sortable" data-sort="importance">Importance</th>'
80
- + '<th>Cluster</th>'
81
- + '<th class="sortable" data-sort="created">Created</th>'
82
- + '</tr></thead><tbody>' + rows + '</tbody></table>';
83
-
84
- // All values above escaped via escapeHtml() — safe for trusted local data
85
- container.textContent = '';
86
- container.insertAdjacentHTML('beforeend', tableHtml);
87
-
88
- var table = container.querySelector('table');
89
- if (table) {
90
- table.addEventListener('click', function(e) {
91
- var th = e.target.closest('th.sortable');
92
- if (th) { handleSort(th); return; }
93
-
94
- // Expand facts button
95
- var expandBtn = e.target.closest('.expand-facts-btn');
96
- if (expandBtn) {
97
- e.stopPropagation();
98
- var memId = expandBtn.getAttribute('data-memory-id');
99
- toggleFactsExpansion(expandBtn, memId);
100
- return;
101
- }
102
-
103
- var row = e.target.closest('tr[data-mem-idx]');
104
- if (row && !e.target.closest('.expand-facts-btn')) {
105
- var idx = parseInt(row.getAttribute('data-mem-idx'), 10);
106
- if (window._slmMemories && window._slmMemories[idx]) {
107
- openMemoryDetail(window._slmMemories[idx]);
108
- }
109
- }
110
- });
111
- }
112
- }
113
-
114
- // ============================================================================
115
- // Column Sorting
116
- // ============================================================================
117
-
118
- var currentSort = { column: null, direction: 'asc' };
119
-
120
- function handleSort(th) {
121
- var col = th.getAttribute('data-sort');
122
- if (!col) return;
123
-
124
- if (currentSort.column === col) {
125
- currentSort.direction = currentSort.direction === 'asc' ? 'desc' : 'asc';
126
- } else {
127
- currentSort.column = col;
128
- currentSort.direction = 'asc';
129
- }
130
-
131
- document.querySelectorAll('#memories-list th.sortable').forEach(function(h) {
132
- h.classList.remove('sort-asc', 'sort-desc');
133
- });
134
- th.classList.add('sort-' + currentSort.direction);
135
-
136
- if (!window._slmMemories) return;
137
- var memories = window._slmMemories.slice();
138
- var dir = currentSort.direction === 'asc' ? 1 : -1;
139
-
140
- memories.sort(function(a, b) {
141
- var av, bv;
142
- switch (col) {
143
- case 'id': return ((a.id || 0) - (b.id || 0)) * dir;
144
- case 'importance': return ((a.importance || 0) - (b.importance || 0)) * dir;
145
- case 'category':
146
- av = (a.category || '').toLowerCase(); bv = (b.category || '').toLowerCase();
147
- return av < bv ? -dir : av > bv ? dir : 0;
148
- case 'project':
149
- av = (a.project_name || '').toLowerCase(); bv = (b.project_name || '').toLowerCase();
150
- return av < bv ? -dir : av > bv ? dir : 0;
151
- case 'created':
152
- av = a.created_at || ''; bv = b.created_at || '';
153
- return av < bv ? -dir : av > bv ? dir : 0;
154
- case 'score': return ((a.score || 0) - (b.score || 0)) * dir;
155
- default: return 0;
156
- }
157
- });
158
-
159
- window._slmMemories = memories;
160
- var showScores = memories.length > 0 && typeof memories[0].score === 'number';
161
- renderMemoriesTable(memories, showScores);
162
- }
163
-
164
- // ============================================================================
165
- // Navigation from Graph (v2.6.5)
166
- // ============================================================================
167
-
168
- // Scroll to a specific memory by ID (called from graph double-click)
169
- function scrollToMemory(memoryId) {
170
- const container = document.getElementById('memories-list');
171
- if (!container) return;
172
-
173
- // Find the memory in current list
174
- if (!window._slmMemories) {
175
- console.warn('No memories loaded yet. Loading all memories...');
176
- loadMemories().then(() => {
177
- setTimeout(() => scrollToMemoryInTable(memoryId), 500);
178
- });
179
- return;
180
- }
181
-
182
- scrollToMemoryInTable(memoryId);
183
- }
184
-
185
- async function toggleFactsExpansion(btn, memoryId) {
186
- var row = btn.closest('tr');
187
- if (!row) return;
188
- var existingExpansion = row.nextElementSibling;
189
- if (existingExpansion && existingExpansion.classList.contains('facts-expansion-row')) {
190
- existingExpansion.remove();
191
- btn.innerHTML = '&#9660;';
192
- return;
193
- }
194
-
195
- btn.innerHTML = '&#8987;';
196
- try {
197
- var resp = await fetch('/api/memories/' + encodeURIComponent(memoryId) + '/facts');
198
- var data = await resp.json();
199
- var expRow = document.createElement('tr');
200
- expRow.className = 'facts-expansion-row';
201
- var expCell = document.createElement('td');
202
- expCell.colSpan = 8;
203
- expCell.style.cssText = 'background:#f8f9fa; padding:8px 16px;';
204
-
205
- if (data.facts && data.facts.length > 0) {
206
- var label = document.createElement('small');
207
- label.className = 'text-muted';
208
- label.textContent = data.fact_count + ' atomic facts:';
209
- expCell.appendChild(label);
210
-
211
- data.facts.forEach(function(f, i) {
212
- var fDiv = document.createElement('div');
213
- fDiv.className = 'small py-1' + (i < data.facts.length - 1 ? ' border-bottom' : '');
214
- var badge = document.createElement('span');
215
- badge.className = 'badge bg-secondary me-1';
216
- badge.style.fontSize = '0.65rem';
217
- badge.textContent = f.fact_type;
218
- fDiv.appendChild(badge);
219
- fDiv.appendChild(document.createTextNode(f.content.substring(0, 200)));
220
- expCell.appendChild(fDiv);
221
- });
222
- } else {
223
- expCell.textContent = 'No atomic facts found for this memory.';
224
- }
225
-
226
- expRow.appendChild(expCell);
227
- row.parentNode.insertBefore(expRow, row.nextSibling);
228
- btn.innerHTML = '&#9650;';
229
- } catch (err) {
230
- btn.innerHTML = '&#9660;';
231
- console.error('Failed to load facts:', err);
232
- }
233
- }
234
-
235
- function scrollToMemoryInTable(memoryId) {
236
- const memId = String(memoryId);
237
-
238
- // Find the memory index
239
- let idx = -1;
240
- if (window._slmMemories) {
241
- idx = window._slmMemories.findIndex(m => String(m.id) === memId);
242
- }
243
-
244
- if (idx === -1) {
245
- console.warn(`Memory #${memoryId} not found in current list`);
246
- return;
247
- }
248
-
249
- // Find the table row
250
- const row = document.querySelector(`tr[data-mem-idx="${idx}"]`);
251
- if (!row) {
252
- console.warn(`Row for memory #${memoryId} not found in DOM`);
253
- return;
254
- }
255
-
256
- // Scroll to row
257
- row.scrollIntoView({ behavior: 'smooth', block: 'center' });
258
-
259
- // Highlight temporarily
260
- row.style.backgroundColor = '#fff3cd';
261
- setTimeout(() => {
262
- row.style.backgroundColor = '';
263
- }, 2000);
264
- }