@cccarv82/freya 2.13.0 → 2.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/web-ui.css +182 -0
- package/cli/web-ui.js +229 -31
- package/cli/web.js +32 -35
- package/package.json +2 -2
package/cli/web-ui.css
CHANGED
|
@@ -557,6 +557,30 @@ body {
|
|
|
557
557
|
border-color: rgba(59, 130, 246, .35);
|
|
558
558
|
}
|
|
559
559
|
|
|
560
|
+
[data-theme="dark"] .pill.ok {
|
|
561
|
+
background: rgba(16, 185, 129, .18);
|
|
562
|
+
color: #6ee7b7;
|
|
563
|
+
border-color: rgba(16, 185, 129, .35);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
[data-theme="dark"] .pill.warn {
|
|
567
|
+
background: rgba(245, 158, 11, .18);
|
|
568
|
+
color: #fbbf24;
|
|
569
|
+
border-color: rgba(245, 158, 11, .35);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
[data-theme="dark"] .pill.info {
|
|
573
|
+
background: rgba(59, 130, 246, .18);
|
|
574
|
+
color: #93c5fd;
|
|
575
|
+
border-color: rgba(59, 130, 246, .35);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
[data-theme="dark"] .pill.err {
|
|
579
|
+
background: rgba(239, 68, 68, .15);
|
|
580
|
+
color: #fca5a5;
|
|
581
|
+
border-color: rgba(239, 68, 68, .35);
|
|
582
|
+
}
|
|
583
|
+
|
|
560
584
|
.centerBody {
|
|
561
585
|
padding: 16px 20px 20px;
|
|
562
586
|
overflow: auto;
|
|
@@ -564,6 +588,7 @@ body {
|
|
|
564
588
|
display: flex;
|
|
565
589
|
flex-direction: column;
|
|
566
590
|
gap: 18px;
|
|
591
|
+
flex: 1;
|
|
567
592
|
}
|
|
568
593
|
|
|
569
594
|
.promptShell {
|
|
@@ -1313,6 +1338,163 @@ textarea:focus {
|
|
|
1313
1338
|
}
|
|
1314
1339
|
}
|
|
1315
1340
|
|
|
1341
|
+
/* ── Insight Pills (Blocker Severity) ── */
|
|
1342
|
+
.insight-pills {
|
|
1343
|
+
display: flex;
|
|
1344
|
+
flex-wrap: wrap;
|
|
1345
|
+
gap: 6px;
|
|
1346
|
+
margin-bottom: 8px;
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
.insight-pill {
|
|
1350
|
+
display: inline-flex;
|
|
1351
|
+
align-items: center;
|
|
1352
|
+
gap: 4px;
|
|
1353
|
+
font-size: 11px;
|
|
1354
|
+
font-weight: 700;
|
|
1355
|
+
padding: 3px 10px;
|
|
1356
|
+
border: 1px solid;
|
|
1357
|
+
letter-spacing: 0.3px;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
.insight-pill.critical { background: rgba(239,68,68,.12); color: #f87171; border-color: rgba(239,68,68,.3); }
|
|
1361
|
+
.insight-pill.high { background: rgba(249,115,22,.12); color: #fb923c; border-color: rgba(249,115,22,.3); }
|
|
1362
|
+
.insight-pill.medium { background: rgba(250,204,21,.12); color: #facc15; border-color: rgba(250,204,21,.3); }
|
|
1363
|
+
.insight-pill.low { background: rgba(34,197,94,.10); color: #4ade80; border-color: rgba(34,197,94,.25); }
|
|
1364
|
+
|
|
1365
|
+
.insight-clear {
|
|
1366
|
+
display: flex;
|
|
1367
|
+
align-items: center;
|
|
1368
|
+
gap: 6px;
|
|
1369
|
+
font-size: 12px;
|
|
1370
|
+
color: #4ade80;
|
|
1371
|
+
font-weight: 600;
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
.insight-section-title {
|
|
1375
|
+
font-weight: 600;
|
|
1376
|
+
font-size: 11px;
|
|
1377
|
+
text-transform: uppercase;
|
|
1378
|
+
letter-spacing: 0.5px;
|
|
1379
|
+
color: var(--muted);
|
|
1380
|
+
margin: 10px 0 6px;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
.insight-steps {
|
|
1384
|
+
margin: 0 0 0 2px;
|
|
1385
|
+
padding: 0;
|
|
1386
|
+
list-style: none;
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
.insight-steps li {
|
|
1390
|
+
position: relative;
|
|
1391
|
+
padding: 4px 0 4px 16px;
|
|
1392
|
+
font-size: 12px;
|
|
1393
|
+
color: var(--muted);
|
|
1394
|
+
line-height: 1.4;
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
.insight-steps li::before {
|
|
1398
|
+
content: '';
|
|
1399
|
+
position: absolute;
|
|
1400
|
+
left: 0;
|
|
1401
|
+
top: 11px;
|
|
1402
|
+
width: 6px;
|
|
1403
|
+
height: 6px;
|
|
1404
|
+
background: var(--accent);
|
|
1405
|
+
opacity: 0.6;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
/* ── Toast Notifications ── */
|
|
1409
|
+
.toast-container {
|
|
1410
|
+
position: fixed;
|
|
1411
|
+
bottom: 20px;
|
|
1412
|
+
right: 20px;
|
|
1413
|
+
z-index: 9999;
|
|
1414
|
+
display: flex;
|
|
1415
|
+
flex-direction: column-reverse;
|
|
1416
|
+
gap: 8px;
|
|
1417
|
+
pointer-events: none;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
.toast {
|
|
1421
|
+
pointer-events: auto;
|
|
1422
|
+
padding: 10px 16px;
|
|
1423
|
+
border: 1px solid var(--line2);
|
|
1424
|
+
background: var(--paper);
|
|
1425
|
+
backdrop-filter: blur(12px);
|
|
1426
|
+
color: var(--text);
|
|
1427
|
+
font-size: 13px;
|
|
1428
|
+
font-weight: 500;
|
|
1429
|
+
font-family: var(--sans);
|
|
1430
|
+
box-shadow: var(--shadow);
|
|
1431
|
+
max-width: 360px;
|
|
1432
|
+
opacity: 0;
|
|
1433
|
+
transform: translateX(100%);
|
|
1434
|
+
animation: toastIn 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
.toast.out {
|
|
1438
|
+
animation: toastOut 0.25s ease-in forwards;
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
.toast.ok { border-left: 3px solid rgba(16, 185, 129, .85); }
|
|
1442
|
+
.toast.err { border-left: 3px solid rgba(239, 68, 68, .85); }
|
|
1443
|
+
.toast.run { border-left: 3px solid rgba(56, 189, 248, .9); }
|
|
1444
|
+
.toast.plan { border-left: 3px solid rgba(250, 204, 21, .9); }
|
|
1445
|
+
|
|
1446
|
+
@keyframes toastIn {
|
|
1447
|
+
from { opacity: 0; transform: translateX(100%); }
|
|
1448
|
+
to { opacity: 1; transform: translateX(0); }
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
@keyframes toastOut {
|
|
1452
|
+
from { opacity: 1; transform: translateX(0); }
|
|
1453
|
+
to { opacity: 0; transform: translateX(100%); }
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
/* ── Loading Skeletons ── */
|
|
1457
|
+
.skeleton {
|
|
1458
|
+
position: relative;
|
|
1459
|
+
overflow: hidden;
|
|
1460
|
+
min-height: 60px;
|
|
1461
|
+
padding: 16px;
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
.skeleton::after {
|
|
1465
|
+
content: '';
|
|
1466
|
+
position: absolute;
|
|
1467
|
+
inset: 0;
|
|
1468
|
+
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.04), transparent);
|
|
1469
|
+
animation: shimmer 1.5s infinite;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
@keyframes shimmer {
|
|
1473
|
+
0% { transform: translateX(-100%); }
|
|
1474
|
+
100% { transform: translateX(100%); }
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
.skeleton-line {
|
|
1478
|
+
height: 12px;
|
|
1479
|
+
background: var(--line);
|
|
1480
|
+
margin-bottom: 10px;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
.skeleton-line:nth-child(1) { width: 80%; }
|
|
1484
|
+
.skeleton-line:nth-child(2) { width: 55%; }
|
|
1485
|
+
.skeleton-line:nth-child(3) { width: 70%; }
|
|
1486
|
+
|
|
1487
|
+
/* ── Content Transitions ── */
|
|
1488
|
+
@keyframes fadeIn {
|
|
1489
|
+
from { opacity: 0; transform: translateY(6px); }
|
|
1490
|
+
to { opacity: 1; transform: translateY(0); }
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
.panelBody > *,
|
|
1494
|
+
.swimlaneContainer > * {
|
|
1495
|
+
animation: fadeIn 0.2s ease-out;
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1316
1498
|
* {
|
|
1317
1499
|
border-radius: 0 !important;
|
|
1318
1500
|
}
|
package/cli/web-ui.js
CHANGED
|
@@ -39,6 +39,37 @@
|
|
|
39
39
|
if (pill) pill.textContent = text;
|
|
40
40
|
const status = $('status');
|
|
41
41
|
if (status) status.textContent = text;
|
|
42
|
+
// Forward important states to toast system
|
|
43
|
+
if (kind === 'err') showToast('err', text);
|
|
44
|
+
else if (kind === 'ok' && text !== 'pronto') showToast('ok', text);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* ── Toast Notification System ── */
|
|
48
|
+
let _toastContainer = null;
|
|
49
|
+
function showToast(kind, text, duration) {
|
|
50
|
+
if (!_toastContainer) {
|
|
51
|
+
_toastContainer = document.createElement('div');
|
|
52
|
+
_toastContainer.className = 'toast-container';
|
|
53
|
+
document.body.appendChild(_toastContainer);
|
|
54
|
+
}
|
|
55
|
+
const el = document.createElement('div');
|
|
56
|
+
el.className = 'toast ' + kind;
|
|
57
|
+
el.textContent = text;
|
|
58
|
+
el.setAttribute('role', 'alert');
|
|
59
|
+
el.setAttribute('aria-live', 'polite');
|
|
60
|
+
_toastContainer.appendChild(el);
|
|
61
|
+
const ms = duration || (kind === 'err' ? 4000 : 2500);
|
|
62
|
+
setTimeout(() => {
|
|
63
|
+
el.classList.add('out');
|
|
64
|
+
setTimeout(() => el.remove(), 300);
|
|
65
|
+
}, ms);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/* ── Skeleton Loading Helper ── */
|
|
69
|
+
function showSkeleton(elId) {
|
|
70
|
+
const el = $(elId);
|
|
71
|
+
if (!el) return;
|
|
72
|
+
el.innerHTML = '<div class="skeleton"><div class="skeleton-line"></div><div class="skeleton-line"></div><div class="skeleton-line"></div></div>';
|
|
42
73
|
}
|
|
43
74
|
|
|
44
75
|
function escapeHtml(str) {
|
|
@@ -131,6 +162,70 @@
|
|
|
131
162
|
return html;
|
|
132
163
|
}
|
|
133
164
|
|
|
165
|
+
function formatPlanForDisplay(rawPlan) {
|
|
166
|
+
try {
|
|
167
|
+
const text = String(rawPlan || '');
|
|
168
|
+
const start = text.indexOf('{');
|
|
169
|
+
if (start === -1) return null;
|
|
170
|
+
let depth = 0, inStr = false, esc = false, jsonStr = null;
|
|
171
|
+
for (let i = start; i < text.length; i++) {
|
|
172
|
+
const ch = text[i];
|
|
173
|
+
if (esc) { esc = false; continue; }
|
|
174
|
+
if (ch === '\\') { esc = true; continue; }
|
|
175
|
+
if (ch === '"') { inStr = !inStr; continue; }
|
|
176
|
+
if (inStr) continue;
|
|
177
|
+
if (ch === '{') depth++;
|
|
178
|
+
if (ch === '}') { depth--; if (depth === 0) { jsonStr = text.slice(start, i + 1); break; } }
|
|
179
|
+
}
|
|
180
|
+
if (!jsonStr) return null;
|
|
181
|
+
const plan = JSON.parse(jsonStr);
|
|
182
|
+
const actions = Array.isArray(plan.actions) ? plan.actions : [];
|
|
183
|
+
if (actions.length === 0) return null;
|
|
184
|
+
|
|
185
|
+
const icons = {
|
|
186
|
+
append_daily_log: '\u{1F4DD}', appenddailylog: '\u{1F4DD}',
|
|
187
|
+
create_task: '\u2705', createtask: '\u2705',
|
|
188
|
+
create_blocker: '\u{1F6A7}', createblocker: '\u{1F6A7}',
|
|
189
|
+
suggest_report: '\u{1F4CA}', suggestreport: '\u{1F4CA}',
|
|
190
|
+
oracle_query: '\u{1F50D}', oraclequery: '\u{1F50D}'
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const lines = actions.map(function(a, i) {
|
|
194
|
+
var type = String(a.type || '').trim().toLowerCase().replace(/_/g, '');
|
|
195
|
+
var icon = icons[type] || icons[String(a.type || '').trim()] || '\u2022';
|
|
196
|
+
var num = i + 1;
|
|
197
|
+
var typeNorm = type.replace(/_/g, '');
|
|
198
|
+
|
|
199
|
+
if (typeNorm === 'appenddailylog') {
|
|
200
|
+
var t = String(a.text || '').slice(0, 140);
|
|
201
|
+
return num + '. ' + icon + ' **Registrar no log:** ' + t + (String(a.text || '').length > 140 ? '...' : '');
|
|
202
|
+
}
|
|
203
|
+
if (typeNorm === 'createtask') {
|
|
204
|
+
var desc = String(a.description || '').slice(0, 120);
|
|
205
|
+
var pri = a.priority ? ' (prioridade: **' + String(a.priority).toUpperCase() + '**)' : '';
|
|
206
|
+
var cat = a.category ? ' [' + a.category + ']' : '';
|
|
207
|
+
return num + '. ' + icon + ' **Criar tarefa:** ' + desc + pri + cat;
|
|
208
|
+
}
|
|
209
|
+
if (typeNorm === 'createblocker') {
|
|
210
|
+
var title = String(a.title || a.description || '').slice(0, 120);
|
|
211
|
+
var sev = a.severity ? ' (severidade: **' + String(a.severity).toUpperCase() + '**)' : '';
|
|
212
|
+
return num + '. ' + icon + ' **Registrar blocker:** ' + title + sev;
|
|
213
|
+
}
|
|
214
|
+
if (typeNorm === 'suggestreport') {
|
|
215
|
+
return num + '. ' + icon + ' **Sugerir relatorio:** ' + String(a.name || a.reportType || '');
|
|
216
|
+
}
|
|
217
|
+
if (typeNorm === 'oraclequery') {
|
|
218
|
+
return num + '. ' + icon + ' **Consultar oracle:** ' + String(a.query || '').slice(0, 120);
|
|
219
|
+
}
|
|
220
|
+
return num + '. \u2022 **' + String(a.type || 'acao') + ':** ' + JSON.stringify(a).slice(0, 100);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
return lines.join('\n');
|
|
224
|
+
} catch (e) {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
134
229
|
function ensureChatSession() {
|
|
135
230
|
if (state.chatSessionId) return state.chatSessionId;
|
|
136
231
|
try {
|
|
@@ -962,6 +1057,7 @@
|
|
|
962
1057
|
}
|
|
963
1058
|
|
|
964
1059
|
async function refreshProjects() {
|
|
1060
|
+
showSkeleton('projectsGrid');
|
|
965
1061
|
try {
|
|
966
1062
|
const [r, b, t] = await Promise.all([
|
|
967
1063
|
api('/api/projects/list', { dir: dirOrDefault() }),
|
|
@@ -1125,6 +1221,7 @@
|
|
|
1125
1221
|
}
|
|
1126
1222
|
|
|
1127
1223
|
async function refreshTimeline() {
|
|
1224
|
+
showSkeleton('timelineGrid');
|
|
1128
1225
|
try {
|
|
1129
1226
|
const r = await api('/api/timeline', { dir: dirOrDefault() });
|
|
1130
1227
|
state.timeline = r.items || [];
|
|
@@ -1259,6 +1356,7 @@
|
|
|
1259
1356
|
}
|
|
1260
1357
|
|
|
1261
1358
|
async function refreshReportsPage() {
|
|
1359
|
+
showSkeleton('reportsGrid');
|
|
1262
1360
|
try {
|
|
1263
1361
|
setPill('run', 'carregando…');
|
|
1264
1362
|
const r = await api('/api/reports/list', { dir: dirOrDefault() });
|
|
@@ -1473,29 +1571,63 @@
|
|
|
1473
1571
|
+ (t.category ? ' · <span style="font-family:var(--mono);">' + escapeHtml(t.category) + '</span>' : '')
|
|
1474
1572
|
+ '</div>'
|
|
1475
1573
|
+ '</div></div>'
|
|
1476
|
-
+ '<div style="display:flex; gap:6px; flex-shrink:0;">'
|
|
1574
|
+
+ '<div class="task-actions" style="display:flex; gap:6px; flex-shrink:0; align-items:center;">'
|
|
1477
1575
|
+ '<button class="btn small complete-btn" type="button" style="padding:3px 10px; font-size:11px;" title="Marcar como concluída">✓ Concluir</button>'
|
|
1478
1576
|
+ '<button class="btn small edit-btn" type="button" style="padding:3px 8px; font-size:11px;">✎</button>'
|
|
1479
1577
|
+ '</div>';
|
|
1480
1578
|
row.querySelector('.edit-btn').onclick = () => editTask(t);
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1579
|
+
|
|
1580
|
+
var attachCompleteHandler = function() {
|
|
1581
|
+
var cBtn = row.querySelector('.complete-btn');
|
|
1582
|
+
if (!cBtn) return;
|
|
1583
|
+
cBtn.onclick = function() {
|
|
1584
|
+
var actionsDiv = row.querySelector('.task-actions');
|
|
1585
|
+
actionsDiv.innerHTML =
|
|
1586
|
+
'<input type="text" class="comment-input" placeholder="Comentario (opcional)" '
|
|
1587
|
+
+ 'style="font-size:11px; padding:3px 8px; background:var(--bg); border:1px solid var(--border); '
|
|
1588
|
+
+ 'color:var(--text); width:180px; outline:none; font-family:var(--mono);" />'
|
|
1589
|
+
+ '<button class="btn small confirm-btn" type="button" style="padding:3px 10px; font-size:11px; '
|
|
1590
|
+
+ 'background:var(--accent); color:#000; font-weight:700;">Confirmar</button>'
|
|
1591
|
+
+ '<button class="btn small cancel-btn" type="button" style="padding:3px 8px; font-size:11px; '
|
|
1592
|
+
+ 'color:var(--muted);">\u2715</button>';
|
|
1593
|
+
|
|
1594
|
+
var input = actionsDiv.querySelector('.comment-input');
|
|
1595
|
+
input.focus();
|
|
1596
|
+
|
|
1597
|
+
var doComplete = async function() {
|
|
1598
|
+
var comment = input.value.trim();
|
|
1599
|
+
actionsDiv.querySelector('.confirm-btn').disabled = true;
|
|
1600
|
+
actionsDiv.querySelector('.confirm-btn').textContent = '\u2026';
|
|
1601
|
+
try {
|
|
1602
|
+
setPill('run', 'concluindo\u2026');
|
|
1603
|
+
var body = { dir: dirOrDefault(), id: t.id };
|
|
1604
|
+
if (comment) body.comment = comment;
|
|
1605
|
+
await api('/api/tasks/complete', body);
|
|
1606
|
+
row.style.opacity = '0.4';
|
|
1607
|
+
row.style.pointerEvents = 'none';
|
|
1608
|
+
await refreshToday();
|
|
1609
|
+
setPill('ok', 'conclu\u00edda');
|
|
1610
|
+
setTimeout(function() { setPill('ok', 'pronto'); }, 800);
|
|
1611
|
+
} catch (e) {
|
|
1612
|
+
actionsDiv.querySelector('.confirm-btn').disabled = false;
|
|
1613
|
+
actionsDiv.querySelector('.confirm-btn').textContent = 'Confirmar';
|
|
1614
|
+
setPill('err', 'falhou');
|
|
1615
|
+
}
|
|
1616
|
+
};
|
|
1617
|
+
|
|
1618
|
+
actionsDiv.querySelector('.confirm-btn').onclick = doComplete;
|
|
1619
|
+
input.onkeydown = function(e) { if (e.key === 'Enter') doComplete(); };
|
|
1620
|
+
actionsDiv.querySelector('.cancel-btn').onclick = function() {
|
|
1621
|
+
actionsDiv.innerHTML =
|
|
1622
|
+
'<button class="btn small complete-btn" type="button" style="padding:3px 10px; font-size:11px;" '
|
|
1623
|
+
+ 'title="Marcar como conclu\u00edda">\u2713 Concluir</button>'
|
|
1624
|
+
+ '<button class="btn small edit-btn" type="button" style="padding:3px 8px; font-size:11px;">\u270E</button>';
|
|
1625
|
+
actionsDiv.querySelector('.edit-btn').onclick = function() { editTask(t); };
|
|
1626
|
+
attachCompleteHandler();
|
|
1627
|
+
};
|
|
1628
|
+
};
|
|
1498
1629
|
};
|
|
1630
|
+
attachCompleteHandler();
|
|
1499
1631
|
return row;
|
|
1500
1632
|
};
|
|
1501
1633
|
|
|
@@ -1568,6 +1700,7 @@
|
|
|
1568
1700
|
}
|
|
1569
1701
|
|
|
1570
1702
|
async function refreshToday() {
|
|
1703
|
+
showSkeleton('swimlaneContainer');
|
|
1571
1704
|
try {
|
|
1572
1705
|
const [t, b] = await Promise.all([
|
|
1573
1706
|
api('/api/tasks/list', { dir: dirOrDefault(), category: 'DO_NOW', status: 'PENDING', limit: 50 }),
|
|
@@ -1586,22 +1719,58 @@
|
|
|
1586
1719
|
if (!el) return;
|
|
1587
1720
|
|
|
1588
1721
|
if (!payload || !payload.summary) {
|
|
1589
|
-
// Hide the strip if no content
|
|
1590
1722
|
if (wrap) wrap.style.display = 'none';
|
|
1591
1723
|
return;
|
|
1592
1724
|
}
|
|
1593
1725
|
|
|
1594
1726
|
const lines = [];
|
|
1595
|
-
|
|
1727
|
+
const summary = String(payload.summary || '');
|
|
1728
|
+
|
|
1729
|
+
// Parse severity counts from summary (e.g. "CRITICAL: 0, HIGH: 2, ...")
|
|
1730
|
+
const sevRegex = /(\w+):\s*(\d+)/g;
|
|
1731
|
+
let match;
|
|
1732
|
+
const counts = [];
|
|
1733
|
+
let totalActive = 0;
|
|
1734
|
+
while ((match = sevRegex.exec(summary)) !== null) {
|
|
1735
|
+
const sev = match[1].toUpperCase();
|
|
1736
|
+
const count = parseInt(match[2], 10);
|
|
1737
|
+
if (sev !== 'TOTAL') {
|
|
1738
|
+
counts.push({ sev, count });
|
|
1739
|
+
totalActive += count;
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
if (counts.length > 0) {
|
|
1744
|
+
// Render as styled pills
|
|
1745
|
+
if (totalActive === 0) {
|
|
1746
|
+
lines.push('<div class="insight-clear"><span style="font-size:16px;">✓</span> Nenhum bloqueio ativo</div>');
|
|
1747
|
+
} else {
|
|
1748
|
+
let pills = '<div class="insight-pills">';
|
|
1749
|
+
for (const c of counts) {
|
|
1750
|
+
if (c.count > 0) {
|
|
1751
|
+
pills += '<span class="insight-pill ' + c.sev.toLowerCase() + '">' + escapeHtml(c.sev) + ': ' + c.count + '</span>';
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
pills += '</div>';
|
|
1755
|
+
lines.push(pills);
|
|
1756
|
+
}
|
|
1757
|
+
} else {
|
|
1758
|
+
// Fallback: summary doesn't match pattern, render as text
|
|
1759
|
+
lines.push('<div style="color:var(--text); font-size:12px;">' + escapeHtml(summary) + '</div>');
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1596
1762
|
if (payload.suggestions && payload.suggestions.length) {
|
|
1597
|
-
lines.push('<div
|
|
1598
|
-
lines.push('<ul
|
|
1763
|
+
lines.push('<div class="insight-section-title">Proximos passos</div>');
|
|
1764
|
+
lines.push('<ul class="insight-steps">' + payload.suggestions.map(function (s) {
|
|
1765
|
+
return '<li>' + escapeHtml(s) + '</li>';
|
|
1766
|
+
}).join('') + '</ul>');
|
|
1599
1767
|
}
|
|
1600
1768
|
if (payload.top && payload.top.length) {
|
|
1601
|
-
lines.push('<div
|
|
1602
|
-
lines.push('<ul
|
|
1603
|
-
const
|
|
1604
|
-
|
|
1769
|
+
lines.push('<div class="insight-section-title">Top blockers</div>');
|
|
1770
|
+
lines.push('<ul class="insight-steps">' + payload.top.map(function (b) {
|
|
1771
|
+
const sev = String(b.severity || '').toUpperCase();
|
|
1772
|
+
const cls = sev === 'CRITICAL' ? 'critical' : sev === 'HIGH' ? 'high' : sev === 'MEDIUM' ? 'medium' : 'low';
|
|
1773
|
+
return '<li><span class="insight-pill ' + cls + '" style="font-size:10px; padding:1px 6px; margin-right:4px;">' + escapeHtml(sev) + '</span>' + escapeHtml(String(b.title || '')) + '</li>';
|
|
1605
1774
|
}).join('') + '</ul>');
|
|
1606
1775
|
}
|
|
1607
1776
|
el.innerHTML = lines.join('');
|
|
@@ -2176,8 +2345,12 @@
|
|
|
2176
2345
|
const ok = confirm(msg);
|
|
2177
2346
|
if (!ok) return;
|
|
2178
2347
|
|
|
2179
|
-
const
|
|
2180
|
-
|
|
2348
|
+
const discordInput = $('discord');
|
|
2349
|
+
const teamsInput = $('teams');
|
|
2350
|
+
const webhookUrl = target === 'discord'
|
|
2351
|
+
? (discordInput ? discordInput.value.trim() : (state.discordUrl || ''))
|
|
2352
|
+
: (teamsInput ? teamsInput.value.trim() : (state.teamsUrl || ''));
|
|
2353
|
+
if (!webhookUrl) throw new Error('Configure o webhook em Settings antes de publicar.');
|
|
2181
2354
|
setPill('run', 'publish…');
|
|
2182
2355
|
const mode = state.prettyPublish ? 'pretty' : 'chunks';
|
|
2183
2356
|
await api('/api/publish', { webhookUrl, text: state.lastText, mode, allowSecrets: true });
|
|
@@ -2253,7 +2426,8 @@
|
|
|
2253
2426
|
|
|
2254
2427
|
// Show plan output in Preview panel
|
|
2255
2428
|
const header = r.ok === false ? '## Agent Plan (planner unavailable)\n\n' : '## Agent Plan (draft)\n\n';
|
|
2256
|
-
const
|
|
2429
|
+
const formatted = r.ok !== false ? formatPlanForDisplay(r.plan) : null;
|
|
2430
|
+
const planOut = header + (formatted || r.plan || '');
|
|
2257
2431
|
setOut(planOut);
|
|
2258
2432
|
chatAppend('assistant', planOut, { markdown: true });
|
|
2259
2433
|
ta.value = '';
|
|
@@ -2389,10 +2563,13 @@
|
|
|
2389
2563
|
if (side) side.textContent = defaults.workspaceDir;
|
|
2390
2564
|
}
|
|
2391
2565
|
if (defaults && defaults.settings) {
|
|
2566
|
+
state.discordUrl = defaults.settings.discordWebhookUrl || '';
|
|
2567
|
+
state.teamsUrl = defaults.settings.teamsWebhookUrl || '';
|
|
2568
|
+
// Populate inputs if they exist (e.g. Settings page)
|
|
2392
2569
|
const discord = $('discord');
|
|
2393
2570
|
const teams = $('teams');
|
|
2394
|
-
if (discord) discord.value =
|
|
2395
|
-
if (teams) teams.value =
|
|
2571
|
+
if (discord) discord.value = state.discordUrl;
|
|
2572
|
+
if (teams) teams.value = state.teamsUrl;
|
|
2396
2573
|
}
|
|
2397
2574
|
} catch (e) {
|
|
2398
2575
|
// ignore
|
|
@@ -2449,6 +2626,27 @@
|
|
|
2449
2626
|
|
|
2450
2627
|
setPill('ok', 'pronto');
|
|
2451
2628
|
|
|
2629
|
+
/* ── Global Keyboard Shortcuts ── */
|
|
2630
|
+
document.addEventListener('keydown', (e) => {
|
|
2631
|
+
// Ctrl/Cmd+Enter: Save & Process from anywhere
|
|
2632
|
+
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
|
|
2633
|
+
e.preventDefault();
|
|
2634
|
+
if (typeof saveAndPlan === 'function') saveAndPlan();
|
|
2635
|
+
return;
|
|
2636
|
+
}
|
|
2637
|
+
// Ctrl/Cmd+K: Focus search/input on current page
|
|
2638
|
+
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
|
|
2639
|
+
e.preventDefault();
|
|
2640
|
+
const target = $('inboxText') || $('reportsFilter') || $('projectsFilter') || $('timelineFilter');
|
|
2641
|
+
if (target) target.focus();
|
|
2642
|
+
return;
|
|
2643
|
+
}
|
|
2644
|
+
// Escape: Blur active element
|
|
2645
|
+
if (e.key === 'Escape' && document.activeElement) {
|
|
2646
|
+
document.activeElement.blur();
|
|
2647
|
+
}
|
|
2648
|
+
});
|
|
2649
|
+
|
|
2452
2650
|
// Expose handlers for inline onclick
|
|
2453
2651
|
window.doInit = doInit;
|
|
2454
2652
|
window.doUpdate = doUpdate;
|
package/cli/web.js
CHANGED
|
@@ -1021,7 +1021,7 @@ function buildDocsHtml(safeDefault, appVersion) {
|
|
|
1021
1021
|
<div class="frame">
|
|
1022
1022
|
<div class="shell">
|
|
1023
1023
|
|
|
1024
|
-
<aside class="rail">
|
|
1024
|
+
<aside class="rail" role="navigation" aria-label="Menu principal">
|
|
1025
1025
|
<div class="railTop">
|
|
1026
1026
|
<div class="railLogo">F</div>
|
|
1027
1027
|
</div>
|
|
@@ -1053,7 +1053,7 @@ function buildDocsHtml(safeDefault, appVersion) {
|
|
|
1053
1053
|
</div>
|
|
1054
1054
|
</aside>
|
|
1055
1055
|
|
|
1056
|
-
<main class="center">
|
|
1056
|
+
<main class="center" role="main">
|
|
1057
1057
|
<div class="topbar">
|
|
1058
1058
|
<div class="brandLine">
|
|
1059
1059
|
<span class="spark"></span>
|
|
@@ -1160,7 +1160,7 @@ function buildHtml(safeDefault, appVersion) {
|
|
|
1160
1160
|
<div class="frame">
|
|
1161
1161
|
<div class="shell">
|
|
1162
1162
|
|
|
1163
|
-
<aside class="rail">
|
|
1163
|
+
<aside class="rail" role="navigation" aria-label="Menu principal">
|
|
1164
1164
|
<div class="railTop">
|
|
1165
1165
|
<div class="railLogo">F</div>
|
|
1166
1166
|
</div>
|
|
@@ -1190,7 +1190,7 @@ function buildHtml(safeDefault, appVersion) {
|
|
|
1190
1190
|
</div>
|
|
1191
1191
|
</aside>
|
|
1192
1192
|
|
|
1193
|
-
<main class="center">
|
|
1193
|
+
<main class="center" role="main">
|
|
1194
1194
|
<div class="topbar">
|
|
1195
1195
|
<div class="brandLine">
|
|
1196
1196
|
<span class="spark"></span>
|
|
@@ -1223,7 +1223,7 @@ function buildHtml(safeDefault, appVersion) {
|
|
|
1223
1223
|
</div>
|
|
1224
1224
|
|
|
1225
1225
|
<!-- Textarea -->
|
|
1226
|
-
<textarea id="inboxText" placeholder="Cole updates, decisões, blockers... ou faça uma pergunta à Freya. ▸ Salvar & Processar → extrai tarefas e blockers do texto ▸ Perguntar → consulta o histórico via busca semântica (RAG)" style="resize:none; min-height: 160px; border-radius: 0; border-left: none; border-right: none; border-top: none; border-bottom: 1px solid var(--border); padding: 14px 16px; font-size: 13px; line-height: 1.6;"
|
|
1226
|
+
<textarea id="inboxText" aria-label="Entrada de texto para processar ou perguntar" placeholder="Cole updates, decisões, blockers... ou faça uma pergunta à Freya. ▸ Salvar & Processar → extrai tarefas e blockers do texto ▸ Perguntar → consulta o histórico via busca semântica (RAG)" style="resize:none; min-height: 160px; border-radius: 0; border-left: none; border-right: none; border-top: none; border-bottom: 1px solid var(--border); padding: 14px 16px; font-size: 13px; line-height: 1.6;"
|
|
1227
1227
|
onkeydown="if((event.metaKey||event.ctrlKey)&&event.key==='Enter'){event.preventDefault();window.saveAndPlan();}"></textarea>
|
|
1228
1228
|
|
|
1229
1229
|
<!-- Actions bar -->
|
|
@@ -1311,24 +1311,6 @@ function buildHtml(safeDefault, appVersion) {
|
|
|
1311
1311
|
</div>
|
|
1312
1312
|
</section>
|
|
1313
1313
|
|
|
1314
|
-
<div class="panel">
|
|
1315
|
-
<div class="panelHead"><b>Configurações de publicação</b></div>
|
|
1316
|
-
<div class="panelBody">
|
|
1317
|
-
<label>Discord webhook URL</label>
|
|
1318
|
-
<input id="discord" placeholder="https://discord.com/api/webhooks/..." />
|
|
1319
|
-
<div style="height:10px"></div>
|
|
1320
|
-
|
|
1321
|
-
<label>Teams webhook URL</label>
|
|
1322
|
-
<input id="teams" placeholder="https://..." />
|
|
1323
|
-
<div class="help">Os webhooks ficam salvos na workspace em <code>data/settings/settings.json</code>.</div>
|
|
1324
|
-
|
|
1325
|
-
<div style="height:10px"></div>
|
|
1326
|
-
<label style="display:flex; align-items:center; gap:10px; user-select:none; margin: 6px 0 12px 0">
|
|
1327
|
-
<input id="prettyPublish" type="checkbox" checked style="width:auto" onchange="togglePrettyPublish()" />
|
|
1328
|
-
Publicação bonita (cards/embeds)
|
|
1329
|
-
</label>
|
|
1330
|
-
</div>
|
|
1331
|
-
</div>
|
|
1332
1314
|
</div>
|
|
1333
1315
|
</main>
|
|
1334
1316
|
</div>
|
|
@@ -1358,7 +1340,7 @@ function buildReportsHtml(safeDefault, appVersion) {
|
|
|
1358
1340
|
<div class="frame">
|
|
1359
1341
|
<div class="shell">
|
|
1360
1342
|
|
|
1361
|
-
<aside class="rail">
|
|
1343
|
+
<aside class="rail" role="navigation" aria-label="Menu principal">
|
|
1362
1344
|
<div class="railTop">
|
|
1363
1345
|
<div class="railLogo">F</div>
|
|
1364
1346
|
</div>
|
|
@@ -1388,7 +1370,7 @@ function buildReportsHtml(safeDefault, appVersion) {
|
|
|
1388
1370
|
</div>
|
|
1389
1371
|
</aside>
|
|
1390
1372
|
|
|
1391
|
-
<main class="center reportsPage" id="reportsPage">
|
|
1373
|
+
<main class="center reportsPage" role="main" id="reportsPage">
|
|
1392
1374
|
<div class="topbar">
|
|
1393
1375
|
<div class="brandLine">
|
|
1394
1376
|
<span class="spark"></span>
|
|
@@ -1471,7 +1453,7 @@ function buildProjectsHtml(safeDefault, appVersion) {
|
|
|
1471
1453
|
<div class="frame">
|
|
1472
1454
|
<div class="shell">
|
|
1473
1455
|
|
|
1474
|
-
<aside class="rail">
|
|
1456
|
+
<aside class="rail" role="navigation" aria-label="Menu principal">
|
|
1475
1457
|
<div class="railTop">
|
|
1476
1458
|
<div class="railLogo">F</div>
|
|
1477
1459
|
</div>
|
|
@@ -1500,7 +1482,7 @@ function buildProjectsHtml(safeDefault, appVersion) {
|
|
|
1500
1482
|
</div>
|
|
1501
1483
|
</aside>
|
|
1502
1484
|
|
|
1503
|
-
<main class="center reportsPage" id="projectsPage">
|
|
1485
|
+
<main class="center reportsPage" role="main" id="projectsPage">
|
|
1504
1486
|
<div class="topbar">
|
|
1505
1487
|
<div class="brandLine">
|
|
1506
1488
|
<span class="spark"></span>
|
|
@@ -1673,7 +1655,7 @@ function buildGraphHtml(safeDefault, appVersion) {
|
|
|
1673
1655
|
<div class="frame">
|
|
1674
1656
|
<div class="shell">
|
|
1675
1657
|
|
|
1676
|
-
<aside class="rail">
|
|
1658
|
+
<aside class="rail" role="navigation" aria-label="Menu principal">
|
|
1677
1659
|
<div class="railTop">
|
|
1678
1660
|
<div class="railLogo">F</div>
|
|
1679
1661
|
</div>
|
|
@@ -1702,7 +1684,7 @@ function buildGraphHtml(safeDefault, appVersion) {
|
|
|
1702
1684
|
</div>
|
|
1703
1685
|
</aside>
|
|
1704
1686
|
|
|
1705
|
-
<main class="center reportsPage" id="graphPage">
|
|
1687
|
+
<main class="center reportsPage" role="main" id="graphPage">
|
|
1706
1688
|
<div class="topbar">
|
|
1707
1689
|
<div class="brandLine">
|
|
1708
1690
|
<span class="spark"></span>
|
|
@@ -1764,7 +1746,7 @@ function buildCompanionHtml(safeDefault, appVersion) {
|
|
|
1764
1746
|
<div class="frame">
|
|
1765
1747
|
<div class="shell">
|
|
1766
1748
|
|
|
1767
|
-
<aside class="rail">
|
|
1749
|
+
<aside class="rail" role="navigation" aria-label="Menu principal">
|
|
1768
1750
|
<div class="railTop">
|
|
1769
1751
|
<div class="railLogo">F</div>
|
|
1770
1752
|
</div>
|
|
@@ -1793,7 +1775,7 @@ function buildCompanionHtml(safeDefault, appVersion) {
|
|
|
1793
1775
|
</div>
|
|
1794
1776
|
</aside>
|
|
1795
1777
|
|
|
1796
|
-
<main class="center reportsPage" id="healthPage">
|
|
1778
|
+
<main class="center reportsPage" role="main" id="healthPage">
|
|
1797
1779
|
<div class="topbar">
|
|
1798
1780
|
<div class="brandLine">
|
|
1799
1781
|
<span class="spark"></span>
|
|
@@ -3638,7 +3620,8 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
3638
3620
|
completedAt: t.completed_at,
|
|
3639
3621
|
projectSlug: t.project_slug,
|
|
3640
3622
|
priority: meta.priority,
|
|
3641
|
-
streamSlug: meta.streamSlug
|
|
3623
|
+
streamSlug: meta.streamSlug,
|
|
3624
|
+
comments: Array.isArray(meta.comments) ? meta.comments : []
|
|
3642
3625
|
};
|
|
3643
3626
|
});
|
|
3644
3627
|
|
|
@@ -3649,10 +3632,24 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
3649
3632
|
const id = String(payload.id || '').trim();
|
|
3650
3633
|
if (!id) return safeJson(res, 400, { error: 'Missing id' });
|
|
3651
3634
|
|
|
3635
|
+
const comment = typeof payload.comment === 'string' ? payload.comment.trim() : '';
|
|
3652
3636
|
const now = new Date().toISOString();
|
|
3653
|
-
const info = dl.db.prepare(`UPDATE tasks SET status = 'COMPLETED', completed_at = ? WHERE id = ?`).run(now, id);
|
|
3654
3637
|
|
|
3655
|
-
if (
|
|
3638
|
+
if (comment) {
|
|
3639
|
+
const row = dl.db.prepare('SELECT metadata FROM tasks WHERE id = ?').get(id);
|
|
3640
|
+
if (!row) return safeJson(res, 404, { error: 'Task not found' });
|
|
3641
|
+
let meta = {};
|
|
3642
|
+
try { meta = row.metadata ? JSON.parse(row.metadata) : {}; } catch (_) { meta = {}; }
|
|
3643
|
+
if (!Array.isArray(meta.comments)) meta.comments = [];
|
|
3644
|
+
meta.comments.push({ text: comment, createdAt: now });
|
|
3645
|
+
const info = dl.db.prepare(`UPDATE tasks SET status = 'COMPLETED', completed_at = ?, metadata = ? WHERE id = ?`)
|
|
3646
|
+
.run(now, JSON.stringify(meta), id);
|
|
3647
|
+
if (info.changes === 0) return safeJson(res, 404, { error: 'Task not found' });
|
|
3648
|
+
} else {
|
|
3649
|
+
const info = dl.db.prepare(`UPDATE tasks SET status = 'COMPLETED', completed_at = ? WHERE id = ?`).run(now, id);
|
|
3650
|
+
if (info.changes === 0) return safeJson(res, 404, { error: 'Task not found' });
|
|
3651
|
+
}
|
|
3652
|
+
|
|
3656
3653
|
return safeJson(res, 200, { ok: true, task: { id, status: 'COMPLETED', completedAt: now } });
|
|
3657
3654
|
}
|
|
3658
3655
|
|
|
@@ -3936,7 +3933,7 @@ function buildSettingsHtml(safeDefault, appVersion) {
|
|
|
3936
3933
|
<div class="frame">
|
|
3937
3934
|
<div class="shell">
|
|
3938
3935
|
|
|
3939
|
-
<aside class="rail">
|
|
3936
|
+
<aside class="rail" role="navigation" aria-label="Menu principal">
|
|
3940
3937
|
<div class="railTop">
|
|
3941
3938
|
<div class="railLogo">F</div>
|
|
3942
3939
|
</div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cccarv82/freya",
|
|
3
|
-
"version": "2.13.
|
|
3
|
+
"version": "2.13.2",
|
|
4
4
|
"description": "Personal AI Assistant with local-first persistence",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"health": "node scripts/validate-data.js && node scripts/validate-structure.js",
|
|
@@ -35,4 +35,4 @@
|
|
|
35
35
|
"pdf-lib": "^1.17.1",
|
|
36
36
|
"sql.js": "^1.12.0"
|
|
37
37
|
}
|
|
38
|
-
}
|
|
38
|
+
}
|