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,252 +0,0 @@
1
- // SPDX-License-Identifier: Elastic-2.0
2
- // Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
3
- // Compliance tab — audit trail, retention policies, ABAC (v2.8)
4
- // NOTE: All dynamic values use textContent or escapeHtml() from core.js before DOM insertion.
5
-
6
- var _complianceData = null;
7
-
8
- async function loadCompliance() {
9
- var filterEl = document.getElementById('cp-audit-filter');
10
- var filterValue = filterEl ? filterEl.value : '';
11
-
12
- try {
13
- var url = '/api/compliance/status';
14
- if (filterValue) url += '?event_type=' + encodeURIComponent(filterValue);
15
- var response = await fetch(url);
16
- var data = await response.json();
17
- _complianceData = data;
18
-
19
- if (!data.available) {
20
- showEmpty('compliance-audit-content', 'shield-lock', 'Compliance engine not available. Upgrade to v2.8.');
21
- return;
22
- }
23
-
24
- renderComplianceStats(data);
25
- renderCompliancePolicies(data);
26
- renderComplianceAudit(data);
27
-
28
- var badge = document.getElementById('compliance-profile-badge');
29
- if (badge) badge.textContent = data.active_profile || 'default';
30
- } catch (error) {
31
- console.error('Error loading compliance:', error);
32
- }
33
- }
34
-
35
- function renderComplianceStats(data) {
36
- var stats = data.stats || {};
37
- animateCounter('cp-audit-count', stats.audit_count || 0);
38
- animateCounter('cp-retention-count', stats.retention_count || 0);
39
- animateCounter('cp-abac-count', stats.abac_count || 0);
40
- }
41
-
42
- function renderCompliancePolicies(data) {
43
- var container = document.getElementById('compliance-policies-content');
44
- if (!container) return;
45
- var policies = data.retention_policies || [];
46
- container.textContent = '';
47
-
48
- if (policies.length === 0) {
49
- var empty = document.createElement('div');
50
- empty.className = 'text-center text-muted py-3';
51
- empty.textContent = 'No retention policies configured. Create one above or use the set_retention_policy MCP tool.';
52
- container.appendChild(empty);
53
- return;
54
- }
55
-
56
- var table = document.createElement('table');
57
- table.className = 'table table-sm table-hover mb-0';
58
- var thead = document.createElement('thead');
59
- var headRow = document.createElement('tr');
60
- ['Policy Name', 'Retention (days)', 'Category', 'Action', 'Created'].forEach(function(h) {
61
- var th = document.createElement('th');
62
- th.textContent = h;
63
- headRow.appendChild(th);
64
- });
65
- thead.appendChild(headRow);
66
- table.appendChild(thead);
67
-
68
- var tbody = document.createElement('tbody');
69
- for (var i = 0; i < policies.length; i++) {
70
- var pol = policies[i];
71
- var row = document.createElement('tr');
72
-
73
- var nameCell = document.createElement('td');
74
- var nameIcon = document.createElement('i');
75
- nameIcon.className = 'bi bi-shield-check text-success me-1';
76
- nameCell.appendChild(nameIcon);
77
- nameCell.appendChild(document.createTextNode(pol.name || ''));
78
- row.appendChild(nameCell);
79
-
80
- var daysCell = document.createElement('td');
81
- daysCell.textContent = (pol.retention_days || 0) + ' days';
82
- row.appendChild(daysCell);
83
-
84
- var catCell = document.createElement('td');
85
- if (pol.category) {
86
- var catBadge = document.createElement('span');
87
- catBadge.className = 'badge bg-info';
88
- catBadge.textContent = pol.category;
89
- catCell.appendChild(catBadge);
90
- } else {
91
- catCell.textContent = 'All';
92
- }
93
- row.appendChild(catCell);
94
-
95
- var actionCell = document.createElement('td');
96
- var actionColors = { archive: 'bg-secondary', tombstone: 'bg-danger', notify: 'bg-warning' };
97
- var actionBadge = document.createElement('span');
98
- actionBadge.className = 'badge ' + (actionColors[pol.action] || 'bg-secondary');
99
- actionBadge.textContent = pol.action || '';
100
- actionCell.appendChild(actionBadge);
101
- row.appendChild(actionCell);
102
-
103
- var dateCell = document.createElement('td');
104
- dateCell.className = 'small text-muted';
105
- dateCell.textContent = formatDate(pol.created_at || '');
106
- row.appendChild(dateCell);
107
-
108
- tbody.appendChild(row);
109
- }
110
- table.appendChild(tbody);
111
- container.appendChild(table);
112
- }
113
-
114
- function renderComplianceAudit(data) {
115
- var container = document.getElementById('compliance-audit-content');
116
- if (!container) return;
117
- var events = data.audit_events || [];
118
- container.textContent = '';
119
-
120
- if (events.length === 0) {
121
- var empty = document.createElement('div');
122
- empty.className = 'text-center text-muted py-3';
123
- empty.textContent = 'No audit events recorded yet.';
124
- container.appendChild(empty);
125
- return;
126
- }
127
-
128
- var wrapper = document.createElement('div');
129
- wrapper.style.maxHeight = '400px';
130
- wrapper.style.overflowY = 'auto';
131
-
132
- var table = document.createElement('table');
133
- table.className = 'table table-sm table-hover mb-0';
134
- var thead = document.createElement('thead');
135
- thead.style.position = 'sticky';
136
- thead.style.top = '0';
137
- thead.style.backgroundColor = 'var(--bs-body-bg)';
138
- var headRow = document.createElement('tr');
139
- ['Timestamp', 'Event', 'Actor', 'Action', 'Target', 'Result'].forEach(function(h) {
140
- var th = document.createElement('th');
141
- th.textContent = h;
142
- headRow.appendChild(th);
143
- });
144
- thead.appendChild(headRow);
145
- table.appendChild(thead);
146
-
147
- var eventBadgeColors = {
148
- recall: 'bg-primary',
149
- remember: 'bg-success',
150
- delete: 'bg-danger',
151
- lifecycle_transition: 'bg-warning',
152
- access_denied: 'bg-danger',
153
- retention_enforced: 'bg-warning'
154
- };
155
-
156
- var tbody = document.createElement('tbody');
157
- for (var i = 0; i < events.length; i++) {
158
- var ev = events[i];
159
- var row = document.createElement('tr');
160
-
161
- var tsCell = document.createElement('td');
162
- tsCell.className = 'small text-muted';
163
- tsCell.textContent = formatDateFull(ev.timestamp || '');
164
- row.appendChild(tsCell);
165
-
166
- var typeCell = document.createElement('td');
167
- var typeBadge = document.createElement('span');
168
- typeBadge.className = 'badge ' + (eventBadgeColors[ev.event_type] || 'bg-secondary');
169
- typeBadge.textContent = ev.event_type || '';
170
- typeCell.appendChild(typeBadge);
171
- row.appendChild(typeCell);
172
-
173
- var actorCell = document.createElement('td');
174
- actorCell.textContent = ev.actor || '';
175
- row.appendChild(actorCell);
176
-
177
- var actionCell = document.createElement('td');
178
- actionCell.textContent = ev.action || '';
179
- row.appendChild(actionCell);
180
-
181
- var targetCell = document.createElement('td');
182
- targetCell.className = 'small';
183
- targetCell.textContent = ev.target || '';
184
- row.appendChild(targetCell);
185
-
186
- var resultCell = document.createElement('td');
187
- if (ev.result === 'success' || ev.result === 'allowed') {
188
- var okBadge = document.createElement('span');
189
- okBadge.className = 'badge bg-success';
190
- okBadge.textContent = ev.result;
191
- resultCell.appendChild(okBadge);
192
- } else if (ev.result === 'denied' || ev.result === 'error') {
193
- var failBadge = document.createElement('span');
194
- failBadge.className = 'badge bg-danger';
195
- failBadge.textContent = ev.result;
196
- resultCell.appendChild(failBadge);
197
- } else {
198
- resultCell.textContent = ev.result || '';
199
- }
200
- row.appendChild(resultCell);
201
-
202
- tbody.appendChild(row);
203
- }
204
- table.appendChild(tbody);
205
- wrapper.appendChild(table);
206
- container.appendChild(wrapper);
207
- }
208
-
209
- async function createRetentionPolicy() {
210
- var nameInput = document.getElementById('cp-policy-name');
211
- var daysInput = document.getElementById('cp-retention-days');
212
- var categoryInput = document.getElementById('cp-category');
213
- var actionSelect = document.getElementById('cp-action');
214
-
215
- var policyName = (nameInput.value || '').trim();
216
- if (!policyName) {
217
- showToast('Enter a policy name.');
218
- return;
219
- }
220
-
221
- var days = parseInt(daysInput.value, 10);
222
- if (isNaN(days) || days <= 0) {
223
- showToast('Enter a valid number of days.');
224
- return;
225
- }
226
-
227
- try {
228
- var response = await fetch('/api/compliance/retention-policy', {
229
- method: 'POST',
230
- headers: { 'Content-Type': 'application/json' },
231
- body: JSON.stringify({
232
- name: policyName,
233
- retention_days: days,
234
- category: categoryInput.value.trim() || undefined,
235
- action: actionSelect.value
236
- })
237
- });
238
- var data = await response.json();
239
- if (response.ok) {
240
- showToast('Retention policy created.');
241
- nameInput.value = '';
242
- daysInput.value = '90';
243
- categoryInput.value = '';
244
- loadCompliance(); // Refresh
245
- } else {
246
- showToast(data.detail || 'Failed to create policy.');
247
- }
248
- } catch (error) {
249
- console.error('Error creating retention policy:', error);
250
- showToast('Error creating retention policy.');
251
- }
252
- }
package/ui/js/core.js DELETED
@@ -1,246 +0,0 @@
1
- // SuperLocalMemory V2 - Core Utilities
2
- // Shared functions used by all other modules.
3
- // Security: All dynamic text MUST pass through escapeHtml() before DOM insertion.
4
- // Data originates from our own trusted local SQLite database (localhost only).
5
-
6
- // ============================================================================
7
- // Dark Mode
8
- // ============================================================================
9
-
10
- function initDarkMode() {
11
- var saved = localStorage.getItem('slm-theme');
12
- var theme;
13
- if (saved) {
14
- theme = saved;
15
- } else {
16
- theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
17
- }
18
- applyTheme(theme);
19
- }
20
-
21
- function applyTheme(theme) {
22
- document.documentElement.setAttribute('data-bs-theme', theme);
23
- var icon = document.getElementById('theme-icon');
24
- if (icon) {
25
- icon.className = theme === 'dark' ? 'bi bi-moon-stars-fill' : 'bi bi-sun-fill';
26
- }
27
- }
28
-
29
- function toggleDarkMode() {
30
- var current = document.documentElement.getAttribute('data-bs-theme');
31
- var next = current === 'dark' ? 'light' : 'dark';
32
- localStorage.setItem('slm-theme', next);
33
- applyTheme(next);
34
- }
35
-
36
- // ============================================================================
37
- // Animated Counter
38
- // ============================================================================
39
-
40
- function animateCounter(elementId, target) {
41
- var el = document.getElementById(elementId);
42
- if (!el) return;
43
- var duration = 600;
44
- var startTime = null;
45
-
46
- function step(timestamp) {
47
- if (!startTime) startTime = timestamp;
48
- var progress = Math.min((timestamp - startTime) / duration, 1);
49
- var eased = 1 - Math.pow(1 - progress, 3);
50
- el.textContent = Math.floor(eased * target).toLocaleString();
51
- if (progress < 1) {
52
- requestAnimationFrame(step);
53
- } else {
54
- el.textContent = target.toLocaleString();
55
- }
56
- }
57
-
58
- if (target === 0) {
59
- el.textContent = '0';
60
- } else {
61
- requestAnimationFrame(step);
62
- }
63
- }
64
-
65
- // ============================================================================
66
- // HTML Escaping — all dynamic text MUST pass through this before DOM insertion
67
- // ============================================================================
68
-
69
- function escapeHtml(text) {
70
- if (!text) return '';
71
- var div = document.createElement('div');
72
- div.appendChild(document.createTextNode(String(text)));
73
- return div.innerHTML;
74
- }
75
-
76
- // ============================================================================
77
- // Loading / Empty State helpers
78
- // ============================================================================
79
-
80
- function showLoading(containerId, message) {
81
- var el = document.getElementById(containerId);
82
- if (!el) return;
83
- el.textContent = '';
84
- var wrapper = document.createElement('div');
85
- wrapper.className = 'loading';
86
- var spinner = document.createElement('div');
87
- spinner.className = 'spinner-border text-primary';
88
- spinner.setAttribute('role', 'status');
89
- var msg = document.createElement('div');
90
- msg.textContent = message || 'Loading...';
91
- wrapper.appendChild(spinner);
92
- wrapper.appendChild(msg);
93
- el.appendChild(wrapper);
94
- }
95
-
96
- function showEmpty(containerId, icon, message) {
97
- var el = document.getElementById(containerId);
98
- if (!el) return;
99
- el.textContent = '';
100
- var wrapper = document.createElement('div');
101
- wrapper.className = 'empty-state';
102
- var iconEl = document.createElement('i');
103
- iconEl.className = 'bi bi-' + icon + ' d-block';
104
- var p = document.createElement('p');
105
- p.textContent = message;
106
- wrapper.appendChild(iconEl);
107
- wrapper.appendChild(p);
108
- el.appendChild(wrapper);
109
- }
110
-
111
- // ============================================================================
112
- // Safe HTML builder — tagged template for escaped interpolation
113
- // ============================================================================
114
-
115
- function safeHtml(templateParts) {
116
- var args = Array.prototype.slice.call(arguments, 1);
117
- var result = '';
118
- for (var i = 0; i < templateParts.length; i++) {
119
- result += templateParts[i];
120
- if (i < args.length) {
121
- result += escapeHtml(String(args[i]));
122
- }
123
- }
124
- return result;
125
- }
126
-
127
- // ============================================================================
128
- // File Download helper
129
- // ============================================================================
130
-
131
- function downloadFile(filename, content, mimeType) {
132
- var blob = new Blob([content], { type: mimeType });
133
- var url = URL.createObjectURL(blob);
134
- var a = document.createElement('a');
135
- a.href = url;
136
- a.download = filename;
137
- document.body.appendChild(a);
138
- a.click();
139
- document.body.removeChild(a);
140
- URL.revokeObjectURL(url);
141
- }
142
-
143
- // ============================================================================
144
- // Toast notification
145
- // ============================================================================
146
-
147
- function showToast(message) {
148
- var toast = document.createElement('div');
149
- toast.style.cssText = 'position:fixed;bottom:24px;right:24px;background:#333;color:#fff;padding:10px 20px;border-radius:8px;font-size:0.9rem;z-index:9999;opacity:0;transition:opacity 0.3s;';
150
- toast.textContent = message;
151
- document.body.appendChild(toast);
152
- requestAnimationFrame(function() { toast.style.opacity = '1'; });
153
- setTimeout(function() {
154
- toast.style.opacity = '0';
155
- setTimeout(function() {
156
- if (toast.parentNode) document.body.removeChild(toast);
157
- }, 300);
158
- }, 2000);
159
- }
160
-
161
- // ============================================================================
162
- // Date Formatters
163
- // ============================================================================
164
-
165
- function formatDate(dateString) {
166
- if (!dateString) return '-';
167
- var date = new Date(dateString);
168
- return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
169
- }
170
-
171
- function formatDateFull(dateString) {
172
- if (!dateString) return '-';
173
- var date = new Date(dateString);
174
- return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' });
175
- }
176
-
177
- // ============================================================================
178
- // Stats (loaded on startup)
179
- // ============================================================================
180
-
181
- async function loadStats() {
182
- try {
183
- var response = await fetch('/api/stats');
184
- var data = await response.json();
185
- var ov = data.overview || {};
186
- animateCounter('stat-memories', ov.total_memories || 0);
187
- animateCounter('stat-clusters', ov.total_clusters || 0);
188
- animateCounter('stat-nodes', ov.graph_nodes || 0);
189
- animateCounter('stat-edges', ov.graph_edges || 0);
190
- populateFilters(data.categories || [], data.projects || []);
191
- } catch (error) {
192
- console.error('Error loading stats:', error);
193
- // On error (fresh install, server starting), show 0 instead of "-"
194
- animateCounter('stat-memories', 0);
195
- animateCounter('stat-clusters', 0);
196
- animateCounter('stat-nodes', 0);
197
- animateCounter('stat-edges', 0);
198
- }
199
- }
200
-
201
- // Refresh entire dashboard — called by the refresh button in the header
202
- function refreshDashboard() {
203
- loadProfiles();
204
- loadStats();
205
- if (typeof loadGraph === 'function') loadGraph();
206
- if (typeof loadMemories === 'function') loadMemories();
207
- if (typeof loadEventStats === 'function') loadEventStats();
208
- if (typeof loadAgents === 'function') loadAgents();
209
- }
210
-
211
- function populateFilters(categories, projects) {
212
- var categorySelect = document.getElementById('filter-category');
213
- var projectSelect = document.getElementById('filter-project');
214
- categories.forEach(function(cat) {
215
- if (cat.category) {
216
- var option = document.createElement('option');
217
- option.value = cat.category;
218
- option.textContent = cat.category + ' (' + cat.count + ')';
219
- categorySelect.appendChild(option);
220
- }
221
- });
222
- projects.forEach(function(proj) {
223
- if (proj.project_name) {
224
- var option = document.createElement('option');
225
- option.value = proj.project_name;
226
- option.textContent = proj.project_name + ' (' + proj.count + ')';
227
- projectSelect.appendChild(option);
228
- }
229
- });
230
- }
231
-
232
- // ============================================================================
233
- // Application Init (DOMContentLoaded)
234
- // ============================================================================
235
-
236
- window.addEventListener('DOMContentLoaded', function() {
237
- initDarkMode();
238
- loadProfiles();
239
- loadStats();
240
- loadGraph();
241
-
242
- // v2.5 — Event Bus + Agent Registry (graceful if functions don't exist)
243
- if (typeof initEventStream === 'function') initEventStream();
244
- if (typeof loadEventStats === 'function') loadEventStats();
245
- if (typeof loadAgents === 'function') loadAgents();
246
- });
@@ -1,110 +0,0 @@
1
- // SuperLocalMemory V3 — Dashboard
2
- // Part of Qualixar | https://superlocalmemory.com
3
-
4
- // Auto-refresh dashboard when tab becomes visible (fixes stale data after settings change)
5
- document.addEventListener('visibilitychange', function() {
6
- if (!document.hidden) loadDashboard();
7
- });
8
-
9
- // Also refresh when navigating back to dashboard tab in SPA
10
- window.addEventListener('hashchange', function() {
11
- if (window.location.hash === '' || window.location.hash === '#dashboard') {
12
- loadDashboard();
13
- }
14
- });
15
-
16
- // Refresh on focus (covers alt-tab back to browser)
17
- window.addEventListener('focus', function() { loadDashboard(); });
18
-
19
- async function loadDashboard() {
20
- try {
21
- var response = await fetch('/api/v3/dashboard');
22
- if (!response.ok) return;
23
- var data = await response.json();
24
-
25
- document.getElementById('dashboard-mode').textContent = 'Mode ' + data.mode.toUpperCase();
26
- document.getElementById('dashboard-mode-desc').textContent = data.mode_name + (data.provider !== 'none' ? ' — ' + data.provider : '');
27
- document.getElementById('dashboard-memory-count').textContent = data.fact_count || data.memory_count || '0';
28
- document.getElementById('dashboard-provider').textContent = data.provider === 'none' ? 'None' : data.provider;
29
- document.getElementById('dashboard-model').textContent = data.model || '';
30
- document.getElementById('dashboard-profile').textContent = data.profile || 'default';
31
- document.getElementById('dashboard-basedir').textContent = data.base_dir || '~/.superlocalmemory';
32
- var ver = data.version || '';
33
- var dashVer = document.getElementById('dashboard-version');
34
- var settVer = document.getElementById('settings-version');
35
- if (dashVer) dashVer.textContent = ver;
36
- if (settVer) settVer.textContent = ver;
37
-
38
- // Update mode badge in navbar
39
- var badge = document.getElementById('mode-badge');
40
- if (badge) badge.textContent = 'Mode ' + data.mode.toUpperCase();
41
-
42
- // Highlight active mode button
43
- document.querySelectorAll('.mode-btn').forEach(function(btn) {
44
- btn.classList.toggle('active', btn.dataset.mode === data.mode);
45
- });
46
- } catch (e) {
47
- console.log('Dashboard load error:', e);
48
- }
49
- }
50
-
51
- // Mode switch buttons
52
- document.addEventListener('click', function(e) {
53
- if (e.target.classList.contains('mode-btn')) {
54
- var mode = e.target.dataset.mode;
55
- fetch('/api/v3/mode', {
56
- method: 'PUT',
57
- headers: {'Content-Type': 'application/json'},
58
- body: JSON.stringify({mode: mode})
59
- }).then(function() { loadDashboard(); });
60
- }
61
- });
62
-
63
- // Quick store
64
- document.getElementById('quick-store-btn')?.addEventListener('click', function() {
65
- var input = document.getElementById('quick-store-input');
66
- var content = input.value.trim();
67
- if (!content) return;
68
- fetch('/api/memories', {
69
- method: 'POST',
70
- headers: {'Content-Type': 'application/json'},
71
- body: JSON.stringify({content: content})
72
- }).then(function(r) { return r.json(); }).then(function(data) {
73
- input.value = '';
74
- loadDashboard();
75
- alert('Stored!');
76
- });
77
- });
78
-
79
- // Quick recall
80
- document.getElementById('quick-recall-btn')?.addEventListener('click', function() {
81
- var query = document.getElementById('quick-recall-input').value.trim();
82
- if (!query) return;
83
- fetch('/api/search', {
84
- method: 'POST',
85
- headers: {'Content-Type': 'application/json'},
86
- body: JSON.stringify({query: query, limit: 5})
87
- }).then(function(r) { return r.json(); }).then(function(data) {
88
- var div = document.getElementById('quick-recall-results');
89
- if (!data.results || data.results.length === 0) {
90
- div.textContent = 'No results found.';
91
- return;
92
- }
93
- // Build results using DOM methods for safety
94
- div.textContent = '';
95
- data.results.forEach(function(r, i) {
96
- var row = document.createElement('div');
97
- row.className = 'border-bottom py-1';
98
- var strong = document.createElement('strong');
99
- strong.textContent = (i + 1) + '. ';
100
- row.appendChild(strong);
101
- var text = document.createTextNode((r.content || r.text || '').substring(0, 150) + ' ');
102
- row.appendChild(text);
103
- var scoreSpan = document.createElement('span');
104
- scoreSpan.className = 'text-muted';
105
- scoreSpan.textContent = '(' + (r.score || 0).toFixed(2) + ')';
106
- row.appendChild(scoreSpan);
107
- div.appendChild(row);
108
- });
109
- });
110
- });