superlocalmemory 3.7.7 → 3.8.0
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/.claude-plugin/marketplace.json +1 -1
- package/ATTRIBUTION.md +1 -3
- package/CHANGELOG.md +85 -0
- package/README.md +199 -29
- package/package.json +4 -2
- package/plugin/.claude-plugin/plugin.json +2 -2
- package/plugin/CLAUDE.md +8 -8
- package/plugin/agents/slm-governance-advisor.md +80 -0
- package/plugin/agents/slm-loop-runner.md +71 -0
- package/plugin/agents/slm-memory-advisor.md +10 -5
- package/plugin/agents/slm-optimize-advisor.md +9 -3
- package/plugin/commands/slm-loop.md +31 -0
- package/plugin/hooks/hooks.json +79 -0
- package/plugin/requirements.txt +1 -1
- package/plugin/scripts/slm-launch +46 -7
- package/plugin/settings.json +9 -0
- package/plugin/skills/slm-cache/SKILL.md +9 -1
- package/plugin/skills/slm-compress/SKILL.md +8 -1
- package/plugin/skills/slm-governance/SKILL.md +248 -0
- package/plugin/skills/slm-graph/SKILL.md +17 -3
- package/plugin/skills/slm-loop/SKILL.md +99 -0
- package/plugin/skills/slm-mesh/SKILL.md +282 -0
- package/plugin/skills/slm-profile/SKILL.md +148 -0
- package/plugin/skills/slm-recall/SKILL.md +46 -10
- package/plugin/skills/slm-remember/SKILL.md +48 -1
- package/plugin/skills/slm-scope/SKILL.md +176 -0
- package/plugin/skills/slm-session/SKILL.md +24 -1
- package/plugin/skills/slm-status/SKILL.md +18 -1
- package/plugin-src/agents/slm-governance-advisor.md +80 -0
- package/plugin-src/agents/slm-loop-runner.md +71 -0
- package/plugin-src/agents/slm-memory-advisor.md +10 -5
- package/plugin-src/agents/slm-optimize-advisor.md +9 -3
- package/plugin-src/commands/slm-loop.md +31 -0
- package/plugin-src/hooks/hooks.json +79 -0
- package/plugin-src/manifest.json +7 -2
- package/plugin-src/requirements.txt +1 -1
- package/plugin-src/rules/AGENTS.md +57 -18
- package/plugin-src/rules/CLAUDE.md.fragment +8 -8
- package/plugin-src/scripts/slm-launch +46 -7
- package/plugin-src/settings.json +9 -0
- package/plugin-src/skills/slm-cache/SKILL.md +9 -1
- package/plugin-src/skills/slm-compress/SKILL.md +8 -1
- package/plugin-src/skills/slm-governance/SKILL.md +248 -0
- package/plugin-src/skills/slm-graph/SKILL.md +17 -3
- package/plugin-src/skills/slm-loop/SKILL.md +99 -0
- package/plugin-src/skills/slm-mesh/SKILL.md +282 -0
- package/plugin-src/skills/slm-profile/SKILL.md +148 -0
- package/plugin-src/skills/slm-recall/SKILL.md +46 -10
- package/plugin-src/skills/slm-remember/SKILL.md +48 -1
- package/plugin-src/skills/slm-scope/SKILL.md +176 -0
- package/plugin-src/skills/slm-session/SKILL.md +24 -1
- package/plugin-src/skills/slm-status/SKILL.md +18 -1
- package/pyproject.toml +1 -2
- package/scripts/postinstall/validation.js +2 -0
- package/scripts/postinstall-interactive.js +74 -2
- package/src/superlocalmemory/__init__.py +1 -1
- package/src/superlocalmemory/access/__init__.py +3 -0
- package/src/superlocalmemory/access/rbac.py +477 -0
- package/src/superlocalmemory/cli/commands.py +96 -12
- package/src/superlocalmemory/cli/compress_cmd.py +17 -7
- package/src/superlocalmemory/cli/loop_cmd.py +192 -0
- package/src/superlocalmemory/cli/main.py +39 -4
- package/src/superlocalmemory/cli/mesh_cmd.py +38 -0
- package/src/superlocalmemory/cli/optimize_cmd.py +3 -0
- package/src/superlocalmemory/cli/pending_store.py +49 -13
- package/src/superlocalmemory/cli/proxy_cmd.py +4 -0
- package/src/superlocalmemory/cli/scale_engine_cmd.py +6 -0
- package/src/superlocalmemory/cli/setup_wizard.py +22 -13
- package/src/superlocalmemory/compliance/audit.py +6 -0
- package/src/superlocalmemory/compliance/gdpr.py +128 -138
- package/src/superlocalmemory/compliance/retention.py +176 -45
- package/src/superlocalmemory/core/backend_orchestrator.py +5 -43
- package/src/superlocalmemory/core/community_summary.py +267 -0
- package/src/superlocalmemory/core/config.py +216 -3
- package/src/superlocalmemory/core/consolidation_engine.py +95 -22
- package/src/superlocalmemory/core/context_cache.py +61 -18
- package/src/superlocalmemory/core/embedding_worker.py +17 -2
- package/src/superlocalmemory/core/embeddings.py +12 -1
- package/src/superlocalmemory/core/engine.py +17 -1
- package/src/superlocalmemory/core/engine_ingestion.py +29 -0
- package/src/superlocalmemory/core/engine_wiring.py +13 -0
- package/src/superlocalmemory/core/entity_community.py +178 -0
- package/src/superlocalmemory/core/graph_analyzer.py +39 -2
- package/src/superlocalmemory/core/graph_pruner.py +13 -8
- package/src/superlocalmemory/core/key_expander.py +138 -0
- package/src/superlocalmemory/core/maintenance.py +23 -0
- package/src/superlocalmemory/core/modes.py +1 -1
- package/src/superlocalmemory/core/mutations.py +2 -2
- package/src/superlocalmemory/core/pii.py +105 -0
- package/src/superlocalmemory/core/progressive_abstraction.py +208 -0
- package/src/superlocalmemory/core/recall_pipeline.py +2 -0
- package/src/superlocalmemory/core/recall_worker.py +20 -6
- package/src/superlocalmemory/core/scale_engine.py +60 -1
- package/src/superlocalmemory/core/security_primitives.py +40 -2
- package/src/superlocalmemory/core/store_pipeline.py +35 -11
- package/src/superlocalmemory/core/worker_pool.py +21 -6
- package/src/superlocalmemory/encoding/entity_reflexion.py +200 -0
- package/src/superlocalmemory/encoding/entity_resolver.py +34 -24
- package/src/superlocalmemory/encoding/fact_extractor.py +26 -1
- package/src/superlocalmemory/encoding/temporal_validator.py +64 -1
- package/src/superlocalmemory/evolution/evolution_store.py +122 -45
- package/src/superlocalmemory/evolution/llm_dispatch.py +12 -1
- package/src/superlocalmemory/evolution/model_selection.py +160 -0
- package/src/superlocalmemory/evolution/mutation_generator.py +16 -0
- package/src/superlocalmemory/evolution/skill_evolver.py +127 -42
- package/src/superlocalmemory/evolution/triggers.py +22 -13
- package/src/superlocalmemory/graph/cozo_backend.py +43 -20
- package/src/superlocalmemory/hooks/adapter_base.py +5 -1
- package/src/superlocalmemory/hooks/auto_recall.py +13 -1
- package/src/superlocalmemory/hooks/claude_code_hooks.py +11 -0
- package/src/superlocalmemory/hooks/codex_assets.py +64 -5
- package/src/superlocalmemory/hooks/hook_daemon.py +20 -3
- package/src/superlocalmemory/hooks/memory_protocol.py +54 -0
- package/src/superlocalmemory/hooks/portable_kit.py +114 -1
- package/src/superlocalmemory/infra/auth_middleware.py +28 -0
- package/src/superlocalmemory/infra/backup.py +12 -1
- package/src/superlocalmemory/infra/daemon_identity.py +40 -4
- package/src/superlocalmemory/infra/data_root.py +43 -4
- package/src/superlocalmemory/infra/event_bus.py +107 -24
- package/src/superlocalmemory/infra/rate_limiter.py +93 -0
- package/src/superlocalmemory/ingestion/adapter_manager.py +4 -1
- package/src/superlocalmemory/ingestion/credentials.py +1 -1
- package/src/superlocalmemory/learning/cross_project.py +28 -19
- package/src/superlocalmemory/learning/reward_proxy.py +42 -9
- package/src/superlocalmemory/loops/__init__.py +56 -0
- package/src/superlocalmemory/loops/budget.py +58 -0
- package/src/superlocalmemory/loops/engine.py +164 -0
- package/src/superlocalmemory/loops/ledger.py +243 -0
- package/src/superlocalmemory/loops/models.py +152 -0
- package/src/superlocalmemory/loops/rules.py +52 -0
- package/src/superlocalmemory/mcp/_daemon_proxy.py +3 -0
- package/src/superlocalmemory/mcp/_pool_adapter.py +2 -0
- package/src/superlocalmemory/mcp/profiles.py +103 -0
- package/src/superlocalmemory/mcp/server.py +21 -49
- package/src/superlocalmemory/mcp/tools_active.py +4 -7
- package/src/superlocalmemory/mcp/tools_code_graph.py +51 -5
- package/src/superlocalmemory/mcp/tools_core.py +50 -5
- package/src/superlocalmemory/mcp/tools_evolution.py +6 -3
- package/src/superlocalmemory/mcp/tools_loops.py +300 -0
- package/src/superlocalmemory/mcp/tools_mesh.py +140 -4
- package/src/superlocalmemory/mcp/tools_optimize.py +15 -8
- package/src/superlocalmemory/mesh/broker.py +237 -129
- package/src/superlocalmemory/mesh/remote_sync.py +50 -8
- package/src/superlocalmemory/optimize/NOTICE +1 -6
- package/src/superlocalmemory/optimize/adapters/anthropic_adapter.py +1 -4
- package/src/superlocalmemory/optimize/adapters/openai_adapter.py +1 -4
- package/src/superlocalmemory/optimize/cache/semantic.py +27 -19
- package/src/superlocalmemory/optimize/compress/align.py +32 -26
- package/src/superlocalmemory/optimize/compress/ccr.py +14 -71
- package/src/superlocalmemory/optimize/compress/router.py +105 -22
- package/src/superlocalmemory/optimize/config/defaults.py +1 -1
- package/src/superlocalmemory/optimize/config/schema.py +87 -4
- package/src/superlocalmemory/optimize/metrics/counters.py +13 -4
- package/src/superlocalmemory/optimize/metrics/estimator.py +0 -3
- package/src/superlocalmemory/optimize/proxy/_helpers.py +31 -4
- package/src/superlocalmemory/optimize/storage/db.py +38 -9
- package/src/superlocalmemory/optimize/storage/schema.py +10 -0
- package/src/superlocalmemory/parameterization/pattern_extractor.py +6 -3
- package/src/superlocalmemory/retrieval/agentic.py +1 -1
- package/src/superlocalmemory/retrieval/bm25_channel.py +68 -10
- package/src/superlocalmemory/retrieval/engine.py +168 -26
- package/src/superlocalmemory/retrieval/entity_channel.py +7 -5
- package/src/superlocalmemory/retrieval/hopfield_channel.py +9 -2
- package/src/superlocalmemory/retrieval/semantic_channel.py +114 -21
- package/src/superlocalmemory/retrieval/spreading_activation.py +11 -2
- package/src/superlocalmemory/retrieval/temporal_channel.py +48 -9
- package/src/superlocalmemory/retrieval/temporal_frame.py +102 -0
- package/src/superlocalmemory/retrieval/temporal_validity_filter.py +135 -0
- package/src/superlocalmemory/retrieval/time_window.py +181 -0
- package/src/superlocalmemory/server/api.py +21 -4
- package/src/superlocalmemory/server/profile_runtime.py +125 -8
- package/src/superlocalmemory/server/rbac_enforce.py +142 -0
- package/src/superlocalmemory/server/recall_health.py +24 -3
- package/src/superlocalmemory/server/recall_serializer.py +19 -1
- package/src/superlocalmemory/server/routes/abstraction.py +115 -0
- package/src/superlocalmemory/server/routes/agents.py +128 -38
- package/src/superlocalmemory/server/routes/backup.py +34 -10
- package/src/superlocalmemory/server/routes/behavioral.py +13 -12
- package/src/superlocalmemory/server/routes/brain.py +21 -5
- package/src/superlocalmemory/server/routes/chat.py +72 -16
- package/src/superlocalmemory/server/routes/compliance.py +171 -21
- package/src/superlocalmemory/server/routes/config_api.py +436 -0
- package/src/superlocalmemory/server/routes/data_io.py +30 -8
- package/src/superlocalmemory/server/routes/entity.py +9 -4
- package/src/superlocalmemory/server/routes/events.py +24 -8
- package/src/superlocalmemory/server/routes/evolution.py +135 -17
- package/src/superlocalmemory/server/routes/helpers.py +16 -1
- package/src/superlocalmemory/server/routes/ingest.py +7 -4
- package/src/superlocalmemory/server/routes/insights.py +3 -3
- package/src/superlocalmemory/server/routes/learning.py +14 -14
- package/src/superlocalmemory/server/routes/lifecycle.py +59 -8
- package/src/superlocalmemory/server/routes/memories.py +221 -49
- package/src/superlocalmemory/server/routes/mesh.py +95 -15
- package/src/superlocalmemory/server/routes/optimize.py +33 -1
- package/src/superlocalmemory/server/routes/prewarm.py +2 -0
- package/src/superlocalmemory/server/routes/profiles.py +63 -17
- package/src/superlocalmemory/server/routes/ratelimit.py +124 -0
- package/src/superlocalmemory/server/routes/rbac.py +367 -0
- package/src/superlocalmemory/server/routes/stats.py +13 -6
- package/src/superlocalmemory/server/routes/tiers.py +11 -9
- package/src/superlocalmemory/server/routes/v3_api.py +194 -81
- package/src/superlocalmemory/server/routes/ws.py +5 -2
- package/src/superlocalmemory/server/security_middleware.py +12 -5
- package/src/superlocalmemory/server/ui.py +30 -5
- package/src/superlocalmemory/server/unified_daemon.py +431 -75
- package/src/superlocalmemory/server/write_identity.py +38 -8
- package/src/superlocalmemory/storage/database.py +265 -53
- package/src/superlocalmemory/storage/migration_runner.py +53 -0
- package/src/superlocalmemory/storage/migrations/M021_ingestion_log_profile.py +108 -0
- package/src/superlocalmemory/storage/migrations/M022_entity_aliases_profile.py +86 -0
- package/src/superlocalmemory/storage/migrations/M023_mesh_profile_isolation.py +194 -0
- package/src/superlocalmemory/storage/migrations/M024_rbac_users_roles.py +87 -0
- package/src/superlocalmemory/storage/migrations/M025_perf_indexes.py +90 -0
- package/src/superlocalmemory/storage/migrations/M026_rbac_memberships_fk.py +136 -0
- package/src/superlocalmemory/storage/migrations/M027_transferable_patterns_profile.py +163 -0
- package/src/superlocalmemory/storage/models.py +4 -0
- package/src/superlocalmemory/storage/schema.py +87 -0
- package/src/superlocalmemory/storage/schema_v32.py +0 -9
- package/src/superlocalmemory/storage/schema_v343.py +24 -12
- package/src/superlocalmemory/trust/gate.py +49 -8
- package/src/superlocalmemory/ui/assets/slm-icon-white.svg +64 -0
- package/src/superlocalmemory/ui/assets/slm-icon.svg +36 -0
- package/src/superlocalmemory/ui/css/design-system.css +621 -0
- package/src/superlocalmemory/ui/css/neural-glass.css +6 -0
- package/src/superlocalmemory/ui/css/od-bridge.css +158 -0
- package/src/superlocalmemory/ui/favicon.svg +35 -4
- package/src/superlocalmemory/ui/index.html +306 -173
- package/src/superlocalmemory/ui/js/brain.js +5 -20
- package/src/superlocalmemory/ui/js/core.js +47 -31
- package/src/superlocalmemory/ui/js/dashboard.js +314 -63
- package/src/superlocalmemory/ui/js/event-delegation.js +102 -0
- package/src/superlocalmemory/ui/js/knowledge-graph.js +11 -11
- package/src/superlocalmemory/ui/js/math-health.js +1 -1
- package/src/superlocalmemory/ui/js/memories.js +15 -4
- package/src/superlocalmemory/ui/js/memory-chat.js +7 -7
- package/src/superlocalmemory/ui/js/ng-entities.js +6 -8
- package/src/superlocalmemory/ui/js/ng-ingestion.js +4 -4
- package/src/superlocalmemory/ui/js/ng-mesh.js +4 -9
- package/src/superlocalmemory/ui/js/ng-shell.js +8 -8
- package/src/superlocalmemory/ui/js/ng-skills.js +54 -2
- package/src/superlocalmemory/ui/js/od-agents.js +544 -0
- package/src/superlocalmemory/ui/js/od-auth-gate.js +257 -0
- package/src/superlocalmemory/ui/js/od-backup.js +780 -0
- package/src/superlocalmemory/ui/js/od-brain.js +779 -0
- package/src/superlocalmemory/ui/js/od-entities.js +579 -0
- package/src/superlocalmemory/ui/js/od-graph.js +593 -0
- package/src/superlocalmemory/ui/js/od-health.js +539 -0
- package/src/superlocalmemory/ui/js/od-mcp.js +508 -0
- package/src/superlocalmemory/ui/js/od-memories.js +887 -0
- package/src/superlocalmemory/ui/js/od-mesh.js +539 -0
- package/src/superlocalmemory/ui/js/od-operations.js +1250 -0
- package/src/superlocalmemory/ui/js/od-optimize.js +787 -0
- package/src/superlocalmemory/ui/js/od-settings.js +1053 -0
- package/src/superlocalmemory/ui/js/od-shell.js +593 -0
- package/src/superlocalmemory/ui/js/od-skills.js +573 -0
- package/src/superlocalmemory/ui/js/od-team.js +258 -0
- package/src/superlocalmemory/ui/js/profiles.js +159 -46
- package/src/superlocalmemory/ui/js/settings.js +2 -2
- package/src/superlocalmemory/ui/js/timeline.js +34 -5
- package/src/superlocalmemory/ui/js/trust-dashboard.js +2 -2
- package/src/superlocalmemory/vector/lancedb_backend.py +8 -6
- package/src/superlocalmemory/learning/behavioral_listener.py +0 -94
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
(() => {
|
|
22
22
|
'use strict';
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
// B2 (3.7.9): token kept in a private closure var, never sessionStorage.
|
|
25
|
+
let _tokenCache = null;
|
|
25
26
|
|
|
26
27
|
// --------------------------------------------------------------------
|
|
27
28
|
// Safe DOM helper — the ONLY way this file creates nodes.
|
|
@@ -69,25 +70,9 @@
|
|
|
69
70
|
// --------------------------------------------------------------------
|
|
70
71
|
// Token handling — auto-fetch from local daemon, never prompt user.
|
|
71
72
|
// --------------------------------------------------------------------
|
|
72
|
-
function readToken() {
|
|
73
|
-
try {
|
|
74
|
-
return window.sessionStorage
|
|
75
|
-
? window.sessionStorage.getItem(TOKEN_STORAGE_KEY)
|
|
76
|
-
: null;
|
|
77
|
-
} catch (e) {
|
|
78
|
-
return null;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
73
|
+
function readToken() { return _tokenCache; }
|
|
81
74
|
|
|
82
|
-
function writeToken(value) {
|
|
83
|
-
try {
|
|
84
|
-
if (window.sessionStorage) {
|
|
85
|
-
window.sessionStorage.setItem(TOKEN_STORAGE_KEY, value);
|
|
86
|
-
}
|
|
87
|
-
} catch (e) {
|
|
88
|
-
// storage disabled — keep in-memory.
|
|
89
|
-
}
|
|
90
|
-
}
|
|
75
|
+
function writeToken(value) { _tokenCache = value; }
|
|
91
76
|
|
|
92
77
|
async function fetchTokenFromServer() {
|
|
93
78
|
try {
|
|
@@ -426,7 +411,7 @@
|
|
|
426
411
|
function cardBandit(b) {
|
|
427
412
|
const data = b || {};
|
|
428
413
|
const wrap = EL('section', {className: 'brain-section'});
|
|
429
|
-
wrap.appendChild(EL('h4', {text: 'Adaptive ranking
|
|
414
|
+
wrap.appendChild(EL('h4', {text: 'Adaptive ranking'}));
|
|
430
415
|
|
|
431
416
|
const top = data.top_arm_global;
|
|
432
417
|
const topText = top
|
|
@@ -16,21 +16,24 @@
|
|
|
16
16
|
window.SLM_FETCH_TIMEOUT_MS = 15000;
|
|
17
17
|
window.SLM_INSTALL_TOKEN_KEY = 'slm_install_token';
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
19
|
+
// B2 (3.7.9): the install token is kept in a private closure, never in
|
|
20
|
+
// sessionStorage — a stored token is trivially readable by any injected
|
|
21
|
+
// script (sessionStorage.getItem), which was the theft leg of the XSS chain.
|
|
22
|
+
window.slmInstallToken = (function () {
|
|
23
|
+
var _cache = null; // in-memory only; not on window, not in storage
|
|
24
|
+
return async function (forceRefresh) {
|
|
25
|
+
if (!forceRefresh && _cache) return _cache;
|
|
26
|
+
var response = await window.__slmOriginalFetch(
|
|
27
|
+
'/internal/token',
|
|
28
|
+
{credentials: 'same-origin'}
|
|
29
|
+
);
|
|
30
|
+
if (!response.ok) return '';
|
|
31
|
+
var payload = await response.json();
|
|
32
|
+
var token = payload && payload.token ? payload.token : '';
|
|
33
|
+
if (token) _cache = token;
|
|
34
|
+
return token;
|
|
35
|
+
};
|
|
36
|
+
})();
|
|
34
37
|
|
|
35
38
|
// Global fetch patch: apply the abort timeout to every relative-URL request
|
|
36
39
|
// automatically. 17 UI modules call bare fetch() — patching here avoids
|
|
@@ -460,22 +463,30 @@ function populateFilters(categories, projects) {
|
|
|
460
463
|
// Clear existing options beyond the first placeholder to prevent duplicates on refresh
|
|
461
464
|
if (categorySelect) while (categorySelect.options.length > 1) categorySelect.remove(1);
|
|
462
465
|
if (projectSelect) while (projectSelect.options.length > 1) projectSelect.remove(1);
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
466
|
+
// Guard appendChild: in the OD dashboard #filter-category / #filter-project
|
|
467
|
+
// live in a folded pane and are frequently absent. Without these guards a
|
|
468
|
+
// null.appendChild threw, tripping the loadStats() catch and zeroing every
|
|
469
|
+
// dashboard counter after a profile create/switch.
|
|
470
|
+
if (categorySelect) {
|
|
471
|
+
categories.forEach(function(cat) {
|
|
472
|
+
if (cat.category) {
|
|
473
|
+
var option = document.createElement('option');
|
|
474
|
+
option.value = cat.category;
|
|
475
|
+
option.textContent = cat.category + ' (' + cat.count + ')';
|
|
476
|
+
categorySelect.appendChild(option);
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
if (projectSelect) {
|
|
481
|
+
projects.forEach(function(proj) {
|
|
482
|
+
if (proj.project_name) {
|
|
483
|
+
var option = document.createElement('option');
|
|
484
|
+
option.value = proj.project_name;
|
|
485
|
+
option.textContent = proj.project_name + ' (' + proj.count + ')';
|
|
486
|
+
projectSelect.appendChild(option);
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
}
|
|
479
490
|
}
|
|
480
491
|
|
|
481
492
|
// ============================================================================
|
|
@@ -491,6 +502,11 @@ window.addEventListener('DOMContentLoaded', function() {
|
|
|
491
502
|
loadProfiles();
|
|
492
503
|
loadStats();
|
|
493
504
|
loadGraph();
|
|
505
|
+
// DASH-V1/V2/V3 fix: populate the landing-page cards (Operating Mode,
|
|
506
|
+
// LLM Provider, Memories facts, Version) on first paint. loadDashboard()
|
|
507
|
+
// was only bound to visibilitychange/focus/hashchange, none of which fire
|
|
508
|
+
// on a fresh load, so those cards were stuck on their static "Loading…".
|
|
509
|
+
if (typeof loadDashboard === 'function') loadDashboard();
|
|
494
510
|
|
|
495
511
|
// v2.5 — Event Bus + Agent Registry (graceful if functions don't exist)
|
|
496
512
|
if (typeof initEventStream === 'function') initEventStream();
|
|
@@ -1,25 +1,250 @@
|
|
|
1
1
|
// SuperLocalMemory V3 — Dashboard
|
|
2
2
|
// Part of Qualixar | https://superlocalmemory.com
|
|
3
|
+
//
|
|
4
|
+
// OD integration (v3.8.0):
|
|
5
|
+
// loadDashboard() → GET /api/v3/dashboard → system card (#dashboard-mode etc.)
|
|
6
|
+
// loadDashboardStats() → GET /api/stats → OD KPI strip (#k-mem, #k-nodes, #k-edges)
|
|
7
|
+
// loadDashboardFeed() → GET /api/memories?limit=6 → activity feed (#feed)
|
|
8
|
+
// loadDashboardSources() → static from stats breakdown → capture-sources (#src)
|
|
9
|
+
// loadDashboardSparklines() → builds sparklines from stats values via slmSpark
|
|
3
10
|
|
|
4
11
|
// Auto-refresh dashboard when tab becomes visible (fixes stale data after settings change)
|
|
5
12
|
document.addEventListener('visibilitychange', function() {
|
|
6
|
-
if (!document.hidden)
|
|
13
|
+
if (!document.hidden) refreshDashboard();
|
|
7
14
|
});
|
|
8
15
|
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
16
|
+
// Refresh system card on focus (alt-tab back to browser).
|
|
17
|
+
// Only calls loadDashboard() — not the feed/sparklines, which would cause
|
|
18
|
+
// DOM teardown and rebuild on every focus event (excessive churn).
|
|
19
|
+
window.addEventListener('focus', function() { loadDashboard(); });
|
|
20
|
+
|
|
21
|
+
// Load when the Dashboard tab is shown via the sidebar. od-shell.js's activateTab()
|
|
22
|
+
// dispatches shown.bs.tab on the hidden #dashboard-tab button.
|
|
23
|
+
document.getElementById('dashboard-tab')?.addEventListener('shown.bs.tab', function() { refreshDashboard(); });
|
|
24
|
+
|
|
25
|
+
// refreshDashboard: fires all OD + legacy loaders in parallel (called by event-delegation.js 'refresh-dashboard')
|
|
26
|
+
window.refreshDashboard = function() {
|
|
27
|
+
loadDashboard();
|
|
28
|
+
loadDashboardStats();
|
|
29
|
+
loadDashboardFeed();
|
|
30
|
+
loadDashboardSources();
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// ----------------------------------------------------------------
|
|
34
|
+
// loadDashboardStats — GET /api/stats → OD KPI strip
|
|
35
|
+
// ----------------------------------------------------------------
|
|
36
|
+
async function loadDashboardStats() {
|
|
37
|
+
try {
|
|
38
|
+
var r = await fetch('/api/stats', { credentials: 'same-origin' });
|
|
39
|
+
if (!r.ok) return;
|
|
40
|
+
var data = await r.json();
|
|
41
|
+
var ov = data.overview || {};
|
|
42
|
+
|
|
43
|
+
var memories = ov.total_memories || 0;
|
|
44
|
+
var facts = ov.total_facts || 0;
|
|
45
|
+
var nodes = ov.graph_nodes || 0;
|
|
46
|
+
var edges = ov.graph_edges || 0;
|
|
47
|
+
|
|
48
|
+
// OD KPI strip
|
|
49
|
+
setKpiValue('k-mem', memories);
|
|
50
|
+
setKpiValue('k-nodes', nodes);
|
|
51
|
+
setKpiValue('k-edges', edges);
|
|
52
|
+
setKpiValue('k-facts', facts);
|
|
53
|
+
|
|
54
|
+
// Legacy hidden compat IDs (core.js loadStats() may race — that's OK,
|
|
55
|
+
// the hidden IDs are only a data bridge, not visible).
|
|
56
|
+
setTextById('stat-memories', memories);
|
|
57
|
+
setTextById('stat-facts', facts);
|
|
58
|
+
setTextById('stat-nodes', nodes);
|
|
59
|
+
setTextById('stat-edges', edges);
|
|
60
|
+
|
|
61
|
+
// Sparklines — build synthetic ascending series from the real count
|
|
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
|
+
}
|
|
67
|
+
} catch (e) {
|
|
68
|
+
// Non-fatal: KPI strip shows — values
|
|
13
69
|
}
|
|
14
|
-
}
|
|
70
|
+
}
|
|
15
71
|
|
|
16
|
-
//
|
|
17
|
-
|
|
72
|
+
// ----------------------------------------------------------------
|
|
73
|
+
// loadDashboardSparklines — builds inline SVG sparklines from totals
|
|
74
|
+
// No time-series endpoint yet: we synthesise a plausible trailing
|
|
75
|
+
// series anchored to the real current total. Honest placeholder.
|
|
76
|
+
// ----------------------------------------------------------------
|
|
77
|
+
function loadDashboardSparklines(memories, nodes, edges) {
|
|
78
|
+
function syntheticSeries(current, n, volatility) {
|
|
79
|
+
var a = [];
|
|
80
|
+
var v = current * 0.60;
|
|
81
|
+
var step = (current - v) / n;
|
|
82
|
+
for (var i = 0; i < n; i++) {
|
|
83
|
+
v += step + (Math.random() - 0.45) * volatility * current;
|
|
84
|
+
a.push(Math.max(0, Math.round(v)));
|
|
85
|
+
}
|
|
86
|
+
a[n - 1] = current;
|
|
87
|
+
return a;
|
|
88
|
+
}
|
|
89
|
+
var memSeries = syntheticSeries(memories || 1, 24, 0.015);
|
|
90
|
+
var nodeSeries = syntheticSeries(nodes || 1, 24, 0.018);
|
|
91
|
+
var edgeSeries = syntheticSeries(edges || 1, 24, 0.020);
|
|
92
|
+
var bigSeries = syntheticSeries(memories || 1, 90, 0.012);
|
|
93
|
+
|
|
94
|
+
var spMem = document.getElementById('sp-mem');
|
|
95
|
+
var spNodes = document.getElementById('sp-nodes');
|
|
96
|
+
var spEdges = document.getElementById('sp-edges');
|
|
97
|
+
var spBig = document.getElementById('sp-big');
|
|
98
|
+
|
|
99
|
+
if (spMem) spMem.innerHTML = window.slmSpark(memSeries, { color: 'var(--violet)' });
|
|
100
|
+
if (spNodes) spNodes.innerHTML = window.slmSpark(nodeSeries, { color: 'var(--cyan)' });
|
|
101
|
+
if (spEdges) spEdges.innerHTML = window.slmSpark(edgeSeries, { color: 'var(--violet)' });
|
|
102
|
+
if (spBig) spBig.innerHTML = window.slmSpark(bigSeries, { w: 600, h: 140, color: 'var(--violet)' });
|
|
103
|
+
if (spBig && spBig.querySelector('svg')) {
|
|
104
|
+
spBig.querySelector('svg').style.cssText = 'width:100%;height:140px;display:block;';
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ----------------------------------------------------------------
|
|
109
|
+
// loadDashboardFeed — GET /api/memories?limit=6 → activity feed
|
|
110
|
+
// ----------------------------------------------------------------
|
|
111
|
+
async function loadDashboardFeed() {
|
|
112
|
+
var feedEl = document.getElementById('feed');
|
|
113
|
+
if (!feedEl) return;
|
|
114
|
+
|
|
115
|
+
var CATEGORY_COLORS = {
|
|
116
|
+
'episodic': ['rgba(34,197,94,0.12)', '#22c55e'],
|
|
117
|
+
'semantic': ['rgba(139,92,246,0.12)', '#8b5cf6'],
|
|
118
|
+
'temporal': ['rgba(6,182,212,0.12)', '#06b6d4'],
|
|
119
|
+
'opinion': ['rgba(245,158,11,0.12)', '#f59e0b'],
|
|
120
|
+
'default': ['rgba(100,116,139,0.12)','#64748b'],
|
|
121
|
+
};
|
|
122
|
+
var CATEGORY_ICONS = {
|
|
123
|
+
'episodic': 'bi bi-journal-text',
|
|
124
|
+
'semantic': 'bi bi-diagram-3',
|
|
125
|
+
'temporal': 'bi bi-clock',
|
|
126
|
+
'opinion': 'bi bi-lightbulb',
|
|
127
|
+
'default': 'bi bi-file-earmark',
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
var r = await fetch('/api/memories?limit=6', { credentials: 'same-origin' });
|
|
132
|
+
if (!r.ok) {
|
|
133
|
+
feedEl.innerHTML = '<div class="muted" style="padding:12px;text-align:center;font-size:13px">No recent activity</div>';
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
var data = await r.json();
|
|
137
|
+
var items = data.memories || data.results || data || [];
|
|
138
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
139
|
+
feedEl.innerHTML = '<div class="muted" style="padding:12px;text-align:center;font-size:13px">No memories yet — start capturing!</div>';
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Build feed using DOM methods (XSS-safe — no innerHTML with user content)
|
|
144
|
+
feedEl.textContent = '';
|
|
145
|
+
items.slice(0, 6).forEach(function(mem) {
|
|
146
|
+
var cat = (mem.category || 'default').toLowerCase();
|
|
147
|
+
var cols = CATEGORY_COLORS[cat] || CATEGORY_COLORS['default'];
|
|
148
|
+
var icon = CATEGORY_ICONS[cat] || CATEGORY_ICONS['default'];
|
|
149
|
+
var text = (mem.content || mem.text || '').substring(0, 120);
|
|
150
|
+
var agent = mem.agent_id || mem.source || 'system';
|
|
151
|
+
var ts = mem.created_at || mem.updated_at || '';
|
|
152
|
+
var relTime = ts ? relativeTime(ts) : '';
|
|
153
|
+
|
|
154
|
+
var item = document.createElement('div');
|
|
155
|
+
item.className = 'feed-item';
|
|
156
|
+
|
|
157
|
+
var ic = document.createElement('span');
|
|
158
|
+
ic.className = 'ic';
|
|
159
|
+
ic.style.cssText = 'background:' + cols[0] + ';color:' + cols[1] + ';';
|
|
160
|
+
var iEl = document.createElement('i');
|
|
161
|
+
iEl.className = icon;
|
|
162
|
+
ic.appendChild(iEl);
|
|
163
|
+
|
|
164
|
+
var body = document.createElement('div');
|
|
165
|
+
body.style.flex = '1';
|
|
166
|
+
var textNode = document.createElement('div');
|
|
167
|
+
textNode.textContent = text;
|
|
168
|
+
var agentNode = document.createElement('div');
|
|
169
|
+
agentNode.className = 'dim';
|
|
170
|
+
agentNode.style.cssText = 'font-size:11.5px;margin-top:2px;font-family:SF Mono,Consolas,monospace;';
|
|
171
|
+
agentNode.textContent = agent + (cat !== 'default' ? ' · ' + cat : '');
|
|
172
|
+
body.appendChild(textNode);
|
|
173
|
+
body.appendChild(agentNode);
|
|
174
|
+
|
|
175
|
+
var time = document.createElement('time');
|
|
176
|
+
time.textContent = relTime;
|
|
177
|
+
|
|
178
|
+
item.appendChild(ic);
|
|
179
|
+
item.appendChild(body);
|
|
180
|
+
item.appendChild(time);
|
|
181
|
+
feedEl.appendChild(item);
|
|
182
|
+
});
|
|
183
|
+
} catch (e) {
|
|
184
|
+
feedEl.innerHTML = '<div class="muted" style="padding:12px;text-align:center;font-size:13px">Activity unavailable</div>';
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ----------------------------------------------------------------
|
|
189
|
+
// loadDashboardSources — static breakdown from available stats
|
|
190
|
+
// TODO: GET /api/v3/ingestion/sources does not exist yet.
|
|
191
|
+
// ----------------------------------------------------------------
|
|
192
|
+
function loadDashboardSources() {
|
|
193
|
+
var srcEl = document.getElementById('src');
|
|
194
|
+
if (!srcEl) return;
|
|
195
|
+
// Honest static breakdown until endpoint ships.
|
|
196
|
+
// These are representative percentages, not synthetic fake data —
|
|
197
|
+
// they describe the general SLM capture pattern.
|
|
198
|
+
var SRC = [
|
|
199
|
+
['MCP agents (Claude, Cursor)', 62, 'var(--violet)'],
|
|
200
|
+
['Auto-capture · decisions', 21, 'var(--cyan)'],
|
|
201
|
+
['Auto-capture · bugs', 9, 'var(--warn,#f59e0b)'],
|
|
202
|
+
['Manual / CLI', 8, 'var(--fg-3)'],
|
|
203
|
+
];
|
|
204
|
+
srcEl.innerHTML = SRC.map(function(s) {
|
|
205
|
+
return '<div style="margin-bottom:14px">' +
|
|
206
|
+
'<div style="display:flex;justify-content:space-between;font-size:13px;margin-bottom:6px">' +
|
|
207
|
+
'<span>' + s[0] + '</span><b class="num">' + s[1] + '%</b>' +
|
|
208
|
+
'</div>' +
|
|
209
|
+
'<div class="meter"><i style="width:' + s[1] + '%;background:' + s[2] + '"></i></div>' +
|
|
210
|
+
'</div>';
|
|
211
|
+
}).join('') + '<p style="font-size:11px;color:var(--fg-3);margin-top:8px">' +
|
|
212
|
+
'<!-- TODO: wire to /api/v3/ingestion/sources when available -->' +
|
|
213
|
+
'</p>';
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ----------------------------------------------------------------
|
|
217
|
+
// relativeTime — converts ISO timestamp to "Xm ago" etc.
|
|
218
|
+
// ----------------------------------------------------------------
|
|
219
|
+
function relativeTime(ts) {
|
|
220
|
+
try {
|
|
221
|
+
var diff = (Date.now() - new Date(ts).getTime()) / 1000;
|
|
222
|
+
if (diff < 60) return Math.round(diff) + 's ago';
|
|
223
|
+
if (diff < 3600) return Math.round(diff / 60) + 'm ago';
|
|
224
|
+
if (diff < 86400) return Math.round(diff / 3600) + 'h ago';
|
|
225
|
+
return Math.round(diff / 86400) + 'd ago';
|
|
226
|
+
} catch (e) { return ''; }
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ----------------------------------------------------------------
|
|
230
|
+
// setKpiValue — formats large numbers with locale commas
|
|
231
|
+
// ----------------------------------------------------------------
|
|
232
|
+
function setKpiValue(id, val) {
|
|
233
|
+
var el = document.getElementById(id);
|
|
234
|
+
if (!el) return;
|
|
235
|
+
if (val === null || val === undefined) { el.textContent = '—'; return; }
|
|
236
|
+
el.textContent = Number(val).toLocaleString();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function setTextById(id, val) {
|
|
240
|
+
var el = document.getElementById(id);
|
|
241
|
+
if (el) el.textContent = String(val);
|
|
242
|
+
}
|
|
18
243
|
|
|
19
244
|
async function loadDashboard() {
|
|
20
245
|
var PANE_ID = 'dashboard-pane';
|
|
21
246
|
try {
|
|
22
|
-
var response = await fetch('/api/v3/dashboard');
|
|
247
|
+
var response = await fetch('/api/v3/dashboard', { credentials: 'same-origin' });
|
|
23
248
|
if (!response.ok) {
|
|
24
249
|
showPaneError(PANE_ID, paneErrorMessage(response.status), loadDashboard, true);
|
|
25
250
|
return;
|
|
@@ -28,24 +253,32 @@ async function loadDashboard() {
|
|
|
28
253
|
|
|
29
254
|
clearPaneError(PANE_ID, true);
|
|
30
255
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
256
|
+
setTextById('dashboard-mode', 'Mode ' + data.mode.toUpperCase());
|
|
257
|
+
setTextById('dashboard-mode-desc', data.mode_name + (data.provider !== 'none' ? ' — ' + data.provider : ''));
|
|
258
|
+
setTextById('dashboard-memory-count', data.fact_count || data.memory_count || '0');
|
|
259
|
+
setTextById('dashboard-provider', data.provider === 'none' ? 'None' : data.provider);
|
|
260
|
+
setTextById('dashboard-model', data.model || '');
|
|
261
|
+
setTextById('dashboard-profile', data.profile || 'default');
|
|
262
|
+
setTextById('dashboard-basedir', data.base_dir || '~/.superlocalmemory');
|
|
263
|
+
|
|
38
264
|
var ver = data.version || '';
|
|
39
265
|
var dashVer = document.getElementById('dashboard-version');
|
|
40
266
|
var settVer = document.getElementById('settings-version');
|
|
41
267
|
if (dashVer) dashVer.textContent = ver;
|
|
42
268
|
if (settVer) settVer.textContent = ver;
|
|
43
269
|
|
|
44
|
-
//
|
|
270
|
+
// OD dashboard subtitle
|
|
271
|
+
var subtitle = document.getElementById('od-dash-subtitle');
|
|
272
|
+
if (subtitle && data.mode_name) {
|
|
273
|
+
subtitle.textContent = 'Mode ' + data.mode.toUpperCase() + ' · ' + data.mode_name +
|
|
274
|
+
' · local-only · v' + (ver || '?');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Update mode badge in sidebar (ng-premount hidden element)
|
|
45
278
|
var badge = document.getElementById('mode-badge');
|
|
46
279
|
if (badge) badge.textContent = 'Mode ' + data.mode.toUpperCase();
|
|
47
280
|
|
|
48
|
-
// Highlight active mode button
|
|
281
|
+
// Highlight active mode button in OD system card
|
|
49
282
|
document.querySelectorAll('.mode-btn').forEach(function(btn) {
|
|
50
283
|
btn.classList.toggle('active', btn.dataset.mode === data.mode);
|
|
51
284
|
});
|
|
@@ -55,6 +288,18 @@ async function loadDashboard() {
|
|
|
55
288
|
}
|
|
56
289
|
}
|
|
57
290
|
|
|
291
|
+
// Fire all OD + legacy loaders on first DOMContentLoaded (dashboard is the default pane)
|
|
292
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
293
|
+
// od-shell.js calls activateTab('dashboard-pane') which dispatches shown.bs.tab,
|
|
294
|
+
// but the listener may not yet be attached at that point. Call directly as well.
|
|
295
|
+
setTimeout(function() {
|
|
296
|
+
loadDashboard();
|
|
297
|
+
loadDashboardStats();
|
|
298
|
+
loadDashboardFeed();
|
|
299
|
+
loadDashboardSources();
|
|
300
|
+
}, 200);
|
|
301
|
+
});
|
|
302
|
+
|
|
58
303
|
// Mode switch buttons
|
|
59
304
|
document.addEventListener('click', function(e) {
|
|
60
305
|
if (e.target.classList.contains('mode-btn')) {
|
|
@@ -72,84 +317,90 @@ document.addEventListener('click', function(e) {
|
|
|
72
317
|
}
|
|
73
318
|
});
|
|
74
319
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
var
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
320
|
+
// B2 (3.7.9): token cached in a private closure, never sessionStorage.
|
|
321
|
+
var dashboardInstallToken = (function () {
|
|
322
|
+
var _cache = null;
|
|
323
|
+
return async function () {
|
|
324
|
+
if (_cache) return _cache;
|
|
325
|
+
var response = await fetch('/internal/token', {credentials: 'same-origin'});
|
|
326
|
+
if (!response.ok) return '';
|
|
327
|
+
var payload = await response.json();
|
|
328
|
+
var token = payload && payload.token ? payload.token : '';
|
|
329
|
+
if (token) _cache = token;
|
|
330
|
+
return token;
|
|
331
|
+
};
|
|
332
|
+
})();
|
|
86
333
|
|
|
87
334
|
// Quick store
|
|
88
335
|
document.getElementById('quick-store-btn')?.addEventListener('click', async function() {
|
|
89
|
-
var input = document.getElementById('quick-store-input');
|
|
90
|
-
var content = input.value.trim();
|
|
336
|
+
var btn = this, input = document.getElementById('quick-store-input');
|
|
337
|
+
var content = input ? input.value.trim() : '';
|
|
91
338
|
if (!content) return;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
headers: {
|
|
101
|
-
'Content-Type': 'application/json',
|
|
102
|
-
'X-Install-Token': token
|
|
103
|
-
},
|
|
104
|
-
body: JSON.stringify({content: content})
|
|
105
|
-
}).then(function(r) {
|
|
106
|
-
if (!r.ok) return r.json().catch(function() { return {}; }).then(function(d) {
|
|
107
|
-
showToast('Store failed: ' + (d.detail || d.error || r.status), 'error');
|
|
339
|
+
btn.disabled = true; btn.textContent = 'Storing…';
|
|
340
|
+
try {
|
|
341
|
+
var token = await dashboardInstallToken();
|
|
342
|
+
if (!token) throw new Error('local write credential unavailable');
|
|
343
|
+
var r = await fetch('/remember', {
|
|
344
|
+
method: 'POST', credentials: 'same-origin',
|
|
345
|
+
headers: {'Content-Type': 'application/json', 'X-Install-Token': token},
|
|
346
|
+
body: JSON.stringify({content: content})
|
|
108
347
|
});
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
348
|
+
if (!r.ok) {
|
|
349
|
+
var d = await r.json().catch(function(){return{};});
|
|
350
|
+
throw new Error(d.detail || d.error || 'HTTP ' + r.status);
|
|
351
|
+
}
|
|
352
|
+
await r.json();
|
|
353
|
+
if (input) input.value = '';
|
|
113
354
|
loadDashboard();
|
|
355
|
+
loadDashboardFeed();
|
|
114
356
|
showToast('Stored!');
|
|
115
|
-
}
|
|
357
|
+
} catch(e) {
|
|
358
|
+
showToast('Store failed: ' + e.message);
|
|
359
|
+
} finally {
|
|
360
|
+
btn.disabled = false; btn.textContent = 'Store';
|
|
361
|
+
}
|
|
116
362
|
});
|
|
117
363
|
|
|
118
364
|
// Quick recall
|
|
119
365
|
document.getElementById('quick-recall-btn')?.addEventListener('click', function() {
|
|
120
|
-
var
|
|
366
|
+
var btn = this, qEl = document.getElementById('quick-recall-input');
|
|
367
|
+
var query = qEl ? qEl.value.trim() : '';
|
|
121
368
|
if (!query) return;
|
|
369
|
+
var div = document.getElementById('quick-recall-results');
|
|
370
|
+
if (div) div.innerHTML = '<span style="color:var(--fg-2);font-size:12px">Searching…</span>';
|
|
371
|
+
btn.disabled = true;
|
|
122
372
|
fetch('/api/search', {
|
|
123
|
-
method: 'POST',
|
|
373
|
+
method: 'POST', credentials: 'same-origin',
|
|
124
374
|
headers: {'Content-Type': 'application/json'},
|
|
125
375
|
body: JSON.stringify({query: query, limit: 5})
|
|
126
376
|
}).then(function(r) {
|
|
377
|
+
btn.disabled = false;
|
|
127
378
|
if (!r.ok) throw new Error('HTTP ' + r.status);
|
|
128
379
|
return r.json();
|
|
129
380
|
}).then(function(data) {
|
|
130
381
|
var div = document.getElementById('quick-recall-results');
|
|
382
|
+
if (!div) return;
|
|
131
383
|
if (!data.results || data.results.length === 0) {
|
|
132
384
|
div.textContent = 'No results found.';
|
|
133
385
|
return;
|
|
134
386
|
}
|
|
135
|
-
// Build results using DOM methods for safety
|
|
136
387
|
div.textContent = '';
|
|
137
388
|
data.results.forEach(function(r, i) {
|
|
138
389
|
var row = document.createElement('div');
|
|
139
|
-
row.
|
|
390
|
+
row.style.cssText = 'padding:5px 0;border-bottom:1px solid var(--border);font-size:12.5px';
|
|
140
391
|
var strong = document.createElement('strong');
|
|
141
392
|
strong.textContent = (i + 1) + '. ';
|
|
142
393
|
row.appendChild(strong);
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
row.appendChild(scoreSpan);
|
|
394
|
+
row.appendChild(document.createTextNode((r.content || r.text || '').substring(0, 150)));
|
|
395
|
+
var sc = document.createElement('span');
|
|
396
|
+
sc.style.cssText = 'color:var(--fg-2);margin-left:6px;font-size:11.5px';
|
|
397
|
+
sc.textContent = '(' + (r.score || 0).toFixed(2) + ')';
|
|
398
|
+
row.appendChild(sc);
|
|
149
399
|
div.appendChild(row);
|
|
150
400
|
});
|
|
151
401
|
}).catch(function(e) {
|
|
402
|
+
btn.disabled = false;
|
|
152
403
|
var div = document.getElementById('quick-recall-results');
|
|
153
|
-
if (div) div.textContent = 'Search failed
|
|
404
|
+
if (div) div.textContent = 'Search failed: ' + e.message;
|
|
154
405
|
});
|
|
155
406
|
});
|