@cccarv82/freya 2.3.10 → 2.3.12

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/cli/web-ui.js CHANGED
@@ -139,14 +139,14 @@
139
139
  state.chatSessionId = fromLocal;
140
140
  return state.chatSessionId;
141
141
  }
142
- } catch {}
142
+ } catch { }
143
143
 
144
144
  const id = (typeof crypto !== 'undefined' && crypto.randomUUID)
145
145
  ? crypto.randomUUID()
146
146
  : ('sess-' + Date.now() + '-' + Math.random().toString(16).slice(2, 8));
147
147
 
148
148
  state.chatSessionId = id;
149
- try { localStorage.setItem('freya.chatSessionId', id); } catch {}
149
+ try { localStorage.setItem('freya.chatSessionId', id); } catch { }
150
150
  return id;
151
151
  }
152
152
 
@@ -197,7 +197,7 @@
197
197
  // keep newest in view
198
198
  try {
199
199
  thread.scrollTop = thread.scrollHeight;
200
- } catch {}
200
+ } catch { }
201
201
  }
202
202
 
203
203
  async function loadChatHistory() {
@@ -230,7 +230,7 @@
230
230
  bubble.appendChild(body);
231
231
  thread.appendChild(bubble);
232
232
  }
233
- try { thread.scrollTop = thread.scrollHeight; } catch {}
233
+ try { thread.scrollTop = thread.scrollHeight; } catch { }
234
234
  }
235
235
  } catch {
236
236
  // ignore
@@ -285,7 +285,7 @@
285
285
  el.innerHTML = renderMarkdown(state.lastText);
286
286
  } catch (e) {
287
287
  // Fallback: never hide the error/output if markdown rendering breaks
288
- try { console.error('renderMarkdown failed:', e); } catch {}
288
+ try { console.error('renderMarkdown failed:', e); } catch { }
289
289
  el.textContent = state.lastText;
290
290
  }
291
291
  }
@@ -367,9 +367,9 @@
367
367
 
368
368
  function saveLocal() {
369
369
  localStorage.setItem('freya.dir', $('dir').value);
370
- try { localStorage.setItem('freya.autoApply', state.autoApply ? '1' : '0'); } catch {}
371
- try { localStorage.setItem('freya.autoRunReports', state.autoRunReports ? '1' : '0'); } catch {}
372
- try { localStorage.setItem('freya.prettyPublish', state.prettyPublish ? '1' : '0'); } catch {}
370
+ try { localStorage.setItem('freya.autoApply', state.autoApply ? '1' : '0'); } catch { }
371
+ try { localStorage.setItem('freya.autoRunReports', state.autoRunReports ? '1' : '0'); } catch { }
372
+ try { localStorage.setItem('freya.prettyPublish', state.prettyPublish ? '1' : '0'); } catch { }
373
373
  }
374
374
 
375
375
  function loadLocal() {
@@ -388,7 +388,7 @@
388
388
  if (v3 !== null) state.prettyPublish = v3 === '1';
389
389
  const cb3 = $('prettyPublish');
390
390
  if (cb3) cb3.checked = !!state.prettyPublish;
391
- } catch {}
391
+ } catch { }
392
392
 
393
393
  const def = (window.__FREYA_DEFAULT_DIR && window.__FREYA_DEFAULT_DIR !== '__FREYA_DEFAULT_DIR__')
394
394
  ? window.__FREYA_DEFAULT_DIR
@@ -655,7 +655,8 @@
655
655
  };
656
656
  }
657
657
 
658
- grid.appendChild(card); }
658
+ grid.appendChild(card);
659
+ }
659
660
  }
660
661
 
