prism-mcp-server 20.21.20 → 20.21.21
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/README.md +7 -0
- package/dist/dashboard/ledgerReader.js +4 -2
- package/dist/dashboard/readMessages.js +48 -0
- package/dist/dashboard/server.js +13 -10
- package/dist/dashboard/ui.js +231 -386
- package/dist/storage/portalContracts.js +23 -0
- package/dist/storage/sqlite.js +7 -4
- package/dist/storage/supabase.js +32 -24
- package/dist/storage/synalux.js +14 -1
- package/dist/tools/hygieneHandlers.js +7 -2
- package/package.json +1 -1
package/dist/dashboard/ui.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* - Responsive grid layout
|
|
14
14
|
* ═══════════════════════════════════════════════════════════════════
|
|
15
15
|
*/
|
|
16
|
+
import { ANALYTICS_DATE_PATTERN, ANALYTICS_METRIC_IDS, DASHBOARD_READ_MESSAGES } from './readMessages.js';
|
|
16
17
|
export function renderDashboardLocalOpenHTML() {
|
|
17
18
|
return `<!DOCTYPE html>
|
|
18
19
|
<html lang="en">
|
|
@@ -191,6 +192,7 @@ export function renderDashboardHTML(version) {
|
|
|
191
192
|
|
|
192
193
|
/* ─── Glass Cards ─── */
|
|
193
194
|
.card {
|
|
195
|
+
min-width: 0; overflow-wrap: anywhere;
|
|
194
196
|
background: var(--bg-glass); backdrop-filter: blur(16px);
|
|
195
197
|
border: 1px solid var(--border-glass); border-radius: var(--radius);
|
|
196
198
|
padding: 1.5rem; transition: border-color 0.3s ease, box-shadow 0.3s ease;
|
|
@@ -206,8 +208,10 @@ export function renderDashboardHTML(version) {
|
|
|
206
208
|
}
|
|
207
209
|
|
|
208
210
|
/* ─── Grid Layout ─── */
|
|
209
|
-
.grid { display: grid; gap: 1.5rem; }
|
|
210
|
-
.grid-main { grid-template-columns: 1fr 2fr; }
|
|
211
|
+
.grid { display: grid; gap: 1.5rem; min-width: 0; }
|
|
212
|
+
.grid-main { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); }
|
|
213
|
+
.graph-filters select { color: var(--text-primary); background: var(--bg-secondary); border: 1px solid var(--border-glass); border-radius: 6px; max-width: 100%; }
|
|
214
|
+
.graph-filters option { color: var(--text-primary); background: var(--bg-secondary); }
|
|
211
215
|
@media (max-width: 900px) { .grid-main { grid-template-columns: 1fr; } }
|
|
212
216
|
|
|
213
217
|
/* ─── PWA Mobile Overrides (v5.4) ─── */
|
|
@@ -864,8 +868,9 @@ export function renderDashboardHTML(version) {
|
|
|
864
868
|
<!-- Brain Health (v2.2.0) -->
|
|
865
869
|
<div class="card" id="healthCard" style="display:none">
|
|
866
870
|
<div class="card-title">
|
|
867
|
-
<span class="dot" style="background:var(--accent-green)"></span>
|
|
868
|
-
<button class="
|
|
871
|
+
<span class="dot" style="background:var(--accent-green)"></span> ${DASHBOARD_READ_MESSAGES.memoryScanTitle}
|
|
872
|
+
<button class="refresh-btn" id="healthRefreshBtn" type="button" onclick="refreshHealthScan()">${DASHBOARD_READ_MESSAGES.refreshScan}</button>
|
|
873
|
+
<button class="cleanup-btn" id="cleanupBtn" onclick="cleanupIssues()" style="display:none" title="${DASHBOARD_READ_MESSAGES.repairCoverage}">🧹 Fix Issues</button>
|
|
869
874
|
</div>
|
|
870
875
|
<div class="health-status">
|
|
871
876
|
<div class="health-dot unknown" id="healthDot"></div>
|
|
@@ -875,6 +880,8 @@ export function renderDashboardHTML(version) {
|
|
|
875
880
|
</div>
|
|
876
881
|
</div>
|
|
877
882
|
<div class="health-issues" id="healthIssues"></div>
|
|
883
|
+
<p id="healthCoverageNote" style="color:var(--text-muted);font-size:0.75rem"></p>
|
|
884
|
+
<p id="healthRepairNote" style="color:var(--text-muted);font-size:0.75rem">${DASHBOARD_READ_MESSAGES.repairCoverage}</p>
|
|
878
885
|
<!-- Repair progress bar (v6.1.4) -->
|
|
879
886
|
<div class="health-progress-wrap" id="healthProgressWrap">
|
|
880
887
|
<div class="health-progress-header">
|
|
@@ -892,13 +899,15 @@ export function renderDashboardHTML(version) {
|
|
|
892
899
|
<div class="card-title">
|
|
893
900
|
<span class="dot" style="background:var(--accent-purple)"></span>
|
|
894
901
|
Memory Analytics 📊
|
|
902
|
+
<button class="refresh-btn" id="analyticsRetryBtn" type="button" onclick="loadAnalytics(document.getElementById('projectSelect').value)">${DASHBOARD_READ_MESSAGES.retry}</button>
|
|
895
903
|
</div>
|
|
904
|
+
<div id="analyticsReadStatus" role="status" style="display:none;color:var(--accent-rose);font-size:0.85rem;margin-bottom:0.75rem"></div>
|
|
896
905
|
<div class="sparkline" id="sparkline" title="Sessions per day (last 14 days)"></div>
|
|
897
906
|
<div style="font-size:0.68rem;color:var(--text-muted);text-align:right">Sessions / day (14d)</div>
|
|
898
907
|
<div class="analytics-stats">
|
|
899
|
-
<div class="astat"><div class="astat-val" id="astat-entries">—</div><div class="astat-label"
|
|
908
|
+
<div class="astat"><div class="astat-val" id="astat-entries">—</div><div class="astat-label">${DASHBOARD_READ_MESSAGES.activeEntries}</div></div>
|
|
900
909
|
<div class="astat"><div class="astat-val" id="astat-rollups">—</div><div class="astat-label">Rollups</div></div>
|
|
901
|
-
<div class="astat"><div class="astat-val" id="astat-savings">—</div><div class="astat-label"
|
|
910
|
+
<div class="astat"><div class="astat-val" id="astat-savings">—</div><div class="astat-label">${DASHBOARD_READ_MESSAGES.consolidatedEntries}</div></div>
|
|
902
911
|
<div class="astat"><div class="astat-val" id="astat-avglen">—</div><div class="astat-label">Avg summary chars</div></div>
|
|
903
912
|
</div>
|
|
904
913
|
</div>
|
|
@@ -1017,7 +1026,7 @@ export function renderDashboardHTML(version) {
|
|
|
1017
1026
|
</div>
|
|
1018
1027
|
|
|
1019
1028
|
<!-- v5.1 Graph Filters -->
|
|
1020
|
-
<div style="display:flex; gap:0.5rem; margin-bottom:1rem; flex-wrap:wrap; align-items:center;">
|
|
1029
|
+
<div class="graph-filters" style="display:flex; gap:0.5rem; margin-bottom:1rem; flex-wrap:wrap; align-items:center;">
|
|
1021
1030
|
<select id="graphProjectFilter" class="input-modern" style="min-width:120px; font-size:0.75rem; padding:0.3rem 0.5rem" onchange="loadGraph()">
|
|
1022
1031
|
<option value="">All Projects</option>
|
|
1023
1032
|
</select>
|
|
@@ -1131,7 +1140,7 @@ export function renderDashboardHTML(version) {
|
|
|
1131
1140
|
<div class="card" style="margin-top:1rem">
|
|
1132
1141
|
<div class="card-title" style="display:flex;align-items:center;gap:0.5rem">
|
|
1133
1142
|
<span class="dot" style="background:var(--accent-blue)"></span>
|
|
1134
|
-
|
|
1143
|
+
${DASHBOARD_READ_MESSAGES.graphActivityTitle}
|
|
1135
1144
|
<div style="flex:1"></div>
|
|
1136
1145
|
<span id="graphHealthWarnings" style="display:inline-flex;gap:0.3rem"></span>
|
|
1137
1146
|
<button onclick="loadGraphMetrics()" class="refresh-btn">↻</button>
|
|
@@ -1185,251 +1194,34 @@ export function renderDashboardHTML(version) {
|
|
|
1185
1194
|
</div>
|
|
1186
1195
|
</div>
|
|
1187
1196
|
|
|
1188
|
-
<!-- VM Lab
|
|
1189
|
-
<div id="vm-content" class="fade-in" style="display:none;
|
|
1197
|
+
<!-- VM Lab: inventory must come from a runtime provider. -->
|
|
1198
|
+
<div id="vm-content" class="fade-in" style="display:none;max-width:1100px;margin:auto;padding:0 1rem;">
|
|
1190
1199
|
<div class="card">
|
|
1191
|
-
<div class="card-title">
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
<span style="margin-left:auto;font-size:0.72rem;color:var(--text-muted)">14 templates</span>
|
|
1195
|
-
</div>
|
|
1196
|
-
<div class="vm-gallery">
|
|
1197
|
-
<div class="vm-card"><span class="vm-tier free">FREE</span><div class="vm-icon">🐧</div><div class="vm-name">Ubuntu 24.04 LTS</div><div class="vm-specs">4 cores · 8 GB · 64 GB SSD</div></div>
|
|
1198
|
-
<div class="vm-card"><span class="vm-tier free">FREE</span><div class="vm-icon">🐧</div><div class="vm-name">Debian 12 Minimal</div><div class="vm-specs">2 cores · 4 GB · 32 GB SSD</div></div>
|
|
1199
|
-
<div class="vm-card"><span class="vm-tier standard">STD</span><div class="vm-icon">🪣</div><div class="vm-name">Windows 11 Dev</div><div class="vm-specs">4 cores · 16 GB · 128 GB SSD</div></div>
|
|
1200
|
-
<div class="vm-card"><span class="vm-tier standard">STD</span><div class="vm-icon">🪣</div><div class="vm-name">Windows Server 2022</div><div class="vm-specs">8 cores · 32 GB · 256 GB SSD</div></div>
|
|
1201
|
-
<div class="vm-card"><span class="vm-tier advanced">ADV</span><div class="vm-icon">📱</div><div class="vm-name">iOS 18 Simulator</div><div class="vm-specs">4 cores · 8 GB · 64 GB SSD</div></div>
|
|
1202
|
-
<div class="vm-card"><span class="vm-tier advanced">ADV</span><div class="vm-icon">📱</div><div class="vm-name">iPadOS 18</div><div class="vm-specs">4 cores · 8 GB · 64 GB SSD</div></div>
|
|
1203
|
-
<div class="vm-card"><span class="vm-tier advanced">ADV</span><div class="vm-icon">⌚</div><div class="vm-name">watchOS 11</div><div class="vm-specs">2 cores · 2 GB · 16 GB SSD</div></div>
|
|
1204
|
-
<div class="vm-card"><span class="vm-tier standard">STD</span><div class="vm-icon">🤖</div><div class="vm-name">Android 15</div><div class="vm-specs">4 cores · 8 GB · 64 GB SSD</div></div>
|
|
1205
|
-
<div class="vm-card"><span class="vm-tier standard">STD</span><div class="vm-icon">⌚</div><div class="vm-name">Wear OS 5</div><div class="vm-specs">2 cores · 2 GB · 16 GB SSD</div></div>
|
|
1206
|
-
<div class="vm-card"><span class="vm-tier advanced">ADV</span><div class="vm-icon">🌎</div><div class="vm-name">macOS Sequoia</div><div class="vm-specs">8 cores · 16 GB · 256 GB SSD</div></div>
|
|
1207
|
-
<div class="vm-card"><span class="vm-tier enterprise">ENT</span><div class="vm-icon">🥽</div><div class="vm-name">visionOS 2</div><div class="vm-specs">8 cores · 16 GB · 128 GB SSD</div></div>
|
|
1208
|
-
<div class="vm-card"><span class="vm-tier enterprise">ENT</span><div class="vm-icon">🥽</div><div class="vm-name">Meta Quest 3</div><div class="vm-specs">8 cores · 12 GB · 128 GB SSD</div></div>
|
|
1209
|
-
<div class="vm-card"><span class="vm-tier free">FREE</span><div class="vm-icon">🐧</div><div class="vm-name">Alpine Linux</div><div class="vm-specs">1 core · 1 GB · 8 GB SSD</div></div>
|
|
1210
|
-
<div class="vm-card"><span class="vm-tier standard">STD</span><div class="vm-icon">🐧</div><div class="vm-name">Fedora 41</div><div class="vm-specs">4 cores · 8 GB · 64 GB SSD</div></div>
|
|
1211
|
-
</div>
|
|
1212
|
-
</div>
|
|
1213
|
-
|
|
1214
|
-
<div class="card" style="margin-top:1.25rem">
|
|
1215
|
-
<div class="card-title">
|
|
1216
|
-
<span class="dot" style="background:var(--accent-green)"></span>
|
|
1217
|
-
Active VMs
|
|
1218
|
-
<span style="margin-left:auto;font-size:0.72rem;color:var(--text-muted)">0 running</span>
|
|
1219
|
-
</div>
|
|
1220
|
-
<div class="vm-status-list">
|
|
1221
|
-
<div style="text-align:center;padding:2rem;color:var(--text-muted);font-size:0.85rem">
|
|
1222
|
-
<div style="font-size:2rem;margin-bottom:0.5rem">💤</div>
|
|
1223
|
-
No active VMs. Click a template above to create one.
|
|
1224
|
-
</div>
|
|
1225
|
-
</div>
|
|
1200
|
+
<div class="card-title">${DASHBOARD_READ_MESSAGES.vmTitle}</div>
|
|
1201
|
+
<p role="status">${DASHBOARD_READ_MESSAGES.vmUnavailable}</p>
|
|
1202
|
+
<button class="lc-btn compact" type="button" onclick="showFixedToast(dashboardReadMessages.vmActionUnavailable,false)">${DASHBOARD_READ_MESSAGES.vmAvailability}</button>
|
|
1226
1203
|
</div>
|
|
1227
1204
|
</div>
|
|
1228
1205
|
|
|
1229
|
-
<!-- Marketplace
|
|
1230
|
-
<div id="marketplace-content" class="fade-in" style="display:none;
|
|
1206
|
+
<!-- Marketplace: never advertise an unverified catalog or payout terms. -->
|
|
1207
|
+
<div id="marketplace-content" class="fade-in" style="display:none;max-width:1100px;margin:auto;padding:0 1rem;">
|
|
1231
1208
|
<div class="card">
|
|
1232
|
-
<div class="card-title">
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
<button
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
<div style="display:flex;gap:0.75rem;margin-bottom:1rem;align-items:center">
|
|
1239
|
-
<input type="text" class="input-modern" style="flex:1;padding:0.6rem 1rem;font-size:0.9rem" placeholder="Search components, shaders, UI kits...">
|
|
1240
|
-
</div>
|
|
1241
|
-
|
|
1242
|
-
<div class="mp-filters">
|
|
1243
|
-
<button class="mp-filter-chip active">All</button>
|
|
1244
|
-
<button class="mp-filter-chip">🎨 UI</button>
|
|
1245
|
-
<button class="mp-filter-chip">⚙️ Logic</button>
|
|
1246
|
-
<button class="mp-filter-chip">🎧 Audio</button>
|
|
1247
|
-
<button class="mp-filter-chip">✨ Shader</button>
|
|
1248
|
-
<button class="mp-filter-chip">📄 Template</button>
|
|
1249
|
-
<button class="mp-filter-chip">🎮 Game</button>
|
|
1250
|
-
<button class="mp-filter-chip">🤖 AI/ML</button>
|
|
1251
|
-
</div>
|
|
1252
|
-
|
|
1253
|
-
<div class="mp-grid">
|
|
1254
|
-
<div class="mp-card">
|
|
1255
|
-
<div class="mp-header">
|
|
1256
|
-
<div><div class="mp-name">🎨 Glassmorphism UI Kit</div><div class="mp-author">by @studioflow</div></div>
|
|
1257
|
-
<span class="mp-price-badge free">FREE</span>
|
|
1258
|
-
</div>
|
|
1259
|
-
<div class="mp-desc">Premium glass-effect components with dark/light mode support. 24 components included.</div>
|
|
1260
|
-
<button class="mp-install-btn">⬇ Install</button>
|
|
1261
|
-
</div>
|
|
1262
|
-
<div class="mp-card">
|
|
1263
|
-
<div class="mp-header">
|
|
1264
|
-
<div><div class="mp-name">✨ PBR Shader Pack</div><div class="mp-author">by @renderlab</div></div>
|
|
1265
|
-
<span class="mp-price-badge paid">$14.99</span>
|
|
1266
|
-
</div>
|
|
1267
|
-
<div class="mp-desc">Physically-based rendering shaders for Metal, Vulkan, and WebGPU. Includes toon, cel, and water effects.</div>
|
|
1268
|
-
<button class="mp-install-btn">🛒 Purchase</button>
|
|
1269
|
-
</div>
|
|
1270
|
-
<div class="mp-card">
|
|
1271
|
-
<div class="mp-header">
|
|
1272
|
-
<div><div class="mp-name">🎮 Netcode Toolkit</div><div class="mp-author">by @multiplayerlabs</div></div>
|
|
1273
|
-
<span class="mp-price-badge paid">$29.99</span>
|
|
1274
|
-
</div>
|
|
1275
|
-
<div class="mp-desc">Client-side prediction, server reconciliation, and lag compensation for multiplayer games.</div>
|
|
1276
|
-
<button class="mp-install-btn">🛒 Purchase</button>
|
|
1277
|
-
</div>
|
|
1278
|
-
<div class="mp-card">
|
|
1279
|
-
<div class="mp-header">
|
|
1280
|
-
<div><div class="mp-name">🤖 Edge AI Runtime</div><div class="mp-author">by @inferenceio</div></div>
|
|
1281
|
-
<span class="mp-price-badge free">FREE</span>
|
|
1282
|
-
</div>
|
|
1283
|
-
<div class="mp-desc">On-device ML inference with CoreML, ONNX, and TFLite. Includes pose estimation and object detection models.</div>
|
|
1284
|
-
<button class="mp-install-btn">⬇ Install</button>
|
|
1285
|
-
</div>
|
|
1286
|
-
<div class="mp-card">
|
|
1287
|
-
<div class="mp-header">
|
|
1288
|
-
<div><div class="mp-name">🎧 Spatial Audio Engine</div><div class="mp-author">by @soundscapevr</div></div>
|
|
1289
|
-
<span class="mp-price-badge paid">$19.99</span>
|
|
1290
|
-
</div>
|
|
1291
|
-
<div class="mp-desc">HRTF-based 3D audio for VR/AR. Supports ambisonics, reverb zones, and occlusion.</div>
|
|
1292
|
-
<button class="mp-install-btn">🛒 Purchase</button>
|
|
1293
|
-
</div>
|
|
1294
|
-
<div class="mp-card">
|
|
1295
|
-
<div class="mp-header">
|
|
1296
|
-
<div><div class="mp-name">📄 SaaS Starter Kit</div><div class="mp-author">by @launchfast</div></div>
|
|
1297
|
-
<span class="mp-price-badge free">FREE</span>
|
|
1298
|
-
</div>
|
|
1299
|
-
<div class="mp-desc">Full-stack SaaS boilerplate with auth, billing, dashboard, and API. Next.js + Supabase.</div>
|
|
1300
|
-
<button class="mp-install-btn">⬇ Install</button>
|
|
1301
|
-
</div>
|
|
1302
|
-
</div>
|
|
1303
|
-
|
|
1304
|
-
<div style="margin-top:1.25rem;padding:0.85rem;border-radius:var(--radius-sm);background:rgba(139,92,246,0.08);border:1px solid rgba(139,92,246,0.2);font-size:0.78rem;color:var(--text-secondary)">
|
|
1305
|
-
💰 <strong>Revenue Sharing:</strong> Earn 70% of sales on paid components. Payouts via Synalux every 30 days.
|
|
1209
|
+
<div class="card-title">${DASHBOARD_READ_MESSAGES.marketplaceTitle}</div>
|
|
1210
|
+
<p role="status">${DASHBOARD_READ_MESSAGES.marketplaceUnavailable}</p>
|
|
1211
|
+
<div class="lc-row">
|
|
1212
|
+
<button class="lc-btn compact" type="button" onclick="showFixedToast(dashboardReadMessages.marketplaceActionUnavailable,false)">${DASHBOARD_READ_MESSAGES.marketplaceAvailability}</button>
|
|
1213
|
+
<button class="lc-btn export" type="button" onclick="showFixedToast(dashboardReadMessages.marketplaceActionUnavailable,false)">${DASHBOARD_READ_MESSAGES.publishComponent}</button>
|
|
1306
1214
|
</div>
|
|
1307
1215
|
</div>
|
|
1308
1216
|
</div>
|
|
1309
1217
|
|
|
1310
|
-
<!-- Compliance
|
|
1311
|
-
<div id="compliance-content" class="fade-in" style="display:none;
|
|
1218
|
+
<!-- Compliance: this view has no connected live enforcement/audit feed. -->
|
|
1219
|
+
<div id="compliance-content" class="fade-in" style="display:none;max-width:1100px;margin:auto;padding:0 1rem;">
|
|
1312
1220
|
<div class="card">
|
|
1313
|
-
<div class="card-title">
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
</
|
|
1317
|
-
<div class="compliance-layers">
|
|
1318
|
-
<div class="compliance-layer">
|
|
1319
|
-
<div class="cl-icon">📋</div>
|
|
1320
|
-
<div class="cl-name">Registration Gate</div>
|
|
1321
|
-
<div class="cl-status active">Active</div>
|
|
1322
|
-
</div>
|
|
1323
|
-
<div class="compliance-layer">
|
|
1324
|
-
<div class="cl-icon">🌐</div>
|
|
1325
|
-
<div class="cl-name">Geofence Gate</div>
|
|
1326
|
-
<div class="cl-status active">Active</div>
|
|
1327
|
-
</div>
|
|
1328
|
-
<div class="compliance-layer">
|
|
1329
|
-
<div class="cl-icon">🧠</div>
|
|
1330
|
-
<div class="cl-name">Use-Case AI</div>
|
|
1331
|
-
<div class="cl-status active">Active</div>
|
|
1332
|
-
</div>
|
|
1333
|
-
<div class="compliance-layer">
|
|
1334
|
-
<div class="cl-icon">📊</div>
|
|
1335
|
-
<div class="cl-name">Runtime Monitor</div>
|
|
1336
|
-
<div class="cl-status active">Active</div>
|
|
1337
|
-
</div>
|
|
1338
|
-
<div class="compliance-layer">
|
|
1339
|
-
<div class="cl-icon">🚨</div>
|
|
1340
|
-
<div class="cl-name">Kill Switch</div>
|
|
1341
|
-
<div class="cl-status active">Armed</div>
|
|
1342
|
-
</div>
|
|
1343
|
-
<div class="compliance-layer">
|
|
1344
|
-
<div class="cl-icon">🔗</div>
|
|
1345
|
-
<div class="cl-name">Audit Trail</div>
|
|
1346
|
-
<div class="cl-status active">Hash-Chain</div>
|
|
1347
|
-
</div>
|
|
1348
|
-
</div>
|
|
1349
|
-
</div>
|
|
1350
|
-
|
|
1351
|
-
<div class="card" style="margin-top:1.25rem">
|
|
1352
|
-
<div class="card-title">
|
|
1353
|
-
<span class="dot" style="background:var(--accent-rose)"></span>
|
|
1354
|
-
Embargoed Countries — Hard-Blocked
|
|
1355
|
-
</div>
|
|
1356
|
-
<div class="country-badges">
|
|
1357
|
-
<span class="country-badge embargoed">⛔ BY — Belarus</span>
|
|
1358
|
-
<span class="country-badge embargoed">⛔ CU — Cuba</span>
|
|
1359
|
-
<span class="country-badge embargoed">⛔ IR — Iran</span>
|
|
1360
|
-
<span class="country-badge embargoed">⛔ KP — DPRK</span>
|
|
1361
|
-
<span class="country-badge embargoed">⛔ SY — Syria</span>
|
|
1362
|
-
</div>
|
|
1363
|
-
|
|
1364
|
-
<div class="card-title" style="margin-top:1rem">
|
|
1365
|
-
<span class="dot" style="background:var(--accent-amber)"></span>
|
|
1366
|
-
Restricted — Civilian Only
|
|
1367
|
-
</div>
|
|
1368
|
-
<div class="country-badges">
|
|
1369
|
-
<span class="country-badge restricted">🟡 RU — Russia</span>
|
|
1370
|
-
<span class="country-badge restricted">🟡 CN — China</span>
|
|
1371
|
-
<span class="country-badge restricted">🟡 VE</span>
|
|
1372
|
-
<span class="country-badge restricted">🟡 MM</span>
|
|
1373
|
-
<span class="country-badge restricted">🟡 SD</span>
|
|
1374
|
-
<span class="country-badge restricted">🟡 SS</span>
|
|
1375
|
-
<span class="country-badge restricted">🟡 LY</span>
|
|
1376
|
-
<span class="country-badge restricted">🟡 SO</span>
|
|
1377
|
-
<span class="country-badge restricted">🟡 YE</span>
|
|
1378
|
-
<span class="country-badge restricted">🟡 ZW</span>
|
|
1379
|
-
<span class="country-badge restricted">🟡 CD</span>
|
|
1380
|
-
<span class="country-badge restricted">🟡 CF</span>
|
|
1381
|
-
<span class="country-badge restricted">🟡 IQ</span>
|
|
1382
|
-
<span class="country-badge restricted">🟡 LB</span>
|
|
1383
|
-
</div>
|
|
1384
|
-
</div>
|
|
1385
|
-
|
|
1386
|
-
<div class="card" style="margin-top:1.25rem">
|
|
1387
|
-
<div class="card-title">
|
|
1388
|
-
<span class="dot" style="background:var(--accent-purple)"></span>
|
|
1389
|
-
Recent Audit Events 📜
|
|
1390
|
-
</div>
|
|
1391
|
-
<table class="audit-table">
|
|
1392
|
-
<thead>
|
|
1393
|
-
<tr><th>Time</th><th>Event</th><th>Severity</th><th>Details</th></tr>
|
|
1394
|
-
</thead>
|
|
1395
|
-
<tbody>
|
|
1396
|
-
<tr>
|
|
1397
|
-
<td style="font-family:var(--font-mono);font-size:0.72rem">2 min ago</td>
|
|
1398
|
-
<td>Registration screened</td>
|
|
1399
|
-
<td><span class="audit-severity low">Low</span></td>
|
|
1400
|
-
<td style="color:var(--text-muted)">KYC passed — US-based individual</td>
|
|
1401
|
-
</tr>
|
|
1402
|
-
<tr>
|
|
1403
|
-
<td style="font-family:var(--font-mono);font-size:0.72rem">18 min ago</td>
|
|
1404
|
-
<td>Use-case classified</td>
|
|
1405
|
-
<td><span class="audit-severity low">Low</span></td>
|
|
1406
|
-
<td style="color:var(--text-muted)">Project "pet-tracker" — consumer app (2% risk)</td>
|
|
1407
|
-
</tr>
|
|
1408
|
-
<tr>
|
|
1409
|
-
<td style="font-family:var(--font-mono);font-size:0.72rem">1 hr ago</td>
|
|
1410
|
-
<td>Geofence mismatch</td>
|
|
1411
|
-
<td><span class="audit-severity medium">Medium</span></td>
|
|
1412
|
-
<td style="color:var(--text-muted)">IP: DE, Billing: FR — flagged for human review</td>
|
|
1413
|
-
</tr>
|
|
1414
|
-
<tr>
|
|
1415
|
-
<td style="font-family:var(--font-mono);font-size:0.72rem">4 hr ago</td>
|
|
1416
|
-
<td>VPN detected</td>
|
|
1417
|
-
<td><span class="audit-severity medium">Medium</span></td>
|
|
1418
|
-
<td style="color:var(--text-muted)">NordVPN exit node — billing triangulation passed</td>
|
|
1419
|
-
</tr>
|
|
1420
|
-
<tr>
|
|
1421
|
-
<td style="font-family:var(--font-mono);font-size:0.72rem">1 day ago</td>
|
|
1422
|
-
<td>Sanctions check failed</td>
|
|
1423
|
-
<td><span class="audit-severity high">High</span></td>
|
|
1424
|
-
<td style="color:var(--text-muted)">OFAC SDN match — registration blocked</td>
|
|
1425
|
-
</tr>
|
|
1426
|
-
</tbody>
|
|
1427
|
-
</table>
|
|
1428
|
-
</div>
|
|
1429
|
-
|
|
1430
|
-
<div style="margin-top:1.25rem;padding:0.85rem;border-radius:var(--radius-sm);background:rgba(16,185,129,0.08);border:1px solid rgba(16,185,129,0.2);font-size:0.78rem;color:var(--text-secondary)">
|
|
1431
|
-
✅ <strong>All 6 enforcement layers are active.</strong> Ethics enforcement is NOT tier-gated — applies equally to Free, the free account, Pro and Team.
|
|
1432
|
-
<a href="/ethics" style="color:var(--accent-purple);margin-left:0.5rem">View full policy →</a>
|
|
1221
|
+
<div class="card-title">${DASHBOARD_READ_MESSAGES.complianceTitle}</div>
|
|
1222
|
+
<p role="status">${DASHBOARD_READ_MESSAGES.complianceUnavailable}</p>
|
|
1223
|
+
<p>${DASHBOARD_READ_MESSAGES.auditUnavailable}</p>
|
|
1224
|
+
<button class="lc-btn compact" type="button" onclick="showFixedToast(dashboardReadMessages.policyUnavailable,false)">${DASHBOARD_READ_MESSAGES.policyAvailability}</button>
|
|
1433
1225
|
</div>
|
|
1434
1226
|
</div>
|
|
1435
1227
|
|
|
@@ -1486,9 +1278,9 @@ export function renderDashboardHTML(version) {
|
|
|
1486
1278
|
<div class="setting-desc">Default level for session_load_context</div>
|
|
1487
1279
|
</div>
|
|
1488
1280
|
<select class="setting-select" id="select-context-depth" onchange="saveSetting('default_context_depth', this.value)">
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1281
|
+
<option value="standard">Standard</option>
|
|
1282
|
+
<option value="quick">Quick</option>
|
|
1283
|
+
<option value="deep">Deep</option>
|
|
1492
1284
|
</select>
|
|
1493
1285
|
</div>
|
|
1494
1286
|
|
|
@@ -1616,8 +1408,7 @@ Example:\n## Dev Rules\n- Always write tests first\n- Use TypeScript strict mode
|
|
|
1616
1408
|
<button class="skill-clear-btn" onclick="clearCurrentSkill()">🗑️ Clear</button>
|
|
1617
1409
|
</div>
|
|
1618
1410
|
<div class="skill-hint">
|
|
1619
|
-
|
|
1620
|
-
Use Markdown. Changes take effect immediately — no restart needed.
|
|
1411
|
+
${DASHBOARD_READ_MESSAGES.customRoleInstructions}
|
|
1621
1412
|
</div>
|
|
1622
1413
|
</div><!-- /spanel-skills -->
|
|
1623
1414
|
|
|
@@ -1646,7 +1437,7 @@ Example:\n## Dev Rules\n- Always write tests first\n- Use TypeScript strict mode
|
|
|
1646
1437
|
<div class="setting-row">
|
|
1647
1438
|
<div>
|
|
1648
1439
|
<div class="setting-label">Google API Key</div>
|
|
1649
|
-
<div class="setting-desc"
|
|
1440
|
+
<div class="setting-desc">${DASHBOARD_READ_MESSAGES.directGeminiKey}</div>
|
|
1650
1441
|
</div>
|
|
1651
1442
|
<input type="password" id="input-google-api-key"
|
|
1652
1443
|
placeholder="AIza…"
|
|
@@ -1759,8 +1550,7 @@ Example:\n## Dev Rules\n- Always write tests first\n- Use TypeScript strict mode
|
|
|
1759
1550
|
</div>
|
|
1760
1551
|
|
|
1761
1552
|
<div style="margin-top:1rem;padding:0.6rem 0.8rem;background:rgba(139,92,246,0.08);border:1px solid rgba(139,92,246,0.2);border-radius:6px;font-size:0.78rem;color:var(--text-secondary);line-height:1.5">
|
|
1762
|
-
|
|
1763
|
-
Use Claude 3.5 Sonnet for reasoning & <code>nomic-embed-text</code> (free, local) for embeddings.
|
|
1553
|
+
${DASHBOARD_READ_MESSAGES.providerChoice}
|
|
1764
1554
|
</div>
|
|
1765
1555
|
|
|
1766
1556
|
<span class="setting-saved" id="savedToastProviders">Saved ✓</span>
|
|
@@ -1858,6 +1648,19 @@ Example:\n## Dev Rules\n- Always write tests first\n- Use TypeScript strict mode
|
|
|
1858
1648
|
// This HTML is served as a raw template literal; mixing ES6 in the
|
|
1859
1649
|
// inline script causes SyntaxError in some browser/context combos.
|
|
1860
1650
|
// ═══════════════════════════════════════════════════════════════════
|
|
1651
|
+
var dashboardReadMessages = ${JSON.stringify(DASHBOARD_READ_MESSAGES)};
|
|
1652
|
+
var analyticsMetricIds = ${JSON.stringify(ANALYTICS_METRIC_IDS)};
|
|
1653
|
+
var analyticsReadSequence = 0;
|
|
1654
|
+
var pipelineReadSequence = 0;
|
|
1655
|
+
var projectReadSequence = 0;
|
|
1656
|
+
var retentionReadSequence = 0;
|
|
1657
|
+
var teamReadSequence = 0;
|
|
1658
|
+
var intentReadSequence = 0;
|
|
1659
|
+
var graphReadSequence = 0;
|
|
1660
|
+
var healthReadSequence = 0;
|
|
1661
|
+
var graphMetricsReadSequence = 0;
|
|
1662
|
+
var activeGraphNetwork = null;
|
|
1663
|
+
var analyticsDatePattern = new RegExp(${JSON.stringify(ANALYTICS_DATE_PATTERN.source)});
|
|
1861
1664
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
1862
1665
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
1863
1666
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -1924,20 +1727,28 @@ function switchMainTab(tabId) {
|
|
|
1924
1727
|
if (tabId === 'factory') {
|
|
1925
1728
|
loadPipelines();
|
|
1926
1729
|
}
|
|
1730
|
+
if (tabId === 'project' && projectLoaded) loadGraph();
|
|
1927
1731
|
}
|
|
1928
1732
|
// ─── DARK FACTORY (v7.3) ───
|
|
1929
1733
|
var factoryPollTimer = null;
|
|
1930
1734
|
function loadPipelines() {
|
|
1735
|
+
var readSequence = ++pipelineReadSequence;
|
|
1736
|
+
document.getElementById('factoryCount').textContent = dashboardReadMessages.pipelinesLoading;
|
|
1931
1737
|
var statusFilter = document.getElementById('factoryStatusFilter').value;
|
|
1932
1738
|
var url = '/api/pipelines';
|
|
1933
1739
|
if (statusFilter)
|
|
1934
1740
|
url += '?status=' + encodeURIComponent(statusFilter);
|
|
1935
|
-
fetch(url)
|
|
1936
|
-
.then(function (r) {
|
|
1741
|
+
return fetch(url)
|
|
1742
|
+
.then(function (r) {
|
|
1743
|
+
if (!r.ok) throw new Error(dashboardReadMessages.pipelinesUnavailable);
|
|
1744
|
+
return r.json();
|
|
1745
|
+
})
|
|
1937
1746
|
.then(function (data) {
|
|
1747
|
+
if (readSequence !== pipelineReadSequence) return;
|
|
1748
|
+
if (!data || !Array.isArray(data.pipelines)) throw new Error(dashboardReadMessages.pipelinesUnavailable);
|
|
1938
1749
|
var list = document.getElementById('factoryList');
|
|
1939
1750
|
var count = document.getElementById('factoryCount');
|
|
1940
|
-
var pipelines = data.pipelines
|
|
1751
|
+
var pipelines = data.pipelines;
|
|
1941
1752
|
count.textContent = pipelines.length + ' pipeline' + (pipelines.length !== 1 ? 's' : '');
|
|
1942
1753
|
if (pipelines.length === 0) {
|
|
1943
1754
|
list.innerHTML = '<div style="text-align:center;padding:2rem;color:var(--text-muted)"><div style="font-size:2rem;margin-bottom:0.5rem">🏭</div>No pipelines found. Use <code>session_start_pipeline</code> to create one.</div>';
|
|
@@ -1988,7 +1799,10 @@ function loadPipelines() {
|
|
|
1988
1799
|
}
|
|
1989
1800
|
})
|
|
1990
1801
|
.catch(function (err) {
|
|
1991
|
-
|
|
1802
|
+
if (readSequence !== pipelineReadSequence) return;
|
|
1803
|
+
document.getElementById('factoryCount').textContent = '—';
|
|
1804
|
+
document.getElementById('factoryList').innerHTML = '<div role="status" style="color:var(--accent-rose);padding:1rem">' + escapeHtml(dashboardReadMessages.pipelinesUnavailable) +
|
|
1805
|
+
' <button class="refresh-btn" id="factoryRetryBtn" type="button" onclick="loadPipelines()">' + escapeHtml(dashboardReadMessages.retry) + '</button></div>';
|
|
1992
1806
|
});
|
|
1993
1807
|
}
|
|
1994
1808
|
function abortPipeline(id) {
|
|
@@ -2446,7 +2260,62 @@ function signOutAccount() {
|
|
|
2446
2260
|
});
|
|
2447
2261
|
});
|
|
2448
2262
|
})();
|
|
2263
|
+
function isCurrentProjectRead(project, sequence) {
|
|
2264
|
+
return document.getElementById('projectSelect').value === project
|
|
2265
|
+
&& (typeof sequence === 'undefined' || sequence === projectReadSequence);
|
|
2266
|
+
}
|
|
2267
|
+
function applyHealthReport(data) {
|
|
2268
|
+
var partial = !!data && data.coverage === 'embedding_inventory';
|
|
2269
|
+
var totals = data && data.totals;
|
|
2270
|
+
var validStatus = data && (data.status === 'healthy' || data.status === 'degraded' || data.status === 'unhealthy');
|
|
2271
|
+
var validCoverage = data && data.scope === 'account'
|
|
2272
|
+
&& (data.backend === 'synalux' && data.coverage === 'embedding_inventory'
|
|
2273
|
+
|| (data.backend === 'local' || data.backend === 'supabase') && data.coverage === 'backend_scan');
|
|
2274
|
+
var available = !!validStatus && validCoverage && totals && typeof totals.activeEntries === 'number' && isFinite(totals.activeEntries) && totals.activeEntries >= 0
|
|
2275
|
+
&& typeof totals.handoffs === 'number' && isFinite(totals.handoffs) && totals.handoffs >= 0
|
|
2276
|
+
&& (partial || (typeof totals.rollups === 'number' && isFinite(totals.rollups) && totals.rollups >= 0)) && Array.isArray(data.issues)
|
|
2277
|
+
&& data.issues.every(function (issue) { return issue && typeof issue.message === 'string'; });
|
|
2278
|
+
var issues = available ? data.issues : [];
|
|
2279
|
+
document.getElementById('healthCard').style.display = 'block';
|
|
2280
|
+
document.getElementById('healthDot').className = 'health-dot ' + (available && !partial ? data.status : 'unknown');
|
|
2281
|
+
document.getElementById('healthLabel').textContent = !available ? dashboardReadMessages.scanUnavailable
|
|
2282
|
+
: partial ? dashboardReadMessages.partialScan
|
|
2283
|
+
: data.status === 'healthy' ? dashboardReadMessages.scanPassed : dashboardReadMessages.scanIssues;
|
|
2284
|
+
document.getElementById('healthSummary').textContent = available
|
|
2285
|
+
? totals.activeEntries + ' entries · ' + totals.handoffs + ' handoffs' + (partial ? '' : ' · ' + totals.rollups + ' rollups')
|
|
2286
|
+
+ (!partial && typeof totals.crdtMerges === 'number' && totals.crdtMerges > 0 ? ' · ' + totals.crdtMerges + ' merges' : '')
|
|
2287
|
+
: '—';
|
|
2288
|
+
document.getElementById('healthCoverageNote').textContent = !available ? dashboardReadMessages.coverageUnavailable
|
|
2289
|
+
: partial ? dashboardReadMessages.partialCoverage : dashboardReadMessages.backendCoverage;
|
|
2290
|
+
var issueContainer = document.getElementById('healthIssues');
|
|
2291
|
+
issueContainer.textContent = !available ? dashboardReadMessages.scanUnavailable
|
|
2292
|
+
: issues.length ? '' : partial ? dashboardReadMessages.noEmbeddingIssues : dashboardReadMessages.noScanIssues;
|
|
2293
|
+
issues.forEach(function (issue) {
|
|
2294
|
+
var row = document.createElement('div'); row.className = 'issue-row'; row.textContent = issue.message;
|
|
2295
|
+
issueContainer.appendChild(row);
|
|
2296
|
+
});
|
|
2297
|
+
var actionable = issues.some(function (issue) {
|
|
2298
|
+
return (issue.check === 'missing_embeddings' || issue.check === 'orphaned_handoffs') && issue.count > 0;
|
|
2299
|
+
});
|
|
2300
|
+
document.getElementById('cleanupBtn').style.display = actionable ? 'inline-block' : 'none';
|
|
2301
|
+
}
|
|
2302
|
+
function refreshHealthScan() {
|
|
2303
|
+
var sequence = ++healthReadSequence;
|
|
2304
|
+
applyHealthReport(null);
|
|
2305
|
+
document.getElementById('healthLabel').textContent = dashboardReadMessages.scanLoading;
|
|
2306
|
+
document.getElementById('healthIssues').textContent = '';
|
|
2307
|
+
return fetch('/api/health').then(function (response) {
|
|
2308
|
+
if (!response.ok) throw new Error(dashboardReadMessages.scanUnavailable);
|
|
2309
|
+
return response.json();
|
|
2310
|
+
}).then(function (data) {
|
|
2311
|
+
if (sequence === healthReadSequence) applyHealthReport(data);
|
|
2312
|
+
}).catch(function () {
|
|
2313
|
+
if (sequence === healthReadSequence) applyHealthReport(null);
|
|
2314
|
+
});
|
|
2315
|
+
}
|
|
2449
2316
|
function loadProject() {
|
|
2317
|
+
var readSequence = ++projectReadSequence;
|
|
2318
|
+
projectLoaded = false;
|
|
2450
2319
|
return __awaiter(this, void 0, void 0, function () {
|
|
2451
2320
|
var project, res, data, ctx, ledgerEntries, latestLedger, contextTime, latestLedgerTime, useLatestLedger, versionBadge, currentStateSource, currentSummary, todos, todoList, meta, briefingCard, visualCard, visuals, historyEl, ledgerEl, healthRes, healthData, healthCard, healthDot, healthLabel, healthSummary, healthIssues, statusMap, t, issues, cleanupBtn, sevIcons, he_1, e_3;
|
|
2452
2321
|
return __generator(this, function (_a) {
|
|
@@ -2454,6 +2323,7 @@ function loadProject() {
|
|
|
2454
2323
|
case 0:
|
|
2455
2324
|
project = document.getElementById('projectSelect').value;
|
|
2456
2325
|
if (!project) {
|
|
2326
|
+
document.getElementById('loading').style.display = 'none';
|
|
2457
2327
|
var hc = document.getElementById('intentHealthCard');
|
|
2458
2328
|
if (hc) hc.style.display = 'none';
|
|
2459
2329
|
var welcomeEl = document.getElementById('welcome');
|
|
@@ -2476,6 +2346,8 @@ function loadProject() {
|
|
|
2476
2346
|
return [4 /*yield*/, res.json()];
|
|
2477
2347
|
case 3:
|
|
2478
2348
|
data = _a.sent();
|
|
2349
|
+
if (!isCurrentProjectRead(project, readSequence)) return [2 /*return*/];
|
|
2350
|
+
if (!res.ok || !data || data.error) throw new Error(dashboardReadMessages.projectUnavailable);
|
|
2479
2351
|
ctx = data.context || {};
|
|
2480
2352
|
ledgerEntries = Array.isArray(data.ledger) ? data.ledger.slice() : [];
|
|
2481
2353
|
ledgerEntries.sort(function (a, b) {
|
|
@@ -2562,6 +2434,8 @@ function loadProject() {
|
|
|
2562
2434
|
ledgerEl = document.getElementById('ledgerTimeline');
|
|
2563
2435
|
if (ledgerEntries.length > 0) {
|
|
2564
2436
|
ledgerEl.innerHTML = ledgerEntries.map(function (l) {
|
|
2437
|
+
var entryKind = l.is_rollup === true || l.is_rollup === 1 || l.is_rollup === 'true'
|
|
2438
|
+
? dashboardReadMessages.rollupEntry : dashboardReadMessages.sessionEntry;
|
|
2565
2439
|
var summary = l.summary || l.content || 'Entry';
|
|
2566
2440
|
var decisions = l.decisions;
|
|
2567
2441
|
var extra = '';
|
|
@@ -2575,7 +2449,7 @@ function loadProject() {
|
|
|
2575
2449
|
catch (e) { }
|
|
2576
2450
|
}
|
|
2577
2451
|
return '<div class="timeline-item">' +
|
|
2578
|
-
'<div class="meta"><span class="badge badge-amber">
|
|
2452
|
+
'<div class="meta"><span class="badge badge-amber">' + entryKind + '</span>' +
|
|
2579
2453
|
'<span>' + formatDate(l.created_at) + '</span></div>' +
|
|
2580
2454
|
escapeHtml(summary) + extra + '</div>';
|
|
2581
2455
|
}).join('');
|
|
@@ -2585,56 +2459,16 @@ function loadProject() {
|
|
|
2585
2459
|
}
|
|
2586
2460
|
_a.label = 4;
|
|
2587
2461
|
case 4:
|
|
2588
|
-
|
|
2589
|
-
return [4 /*yield*/, fetch('/api/health')];
|
|
2462
|
+
return [4 /*yield*/, refreshHealthScan()];
|
|
2590
2463
|
case 5:
|
|
2591
|
-
|
|
2592
|
-
return [4 /*yield*/, healthRes.json()];
|
|
2593
|
-
case 6:
|
|
2594
|
-
healthData = _a.sent();
|
|
2595
|
-
healthCard = document.getElementById('healthCard');
|
|
2596
|
-
healthDot = document.getElementById('healthDot');
|
|
2597
|
-
healthLabel = document.getElementById('healthLabel');
|
|
2598
|
-
healthSummary = document.getElementById('healthSummary');
|
|
2599
|
-
healthIssues = document.getElementById('healthIssues');
|
|
2600
|
-
// Set the dot color based on status
|
|
2601
|
-
healthDot.className = 'health-dot ' + (healthData.status || 'unknown');
|
|
2602
|
-
statusMap = { healthy: '✅ Healthy', degraded: '⚠️ Degraded', unhealthy: '🔴 Unhealthy' };
|
|
2603
|
-
healthLabel.textContent = statusMap[healthData.status] || '❓ Unknown';
|
|
2604
|
-
t = healthData.totals || {};
|
|
2605
|
-
healthSummary.textContent = (t.activeEntries || 0) + ' entries · ' +
|
|
2606
|
-
(t.handoffs || 0) + ' handoffs · ' +
|
|
2607
|
-
(t.rollups || 0) + ' rollups' +
|
|
2608
|
-
(t.crdtMerges ? ' · 🔄 ' + t.crdtMerges + ' merges' : '');
|
|
2609
|
-
issues = healthData.issues || [];
|
|
2610
|
-
cleanupBtn = document.getElementById('cleanupBtn');
|
|
2611
|
-
if (issues.length > 0) {
|
|
2612
|
-
sevIcons = { error: '🔴', warning: '🟡', info: '🔵' };
|
|
2613
|
-
healthIssues.innerHTML = issues.map(function (i) {
|
|
2614
|
-
return '<div class="issue-row">' +
|
|
2615
|
-
'<span>' + (sevIcons[i.severity] || '❓') + '</span>' +
|
|
2616
|
-
'<span>' + escapeHtml(i.message) + '</span>' +
|
|
2617
|
-
'</div>';
|
|
2618
|
-
}).join('');
|
|
2619
|
-
if (cleanupBtn)
|
|
2620
|
-
cleanupBtn.style.display = 'inline-block';
|
|
2621
|
-
}
|
|
2622
|
-
else {
|
|
2623
|
-
healthIssues.innerHTML = '<div style="color:var(--accent-green);font-size:0.8rem">🎉 No issues found</div>';
|
|
2624
|
-
if (cleanupBtn)
|
|
2625
|
-
cleanupBtn.style.display = 'none';
|
|
2626
|
-
}
|
|
2627
|
-
healthCard.style.display = 'block';
|
|
2628
|
-
return [3 /*break*/, 8];
|
|
2629
|
-
case 7:
|
|
2630
|
-
he_1 = _a.sent();
|
|
2631
|
-
// Health check not available — silently skip
|
|
2632
|
-
console.warn('Health check unavailable:', he_1);
|
|
2464
|
+
_a.sent();
|
|
2633
2465
|
return [3 /*break*/, 8];
|
|
2634
2466
|
case 8:
|
|
2467
|
+
if (!isCurrentProjectRead(project, readSequence)) return [2 /*return*/];
|
|
2635
2468
|
document.getElementById('content').className = 'grid grid-main fade-in';
|
|
2636
2469
|
document.getElementById('content').style.display = 'grid';
|
|
2637
2470
|
projectLoaded = true; // Fix 3: mark project as loaded for tab-switch restore
|
|
2471
|
+
loadGraph(); // Fit the network after the previously hidden panel becomes visible.
|
|
2638
2472
|
|
|
2639
2473
|
// Feature A: Run Intent Health Fetch
|
|
2640
2474
|
fetchIntentHealth(project);
|
|
@@ -2649,10 +2483,10 @@ function loadProject() {
|
|
|
2649
2483
|
return [3 /*break*/, 11];
|
|
2650
2484
|
case 9:
|
|
2651
2485
|
e_3 = _a.sent();
|
|
2652
|
-
|
|
2486
|
+
if (isCurrentProjectRead(project, readSequence)) alert(dashboardReadMessages.projectUnavailable);
|
|
2653
2487
|
return [3 /*break*/, 11];
|
|
2654
2488
|
case 10:
|
|
2655
|
-
document.getElementById('loading').style.display = 'none';
|
|
2489
|
+
if (isCurrentProjectRead(project, readSequence)) document.getElementById('loading').style.display = 'none';
|
|
2656
2490
|
return [7 /*endfinally*/];
|
|
2657
2491
|
case 11: return [2 /*return*/];
|
|
2658
2492
|
}
|
|
@@ -2661,6 +2495,14 @@ function loadProject() {
|
|
|
2661
2495
|
}
|
|
2662
2496
|
// ─── v3.1: Memory Analytics ───────────────────────────────────────────────
|
|
2663
2497
|
function loadAnalytics(project) {
|
|
2498
|
+
if (!isCurrentProjectRead(project)) return Promise.resolve();
|
|
2499
|
+
var readSequence = ++analyticsReadSequence;
|
|
2500
|
+
var parentSequence = projectReadSequence;
|
|
2501
|
+
var readStatus = document.getElementById('analyticsReadStatus');
|
|
2502
|
+
analyticsMetricIds.forEach(function (id) { document.getElementById(id).textContent = '—'; });
|
|
2503
|
+
document.getElementById('sparkline').innerHTML = '';
|
|
2504
|
+
readStatus.textContent = dashboardReadMessages.analyticsLoading;
|
|
2505
|
+
readStatus.style.display = 'block';
|
|
2664
2506
|
return __awaiter(this, void 0, void 0, function () {
|
|
2665
2507
|
var res, d, sparkEl, days, maxCount, e_4;
|
|
2666
2508
|
return __generator(this, function (_a) {
|
|
@@ -2673,6 +2515,20 @@ function loadAnalytics(project) {
|
|
|
2673
2515
|
return [4 /*yield*/, res.json()];
|
|
2674
2516
|
case 2:
|
|
2675
2517
|
d = _a.sent();
|
|
2518
|
+
if (readSequence !== analyticsReadSequence || !isCurrentProjectRead(project, parentSequence)) return [2 /*return*/];
|
|
2519
|
+
if (!res.ok || !d || d.error || typeof d.totalEntries !== 'number'
|
|
2520
|
+
|| typeof d.totalRollups !== 'number' || typeof d.rollupSavings !== 'number'
|
|
2521
|
+
|| typeof d.avgSummaryLength !== 'number' || !isFinite(d.totalEntries)
|
|
2522
|
+
|| !isFinite(d.totalRollups) || !isFinite(d.rollupSavings) || !isFinite(d.avgSummaryLength)
|
|
2523
|
+
|| d.totalEntries < 0 || d.totalRollups < 0 || d.rollupSavings < 0 || d.avgSummaryLength < 0
|
|
2524
|
+
|| !Array.isArray(d.sessionsByDay) || d.sessionsByDay.some(function (day) {
|
|
2525
|
+
return !day || typeof day.date !== 'string' || !analyticsDatePattern.test(day.date)
|
|
2526
|
+
|| typeof day.count !== 'number' || !isFinite(day.count) || day.count < 0;
|
|
2527
|
+
})) {
|
|
2528
|
+
throw new Error(dashboardReadMessages.analyticsUnavailable);
|
|
2529
|
+
}
|
|
2530
|
+
readStatus.textContent = '';
|
|
2531
|
+
readStatus.style.display = 'none';
|
|
2676
2532
|
document.getElementById('astat-entries').textContent = (d.totalEntries || 0);
|
|
2677
2533
|
document.getElementById('astat-rollups').textContent = (d.totalRollups || 0);
|
|
2678
2534
|
document.getElementById('astat-savings').textContent = (d.rollupSavings || 0);
|
|
@@ -2683,18 +2539,23 @@ function loadAnalytics(project) {
|
|
|
2683
2539
|
// Pad with 14 zero days
|
|
2684
2540
|
days = Array.from({ length: 14 }, function (_, i) {
|
|
2685
2541
|
var dt = new Date();
|
|
2686
|
-
dt.
|
|
2542
|
+
dt.setUTCDate(dt.getUTCDate() - (13 - i));
|
|
2687
2543
|
return { date: dt.toISOString().slice(0, 10), count: 0 };
|
|
2688
2544
|
});
|
|
2689
2545
|
}
|
|
2690
2546
|
maxCount = Math.max.apply(null, days.map(function (x) { return x.count || 0; })) || 1;
|
|
2691
2547
|
sparkEl.innerHTML = days.slice(-14).map(function (d) {
|
|
2692
2548
|
var pct = Math.max(4, Math.round(((d.count || 0) / maxCount) * 100));
|
|
2693
|
-
return '<div class="spark-bar" style="height:' + pct + '%" title="' + d.date + ': ' + d.count + '"></div>';
|
|
2549
|
+
return '<div class="spark-bar" style="height:' + pct + '%" title="' + escapeHtml(d.date) + ': ' + d.count + '"></div>';
|
|
2694
2550
|
}).join('');
|
|
2695
2551
|
return [3 /*break*/, 4];
|
|
2696
2552
|
case 3:
|
|
2697
2553
|
e_4 = _a.sent();
|
|
2554
|
+
if (readSequence !== analyticsReadSequence || !isCurrentProjectRead(project, parentSequence)) return [2 /*return*/];
|
|
2555
|
+
analyticsMetricIds.forEach(function (id) { document.getElementById(id).textContent = '—'; });
|
|
2556
|
+
document.getElementById('sparkline').innerHTML = '';
|
|
2557
|
+
readStatus.textContent = dashboardReadMessages.analyticsUnavailable;
|
|
2558
|
+
readStatus.style.display = 'block';
|
|
2698
2559
|
console.warn('Analytics load failed:', e_4);
|
|
2699
2560
|
return [3 /*break*/, 4];
|
|
2700
2561
|
case 4: return [2 /*return*/];
|
|
@@ -2704,6 +2565,9 @@ function loadAnalytics(project) {
|
|
|
2704
2565
|
}
|
|
2705
2566
|
// ─── v3.1: TTL Retention ───────────────────────────────────────────────
|
|
2706
2567
|
function loadRetention(project) {
|
|
2568
|
+
if (!isCurrentProjectRead(project)) return Promise.resolve();
|
|
2569
|
+
var readSequence = ++retentionReadSequence;
|
|
2570
|
+
var parentSequence = projectReadSequence;
|
|
2707
2571
|
return __awaiter(this, void 0, void 0, function () {
|
|
2708
2572
|
var res, d, inp, e_5;
|
|
2709
2573
|
return __generator(this, function (_a) {
|
|
@@ -2716,6 +2580,7 @@ function loadRetention(project) {
|
|
|
2716
2580
|
return [4 /*yield*/, res.json()];
|
|
2717
2581
|
case 2:
|
|
2718
2582
|
d = _a.sent();
|
|
2583
|
+
if (readSequence !== retentionReadSequence || !isCurrentProjectRead(project, parentSequence)) return [2 /*return*/];
|
|
2719
2584
|
inp = document.getElementById('ttlInput');
|
|
2720
2585
|
if (inp)
|
|
2721
2586
|
inp.value = d.ttl_days || 0;
|
|
@@ -3244,7 +3109,29 @@ function getDecayColor(daysSince, decayedImportance, group, baseImportance) {
|
|
|
3244
3109
|
return { bg: hex, border: hex, fontColor: fontBrightness };
|
|
3245
3110
|
}
|
|
3246
3111
|
// ─── Neural Graph (v2.3.0 / v5.1 / v6.2 Decay Heatmap) ───
|
|
3112
|
+
function graphStatisticsElement(container) {
|
|
3113
|
+
var title = container.parentElement.querySelector('.card-title');
|
|
3114
|
+
var stats = title.querySelector('.graph-stats');
|
|
3115
|
+
if (!stats) {
|
|
3116
|
+
stats = document.createElement('span');
|
|
3117
|
+
stats.className = 'graph-stats';
|
|
3118
|
+
stats.style.cssText = 'margin-left:auto;font-size:0.7rem;color:var(--text-muted);font-family:var(--font-mono);font-weight:400;text-transform:none;letter-spacing:0';
|
|
3119
|
+
title.appendChild(stats);
|
|
3120
|
+
}
|
|
3121
|
+
return stats;
|
|
3122
|
+
}
|
|
3123
|
+
function updateGraphStatistics(container, data) {
|
|
3124
|
+
var nodes = data.nodes;
|
|
3125
|
+
var projects = nodes.filter(function (node) { return node.group === 'project'; }).length;
|
|
3126
|
+
var keywords = nodes.filter(function (node) { return node.group === 'keyword'; }).length;
|
|
3127
|
+
graphStatisticsElement(container).textContent = data.graphType === 'memory'
|
|
3128
|
+
? nodes.length + ' ' + dashboardReadMessages.graphSessions + ' · ' + data.edges.length + ' ' + dashboardReadMessages.graphStoredLinks
|
|
3129
|
+
: projects + ' ' + dashboardReadMessages.graphProjects + ' · ' + keywords + ' ' + dashboardReadMessages.graphKeywords + ' · ' + data.edges.length + ' ' + dashboardReadMessages.graphEdges;
|
|
3130
|
+
}
|
|
3247
3131
|
function loadGraph() {
|
|
3132
|
+
var readSequence = ++graphReadSequence;
|
|
3133
|
+
if (activeGraphNetwork && typeof activeGraphNetwork.destroy === 'function') activeGraphNetwork.destroy();
|
|
3134
|
+
activeGraphNetwork = null;
|
|
3248
3135
|
return __awaiter(this, void 0, void 0, function () {
|
|
3249
3136
|
var container, proj, days, imp, qs, url, res, data, graphDataNote, dens, graduatedNodes, denPercentage, dens, MAX_NODES, priority, kept, options, network, allNodes, allEdges, isFiltered, graphTitle, statsSpan, projectCount, kwCount, e_11;
|
|
3250
3137
|
return __generator(this, function (_a) {
|
|
@@ -3253,6 +3140,10 @@ function loadGraph() {
|
|
|
3253
3140
|
container = document.getElementById('network-container');
|
|
3254
3141
|
if (!container)
|
|
3255
3142
|
return [2 /*return*/];
|
|
3143
|
+
graphStatisticsElement(container).textContent = dashboardReadMessages.graphLoading;
|
|
3144
|
+
container.textContent = dashboardReadMessages.graphLoading;
|
|
3145
|
+
document.getElementById('densityStatContainer').style.display = 'none';
|
|
3146
|
+
document.getElementById('graphDataNote').style.display = 'none';
|
|
3256
3147
|
proj = document.getElementById('graphProjectFilter') ? document.getElementById('graphProjectFilter').value : '';
|
|
3257
3148
|
days = document.getElementById('graphDaysFilter') ? document.getElementById('graphDaysFilter').value : '';
|
|
3258
3149
|
imp = document.getElementById('graphImportanceFilter') ? document.getElementById('graphImportanceFilter').value : '';
|
|
@@ -3273,8 +3164,9 @@ function loadGraph() {
|
|
|
3273
3164
|
return [4 /*yield*/, res.json()];
|
|
3274
3165
|
case 3:
|
|
3275
3166
|
data = _a.sent();
|
|
3276
|
-
if (
|
|
3277
|
-
|
|
3167
|
+
if (readSequence !== graphReadSequence) return [2 /*return*/];
|
|
3168
|
+
if (!res.ok || !data || data.error || !Array.isArray(data.nodes) || !Array.isArray(data.edges))
|
|
3169
|
+
throw new Error(dashboardReadMessages.graphUnavailable);
|
|
3278
3170
|
graphDataNote = document.getElementById('graphDataNote');
|
|
3279
3171
|
if (graphDataNote) {
|
|
3280
3172
|
graphDataNote.style.display = data.graphType === 'memory' ? 'block' : 'none';
|
|
@@ -3284,7 +3176,8 @@ function loadGraph() {
|
|
|
3284
3176
|
}
|
|
3285
3177
|
// Empty state — no ledger entries yet
|
|
3286
3178
|
if (data.nodes.length === 0) {
|
|
3287
|
-
container
|
|
3179
|
+
updateGraphStatistics(container, data);
|
|
3180
|
+
container.textContent = dashboardReadMessages.graphEmpty;
|
|
3288
3181
|
dens = document.getElementById('densityStatContainer');
|
|
3289
3182
|
if (dens)
|
|
3290
3183
|
dens.style.display = 'none';
|
|
@@ -3370,6 +3263,7 @@ function loadGraph() {
|
|
|
3370
3263
|
interaction: { hover: true, tooltipDelay: 200 }
|
|
3371
3264
|
};
|
|
3372
3265
|
network = new vis.Network(container, data, options);
|
|
3266
|
+
activeGraphNetwork = network;
|
|
3373
3267
|
allNodes = data.nodes;
|
|
3374
3268
|
allEdges = data.edges;
|
|
3375
3269
|
isFiltered = false;
|
|
@@ -3441,26 +3335,14 @@ function loadGraph() {
|
|
|
3441
3335
|
network.setData({ nodes: allNodes, edges: allEdges });
|
|
3442
3336
|
isFiltered = false;
|
|
3443
3337
|
});
|
|
3444
|
-
|
|
3445
|
-
if (graphTitle) {
|
|
3446
|
-
statsSpan = graphTitle.querySelector('.graph-stats');
|
|
3447
|
-
if (!statsSpan) {
|
|
3448
|
-
statsSpan = document.createElement('span');
|
|
3449
|
-
statsSpan.className = 'graph-stats';
|
|
3450
|
-
statsSpan.style.cssText = 'margin-left:auto;font-size:0.7rem;color:var(--text-muted);font-family:var(--font-mono);font-weight:400;text-transform:none;letter-spacing:0';
|
|
3451
|
-
graphTitle.appendChild(statsSpan);
|
|
3452
|
-
}
|
|
3453
|
-
projectCount = allNodes.filter(function (n) { return n.group === 'project'; }).length;
|
|
3454
|
-
kwCount = allNodes.filter(function (n) { return n.group === 'keyword'; }).length;
|
|
3455
|
-
statsSpan.textContent = data.graphType === 'memory'
|
|
3456
|
-
? allNodes.length + ' sessions · ' + allEdges.length + ' stored links'
|
|
3457
|
-
: projectCount + ' projects · ' + kwCount + ' keywords · ' + allEdges.length + ' edges';
|
|
3458
|
-
}
|
|
3338
|
+
updateGraphStatistics(container, data);
|
|
3459
3339
|
return [3 /*break*/, 5];
|
|
3460
3340
|
case 4:
|
|
3461
3341
|
e_11 = _a.sent();
|
|
3342
|
+
if (readSequence !== graphReadSequence) return [2 /*return*/];
|
|
3462
3343
|
console.error('Graph error', e_11);
|
|
3463
|
-
container.
|
|
3344
|
+
graphStatisticsElement(container).textContent = dashboardReadMessages.graphUnavailable;
|
|
3345
|
+
container.textContent = dashboardReadMessages.graphUnavailable;
|
|
3464
3346
|
return [3 /*break*/, 5];
|
|
3465
3347
|
case 5: return [2 /*return*/];
|
|
3466
3348
|
}
|
|
@@ -4276,6 +4158,8 @@ function showToast(msg) {
|
|
|
4276
4158
|
// ─── Hivemind Radar (v5.3 — Health Watchdog) ───
|
|
4277
4159
|
var hivemindRefreshTimer = null;
|
|
4278
4160
|
function loadTeam() {
|
|
4161
|
+
var readSequence = ++teamReadSequence;
|
|
4162
|
+
var parentSequence = projectReadSequence;
|
|
4279
4163
|
return __awaiter(this, void 0, void 0, function () {
|
|
4280
4164
|
var project, card, res, data, team, list, roleIcons, statusColors, statusLabels, healthyCt, warnCt, summary, e_22;
|
|
4281
4165
|
return __generator(this, function (_a) {
|
|
@@ -4294,6 +4178,7 @@ function loadTeam() {
|
|
|
4294
4178
|
return [4 /*yield*/, res.json()];
|
|
4295
4179
|
case 3:
|
|
4296
4180
|
data = _a.sent();
|
|
4181
|
+
if (readSequence !== teamReadSequence || !isCurrentProjectRead(project, parentSequence)) return [2 /*return*/];
|
|
4297
4182
|
team = data.team || [];
|
|
4298
4183
|
list = document.getElementById('teamList');
|
|
4299
4184
|
if (team.length > 0) {
|
|
@@ -4442,6 +4327,7 @@ function loadSchedulerStatus() {
|
|
|
4442
4327
|
});
|
|
4443
4328
|
}
|
|
4444
4329
|
function loadGraphMetrics() {
|
|
4330
|
+
var readSequence = ++graphMetricsReadSequence;
|
|
4445
4331
|
return __awaiter(this, void 0, void 0, function () {
|
|
4446
4332
|
var el, warn, res, m, parts, synthStatus, tmStatus, pruneRatio, pruneSkipParts, rate, ratePct, rateColor, netNew, netColor, netSign, pruneRatioPct, cogTotal, autoP, clarP, fallP, ambPct, ambColor, fbPct, fbColor, lastRoute, lastConcept, lastConf, badges, e_24;
|
|
4447
4333
|
return __generator(this, function (_a) {
|
|
@@ -4457,9 +4343,11 @@ function loadGraphMetrics() {
|
|
|
4457
4343
|
return [4 /*yield*/, fetch('/api/graph/metrics')];
|
|
4458
4344
|
case 2:
|
|
4459
4345
|
res = _a.sent();
|
|
4346
|
+
if (!res.ok) throw new Error(dashboardReadMessages.graphUnavailable);
|
|
4460
4347
|
return [4 /*yield*/, res.json()];
|
|
4461
4348
|
case 3:
|
|
4462
4349
|
m = _a.sent();
|
|
4350
|
+
if (readSequence !== graphMetricsReadSequence) return [2 /*return*/];
|
|
4463
4351
|
parts = [];
|
|
4464
4352
|
// Synthesis row
|
|
4465
4353
|
parts.push('<div style="display:grid;grid-template-columns:1fr 1fr;gap:0.3rem;margin-bottom:0.5rem">');
|
|
@@ -4530,23 +4418,26 @@ function loadGraphMetrics() {
|
|
|
4530
4418
|
// SLO derivations row (WS4)
|
|
4531
4419
|
if (m.slo) {
|
|
4532
4420
|
parts.push('<div style="border-top:1px solid var(--border-glass);padding-top:0.4rem;margin-top:0.2rem;font-size:0.75rem">');
|
|
4533
|
-
parts.push('<strong>
|
|
4421
|
+
parts.push('<strong>' + escapeHtml(dashboardReadMessages.recordedResults) + '</strong>');
|
|
4422
|
+
if (m.scheduler && m.scheduler.last_sweep_at) {
|
|
4423
|
+
parts.push('<br>' + escapeHtml(dashboardReadMessages.schedulerOutcome) + ': ' + m.scheduler.projects_succeeded_last +
|
|
4424
|
+
' ' + dashboardReadMessages.schedulerSucceeded + ' · ' + m.scheduler.projects_failed_last + ' ' + dashboardReadMessages.schedulerFailed + ' · ' + m.scheduler.projects_processed_last + ' ' + dashboardReadMessages.schedulerAttempted);
|
|
4425
|
+
}
|
|
4534
4426
|
// Synthesis success rate — color-coded
|
|
4535
4427
|
if (m.slo.synthesis_success_rate !== null) {
|
|
4536
4428
|
rate = m.slo.synthesis_success_rate;
|
|
4537
4429
|
ratePct = Math.round(rate * 100);
|
|
4538
4430
|
rateColor = rate >= 0.95 ? 'var(--accent-green)' : rate >= 0.80 ? 'var(--accent-amber)' : 'var(--accent-rose)';
|
|
4539
|
-
parts.push('<br>
|
|
4431
|
+
parts.push('<br>' + escapeHtml(dashboardReadMessages.recordedSuccess) + ': <span style="color:' + rateColor + ';font-weight:600">' + ratePct + '%</span>');
|
|
4540
4432
|
}
|
|
4541
4433
|
else {
|
|
4542
|
-
parts.push('<br>
|
|
4434
|
+
parts.push('<br>' + escapeHtml(dashboardReadMessages.recordedSuccess) + ': <span style="color:var(--text-muted)">—</span>');
|
|
4543
4435
|
}
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
parts.push(' · Prune: ' + pruneRatioPct + '%');
|
|
4436
|
+
parts.push('<br>' + escapeHtml(dashboardReadMessages.lastDirectLinks) + ': ' +
|
|
4437
|
+
(m.synthesis && m.synthesis.last_run_at ? m.synthesis.last_links_created : '—'));
|
|
4438
|
+
parts.push(' · ' + escapeHtml(dashboardReadMessages.lastPrune) + ': ' +
|
|
4439
|
+
(m.pruning && m.pruning.last_run_at && m.pruning.links_scanned_last > 0
|
|
4440
|
+
? Math.round(m.slo.prune_ratio_last_sweep * 100) + '%' : '—'));
|
|
4550
4441
|
// Sweep duration
|
|
4551
4442
|
if (m.slo.scheduler_sweep_duration_ms_last > 0) {
|
|
4552
4443
|
parts.push(' · Sweep: ' + m.slo.scheduler_sweep_duration_ms_last + 'ms');
|
|
@@ -4625,7 +4516,9 @@ function loadGraphMetrics() {
|
|
|
4625
4516
|
return [3 /*break*/, 5];
|
|
4626
4517
|
case 4:
|
|
4627
4518
|
e_24 = _a.sent();
|
|
4519
|
+
if (readSequence !== graphMetricsReadSequence) return [2 /*return*/];
|
|
4628
4520
|
el.innerHTML = '<div style="color:var(--text-muted)">Graph metrics unavailable</div>';
|
|
4521
|
+
if (warn) warn.textContent = '';
|
|
4629
4522
|
return [3 /*break*/, 5];
|
|
4630
4523
|
case 5: return [2 /*return*/];
|
|
4631
4524
|
}
|
|
@@ -4765,60 +4658,7 @@ function cleanupIssues() {
|
|
|
4765
4658
|
btn.textContent = '🧹 Fix Issues';
|
|
4766
4659
|
}
|
|
4767
4660
|
// Re-run health check to refresh the card
|
|
4768
|
-
setTimeout(function () {
|
|
4769
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
4770
|
-
var healthRes, healthData, healthDot, healthLabel, healthSummary, healthIssues, cleanupBtn, statusMap, t, issues, sevIcons, e_27;
|
|
4771
|
-
return __generator(this, function (_a) {
|
|
4772
|
-
switch (_a.label) {
|
|
4773
|
-
case 0:
|
|
4774
|
-
_a.trys.push([0, 3, , 4]);
|
|
4775
|
-
return [4 /*yield*/, fetch('/api/health')];
|
|
4776
|
-
case 1:
|
|
4777
|
-
healthRes = _a.sent();
|
|
4778
|
-
return [4 /*yield*/, healthRes.json()];
|
|
4779
|
-
case 2:
|
|
4780
|
-
healthData = _a.sent();
|
|
4781
|
-
healthDot = document.getElementById('healthDot');
|
|
4782
|
-
healthLabel = document.getElementById('healthLabel');
|
|
4783
|
-
healthSummary = document.getElementById('healthSummary');
|
|
4784
|
-
healthIssues = document.getElementById('healthIssues');
|
|
4785
|
-
cleanupBtn = document.getElementById('cleanupBtn');
|
|
4786
|
-
statusMap = { healthy: '✅ Healthy', degraded: '⚠️ Degraded', unhealthy: '🔴 Unhealthy' };
|
|
4787
|
-
healthDot.className = 'health-dot ' + (healthData.status || 'unknown');
|
|
4788
|
-
healthLabel.textContent = statusMap[healthData.status] || '❓ Unknown';
|
|
4789
|
-
t = healthData.totals || {};
|
|
4790
|
-
healthSummary.textContent = (t.activeEntries || 0) + ' entries · ' + (t.handoffs || 0) + ' handoffs · ' + (t.rollups || 0) + ' rollups' + (t.crdtMerges ? ' · 🔄 ' + t.crdtMerges + ' merges' : '');
|
|
4791
|
-
issues = healthData.issues || [];
|
|
4792
|
-
if (issues.length > 0) {
|
|
4793
|
-
sevIcons = { error: '🔴', warning: '🟡', info: '🔵' };
|
|
4794
|
-
healthIssues.innerHTML = issues.map(function (i) {
|
|
4795
|
-
return '<div class="issue-row"><span>' + (sevIcons[i.severity] || '❓') + '</span><span>' + escapeHtml(i.message) + '</span></div>';
|
|
4796
|
-
}).join('');
|
|
4797
|
-
if (cleanupBtn) {
|
|
4798
|
-
cleanupBtn.disabled = false;
|
|
4799
|
-
cleanupBtn.textContent = '🧹 Fix Issues';
|
|
4800
|
-
cleanupBtn.style.display = 'inline-block';
|
|
4801
|
-
}
|
|
4802
|
-
}
|
|
4803
|
-
else {
|
|
4804
|
-
healthIssues.innerHTML = '<div style="color:var(--accent-green);font-size:0.8rem">🎉 No issues found</div>';
|
|
4805
|
-
if (cleanupBtn)
|
|
4806
|
-
cleanupBtn.style.display = 'none';
|
|
4807
|
-
}
|
|
4808
|
-
return [3 /*break*/, 4];
|
|
4809
|
-
case 3:
|
|
4810
|
-
e_27 = _a.sent();
|
|
4811
|
-
// Health re-check failed — ensure button is usable
|
|
4812
|
-
if (btn) {
|
|
4813
|
-
btn.disabled = false;
|
|
4814
|
-
btn.textContent = '🧹 Fix Issues';
|
|
4815
|
-
}
|
|
4816
|
-
return [3 /*break*/, 4];
|
|
4817
|
-
case 4: return [2 /*return*/];
|
|
4818
|
-
}
|
|
4819
|
-
});
|
|
4820
|
-
});
|
|
4821
|
-
}, 400);
|
|
4661
|
+
setTimeout(function () { refreshHealthScan(); }, 400);
|
|
4822
4662
|
return [3 /*break*/, 5];
|
|
4823
4663
|
case 4:
|
|
4824
4664
|
e_26 = _a.sent();
|
|
@@ -4876,6 +4716,9 @@ if ('serviceWorker' in navigator) {
|
|
|
4876
4716
|
/* --- INTENT HEALTH COMPONENT (ES5 Safe) --- */
|
|
4877
4717
|
|
|
4878
4718
|
function fetchIntentHealth(project) {
|
|
4719
|
+
if (!isCurrentProjectRead(project)) return;
|
|
4720
|
+
var readSequence = ++intentReadSequence;
|
|
4721
|
+
var parentSequence = projectReadSequence;
|
|
4879
4722
|
var container = document.getElementById('intentHealthCardContent');
|
|
4880
4723
|
var card = document.getElementById('intentHealthCard');
|
|
4881
4724
|
if (!container || !card) return;
|
|
@@ -4886,6 +4729,7 @@ function fetchIntentHealth(project) {
|
|
|
4886
4729
|
fetch('/api/intent-health?project=' + encodeURIComponent(project))
|
|
4887
4730
|
.then(function(res) { return res.json(); })
|
|
4888
4731
|
.then(function(data) {
|
|
4732
|
+
if (readSequence !== intentReadSequence || !isCurrentProjectRead(project, parentSequence)) return;
|
|
4889
4733
|
if (data.error) {
|
|
4890
4734
|
container.innerHTML = '<div style="color: #ff7b72; padding: 16px;">Error: ' + escapeHtml(data.error) + '</div>';
|
|
4891
4735
|
return;
|
|
@@ -4893,6 +4737,7 @@ function fetchIntentHealth(project) {
|
|
|
4893
4737
|
renderIntentHealthCard(data);
|
|
4894
4738
|
})
|
|
4895
4739
|
.catch(function(err) {
|
|
4740
|
+
if (readSequence !== intentReadSequence || !isCurrentProjectRead(project, parentSequence)) return;
|
|
4896
4741
|
container.innerHTML = '<div style="color: #ff7b72; padding: 16px;">Failed to load intent health.</div>';
|
|
4897
4742
|
});
|
|
4898
4743
|
}
|