superlocalmemory 3.8.0 → 3.8.2
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.
- package/CHANGELOG.md +112 -0
- package/README.md +32 -120
- package/package.json +9 -2
- package/plugin/.claude-plugin/plugin.json +1 -2
- package/plugin/CLAUDE.md +3 -3
- package/plugin/agents/slm-governance-advisor.md +1 -1
- package/plugin/agents/slm-loop-runner.md +1 -1
- package/plugin/agents/slm-memory-advisor.md +2 -2
- package/plugin/agents/slm-optimize-advisor.md +1 -1
- package/plugin/requirements.txt +1 -1
- package/plugin/skills/slm-cache/SKILL.md +1 -1
- package/plugin/skills/slm-compress/SKILL.md +1 -1
- package/plugin/skills/slm-governance/SKILL.md +1 -1
- package/plugin/skills/slm-graph/SKILL.md +1 -1
- package/plugin/skills/slm-loop/SKILL.md +1 -1
- package/plugin/skills/slm-mesh/SKILL.md +1 -1
- package/plugin/skills/slm-profile/SKILL.md +1 -1
- package/plugin/skills/slm-recall/SKILL.md +3 -5
- package/plugin/skills/slm-remember/SKILL.md +1 -1
- package/plugin/skills/slm-scope/SKILL.md +1 -1
- package/plugin/skills/slm-session/SKILL.md +1 -1
- package/plugin/skills/slm-status/SKILL.md +1 -1
- package/plugin-src/rules/AGENTS.md +2 -1
- package/plugin-src/skills/slm-cache/SKILL.md +1 -1
- package/plugin-src/skills/slm-compress/SKILL.md +1 -1
- package/plugin-src/skills/slm-graph/SKILL.md +1 -1
- package/plugin-src/skills/slm-recall/SKILL.md +3 -5
- package/plugin-src/skills/slm-remember/SKILL.md +1 -1
- package/plugin-src/skills/slm-session/SKILL.md +1 -1
- package/plugin-src/skills/slm-status/SKILL.md +1 -1
- package/pyproject.toml +2 -1
- package/scripts/postinstall.js +7 -1
- package/src/superlocalmemory/__init__.py +1 -1
- package/src/superlocalmemory/cli/commands.py +494 -9
- package/src/superlocalmemory/cli/daemon.py +7 -0
- package/src/superlocalmemory/cli/loop_cmd.py +2 -7
- package/src/superlocalmemory/cli/main.py +72 -7
- package/src/superlocalmemory/cli/setup_wizard.py +142 -16
- package/src/superlocalmemory/cli/version_banner.py +17 -3
- package/src/superlocalmemory/core/backend_orchestrator.py +18 -16
- package/src/superlocalmemory/core/component_healer.py +144 -0
- package/src/superlocalmemory/core/component_registry.py +487 -0
- package/src/superlocalmemory/core/config.py +21 -0
- package/src/superlocalmemory/core/embedding_worker.py +4 -5
- package/src/superlocalmemory/core/embeddings.py +132 -45
- package/src/superlocalmemory/core/engine.py +29 -22
- package/src/superlocalmemory/core/engine_ingestion.py +332 -45
- package/src/superlocalmemory/core/ingestion_command.py +154 -25
- package/src/superlocalmemory/core/injection.py +12 -7
- package/src/superlocalmemory/core/maintenance.py +43 -0
- package/src/superlocalmemory/core/maintenance_scheduler.py +44 -6
- package/src/superlocalmemory/core/recall_pipeline.py +42 -4
- package/src/superlocalmemory/core/store_pipeline.py +195 -20
- package/src/superlocalmemory/hooks/hook_handlers.py +6 -1
- package/src/superlocalmemory/hooks/portable_kit.py +34 -2
- package/src/superlocalmemory/learning/model_rollback.py +3 -0
- package/src/superlocalmemory/learning/ranker_retrain_online.py +2 -0
- package/src/superlocalmemory/learning/reward.py +50 -0
- package/src/superlocalmemory/learning/source_quality.py +523 -1
- package/src/superlocalmemory/loops/ledger.py +25 -5
- package/src/superlocalmemory/mcp/_daemon_proxy.py +6 -2
- package/src/superlocalmemory/mcp/_pool_adapter.py +4 -1
- package/src/superlocalmemory/mcp/server.py +11 -30
- package/src/superlocalmemory/mcp/tools_active.py +1 -1
- package/src/superlocalmemory/mcp/tools_core.py +21 -5
- package/src/superlocalmemory/mcp/tools_learning.py +2 -2
- package/src/superlocalmemory/retrieval/bridge_discovery.py +14 -0
- package/src/superlocalmemory/retrieval/engine.py +53 -21
- package/src/superlocalmemory/retrieval/reranker.py +3 -4
- package/src/superlocalmemory/retrieval/spreading_activation.py +68 -38
- package/src/superlocalmemory/server/config_file.py +90 -0
- package/src/superlocalmemory/server/origin.py +50 -0
- package/src/superlocalmemory/server/routes/backup.py +293 -70
- package/src/superlocalmemory/server/routes/behavioral.py +342 -61
- package/src/superlocalmemory/server/routes/brain.py +57 -16
- package/src/superlocalmemory/server/routes/config_api.py +84 -82
- package/src/superlocalmemory/server/routes/entity.py +100 -23
- package/src/superlocalmemory/server/routes/evolution.py +103 -100
- package/src/superlocalmemory/server/routes/learning.py +286 -105
- package/src/superlocalmemory/server/routes/learning_telemetry.py +153 -0
- package/src/superlocalmemory/server/routes/memories.py +8 -3
- package/src/superlocalmemory/server/routes/mesh.py +121 -32
- package/src/superlocalmemory/server/routes/ratelimit.py +33 -25
- package/src/superlocalmemory/server/routes/stats.py +93 -155
- package/src/superlocalmemory/server/routes/token.py +3 -13
- package/src/superlocalmemory/server/routes/v3_api.py +184 -20
- package/src/superlocalmemory/server/unified_daemon.py +732 -41
- package/src/superlocalmemory/storage/embedding_migrator.py +235 -0
- package/src/superlocalmemory/storage/migration_runner.py +79 -1
- package/src/superlocalmemory/storage/migrations/M010_evolution_config.py +5 -0
- package/src/superlocalmemory/storage/migrations/M028_fact_entity_associations.py +270 -0
- package/src/superlocalmemory/storage/migrations/M029_behavioral_history_indexes.py +137 -0
- package/src/superlocalmemory/storage/migrations/M030_entity_explorer_indexes.py +93 -0
- package/src/superlocalmemory/storage/migrations/__init__.py +4 -0
- package/src/superlocalmemory/storage/schema.py +49 -1
- package/src/superlocalmemory/storage/schema_v32.py +2 -0
- package/src/superlocalmemory/storage/schema_v347.py +4 -0
- package/src/superlocalmemory/ui/index.html +6 -8
- package/src/superlocalmemory/ui/js/core.js +52 -9
- package/src/superlocalmemory/ui/js/dashboard.js +169 -82
- package/src/superlocalmemory/ui/js/od-backup.js +156 -65
- package/src/superlocalmemory/ui/js/od-brain.js +88 -51
- package/src/superlocalmemory/ui/js/od-components.js +147 -0
- package/src/superlocalmemory/ui/js/od-entities.js +65 -22
- package/src/superlocalmemory/ui/js/od-graph.js +46 -4
- package/src/superlocalmemory/ui/js/od-health.js +18 -0
- package/src/superlocalmemory/ui/js/od-memories.js +84 -5
- package/src/superlocalmemory/ui/js/od-mesh.js +23 -9
- package/src/superlocalmemory/ui/js/od-operations.js +36 -0
- package/src/superlocalmemory/ui/js/od-settings.js +186 -63
- package/src/superlocalmemory/ui/js/od-shell.js +249 -33
- package/src/superlocalmemory/ui/js/od-skills.js +44 -17
- package/src/superlocalmemory/ui/js/settings.js +15 -1
- package/plugin-src/.mcp.json +0 -12
- package/plugin-src/agents/slm-governance-advisor.md +0 -80
- package/plugin-src/agents/slm-loop-runner.md +0 -71
- package/plugin-src/agents/slm-memory-advisor.md +0 -49
- package/plugin-src/agents/slm-optimize-advisor.md +0 -44
- package/plugin-src/commands/slm-loop.md +0 -31
- package/plugin-src/hooks/.gitkeep +0 -0
- package/plugin-src/hooks/hooks.json +0 -102
- package/plugin-src/manifest.json +0 -30
- package/plugin-src/requirements.txt +0 -1
- package/plugin-src/rules/CLAUDE.md.fragment +0 -44
- package/plugin-src/scripts/ensure-venv.bat +0 -122
- package/plugin-src/scripts/ensure-venv.sh +0 -105
- package/plugin-src/scripts/slm-launch +0 -62
- package/plugin-src/scripts/slm-launch.bat +0 -23
- package/plugin-src/settings.json +0 -25
- package/plugin-src/skills/slm-governance/SKILL.md +0 -248
- package/plugin-src/skills/slm-loop/SKILL.md +0 -99
- package/plugin-src/skills/slm-mesh/SKILL.md +0 -282
- package/plugin-src/skills/slm-profile/SKILL.md +0 -148
- package/plugin-src/skills/slm-scope/SKILL.md +0 -176
|
@@ -5,29 +5,64 @@
|
|
|
5
5
|
// loadDashboard() → GET /api/v3/dashboard → system card (#dashboard-mode etc.)
|
|
6
6
|
// loadDashboardStats() → GET /api/stats → OD KPI strip (#k-mem, #k-nodes, #k-edges)
|
|
7
7
|
// loadDashboardFeed() → GET /api/memories?limit=6 → activity feed (#feed)
|
|
8
|
-
// loadDashboardSources() →
|
|
9
|
-
//
|
|
8
|
+
// loadDashboardSources() → real provenance from /api/stats → capture-sources (#src)
|
|
9
|
+
// loadDashboardTimeline() → real daily fact counts from /api/timeline → #sp-big
|
|
10
|
+
|
|
11
|
+
var DASHBOARD_CACHE_MS = 30000;
|
|
12
|
+
var dashboardLastRefresh = 0;
|
|
13
|
+
var dashboardRefreshInFlight = null;
|
|
14
|
+
var dashboardCacheGeneration = 0;
|
|
15
|
+
var dashboardRefreshQueued = false;
|
|
16
|
+
|
|
17
|
+
window.slmInvalidateDashboardCache = function() {
|
|
18
|
+
dashboardCacheGeneration += 1;
|
|
19
|
+
dashboardLastRefresh = 0;
|
|
20
|
+
if (dashboardRefreshInFlight) dashboardRefreshQueued = true;
|
|
21
|
+
};
|
|
10
22
|
|
|
11
23
|
// Auto-refresh dashboard when tab becomes visible (fixes stale data after settings change)
|
|
12
24
|
document.addEventListener('visibilitychange', function() {
|
|
13
25
|
if (!document.hidden) refreshDashboard();
|
|
14
26
|
});
|
|
15
27
|
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
window.addEventListener('focus', function() { loadDashboard(); });
|
|
28
|
+
// Focus and tab navigation use a short cache window. This keeps normal
|
|
29
|
+
// browser switching from repeatedly querying a large local database.
|
|
30
|
+
window.addEventListener('focus', function() { refreshDashboard(); });
|
|
20
31
|
|
|
21
32
|
// Load when the Dashboard tab is shown via the sidebar. od-shell.js's activateTab()
|
|
22
33
|
// dispatches shown.bs.tab on the hidden #dashboard-tab button.
|
|
23
34
|
document.getElementById('dashboard-tab')?.addEventListener('shown.bs.tab', function() { refreshDashboard(); });
|
|
24
35
|
|
|
25
|
-
// refreshDashboard
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
// refreshDashboard coalesces concurrent startup/navigation events and keeps a
|
|
37
|
+
// 30-second snapshot. Mutations pass {force:true} to re-read daemon truth.
|
|
38
|
+
window.refreshDashboard = function(options) {
|
|
39
|
+
var force = options && options.force === true;
|
|
40
|
+
var now = Date.now();
|
|
41
|
+
if (dashboardRefreshInFlight) {
|
|
42
|
+
if (force) dashboardRefreshQueued = true;
|
|
43
|
+
return dashboardRefreshInFlight;
|
|
44
|
+
}
|
|
45
|
+
if (!force && dashboardLastRefresh && now - dashboardLastRefresh < DASHBOARD_CACHE_MS) {
|
|
46
|
+
return Promise.resolve();
|
|
47
|
+
}
|
|
48
|
+
var refreshGeneration = dashboardCacheGeneration;
|
|
49
|
+
dashboardRefreshInFlight = Promise.all([
|
|
50
|
+
loadDashboard(),
|
|
51
|
+
loadDashboardStats(),
|
|
52
|
+
loadDashboardTimeline(),
|
|
53
|
+
loadDashboardFeed()
|
|
54
|
+
]).then(function(results) {
|
|
55
|
+
dashboardLastRefresh = refreshGeneration === dashboardCacheGeneration && results.every(Boolean)
|
|
56
|
+
? Date.now()
|
|
57
|
+
: 0;
|
|
58
|
+
}).finally(function() {
|
|
59
|
+
dashboardRefreshInFlight = null;
|
|
60
|
+
}).then(function() {
|
|
61
|
+
if (!dashboardRefreshQueued) return;
|
|
62
|
+
dashboardRefreshQueued = false;
|
|
63
|
+
return window.refreshDashboard({ force: true });
|
|
64
|
+
});
|
|
65
|
+
return dashboardRefreshInFlight;
|
|
31
66
|
};
|
|
32
67
|
|
|
33
68
|
// ----------------------------------------------------------------
|
|
@@ -36,7 +71,7 @@ window.refreshDashboard = function() {
|
|
|
36
71
|
async function loadDashboardStats() {
|
|
37
72
|
try {
|
|
38
73
|
var r = await fetch('/api/stats', { credentials: 'same-origin' });
|
|
39
|
-
if (!r.ok) return;
|
|
74
|
+
if (!r.ok) return false;
|
|
40
75
|
var data = await r.json();
|
|
41
76
|
var ov = data.overview || {};
|
|
42
77
|
|
|
@@ -44,12 +79,14 @@ async function loadDashboardStats() {
|
|
|
44
79
|
var facts = ov.total_facts || 0;
|
|
45
80
|
var nodes = ov.graph_nodes || 0;
|
|
46
81
|
var edges = ov.graph_edges || 0;
|
|
82
|
+
var clusters = ov.total_clusters || 0;
|
|
47
83
|
|
|
48
84
|
// OD KPI strip
|
|
49
85
|
setKpiValue('k-mem', memories);
|
|
50
86
|
setKpiValue('k-nodes', nodes);
|
|
51
87
|
setKpiValue('k-edges', edges);
|
|
52
88
|
setKpiValue('k-facts', facts);
|
|
89
|
+
setKpiValue('k-clu', clusters);
|
|
53
90
|
|
|
54
91
|
// Legacy hidden compat IDs (core.js loadStats() may race — that's OK,
|
|
55
92
|
// the hidden IDs are only a data bridge, not visible).
|
|
@@ -57,51 +94,62 @@ async function loadDashboardStats() {
|
|
|
57
94
|
setTextById('stat-facts', facts);
|
|
58
95
|
setTextById('stat-nodes', nodes);
|
|
59
96
|
setTextById('stat-edges', edges);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
// (no time-series endpoint exists yet; honest placeholder).
|
|
63
|
-
// TODO: replace with GET /api/v3/stats/history when that endpoint ships.
|
|
64
|
-
if (typeof window.slmSpark === 'function') {
|
|
65
|
-
loadDashboardSparklines(memories, nodes, edges);
|
|
66
|
-
}
|
|
97
|
+
loadDashboardSources(data);
|
|
98
|
+
return true;
|
|
67
99
|
} catch (e) {
|
|
68
100
|
// Non-fatal: KPI strip shows — values
|
|
101
|
+
return false;
|
|
69
102
|
}
|
|
70
103
|
}
|
|
71
104
|
|
|
72
105
|
// ----------------------------------------------------------------
|
|
73
|
-
//
|
|
74
|
-
// No time-series endpoint yet: we synthesise a plausible trailing
|
|
75
|
-
// series anchored to the real current total. Honest placeholder.
|
|
106
|
+
// loadDashboardTimeline — renders real daily fact counts from /api/timeline
|
|
76
107
|
// ----------------------------------------------------------------
|
|
77
|
-
function
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
var
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
108
|
+
async function loadDashboardTimeline() {
|
|
109
|
+
var spBig = document.getElementById('sp-big');
|
|
110
|
+
if (!spBig) return true;
|
|
111
|
+
try {
|
|
112
|
+
var response = await fetch('/api/timeline?days=365&group_by=day&include_categories=false', {
|
|
113
|
+
credentials: 'same-origin'
|
|
114
|
+
});
|
|
115
|
+
if (!response.ok) throw new Error('timeline unavailable');
|
|
116
|
+
var stats = await response.json();
|
|
117
|
+
var points = Array.isArray(stats.timeline) ? stats.timeline : [];
|
|
118
|
+
var byDate = {};
|
|
119
|
+
points.forEach(function(point) {
|
|
120
|
+
if (point && point.period) byDate[String(point.period)] = Number(point.count) || 0;
|
|
121
|
+
});
|
|
122
|
+
var counts = [];
|
|
123
|
+
var today = new Date();
|
|
124
|
+
today.setHours(0, 0, 0, 0);
|
|
125
|
+
for (var dayOffset = 364; dayOffset >= 0; dayOffset--) {
|
|
126
|
+
var day = new Date(today);
|
|
127
|
+
day.setDate(today.getDate() - dayOffset);
|
|
128
|
+
counts.push(byDate[day.toISOString().slice(0, 10)] || 0);
|
|
85
129
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
130
|
+
var total = counts.reduce(function(sum, value) { return sum + value; }, 0);
|
|
131
|
+
var avgEl = document.getElementById('k-avg-day');
|
|
132
|
+
if (avgEl) avgEl.textContent = (total / 365).toFixed(1);
|
|
133
|
+
|
|
134
|
+
if (!total || typeof window.slmSpark !== 'function') {
|
|
135
|
+
spBig.textContent = 'No dated memory history recorded for this profile.';
|
|
136
|
+
spBig.className = 'muted';
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
spBig.className = '';
|
|
140
|
+
spBig.innerHTML = window.slmSpark(counts, {
|
|
141
|
+
w: 600,
|
|
142
|
+
h: 140,
|
|
143
|
+
color: 'var(--violet)'
|
|
144
|
+
});
|
|
145
|
+
if (spBig.querySelector('svg')) {
|
|
146
|
+
spBig.querySelector('svg').style.cssText = 'width:100%;height:140px;display:block;';
|
|
147
|
+
}
|
|
148
|
+
return true;
|
|
149
|
+
} catch (e) {
|
|
150
|
+
spBig.textContent = 'Memory history unavailable.';
|
|
151
|
+
spBig.className = 'muted';
|
|
152
|
+
return false;
|
|
105
153
|
}
|
|
106
154
|
}
|
|
107
155
|
|
|
@@ -110,7 +158,7 @@ function loadDashboardSparklines(memories, nodes, edges) {
|
|
|
110
158
|
// ----------------------------------------------------------------
|
|
111
159
|
async function loadDashboardFeed() {
|
|
112
160
|
var feedEl = document.getElementById('feed');
|
|
113
|
-
if (!feedEl) return;
|
|
161
|
+
if (!feedEl) return true;
|
|
114
162
|
|
|
115
163
|
var CATEGORY_COLORS = {
|
|
116
164
|
'episodic': ['rgba(34,197,94,0.12)', '#22c55e'],
|
|
@@ -131,13 +179,13 @@ async function loadDashboardFeed() {
|
|
|
131
179
|
var r = await fetch('/api/memories?limit=6', { credentials: 'same-origin' });
|
|
132
180
|
if (!r.ok) {
|
|
133
181
|
feedEl.innerHTML = '<div class="muted" style="padding:12px;text-align:center;font-size:13px">No recent activity</div>';
|
|
134
|
-
return;
|
|
182
|
+
return false;
|
|
135
183
|
}
|
|
136
184
|
var data = await r.json();
|
|
137
185
|
var items = data.memories || data.results || data || [];
|
|
138
186
|
if (!Array.isArray(items) || items.length === 0) {
|
|
139
187
|
feedEl.innerHTML = '<div class="muted" style="padding:12px;text-align:center;font-size:13px">No memories yet — start capturing!</div>';
|
|
140
|
-
return;
|
|
188
|
+
return true;
|
|
141
189
|
}
|
|
142
190
|
|
|
143
191
|
// Build feed using DOM methods (XSS-safe — no innerHTML with user content)
|
|
@@ -180,37 +228,76 @@ async function loadDashboardFeed() {
|
|
|
180
228
|
item.appendChild(time);
|
|
181
229
|
feedEl.appendChild(item);
|
|
182
230
|
});
|
|
231
|
+
return true;
|
|
183
232
|
} catch (e) {
|
|
184
233
|
feedEl.innerHTML = '<div class="muted" style="padding:12px;text-align:center;font-size:13px">Activity unavailable</div>';
|
|
234
|
+
return false;
|
|
185
235
|
}
|
|
186
236
|
}
|
|
187
237
|
|
|
188
238
|
// ----------------------------------------------------------------
|
|
189
|
-
// loadDashboardSources —
|
|
190
|
-
// TODO: GET /api/v3/ingestion/sources does not exist yet.
|
|
239
|
+
// loadDashboardSources — profile-scoped provenance returned by /api/stats
|
|
191
240
|
// ----------------------------------------------------------------
|
|
192
|
-
function loadDashboardSources() {
|
|
241
|
+
function loadDashboardSources(stats) {
|
|
193
242
|
var srcEl = document.getElementById('src');
|
|
194
243
|
if (!srcEl) return;
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
'
|
|
213
|
-
|
|
244
|
+
var sourceStatus = stats && stats.ingestion_sources_status;
|
|
245
|
+
if (sourceStatus && sourceStatus.available === false) {
|
|
246
|
+
srcEl.textContent = 'Provenance metrics are temporarily unavailable.';
|
|
247
|
+
srcEl.className = 'muted';
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
var sources = stats && Array.isArray(stats.ingestion_sources)
|
|
251
|
+
? stats.ingestion_sources
|
|
252
|
+
: [];
|
|
253
|
+
if (!sources.length) {
|
|
254
|
+
srcEl.textContent = 'No provenance recorded for this profile.';
|
|
255
|
+
srcEl.className = 'muted';
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
var LABELS = {
|
|
260
|
+
'store': 'Historical / engine store',
|
|
261
|
+
'http': 'Dashboard, hooks, and API',
|
|
262
|
+
'http-observe': 'Auto-capture observations',
|
|
263
|
+
'mcp-offline-worker': 'Queued MCP ingestion',
|
|
264
|
+
'cli-sync': 'CLI sync',
|
|
265
|
+
'unknown': 'Unclassified'
|
|
266
|
+
};
|
|
267
|
+
var COLORS = ['var(--violet)', 'var(--cyan)', 'var(--warn,#f59e0b)', 'var(--fg-3)'];
|
|
268
|
+
var total = sources.reduce(function(sum, source) {
|
|
269
|
+
return sum + (Number(source.count) || 0);
|
|
270
|
+
}, 0);
|
|
271
|
+
|
|
272
|
+
srcEl.className = '';
|
|
273
|
+
srcEl.textContent = '';
|
|
274
|
+
sources.forEach(function(source, index) {
|
|
275
|
+
var count = Number(source.count) || 0;
|
|
276
|
+
var percent = total ? Math.round((count / total) * 100) : 0;
|
|
277
|
+
var row = document.createElement('div');
|
|
278
|
+
row.style.marginBottom = '14px';
|
|
279
|
+
|
|
280
|
+
var heading = document.createElement('div');
|
|
281
|
+
heading.style.cssText = 'display:flex;justify-content:space-between;font-size:13px;margin-bottom:6px';
|
|
282
|
+
var label = document.createElement('span');
|
|
283
|
+
label.textContent = LABELS[source.source_type] || source.source_type || 'Unclassified';
|
|
284
|
+
var value = document.createElement('b');
|
|
285
|
+
value.className = 'num';
|
|
286
|
+
value.textContent = count.toLocaleString() + ' · ' + percent + '%';
|
|
287
|
+
heading.appendChild(label);
|
|
288
|
+
heading.appendChild(value);
|
|
289
|
+
|
|
290
|
+
var meter = document.createElement('div');
|
|
291
|
+
meter.className = 'meter';
|
|
292
|
+
var fill = document.createElement('i');
|
|
293
|
+
fill.style.width = percent + '%';
|
|
294
|
+
fill.style.background = COLORS[index % COLORS.length];
|
|
295
|
+
meter.appendChild(fill);
|
|
296
|
+
|
|
297
|
+
row.appendChild(heading);
|
|
298
|
+
row.appendChild(meter);
|
|
299
|
+
srcEl.appendChild(row);
|
|
300
|
+
});
|
|
214
301
|
}
|
|
215
302
|
|
|
216
303
|
// ----------------------------------------------------------------
|
|
@@ -247,7 +334,7 @@ async function loadDashboard() {
|
|
|
247
334
|
var response = await fetch('/api/v3/dashboard', { credentials: 'same-origin' });
|
|
248
335
|
if (!response.ok) {
|
|
249
336
|
showPaneError(PANE_ID, paneErrorMessage(response.status), loadDashboard, true);
|
|
250
|
-
return;
|
|
337
|
+
return false;
|
|
251
338
|
}
|
|
252
339
|
var data = await response.json();
|
|
253
340
|
|
|
@@ -282,9 +369,11 @@ async function loadDashboard() {
|
|
|
282
369
|
document.querySelectorAll('.mode-btn').forEach(function(btn) {
|
|
283
370
|
btn.classList.toggle('active', btn.dataset.mode === data.mode);
|
|
284
371
|
});
|
|
372
|
+
return true;
|
|
285
373
|
} catch (e) {
|
|
286
374
|
showPaneError(PANE_ID, paneErrorMessage(0), loadDashboard, true);
|
|
287
375
|
console.log('Dashboard load error:', e);
|
|
376
|
+
return false;
|
|
288
377
|
}
|
|
289
378
|
}
|
|
290
379
|
|
|
@@ -293,10 +382,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
293
382
|
// od-shell.js calls activateTab('dashboard-pane') which dispatches shown.bs.tab,
|
|
294
383
|
// but the listener may not yet be attached at that point. Call directly as well.
|
|
295
384
|
setTimeout(function() {
|
|
296
|
-
|
|
297
|
-
loadDashboardStats();
|
|
298
|
-
loadDashboardFeed();
|
|
299
|
-
loadDashboardSources();
|
|
385
|
+
refreshDashboard({ force: true });
|
|
300
386
|
}, 200);
|
|
301
387
|
});
|
|
302
388
|
|
|
@@ -351,8 +437,7 @@ document.getElementById('quick-store-btn')?.addEventListener('click', async func
|
|
|
351
437
|
}
|
|
352
438
|
await r.json();
|
|
353
439
|
if (input) input.value = '';
|
|
354
|
-
|
|
355
|
-
loadDashboardFeed();
|
|
440
|
+
refreshDashboard({ force: true });
|
|
356
441
|
showToast('Stored!');
|
|
357
442
|
} catch(e) {
|
|
358
443
|
showToast('Store failed: ' + e.message);
|
|
@@ -372,6 +457,8 @@ document.getElementById('quick-recall-btn')?.addEventListener('click', function(
|
|
|
372
457
|
fetch('/api/search', {
|
|
373
458
|
method: 'POST', credentials: 'same-origin',
|
|
374
459
|
headers: {'Content-Type': 'application/json'},
|
|
460
|
+
slmInvalidatesCache: false,
|
|
461
|
+
slmRequiresWriteAuth: false,
|
|
375
462
|
body: JSON.stringify({query: query, limit: 5})
|
|
376
463
|
}).then(function(r) {
|
|
377
464
|
btn.disabled = false;
|