661
662
  function renderProjects() {
@@ -664,7 +665,7 @@
664
665
  const filter = String(($('projectsFilter') && $('projectsFilter').value) || '').toLowerCase();
665
666
  const items = Array.isArray(state.projects) ? state.projects : [];
666
667
  const filtered = items.filter((p) => {
667
- const hay = [p.client, p.program, p.stream, p.project, p.slug, (p.tags||[]).join(' ')].join(' ').toLowerCase();
668
+ const hay = [p.client, p.program, p.stream, p.project, p.slug, (p.tags || []).join(' ')].join(' ').toLowerCase();
668
669
  return !filter || hay.includes(filter);
669
670
  });
670
671
  el.innerHTML = '';
@@ -847,9 +848,9 @@
847
848
  const c = cards[idx];
848
849
  const card = document.createElement('div');
849
850
  card.className = 'reportCard';
850
- const dateLine = c.body.find((b)=> b.toLowerCase().includes('data'));
851
- const impactLine = c.body.find((b)=> b.toLowerCase().includes('descricao') || b.toLowerCase().includes('impacto'));
852
- const statusLine = c.body.find((b)=> /^status\s*:/i.test(b));
851
+ const dateLine = c.body.find((b) => b.toLowerCase().includes('data'));
852
+ const impactLine = c.body.find((b) => b.toLowerCase().includes('descricao') || b.toLowerCase().includes('impacto'));
853
+ const statusLine = c.body.find((b) => /^status\s*:/i.test(b));
853
854
  const statusRaw = statusLine ? statusLine.split(':').slice(1).join(':').trim().toLowerCase() : '';
854
855
  let statusKey = '';
855
856
  if (['open', 'aberto', 'aberta'].includes(statusRaw)) statusKey = 'open';
@@ -859,7 +860,7 @@
859
860
  card.innerHTML = '<div class="reportTitle">' + escapeHtml(c.title) + '</div>'
860
861
  + (dateLine ? ('<div class="reportMeta">' + escapeHtml(dateLine) + '</div>') : '')
861
862
  + (impactLine ? ('<div class="help" style="margin-top:4px">' + escapeHtml(impactLine) + '</div>') : '')
862
- + c.body.filter((b)=> b!==dateLine && b!==impactLine && b!==statusLine).map((b) => '<div class="help" style="margin-top:4px">' + escapeHtml(b) + '</div>').join('');
863
+ + c.body.filter((b) => b !== dateLine && b !== impactLine && b !== statusLine).map((b) => '<div class="help" style="margin-top:4px">' + escapeHtml(b) + '</div>').join('');
863
864
 
864
865
  if (statusKey) {
865
866
  const actions = document.createElement('div');
@@ -913,7 +914,7 @@
913
914
  el.innerHTML = '';
914
915
  let items = r.items || [];
915
916
  const sort = state.heatmapSort || 'pending';
916
- items = items.slice().sort((a,b)=> (b[sort]||0) - (a[sort]||0));
917
+ items = items.slice().sort((a, b) => (b[sort] || 0) - (a[sort] || 0));
917
918
  for (const it of items) {
918
919
  const row = document.createElement('div');
919
920
  row.className = 'rep';
@@ -1001,6 +1002,12 @@
1001
1002
  if (!isTimeline) window.location.href = '/timeline';
1002
1003
  };
1003
1004
  }
1005
+ if ($('railGraph')) {
1006
+ $('railGraph').onclick = () => {
1007
+ const isGraph = document.body && document.body.dataset && document.body.dataset.page === 'graph';
1008
+ if (!isGraph) window.location.href = '/graph';
1009
+ };
1010
+ }
1004
1011
  if (health) {
1005
1012
  health.onclick = () => {
1006
1013
  const isHealth = document.body && document.body.dataset && document.body.dataset.page === 'companion';
@@ -1172,7 +1179,7 @@
1172
1179
  if (r && r.dir) {
1173
1180
  $('dir').value = r.dir;
1174
1181
  const sp = $('sidePath');
1175
- if (sp) sp.textContent = r.dir;
1182
+ if (sp) sp.textContent = r.dir;
1176
1183
  }
1177
1184
  saveLocal();
1178
1185
  setPill('ok', 'ready');
@@ -1194,7 +1201,7 @@
1194
1201
  setLast(null);
1195
1202
  await refreshReports({ selectLatest: true });
1196
1203
  // Auto health after init
1197
- try { await doHealth(); } catch {}
1204
+ try { await doHealth(); } catch { }
1198
1205
  setPill('ok', 'init ok');
1199
1206
  } catch (e) {
1200
1207
  setPill('err', 'init failed');
@@ -1215,7 +1222,7 @@
1215
1222
  setLast(null);
1216
1223
  await refreshReports({ selectLatest: true });
1217
1224
  // Auto health after update
1218
- try { await doHealth(); } catch {}
1225
+ try { await doHealth(); } catch { }
1219
1226
  setPill('ok', 'update ok');
1220
1227
  } catch (e) {
1221
1228
  setPill('err', 'update failed');
@@ -1303,11 +1310,11 @@
1303
1310
  }
1304
1311
  }
1305
1312
 
1306
- async function refreshRiskSummary() {
1307
- const el = $('riskSummary');
1308
- if (el) el.innerHTML = '<div class="help">Carregando riscos...</div>';
1313
+ async function refreshRiskRadar() {
1314
+ const el = $('riskRadarBox');
1315
+ if (el) el.innerHTML = '<div class="help">Calculando riscos...</div>';
1309
1316
  try {
1310
- const r = await api('/api/risk/summary', { dir: dirOrDefault() });
1317
+ const r = await api('/api/companion/risk-radar', { dir: dirOrDefault() });
1311
1318
  if (!el) return;
1312
1319
  if (r && r.needsInit) {
1313
1320
  el.innerHTML = '<div class="help">Workspace não inicializado.</div>';
@@ -1315,23 +1322,74 @@
1315
1322
  }
1316
1323
  const items = Array.isArray(r.items) ? r.items : [];
1317
1324
  if (!items.length) {
1318
- el.innerHTML = '<div class="help">Sem riscos relevantes.</div>';
1325
+ el.innerHTML = '<div class="help">Radar livre de riscos críticos ou moderados no momento.</div>';
1319
1326
  return;
1320
1327
  }
1321
1328
  const rows = items.map((it) => {
1322
- const age = (it.oldestBlockerDays != null) ? `${it.oldestBlockerDays}d` : 'n/a';
1329
+ const pillClass = it.severity === 'high' ? 'warn' : 'info';
1330
+ const pillLabel = it.severity === 'high' ? 'Alto Risco' : 'Atenção';
1323
1331
  return `<div class=\"rep\">`
1324
1332
  + `<div style=\"display:flex; justify-content:space-between; gap:10px; align-items:center\">`
1325
1333
  + `<div style=\"min-width:0\"><div style=\"font-weight:800\">${escapeHtml(it.slug || '')}</div>`
1326
- + `<div class=\"help\" style=\"margin-top:4px\">Pendentes: ${escapeHtml(String(it.pendingTasks || 0))} · Blockers 7d+: ${escapeHtml(String(it.oldBlockers || 0))} · Mais antigo: ${escapeHtml(age)}</div>`
1334
+ + `<div class=\"help\" style=\"margin-top:4px\">${escapeHtml(it.message || '')}</div>`
1327
1335
  + `</div>`
1328
- + `<div class=\"pill warn\">risco</div>`
1336
+ + `<div class=\"pill ${pillClass}\">${pillLabel}</div>`
1329
1337
  + `</div>`
1330
1338
  + `</div>`;
1331
1339
  }).join('');
1332
1340
  el.innerHTML = rows;
1333
1341
  } catch {
1334
- if (el) el.innerHTML = '<div class="help">Falha ao carregar riscos.</div>';
1342
+ if (el) el.innerHTML = '<div class="help">Falha ao carregar o Radar de Risco.</div>';
1343
+ }
1344
+ }
1345
+
1346
+ async function refreshGraph() {
1347
+ try {
1348
+ if (!window.vis) return; // vis.js not loaded yet
1349
+ const el = $('networkGraph');
1350
+ if (!el) return;
1351
+
1352
+ const r = await api('/api/graph/data', { dir: dirOrDefault() });
1353
+ if (!r || !r.nodes) return;
1354
+
1355
+ const nodes = new vis.DataSet(r.nodes);
1356
+ const edges = new vis.DataSet(r.edges);
1357
+
1358
+ const data = { nodes, edges };
1359
+ const options = {
1360
+ nodes: {
1361
+ shape: 'dot',
1362
+ size: 16,
1363
+ font: { color: 'var(--fg)', face: 'var(--sans)' },
1364
+ borderWidth: 2
1365
+ },
1366
+ edges: {
1367
+ color: { color: 'var(--border)', highlight: 'var(--brand)' },
1368
+ width: 1,
1369
+ smooth: { type: 'continuous' }
1370
+ },
1371
+ groups: {
1372
+ project: { color: { background: 'var(--bg2)', border: 'var(--brand)' } },
1373
+ task: { color: { background: 'var(--bg2)', border: 'var(--info)' }, size: 10 },
1374
+ blocker: { color: { background: 'var(--warn-bg)', border: 'var(--warn)' }, size: 14, shape: 'triangle' },
1375
+ tag: { color: { background: 'var(--bg2)', border: 'var(--border)' }, size: 8, font: { size: 10 } },
1376
+ unassigned: { color: { background: 'var(--bg2)', border: 'var(--border)' }, size: 10 }
1377
+ },
1378
+ physics: {
1379
+ forceAtlas2Based: { gravitationalConstant: -26, centralGravity: 0.005, springLength: 230, springConstant: 0.18 },
1380
+ maxVelocity: 146,
1381
+ solver: 'forceAtlas2Based',
1382
+ timestep: 0.35,
1383
+ stabilization: { iterations: 150 }
1384
+ }
1385
+ };
1386
+
1387
+ if (state.networkInstance) {
1388
+ state.networkInstance.destroy();
1389
+ }
1390
+ state.networkInstance = new vis.Network(el, data, options);
1391
+ } catch (e) {
1392
+ console.error('Failed to load graph data', e);
1335
1393
  }
1336
1394
  }
1337
1395
 
@@ -1577,19 +1635,19 @@
1577
1635
  function togglePrettyPublish() {
1578
1636
  const cb = $('prettyPublish');
1579
1637
  state.prettyPublish = cb ? !!cb.checked : true;
1580
- try { localStorage.setItem('freya.prettyPublish', state.prettyPublish ? '1' : '0'); } catch {}
1638
+ try { localStorage.setItem('freya.prettyPublish', state.prettyPublish ? '1' : '0'); } catch { }
1581
1639
  }
1582
1640
 
1583
1641
  function toggleAutoRunReports() {
1584
1642
  const cb = $('autoRunReports');
1585
1643
  state.autoRunReports = cb ? !!cb.checked : false;
1586
- try { localStorage.setItem('freya.autoRunReports', state.autoRunReports ? '1' : '0'); } catch {}
1644
+ try { localStorage.setItem('freya.autoRunReports', state.autoRunReports ? '1' : '0'); } catch { }
1587
1645
  }
1588
1646
 
1589
1647
  function toggleAutoApply() {
1590
1648
  const cb = $('autoApply');
1591
1649
  state.autoApply = cb ? !!cb.checked : true;
1592
- try { localStorage.setItem('freya.autoApply', state.autoApply ? '1' : '0'); } catch {}
1650
+ try { localStorage.setItem('freya.autoApply', state.autoApply ? '1' : '0'); } catch { }
1593
1651
  }
1594
1652
 
1595
1653
  async function saveAndPlan() {
@@ -1699,8 +1757,8 @@
1699
1757
  chatAppend('assistant', msg, { markdown: true });
1700
1758
 
1701
1759
  // After apply, refresh panels so the UI reflects the new state (tasks/blockers/reports)
1702
- try { await refreshToday(); } catch {}
1703
- try { await refreshReports({ selectLatest: true }); } catch {}
1760
+ try { await refreshToday(); } catch { }
1761
+ try { await refreshReports({ selectLatest: true }); } catch { }
1704
1762
 
1705
1763
  setPill('ok', 'applied');
1706
1764
  setTimeout(() => setPill('ok', 'pronto'), 800);
@@ -1725,15 +1783,16 @@
1725
1783
  const open = localStorage.getItem('freya.devDrawer') === '1';
1726
1784
  if (open) d.open = true;
1727
1785
  d.addEventListener('toggle', () => {
1728
- try { localStorage.setItem('freya.devDrawer', d.open ? '1' : '0'); } catch {}
1786
+ try { localStorage.setItem('freya.devDrawer', d.open ? '1' : '0'); } catch { }
1729
1787
  });
1730
1788
  }
1731
- } catch {}
1789
+ } catch { }
1732
1790
 
