nexo-brain 5.3.26 → 5.3.27

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 (211) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/package.json +1 -1
  3. package/src/server.py +3 -0
  4. package/src/tools_sessions.py +6 -1
  5. package/src/dashboard/static/favicon 2.svg +0 -32
  6. package/src/dashboard/static/nexo-logo 2.png +0 -0
  7. package/src/dashboard/static/nexo-logo 2.svg +0 -40
  8. package/src/dashboard/static/style 2.css +0 -2458
  9. package/src/dashboard/templates/adaptive 2.html +0 -118
  10. package/src/dashboard/templates/artifacts 2.html +0 -133
  11. package/src/dashboard/templates/backups 2.html +0 -136
  12. package/src/dashboard/templates/base 2.html +0 -417
  13. package/src/dashboard/templates/calendar 2.html +0 -591
  14. package/src/dashboard/templates/chat 2.html +0 -356
  15. package/src/dashboard/templates/claims 2.html +0 -259
  16. package/src/dashboard/templates/cortex 2.html +0 -321
  17. package/src/dashboard/templates/credentials 2.html +0 -128
  18. package/src/dashboard/templates/crons 2.html +0 -370
  19. package/src/dashboard/templates/dashboard 2.html +0 -494
  20. package/src/dashboard/templates/dreams 2.html +0 -252
  21. package/src/dashboard/templates/email 2.html +0 -160
  22. package/src/dashboard/templates/evolution 2.html +0 -189
  23. package/src/dashboard/templates/feed 2.html +0 -249
  24. package/src/dashboard/templates/followup_health 2.html +0 -170
  25. package/src/dashboard/templates/graph 2.html +0 -201
  26. package/src/dashboard/templates/guard 2.html +0 -259
  27. package/src/dashboard/templates/inbox 2.html +0 -251
  28. package/src/dashboard/templates/memory 2.html +0 -420
  29. package/src/dashboard/templates/operations 2.html +0 -608
  30. package/src/dashboard/templates/plugins 2.html +0 -185
  31. package/src/dashboard/templates/protocol 2.html +0 -199
  32. package/src/dashboard/templates/rules 2.html +0 -246
  33. package/src/dashboard/templates/sentiment 2.html +0 -247
  34. package/src/dashboard/templates/sessions 2.html +0 -218
  35. package/src/dashboard/templates/skills 2.html +0 -329
  36. package/src/dashboard/templates/somatic 2.html +0 -73
  37. package/src/dashboard/templates/triggers 2.html +0 -133
  38. package/src/dashboard/templates/trust 2.html +0 -360
  39. package/src/db/__init__ 2.py +0 -259
  40. package/src/db/_core 2.py +0 -437
  41. package/src/db/_credentials 2.py +0 -124
  42. package/src/db/_episodic 2.py +0 -762
  43. package/src/db/_evolution 2.py +0 -54
  44. package/src/db/_fts 2.py +0 -406
  45. package/src/db/_goal_profiles 2.py +0 -376
  46. package/src/db/_hot_context 2.py +0 -660
  47. package/src/db/_outcomes 2.py +0 -800
  48. package/src/db/_personal_scripts 2.py +0 -582
  49. package/src/db/_sessions 2.py +0 -330
  50. package/src/db/_tasks 2.py +0 -91
  51. package/src/db/_watchers 2.py +0 -173
  52. package/src/doctor/formatters 2.py +0 -52
  53. package/src/doctor/models 2.py +0 -69
  54. package/src/doctor/planes 2.py +0 -87
  55. package/src/doctor/providers/__init__ 2.py +0 -1
  56. package/src/doctor/providers/deep 2.py +0 -367
  57. package/src/evolution_cycle 2.py +0 -519
  58. package/src/hooks/auto_capture 2.py +0 -208
  59. package/src/hooks/caffeinate-guard 2.sh +0 -8
  60. package/src/hooks/capture-session 2.sh +0 -21
  61. package/src/hooks/capture-tool-logs 2.sh +0 -158
  62. package/src/hooks/daily-briefing-check 2.sh +0 -33
  63. package/src/hooks/heartbeat-enforcement 2.py +0 -90
  64. package/src/hooks/heartbeat-posttool 2.sh +0 -18
  65. package/src/hooks/inbox-hook 2.sh +0 -76
  66. package/src/hooks/post-compact 2.sh +0 -152
  67. package/src/hooks/pre-compact 2.sh +0 -169
  68. package/src/hooks/protocol-guardrail 2.sh +0 -10
  69. package/src/hooks/protocol-pretool-guardrail 2.sh +0 -9
  70. package/src/hooks/session-stop 2.sh +0 -52
  71. package/src/kg_populate 2.py +0 -292
  72. package/src/maintenance 2.py +0 -53
  73. package/src/memory_backends 2.py +0 -71
  74. package/src/migrate_embeddings 2.py +0 -124
  75. package/src/nexo_sdk 2.py +0 -103
  76. package/src/observability 2.py +0 -199
  77. package/src/plugin_loader 2.py +0 -217
  78. package/src/plugins/__init__ 2.py +0 -0
  79. package/src/plugins/artifact_registry 2.py +0 -450
  80. package/src/plugins/backup 2.py +0 -127
  81. package/src/plugins/claims_tools 2.py +0 -119
  82. package/src/plugins/cognitive_memory 2.py +0 -609
  83. package/src/plugins/core_rules 2.py +0 -252
  84. package/src/plugins/cortex 2.py +0 -1155
  85. package/src/plugins/entities 2.py +0 -67
  86. package/src/plugins/episodic_memory 2.py +0 -560
  87. package/src/plugins/evolution 2.py +0 -167
  88. package/src/plugins/goal_engine 2.py +0 -142
  89. package/src/plugins/guard 2.py +0 -862
  90. package/src/plugins/impact 2.py +0 -29
  91. package/src/plugins/knowledge_graph_tools 2.py +0 -137
  92. package/src/plugins/media_memory_tools 2.py +0 -98
  93. package/src/plugins/memory_export 2.py +0 -196
  94. package/src/plugins/outcomes 2.py +0 -130
  95. package/src/plugins/personal_scripts 2.py +0 -117
  96. package/src/plugins/preferences 2.py +0 -47
  97. package/src/plugins/protocol 2.py +0 -1449
  98. package/src/plugins/simple_api 2.py +0 -106
  99. package/src/plugins/skills 2.py +0 -341
  100. package/src/plugins/state_watchers 2.py +0 -79
  101. package/src/plugins/update 2.py +0 -986
  102. package/src/plugins/user_state_tools 2.py +0 -43
  103. package/src/plugins/workflow 2.py +0 -588
  104. package/src/protocol_settings 2.py +0 -59
  105. package/src/public_contribution 2.py +0 -466
  106. package/src/public_evolution_queue 2.py +0 -241
  107. package/src/requirements 2.txt +0 -14
  108. package/src/retroactive_learnings 2.py +0 -373
  109. package/src/rules/__init__ 2.py +0 -0
  110. package/src/rules/core-rules 2.json +0 -331
  111. package/src/rules/migrate 2.py +0 -207
  112. package/src/runtime_power 2.py +0 -874
  113. package/src/script_registry 2.py +0 -1559
  114. package/src/scripts/check-context 2.py +0 -272
  115. package/src/scripts/deep-sleep/apply_findings 2.py +0 -2327
  116. package/src/scripts/deep-sleep/collect 2.py +0 -928
  117. package/src/scripts/deep-sleep/extract 2.py +0 -330
  118. package/src/scripts/deep-sleep/extract-prompt 2.md +0 -285
  119. package/src/scripts/deep-sleep/synthesize 2.py +0 -312
  120. package/src/scripts/deep-sleep/synthesize-prompt 2.md +0 -336
  121. package/src/scripts/nexo-agent-run 2.py +0 -75
  122. package/src/scripts/nexo-auto-update 2.py +0 -6
  123. package/src/scripts/nexo-backup 2.sh +0 -25
  124. package/src/scripts/nexo-brain-activation 2.sh +0 -140
  125. package/src/scripts/nexo-catchup 2.py +0 -300
  126. package/src/scripts/nexo-cognitive-decay 2.py +0 -257
  127. package/src/scripts/nexo-cortex-cycle 2.py +0 -293
  128. package/src/scripts/nexo-cron-wrapper 2.sh +0 -53
  129. package/src/scripts/nexo-daily-self-audit 2.py +0 -2161
  130. package/src/scripts/nexo-dashboard 2.sh +0 -29
  131. package/src/scripts/nexo-deep-sleep 2.sh +0 -86
  132. package/src/scripts/nexo-evolution-run 2.py +0 -1664
  133. package/src/scripts/nexo-followup-hygiene 2.py +0 -139
  134. package/src/scripts/nexo-hook-record 2.py +0 -42
  135. package/src/scripts/nexo-immune 2.py +0 -936
  136. package/src/scripts/nexo-impact-scorer 2.py +0 -117
  137. package/src/scripts/nexo-inbox-hook 2.sh +0 -74
  138. package/src/scripts/nexo-install 2.py +0 -6
  139. package/src/scripts/nexo-learning-housekeep 2.py +0 -401
  140. package/src/scripts/nexo-learning-validator 2.py +0 -266
  141. package/src/scripts/nexo-migrate 2.py +0 -260
  142. package/src/scripts/nexo-outcome-checker 2.py +0 -127
  143. package/src/scripts/nexo-postmortem-consolidator 2.py +0 -456
  144. package/src/scripts/nexo-pre-commit 2.py +0 -120
  145. package/src/scripts/nexo-prevent-sleep 2.sh +0 -35
  146. package/src/scripts/nexo-proactive-dashboard 2.py +0 -354
  147. package/src/scripts/nexo-reflection 2.py +0 -256
  148. package/src/scripts/nexo-runtime-preflight 2.py +0 -274
  149. package/src/scripts/nexo-sleep 2.py +0 -631
  150. package/src/scripts/nexo-snapshot-restore 2.sh +0 -35
  151. package/src/scripts/nexo-sync-clients 2.py +0 -16
  152. package/src/scripts/nexo-synthesis 2.py +0 -475
  153. package/src/scripts/nexo-tcc-approve 2.sh +0 -79
  154. package/src/scripts/nexo-update 2.sh +0 -306
  155. package/src/scripts/nexo-watchdog 2.sh +0 -1207
  156. package/src/scripts/nexo-watchdog-smoke 2.py +0 -119
  157. package/src/scripts/rehydrate_learnings_from_archive 2.py +0 -245
  158. package/src/server 2.py +0 -1296
  159. package/src/skills/run-nexo-audit-phase/guide 2.md +0 -43
  160. package/src/skills/run-nexo-audit-phase/skill 2.json +0 -59
  161. package/src/skills/run-nexo-core-fix-cycle/guide 2.md +0 -17
  162. package/src/skills/run-nexo-core-fix-cycle/script 2.py +0 -276
  163. package/src/skills/run-nexo-core-fix-cycle/skill 2.json +0 -58
  164. package/src/skills/run-release-final-audit/guide 2.md +0 -16
  165. package/src/skills/run-release-final-audit/script 2.py +0 -259
  166. package/src/skills/run-release-final-audit/skill 2.json +0 -77
  167. package/src/skills/run-runtime-doctor/guide 2.md +0 -12
  168. package/src/skills/run-runtime-doctor/script 2.py +0 -21
  169. package/src/skills/run-runtime-doctor/skill 2.json +0 -25
  170. package/src/skills_runtime 2.py +0 -932
  171. package/src/state_watchers_runtime 2.py +0 -475
  172. package/src/storage_router 2.py +0 -32
  173. package/src/system_catalog 2.py +0 -786
  174. package/src/tools_coordination 2.py +0 -103
  175. package/src/tools_credentials 2.py +0 -68
  176. package/src/tools_drive 2.py +0 -487
  177. package/src/tools_hot_context 2.py +0 -163
  178. package/src/tools_learnings 2.py +0 -612
  179. package/src/tools_menu 2.py +0 -229
  180. package/src/tools_reminders 2.py +0 -88
  181. package/src/tools_reminders_crud 2.py +0 -363
  182. package/src/tools_sessions 2.py +0 -1054
  183. package/src/tools_system_catalog 2.py +0 -19
  184. package/src/tools_task_history 2.py +0 -57
  185. package/src/tools_transcripts 2.py +0 -98
  186. package/src/transcript_utils 2.py +0 -412
  187. package/src/user_context 2.py +0 -46
  188. package/src/user_data_portability 2.py +0 -328
  189. package/src/user_state_model 2.py +0 -170
  190. package/templates/CLAUDE.md 2.template +0 -108
  191. package/templates/CODEX.AGENTS.md 2.template +0 -66
  192. package/templates/launchagents/README 2.md +0 -132
  193. package/templates/launchagents/com.nexo.auto-close-sessions 2.plist +0 -39
  194. package/templates/launchagents/com.nexo.catchup 2.plist +0 -39
  195. package/templates/launchagents/com.nexo.cognitive-decay 2.plist +0 -40
  196. package/templates/launchagents/com.nexo.dashboard 2.plist +0 -43
  197. package/templates/launchagents/com.nexo.deep-sleep 2.plist +0 -43
  198. package/templates/launchagents/com.nexo.evolution 2.plist +0 -44
  199. package/templates/launchagents/com.nexo.followup-hygiene 2.plist +0 -45
  200. package/templates/launchagents/com.nexo.immune 2.plist +0 -41
  201. package/templates/launchagents/com.nexo.postmortem 2.plist +0 -45
  202. package/templates/launchagents/com.nexo.self-audit 2.plist +0 -47
  203. package/templates/launchagents/com.nexo.synthesis 2.plist +0 -45
  204. package/templates/launchagents/com.nexo.watchdog 2.plist +0 -37
  205. package/templates/nexo_helper 2.py +0 -301
  206. package/templates/openclaw 2.json +0 -13
  207. package/templates/plugin-template 2.py +0 -40
  208. package/templates/script-template 2.py +0 -59
  209. package/templates/script-template 2.sh +0 -13
  210. package/templates/skill-script-template 2.py +0 -48
  211. package/templates/skill-template 2.md +0 -33