1733
1791
  const isReportsPage = document.body && document.body.dataset && document.body.dataset.page === 'reports';
1734
1792
  const isProjectsPage = document.body && document.body.dataset && document.body.dataset.page === 'projects';
1735
1793
  const isTimelinePage = document.body && document.body.dataset && document.body.dataset.page === 'timeline';
1736
1794
  const isCompanionPage = document.body && document.body.dataset && document.body.dataset.page === 'companion';
1795
+ const isGraphPage = document.body && document.body.dataset && document.body.dataset.page === 'graph';
1737
1796
 
1738
1797
  // Load persisted settings from the workspace + bootstrap (auto-init + auto-health)
1739
1798
  (async () => {
@@ -1771,12 +1830,17 @@
1771
1830
  return;
1772
1831
  }
1773
1832
 
1833
+ if (isGraphPage) {
1834
+ await refreshGraph();
1835
+ return;
1836
+ }
1837
+
1774
1838
  if (isCompanionPage) {
1775
1839
  await refreshHealthChecklist();
1776
1840
  await refreshQualityScore();
1777
1841
  await refreshExecutiveSummary();
1778
1842
  await refreshAnomalies();
1779
- await refreshRiskSummary();
1843
+ await refreshRiskRadar();
1780
1844
  await refreshIncidents();
1781
1845
  await refreshHeatmap();
1782
1846
  return;
@@ -1822,6 +1886,7 @@
1822
1886
  window.refreshReportsPage = refreshReportsPage;
1823
1887
  window.refreshProjects = refreshProjects;
1824
1888
  window.refreshTimeline = refreshTimeline;
1889
+ window.refreshGraph = refreshGraph;
1825
1890
  window.refreshIncidents = refreshIncidents;
1826
1891
  window.refreshHeatmap = refreshHeatmap;
1827
1892
  window.setHeatmapSort = setHeatmapSort;
@@ -1831,7 +1896,7 @@
1831
1896
  window.refreshQualityScore = refreshQualityScore;
1832
1897
  window.refreshExecutiveSummary = refreshExecutiveSummary;
1833
1898
  window.refreshAnomalies = refreshAnomalies;
1834
- window.refreshRiskSummary = refreshRiskSummary;
1899
+ window.refreshRiskRadar = refreshRiskRadar;
1835
1900
  window.copyOut = copyOut;
1836
1901
  window.copyPath = copyPath;
1837
1902
  window.openSelected = openSelected;