@@ -1,417 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en" class="h-full">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>{% block title %}NEXO Brain{% endblock %} — Dashboard</title>
7
- <link rel="icon" href="/static/favicon.svg" type="image/svg+xml">
8
- <script src="https://cdn.tailwindcss.com"></script>
9
- <script>
10
- tailwind.config = {
11
- theme: {
12
- extend: {
13
- fontFamily: {
14
- display: ['Space Grotesk', 'system-ui', 'sans-serif'],
15
- mono: ['JetBrains Mono', 'monospace'],
16
- },
17
- colors: {
18
- nexo: {
19
- 50: '#f3f0ff', 100: '#e9e3ff', 200: '#d4c9ff',
20
- 300: '#b49dff', 400: '#9061ff', 500: '#7C3AED',
21
- 600: '#6d28d9', 700: '#5b21b6', 800: '#4c1d95', 900: '#2e1065',
22
- }
23
- },
24
- animation: {
25
- 'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
26
- 'fade-in': 'fadeIn 0.3s ease-out',
27
- 'slide-in': 'slideIn 0.3s ease-out',
28
- },
29
- keyframes: {
30
- fadeIn: { '0%': { opacity: '0' }, '100%': { opacity: '1' } },
31
- slideIn: { '0%': { opacity: '0', transform: 'translateY(8px)' }, '100%': { opacity: '1', transform: 'translateY(0)' } },
32
- }
33
- }
34
- }
35
- }
36
- </script>
37
- <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
38
- <style>
39
- /* Scrollbar */
40
- ::-webkit-scrollbar { width: 6px; height: 6px; }
41
- ::-webkit-scrollbar-track { background: transparent; }
42
- ::-webkit-scrollbar-thumb { background: #334155; border-radius: 3px; }
43
- ::-webkit-scrollbar-thumb:hover { background: #475569; }
44
-
45
- /* Sidebar group toggle */
46
- .nav-group-items { max-height: 0; overflow: hidden; transition: max-height 0.25s ease-out; }
47
- .nav-group.open .nav-group-items { max-height: 500px; }
48
- .nav-group .chevron { transition: transform 0.2s; }
49
- .nav-group.open .chevron { transform: rotate(90deg); }
50
-
51
- /* Active nav item */
52
- .nav-item.active { background: rgba(124, 58, 237, 0.12); color: #a78bfa; border-left: 2px solid #7C3AED; }
53
- .nav-item { border-left: 2px solid transparent; }
54
- .nav-item:hover:not(.active) { background: rgba(51, 65, 85, 0.3); }
55
-
56
- /* Card hover */
57
- .card { transition: border-color 0.2s, box-shadow 0.2s; }
58
- .card:hover { border-color: rgba(124, 58, 237, 0.3); box-shadow: 0 0 20px rgba(124, 58, 237, 0.08); }
59
-
60
- /* Glow dot */
61
- @keyframes glow { 0%, 100% { box-shadow: 0 0 4px currentColor; } 50% { box-shadow: 0 0 12px currentColor; } }
62
- .glow-dot { animation: glow 2s ease-in-out infinite; }
63
-
64
- /* Chat styles */
65
- .chat-bubble { animation: slideIn 0.3s ease-out; }
66
- </style>
67
- {% block head %}{% endblock %}
68
- </head>
69
- <body class="h-full bg-gray-950 text-slate-200 antialiased font-display">
70
-
71
- <!-- Sidebar -->
72
- <aside id="sidebar" class="fixed left-0 top-0 bottom-0 w-56 bg-slate-900/95 backdrop-blur border-r border-slate-800/80 flex flex-col z-50 overflow-hidden">
73
- <!-- Logo -->
74
- <a href="/" class="flex items-center gap-2.5 px-4 py-3.5 border-b border-slate-800/50 hover:bg-slate-800/30 transition-colors">
75
- <img src="/static/nexo-logo.png" alt="NEXO" class="w-7 h-7">
76
- <span class="text-sm font-display font-semibold text-slate-100 tracking-tight">NEXO Brain</span>
77
- <span class="ml-auto text-[10px] font-mono text-slate-600">v2.0</span>
78
- </a>
79
-
80
- <!-- Navigation -->
81
- <nav class="flex-1 overflow-y-auto py-2 px-2 space-y-0.5" id="main-nav">
82
-
83
- <!-- OVERVIEW -->
84
- <div class="nav-group open" data-group="overview">
85
- <button class="w-full flex items-center gap-2 px-2 py-1.5 text-[10px] uppercase tracking-widest text-slate-500 font-semibold hover:text-slate-400" onclick="toggleGroup(this)">
86
- <svg class="w-3 h-3 chevron" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"/></svg>
87
- Overview
88
- </button>
89
- <div class="nav-group-items">
90
- <a href="/" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="dashboard">
91
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"/></svg>
92
- Cognitive Pulse
93
- </a>
94
- <a href="/feed" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="feed">
95
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M13 10V3L4 14h7v7l9-11h-7z"/></svg>
96
- Activity Feed
97
- </a>
98
- </div>
99
- </div>
100
-
101
- <!-- OPERATIONS -->
102
- <div class="nav-group open" data-group="operations">
103
- <button class="w-full flex items-center gap-2 px-2 py-1.5 text-[10px] uppercase tracking-widest text-slate-500 font-semibold hover:text-slate-400" onclick="toggleGroup(this)">
104
- <svg class="w-3 h-3 chevron" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"/></svg>
105
- Operations
106
- </button>
107
- <div class="nav-group-items">
108
- <a href="/crons" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="crons">
109
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
110
- Crons Control
111
- </a>
112
- <a href="/ops" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="ops">
113
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/></svg>
114
- Ops Center
115
- </a>
116
- <a href="/calendar" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="calendar">
117
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/></svg>
118
- Calendar
119
- </a>
120
- </div>
121
- </div>
122
-
123
- <!-- INTELLIGENCE -->
124
- <div class="nav-group open" data-group="intelligence">
125
- <button class="w-full flex items-center gap-2 px-2 py-1.5 text-[10px] uppercase tracking-widest text-slate-500 font-semibold hover:text-slate-400" onclick="toggleGroup(this)">
126
- <svg class="w-3 h-3 chevron" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"/></svg>
127
- Intelligence
128
- </button>
129
- <div class="nav-group-items">
130
- <a href="/chat" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="chat">
131
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"/></svg>
132
- NEXO Chat
133
- <span class="ml-auto px-1.5 py-0.5 text-[9px] font-bold uppercase bg-nexo-500/20 text-nexo-400 rounded">new</span>
134
- </a>
135
- <a href="/memory" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="memory">
136
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"/></svg>
137
- Memory Flow
138
- </a>
139
- <a href="/dreams" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="dreams">
140
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/></svg>
141
- Dream Journal
142
- </a>
143
- <a href="/skills" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="skills">
144
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z"/></svg>
145
- Skills Lab
146
- </a>
147
- <a href="/trust" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="trust">
148
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"/></svg>
149
- Trust Deep-Dive
150
- </a>
151
- </div>
152
- </div>
153
-
154
- <!-- SECURITY -->
155
- <div class="nav-group" data-group="security">
156
- <button class="w-full flex items-center gap-2 px-2 py-1.5 text-[10px] uppercase tracking-widest text-slate-500 font-semibold hover:text-slate-400" onclick="toggleGroup(this)">
157
- <svg class="w-3 h-3 chevron" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"/></svg>
158
- Security
159
- </button>
160
- <div class="nav-group-items">
161
- <a href="/guard" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="guard">
162
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
163
- Guard Heatmap
164
- </a>
165
- <a href="/protocol" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="protocol">
166
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 12h6m-6 4h6M7 4h10a2 2 0 012 2v12a2 2 0 01-2 2H7a2 2 0 01-2-2V6a2 2 0 012-2z"/></svg>
167
- Protocol Explainability
168
- </a>
169
- <a href="/cortex" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="cortex">
170
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg>
171
- Cortex Monitor
172
- </a>
173
- <a href="/rules" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="rules">
174
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"/></svg>
175
- Core Rules
176
- </a>
177
- <a href="/plugins" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="plugins">
178
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M11 4a2 2 0 114 0v1a1 1 0 001 1h3a1 1 0 011 1v3a1 1 0 01-1 1h-1a2 2 0 100 4h1a1 1 0 011 1v3a1 1 0 01-1 1h-3a1 1 0 01-1-1v-1a2 2 0 10-4 0v1a1 1 0 01-1 1H7a1 1 0 01-1-1v-3a1 1 0 00-1-1H4a2 2 0 110-4h1a1 1 0 001-1V7a1 1 0 011-1h3a1 1 0 001-1V4z"/></svg>
179
- Plugins
180
- </a>
181
- </div>
182
- </div>
183
-
184
- <!-- ADVANCED -->
185
- <div class="nav-group" data-group="advanced">
186
- <button class="w-full flex items-center gap-2 px-2 py-1.5 text-[10px] uppercase tracking-widest text-slate-500 font-semibold hover:text-slate-400" onclick="toggleGroup(this)">
187
- <svg class="w-3 h-3 chevron" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"/></svg>
188
- Advanced
189
- </button>
190
- <div class="nav-group-items">
191
- <a href="/evolution" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="evolution">
192
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"/></svg>
193
- Evolution
194
- </a>
195
- <a href="/claims" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="claims">
196
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/></svg>
197
- Claims Network
198
- </a>
199
- <a href="/sentiment" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="sentiment">
200
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"/></svg>
201
- Sentiment
202
- </a>
203
- <a href="/sessions" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="sessions">
204
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M15 15l-2 5L9 9l11 4-5 2zm0 0l5 5M7.188 2.239l.777 2.897M5.136 7.965l-2.898-.777M13.95 4.05l-2.122 2.122m-5.657 5.656l-2.12 2.122"/></svg>
205
- Sessions
206
- </a>
207
- <a href="/triggers" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="triggers">
208
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"/></svg>
209
- Triggers
210
- </a>
211
- <a href="/artifacts" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="artifacts">
212
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4"/></svg>
213
- Artifacts
214
- </a>
215
- </div>
216
- </div>
217
-
218
- <!-- SYSTEM -->
219
- <div class="nav-group" data-group="system">
220
- <button class="w-full flex items-center gap-2 px-2 py-1.5 text-[10px] uppercase tracking-widest text-slate-500 font-semibold hover:text-slate-400" onclick="toggleGroup(this)">
221
- <svg class="w-3 h-3 chevron" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"/></svg>
222
- System
223
- </button>
224
- <div class="nav-group-items">
225
- <a href="/inbox" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="inbox">
226
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/></svg>
227
- Inbox
228
- </a>
229
- <a href="/email" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="email">
230
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"/></svg>
231
- Email Monitor
232
- </a>
233
- <a href="/credentials" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="credentials">
234
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z"/></svg>
235
- Credentials
236
- </a>
237
- <a href="/backups" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="backups">
238
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"/></svg>
239
- Backups
240
- </a>
241
- <a href="/followup-health" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="followup-health">
242
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/></svg>
243
- Followup Health
244
- </a>
245
- </div>
246
- </div>
247
-
248
- <!-- Knowledge (from existing) -->
249
- <div class="nav-group" data-group="knowledge">
250
- <button class="w-full flex items-center gap-2 px-2 py-1.5 text-[10px] uppercase tracking-widest text-slate-500 font-semibold hover:text-slate-400" onclick="toggleGroup(this)">
251
- <svg class="w-3 h-3 chevron" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"/></svg>
252
- Knowledge
253
- </button>
254
- <div class="nav-group-items">
255
- <a href="/graph" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="graph">
256
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/></svg>
257
- Knowledge Graph
258
- </a>
259
- <a href="/somatic" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="somatic">
260
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"/></svg>
261
- Somatic Markers
262
- </a>
263
- <a href="/adaptive" class="nav-item flex items-center gap-2.5 px-3 py-1.5 rounded-md text-xs text-slate-400 transition-colors" data-page="adaptive">
264
- <svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"/></svg>
265
- Adaptive Mode
266
- </a>
267
- </div>
268
- </div>
269
- </nav>
270
-
271
- <!-- Sidebar footer -->
272
- <div class="border-t border-slate-800/50 p-3">
273
- <div class="flex items-center justify-between">
274
- <div class="flex items-center gap-2">
275
- <div class="w-2 h-2 rounded-full glow-dot" id="system-status-dot" style="color: #10B981; background: #10B981;"></div>
276
- <span class="text-[10px] text-slate-500 uppercase tracking-wider">System</span>
277
- </div>
278
- <span class="text-xs font-mono text-slate-500" id="sidebar-clock">--:--</span>
279
- </div>
280
- <div class="mt-2 flex items-center justify-between">
281
- <span class="text-[10px] text-slate-500">Trust</span>
282
- <span class="text-xs font-mono font-medium text-nexo-400" id="sidebar-trust-value">--</span>
283
- </div>
284
- </div>
285
- </aside>
286
-
287
- <!-- Main content -->
288
- <main class="ml-56 min-h-screen bg-gray-950">
289
- <!-- Top bar -->
290
- <header class="h-12 border-b border-slate-800/50 flex items-center justify-between px-6 sticky top-0 bg-gray-950/95 backdrop-blur-md z-40">
291
- <div class="flex items-center gap-3">
292
- <h1 class="text-sm font-display font-semibold text-slate-100">{% block page_title %}Dashboard{% endblock %}</h1>
293
- {% block page_subtitle %}{% endblock %}
294
- </div>
295
- <div class="flex items-center gap-2">
296
- {% block header_actions %}{% endblock %}
297
- <div class="w-px h-4 bg-slate-700 mx-1"></div>
298
- <span class="text-xs text-slate-500 font-mono tabular-nums" id="current-time">--:--:--</span>
299
- </div>
300
- </header>
301
-
302
- <!-- Page content -->
303
- <div class="p-5 animate-fade-in">
304
- {% block content %}{% endblock %}
305
- </div>
306
- </main>
307
-
308
- <!-- Toast -->
309
- <div id="toast" class="hidden fixed bottom-4 right-4 z-[60] bg-slate-800 border border-slate-700 rounded-lg px-4 py-2.5 text-sm text-slate-200 shadow-xl flex items-center gap-2 animate-slide-in">
310
- <span id="toast-msg"></span>
311
- </div>
312
-
313
- <script>
314
- // -----------------------------------------------------------------------
315
- // Global utilities
316
- // -----------------------------------------------------------------------
317
- function escapeHtml(str) {
318
- const div = document.createElement('div');
319
- div.textContent = str || '';
320
- return div.innerHTML;
321
- }
322
-
323
- function showToast(msg, duration = 2500) {
324
- const t = document.getElementById('toast');
325
- document.getElementById('toast-msg').textContent = msg;
326
- t.classList.remove('hidden');
327
- t.classList.add('flex');
328
- clearTimeout(t._timer);
329
- t._timer = setTimeout(() => { t.classList.add('hidden'); t.classList.remove('flex'); }, duration);
330
- }
331
-
332
- async function fetchJSON(url) {
333
- try {
334
- const res = await fetch(url);
335
- if (!res.ok) { let d = `HTTP ${res.status}`; try { const b = await res.json(); d = b.error || b.detail || d; } catch {} throw new Error(d); }
336
- return await res.json();
337
- } catch (err) { console.error(`fetchJSON(${url}):`, err); return null; }
338
- }
339
-
340
- function relativeTime(dateStr) {
341
- if (!dateStr) return '--';
342
- const d = new Date(dateStr);
343
- const now = new Date();
344
- const diff = Math.floor((now - d) / 1000);
345
- if (diff < 60) return 'just now';
346
- if (diff < 3600) return Math.floor(diff / 60) + 'm ago';
347
- if (diff < 86400) return Math.floor(diff / 3600) + 'h ago';
348
- return Math.floor(diff / 86400) + 'd ago';
349
- }
350
-
351
- function relativeDate(dateStr) {
352
- if (!dateStr) return 'no date';
353
- const d = new Date(dateStr + 'T00:00:00');
354
- const now = new Date(); now.setHours(0, 0, 0, 0);
355
- const diff = Math.round((d - now) / 86400000);
356
- if (diff < -1) return Math.abs(diff) + 'd overdue';
357
- if (diff === -1) return 'yesterday';
358
- if (diff === 0) return 'today';
359
- if (diff === 1) return 'tomorrow';
360
- return d.toLocaleDateString('en', { month: 'short', day: 'numeric' });
361
- }
362
-
363
- function formatNumber(n) {
364
- if (n === null || n === undefined) return '--';
365
- return n.toLocaleString();
366
- }
367
-
368
- // -----------------------------------------------------------------------
369
- // Clock
370
- // -----------------------------------------------------------------------
371
- function updateClock() {
372
- const now = new Date();
373
- const timeStr = now.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
374
- document.getElementById('current-time').textContent = timeStr;
375
- document.getElementById('sidebar-clock').textContent = now.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' });
376
- }
377
- updateClock();
378
- setInterval(updateClock, 1000);
379
-
380
- // -----------------------------------------------------------------------
381
- // Navigation
382
- // -----------------------------------------------------------------------
383
- function toggleGroup(btn) {
384
- btn.closest('.nav-group').classList.toggle('open');
385
- }
386
-
387
- // Highlight active nav item
388
- (function() {
389
- const path = window.location.pathname;
390
- document.querySelectorAll('.nav-item').forEach(item => {
391
- const href = item.getAttribute('href');
392
- if (href === path || (path === '/' && href === '/')) {
393
- item.classList.add('active');
394
- // Open parent group
395
- const group = item.closest('.nav-group');
396
- if (group) group.classList.add('open');
397
- }
398
- });
399
- })();
400
-
401
- // -----------------------------------------------------------------------
402
- // Sidebar trust score (global, updates on all pages)
403
- // -----------------------------------------------------------------------
404
- async function updateSidebarTrust() {
405
- const data = await fetchJSON('/api/trust');
406
- if (data) {
407
- const score = data.current_score ?? 0;
408
- document.getElementById('sidebar-trust-value').textContent = score.toFixed(1);
409
- }
410
- }
411
- updateSidebarTrust();
412
- setInterval(updateSidebarTrust, 120000);
413
- </script>
414
-
415
- {% block scripts %}{% endblock %}
416
- </body>
417
- </html>