@cccarv82/freya 2.9.0 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/web-ui.js +182 -81
- package/cli/web.js +91 -78
- package/package.json +1 -1
package/cli/web-ui.js
CHANGED
|
@@ -330,6 +330,47 @@
|
|
|
330
330
|
}
|
|
331
331
|
}
|
|
332
332
|
|
|
333
|
+
// Unified input → Oracle (RAG): reads from the main inboxText textarea
|
|
334
|
+
async function askFreyaFromInput() {
|
|
335
|
+
const ta = $('inboxText');
|
|
336
|
+
const query = ta ? (ta.value || '').trim() : '';
|
|
337
|
+
if (!query) {
|
|
338
|
+
setPill('err', 'vazio');
|
|
339
|
+
setTimeout(() => setPill('ok', 'pronto'), 800);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (ta) ta.value = '';
|
|
344
|
+
|
|
345
|
+
// Update mode tag
|
|
346
|
+
const tag = $('chatModeTag');
|
|
347
|
+
if (tag) { tag.textContent = 'oracle'; tag.style.color = 'var(--accent)'; tag.style.borderColor = 'var(--accent)'; }
|
|
348
|
+
|
|
349
|
+
chatAppend('user', query);
|
|
350
|
+
setPill('run', 'pesquisando…');
|
|
351
|
+
|
|
352
|
+
const typingId = 'typing-' + Date.now();
|
|
353
|
+
chatAppend('assistant', '<div class="typing-indicator"><span></span><span></span><span></span></div>', { id: typingId, html: true });
|
|
354
|
+
|
|
355
|
+
try {
|
|
356
|
+
const sessionId = ensureChatSession();
|
|
357
|
+
const r = await api('/api/chat/ask', { dir: dirOrDefault(), sessionId, query });
|
|
358
|
+
const answer = r && r.answer ? r.answer : 'Não encontrei registro';
|
|
359
|
+
|
|
360
|
+
const el = $(typingId);
|
|
361
|
+
if (el) el.remove();
|
|
362
|
+
|
|
363
|
+
chatAppend('assistant', answer, { markdown: true });
|
|
364
|
+
setPill('ok', 'pronto');
|
|
365
|
+
if (tag) setTimeout(() => { tag.textContent = 'oracle'; tag.style.color = ''; tag.style.borderColor = ''; }, 2000);
|
|
366
|
+
} catch (e) {
|
|
367
|
+
const el = $(typingId);
|
|
368
|
+
if (el) el.remove();
|
|
369
|
+
setPill('err', 'falhou');
|
|
370
|
+
chatAppend('assistant', '❌ ' + String(e && e.message ? e.message : e));
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
333
374
|
function setOut(text) {
|
|
334
375
|
state.lastText = text || '';
|
|
335
376
|
const el = $('reportPreview');
|
|
@@ -1317,11 +1358,65 @@
|
|
|
1317
1358
|
}
|
|
1318
1359
|
}
|
|
1319
1360
|
|
|
1361
|
+
// Priority → color + icon
|
|
1362
|
+
function priColor(pri) {
|
|
1363
|
+
const p = String(pri || '').toUpperCase();
|
|
1364
|
+
if (p === 'CRITICAL') return { dot: '#ef4444', label: 'CRÍTICA' };
|
|
1365
|
+
if (p === 'HIGH') return { dot: '#f97316', label: 'ALTA' };
|
|
1366
|
+
if (p === 'MEDIUM') return { dot: '#eab308', label: 'MÉDIA' };
|
|
1367
|
+
if (p === 'LOW') return { dot: '#22c55e', label: 'BAIXA' };
|
|
1368
|
+
return { dot: 'var(--muted)', label: p || '—' };
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
// Severity → color
|
|
1372
|
+
function sevColor(sev) {
|
|
1373
|
+
const s = String(sev || '').toUpperCase();
|
|
1374
|
+
if (s === 'CRITICAL') return '#ef4444';
|
|
1375
|
+
if (s === 'HIGH') return '#f97316';
|
|
1376
|
+
if (s === 'MEDIUM') return '#eab308';
|
|
1377
|
+
return '#94a3b8';
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1320
1380
|
function renderSwimlanes(tasks, blockers) {
|
|
1321
1381
|
const el = $('swimlaneContainer');
|
|
1322
1382
|
if (!el) return;
|
|
1323
1383
|
el.innerHTML = '';
|
|
1324
1384
|
|
|
1385
|
+
// Update top-level summary chips
|
|
1386
|
+
const chips = $('focusSummaryChips');
|
|
1387
|
+
if (chips) {
|
|
1388
|
+
chips.innerHTML = '';
|
|
1389
|
+
if (blockers.length > 0) {
|
|
1390
|
+
const bc = document.createElement('span');
|
|
1391
|
+
bc.style.cssText = 'font-size:11px; font-weight:700; padding:2px 8px; border-radius:10px; background:rgba(239,68,68,0.12); color:#f87171; border:1px solid rgba(239,68,68,0.3);';
|
|
1392
|
+
bc.textContent = blockers.length + ' blocker' + (blockers.length > 1 ? 's' : '');
|
|
1393
|
+
chips.appendChild(bc);
|
|
1394
|
+
}
|
|
1395
|
+
if (tasks.length > 0) {
|
|
1396
|
+
const tc = document.createElement('span');
|
|
1397
|
+
tc.style.cssText = 'font-size:11px; font-weight:700; padding:2px 8px; border-radius:10px; background:rgba(34,197,94,0.10); color:#4ade80; border:1px solid rgba(34,197,94,0.25);';
|
|
1398
|
+
tc.textContent = tasks.length + ' task' + (tasks.length > 1 ? 's' : '');
|
|
1399
|
+
chips.appendChild(tc);
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
// Update progress bar (tasks completed today vs total — we show pending count)
|
|
1404
|
+
const progWrap = $('focusProgressWrap');
|
|
1405
|
+
const progBar = $('focusProgressBar');
|
|
1406
|
+
const progLabel = $('focusProgressLabel');
|
|
1407
|
+
if (progWrap) {
|
|
1408
|
+
if (tasks.length > 0 || blockers.length > 0) {
|
|
1409
|
+
progWrap.style.display = 'flex';
|
|
1410
|
+
// We only have pending tasks here; show blockers impact
|
|
1411
|
+
const blocker_pct = blockers.length === 0 ? 100 : Math.max(10, Math.round((1 - blockers.length / Math.max(tasks.length + blockers.length, 1)) * 100));
|
|
1412
|
+
if (progBar) progBar.style.width = blocker_pct + '%';
|
|
1413
|
+
if (progBar) progBar.style.background = blockers.length > 0 ? '#f97316' : 'var(--accent)';
|
|
1414
|
+
if (progLabel) progLabel.textContent = blockers.length > 0 ? blockers.length + ' blocking' : 'tudo livre';
|
|
1415
|
+
} else {
|
|
1416
|
+
progWrap.style.display = 'none';
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1325
1420
|
const groups = {};
|
|
1326
1421
|
const addGroup = (slug) => {
|
|
1327
1422
|
const key = slug || 'Global / Sem Projeto';
|
|
@@ -1337,41 +1432,55 @@
|
|
|
1337
1432
|
if (b === 'Global / Sem Projeto') return -1;
|
|
1338
1433
|
return a.localeCompare(b);
|
|
1339
1434
|
});
|
|
1340
|
-
|
|
1341
1435
|
sortedKeys.sort((a, b) => groups[b].blockers.length - groups[a].blockers.length);
|
|
1342
1436
|
|
|
1343
1437
|
if (sortedKeys.length === 0) {
|
|
1344
1438
|
const empty = document.createElement('div');
|
|
1345
|
-
empty.
|
|
1346
|
-
empty.
|
|
1439
|
+
empty.style.cssText = 'display:flex; flex-direction:column; align-items:center; justify-content:center; padding:48px 24px; gap:8px; flex:1;';
|
|
1440
|
+
empty.innerHTML = '<div style="font-size:28px; line-height:1;">✅</div>'
|
|
1441
|
+
+ '<div style="font-size:14px; font-weight:600; color:var(--text);">Dia limpo!</div>'
|
|
1442
|
+
+ '<div style="font-size:12px; color:var(--muted); text-align:center;">Nenhuma tarefa pendente (DO_NOW) nem bloqueios em aberto. Use o campo acima para registrar o que estiver fazendo.</div>';
|
|
1347
1443
|
el.appendChild(empty);
|
|
1348
1444
|
return;
|
|
1349
1445
|
}
|
|
1350
1446
|
|
|
1351
1447
|
const createTaskRow = (t) => {
|
|
1352
1448
|
const row = document.createElement('div');
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
row.
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
+ '<div style="
|
|
1360
|
-
+ '<
|
|
1361
|
-
+ '<
|
|
1449
|
+
const pc = priColor(t.priority);
|
|
1450
|
+
row.style.cssText = 'display:flex; justify-content:space-between; gap:10px; align-items:center; padding:10px 16px; background:var(--bg); border-bottom:1px solid var(--border); transition:background 0.15s;';
|
|
1451
|
+
row.onmouseover = () => row.style.background = 'var(--paper)';
|
|
1452
|
+
row.onmouseout = () => row.style.background = 'var(--bg)';
|
|
1453
|
+
row.innerHTML =
|
|
1454
|
+
'<div style="display:flex; align-items:center; gap:10px; min-width:0; flex:1;">'
|
|
1455
|
+
+ '<div style="width:8px; height:8px; border-radius:50%; background:' + pc.dot + '; flex-shrink:0;" title="Prioridade: ' + escapeHtml(pc.label) + '"></div>'
|
|
1456
|
+
+ '<div style="min-width:0;">'
|
|
1457
|
+
+ '<div style="font-weight:600; font-size:13px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">' + escapeHtml(t.description || '') + '</div>'
|
|
1458
|
+
+ '<div style="font-size:11px; color:var(--muted); margin-top:2px;">'
|
|
1459
|
+
+ escapeHtml(pc.label)
|
|
1460
|
+
+ (t.category ? ' · <span style="font-family:var(--mono);">' + escapeHtml(t.category) + '</span>' : '')
|
|
1362
1461
|
+ '</div>'
|
|
1462
|
+
+ '</div></div>'
|
|
1463
|
+
+ '<div style="display:flex; gap:6px; flex-shrink:0;">'
|
|
1464
|
+
+ '<button class="btn small complete-btn" type="button" style="padding:3px 10px; font-size:11px;" title="Marcar como concluída">✓ Concluir</button>'
|
|
1465
|
+
+ '<button class="btn small edit-btn" type="button" style="padding:3px 8px; font-size:11px;">✎</button>'
|
|
1363
1466
|
+ '</div>';
|
|
1364
1467
|
row.querySelector('.edit-btn').onclick = () => editTask(t);
|
|
1365
1468
|
row.querySelector('.complete-btn').onclick = async () => {
|
|
1469
|
+
const btn = row.querySelector('.complete-btn');
|
|
1470
|
+
btn.disabled = true;
|
|
1471
|
+
btn.textContent = '…';
|
|
1366
1472
|
try {
|
|
1367
|
-
setPill('run', '
|
|
1473
|
+
setPill('run', 'concluindo…');
|
|
1368
1474
|
await api('/api/tasks/complete', { dir: dirOrDefault(), id: t.id });
|
|
1475
|
+
row.style.opacity = '0.4';
|
|
1476
|
+
row.style.pointerEvents = 'none';
|
|
1369
1477
|
await refreshToday();
|
|
1370
|
-
setPill('ok', '
|
|
1478
|
+
setPill('ok', 'concluída');
|
|
1371
1479
|
setTimeout(() => setPill('ok', 'pronto'), 800);
|
|
1372
1480
|
} catch (e) {
|
|
1373
|
-
|
|
1374
|
-
|
|
1481
|
+
btn.disabled = false;
|
|
1482
|
+
btn.textContent = '✓ Concluir';
|
|
1483
|
+
setPill('err', 'falhou');
|
|
1375
1484
|
}
|
|
1376
1485
|
};
|
|
1377
1486
|
return row;
|
|
@@ -1379,81 +1488,57 @@
|
|
|
1379
1488
|
|
|
1380
1489
|
const createBlockerRow = (b) => {
|
|
1381
1490
|
const row = document.createElement('div');
|
|
1382
|
-
row.className = 'rep';
|
|
1383
|
-
row.style.borderLeft = '4px solid var(--warn)';
|
|
1384
|
-
row.style.background = 'var(--warn-bg)';
|
|
1385
1491
|
const sev = String(b.severity || '').toUpperCase();
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1492
|
+
const color = sevColor(sev);
|
|
1493
|
+
const when = fmtWhen(new Date(b.createdAt || Date.now()).getTime());
|
|
1494
|
+
row.style.cssText = 'display:flex; justify-content:space-between; gap:10px; align-items:center; padding:10px 16px; border-bottom:1px solid rgba(239,68,68,0.15); border-left:3px solid ' + color + '; background:rgba(239,68,68,0.04); transition:background 0.15s;';
|
|
1495
|
+
row.onmouseover = () => row.style.background = 'rgba(239,68,68,0.08)';
|
|
1496
|
+
row.onmouseout = () => row.style.background = 'rgba(239,68,68,0.04)';
|
|
1497
|
+
row.innerHTML =
|
|
1498
|
+
'<div style="display:flex; align-items:flex-start; gap:10px; min-width:0; flex:1;">'
|
|
1499
|
+
+ '<div style="flex-shrink:0; margin-top:1px;">'
|
|
1500
|
+
+ '<span style="font-size:10px; font-weight:800; letter-spacing:0.5px; padding:2px 6px; border-radius:4px; background:' + color + '22; color:' + color + '; border:1px solid ' + color + '44;">' + escapeHtml(sev || 'BLOCKER') + '</span>'
|
|
1389
1501
|
+ '</div>'
|
|
1390
|
-
+ '<div style="
|
|
1391
|
-
+ '<div style="
|
|
1392
|
-
+ '<
|
|
1393
|
-
+ '</div>'
|
|
1394
|
-
+ '
|
|
1502
|
+
+ '<div style="min-width:0; flex:1;">'
|
|
1503
|
+
+ '<div style="font-weight:600; font-size:13px; color:var(--text);">' + escapeHtml(b.title || '') + '</div>'
|
|
1504
|
+
+ '<div style="font-size:11px; color:var(--muted); margin-top:3px;">🕐 ' + escapeHtml(when) + '</div>'
|
|
1505
|
+
+ '</div></div>'
|
|
1506
|
+
+ '<button class="btn small edit-btn" type="button" style="padding:3px 8px; font-size:11px; flex-shrink:0; border-color:' + color + '66; color:' + color + ';">✎</button>';
|
|
1395
1507
|
row.querySelector('.edit-btn').onclick = () => editBlocker(b);
|
|
1396
1508
|
return row;
|
|
1397
1509
|
};
|
|
1398
1510
|
|
|
1399
1511
|
for (const key of sortedKeys) {
|
|
1400
1512
|
const g = groups[key];
|
|
1513
|
+
const bCount = g.blockers.length;
|
|
1514
|
+
const tCount = g.tasks.length;
|
|
1515
|
+
const hasBlockers = bCount > 0;
|
|
1516
|
+
|
|
1401
1517
|
const swimlane = document.createElement('div');
|
|
1402
|
-
swimlane.
|
|
1403
|
-
swimlane.style.border = '1px solid var(--border)';
|
|
1404
|
-
swimlane.style.borderRadius = '8px';
|
|
1405
|
-
swimlane.style.boxShadow = 'none';
|
|
1406
|
-
swimlane.style.overflow = 'hidden';
|
|
1518
|
+
swimlane.style.cssText = 'border-bottom:1px solid var(--border); overflow:hidden;';
|
|
1407
1519
|
|
|
1408
1520
|
const head = document.createElement('div');
|
|
1409
|
-
head.style.
|
|
1410
|
-
|
|
1411
|
-
head.style.alignItems = 'center';
|
|
1412
|
-
head.style.cursor = 'pointer';
|
|
1413
|
-
head.style.padding = '10px 16px';
|
|
1414
|
-
head.style.background = 'var(--bg2)';
|
|
1415
|
-
head.style.borderBottom = '1px solid var(--border)';
|
|
1416
|
-
head.style.transition = 'background 0.2s';
|
|
1521
|
+
head.style.cssText = 'display:flex; justify-content:space-between; align-items:center; cursor:pointer; padding:9px 16px; background:var(--bg2); transition:background 0.15s;'
|
|
1522
|
+
+ (hasBlockers ? 'border-left:3px solid #f97316;' : 'border-left:3px solid transparent;');
|
|
1417
1523
|
head.onmouseover = () => head.style.background = 'var(--paper2)';
|
|
1418
1524
|
head.onmouseout = () => head.style.background = 'var(--bg2)';
|
|
1419
1525
|
|
|
1420
|
-
const bCount = g.blockers.length;
|
|
1421
|
-
const tCount = g.tasks.length;
|
|
1422
|
-
|
|
1423
1526
|
head.innerHTML = `
|
|
1424
|
-
<div style="
|
|
1425
|
-
<svg width="
|
|
1426
|
-
<span style="font-family:
|
|
1527
|
+
<div style="display:flex; align-items:center; gap:8px; min-width:0;">
|
|
1528
|
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0; opacity:0.5; transition:transform 0.2s;"><polyline points="6 9 12 15 18 9"></polyline></svg>
|
|
1529
|
+
<span style="font-family:var(--mono); font-size:12px; font-weight:700; color:var(--accent); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">${escapeHtml(key)}</span>
|
|
1427
1530
|
</div>
|
|
1428
|
-
<div style="display:flex; gap:
|
|
1429
|
-
${bCount > 0 ? `<span style="
|
|
1430
|
-
${tCount > 0 ? `<span style="
|
|
1531
|
+
<div style="display:flex; gap:5px; align-items:center; flex-shrink:0;">
|
|
1532
|
+
${bCount > 0 ? `<span style="font-size:11px; font-weight:700; padding:2px 7px; border-radius:10px; background:rgba(239,68,68,0.12); color:#f87171; border:1px solid rgba(239,68,68,0.3);">⛔ ${bCount}</span>` : ''}
|
|
1533
|
+
${tCount > 0 ? `<span style="font-size:11px; font-weight:700; padding:2px 7px; border-radius:10px; background:rgba(34,197,94,0.10); color:#4ade80; border:1px solid rgba(34,197,94,0.25);">✓ ${tCount}</span>` : ''}
|
|
1431
1534
|
</div>
|
|
1432
1535
|
`;
|
|
1433
1536
|
|
|
1434
1537
|
const body = document.createElement('div');
|
|
1435
|
-
body.style.
|
|
1436
|
-
|
|
1437
|
-
body.
|
|
1438
|
-
|
|
1439
|
-
for (const b of g.blockers) {
|
|
1440
|
-
const r = createBlockerRow(b);
|
|
1441
|
-
r.style.margin = '0';
|
|
1442
|
-
r.style.borderRadius = '0';
|
|
1443
|
-
r.style.borderBottom = '1px solid var(--border)';
|
|
1444
|
-
r.style.borderLeft = '4px solid var(--warn)';
|
|
1445
|
-
r.style.boxShadow = 'none';
|
|
1446
|
-
body.appendChild(r);
|
|
1447
|
-
}
|
|
1448
|
-
for (const t of g.tasks) {
|
|
1449
|
-
const r = createTaskRow(t);
|
|
1450
|
-
r.style.margin = '0';
|
|
1451
|
-
r.style.borderRadius = '0';
|
|
1452
|
-
r.style.borderBottom = '1px solid var(--border)';
|
|
1453
|
-
r.style.borderLeft = '4px solid transparent';
|
|
1454
|
-
r.style.boxShadow = 'none';
|
|
1455
|
-
body.appendChild(r);
|
|
1456
|
-
}
|
|
1538
|
+
body.style.cssText = 'display:flex; flex-direction:column; background:var(--bg);';
|
|
1539
|
+
|
|
1540
|
+
for (const b of g.blockers) body.appendChild(createBlockerRow(b));
|
|
1541
|
+
for (const t of g.tasks) body.appendChild(createTaskRow(t));
|
|
1457
1542
|
|
|
1458
1543
|
let isOpen = true;
|
|
1459
1544
|
head.onclick = () => {
|
|
@@ -1484,22 +1569,30 @@
|
|
|
1484
1569
|
|
|
1485
1570
|
function renderBlockersInsights(payload) {
|
|
1486
1571
|
const el = $('blockersInsights');
|
|
1572
|
+
const wrap = $('blockersInsightsWrap');
|
|
1487
1573
|
if (!el) return;
|
|
1574
|
+
|
|
1488
1575
|
if (!payload || !payload.summary) {
|
|
1489
|
-
|
|
1576
|
+
// Hide the strip if no content
|
|
1577
|
+
if (wrap) wrap.style.display = 'none';
|
|
1490
1578
|
return;
|
|
1491
1579
|
}
|
|
1580
|
+
|
|
1492
1581
|
const lines = [];
|
|
1493
|
-
lines.push('<div
|
|
1582
|
+
lines.push('<div style="margin-bottom:6px; color:var(--text);">' + escapeHtml(payload.summary) + '</div>');
|
|
1494
1583
|
if (payload.suggestions && payload.suggestions.length) {
|
|
1495
|
-
lines.push('<div
|
|
1496
|
-
lines.push('<ul style="margin:
|
|
1584
|
+
lines.push('<div style="font-weight:600; font-size:11px; text-transform:uppercase; letter-spacing:0.5px; color:var(--muted); margin:8px 0 4px;">Próximos passos</div>');
|
|
1585
|
+
lines.push('<ul style="margin:0 0 0 14px; padding:0;">' + payload.suggestions.map((s) => '<li style="margin-bottom:3px;">' + escapeHtml(s) + '</li>').join('') + '</ul>');
|
|
1497
1586
|
}
|
|
1498
1587
|
if (payload.top && payload.top.length) {
|
|
1499
|
-
lines.push('<div
|
|
1500
|
-
lines.push('<ul style="margin:
|
|
1588
|
+
lines.push('<div style="font-weight:600; font-size:11px; text-transform:uppercase; letter-spacing:0.5px; color:var(--muted); margin:8px 0 4px;">Top blockers</div>');
|
|
1589
|
+
lines.push('<ul style="margin:0 0 0 14px; padding:0;">' + payload.top.map((b) => {
|
|
1590
|
+
const c = sevColor(String(b.severity || '').toUpperCase());
|
|
1591
|
+
return '<li style="margin-bottom:3px;"><span style="color:' + c + '; font-weight:700;">' + escapeHtml(String(b.severity || '')) + '</span> — ' + escapeHtml(String(b.title || '')) + '</li>';
|
|
1592
|
+
}).join('') + '</ul>');
|
|
1501
1593
|
}
|
|
1502
1594
|
el.innerHTML = lines.join('');
|
|
1595
|
+
if (wrap) wrap.style.display = 'block';
|
|
1503
1596
|
}
|
|
1504
1597
|
|
|
1505
1598
|
async function refreshBlockersInsights() {
|
|
@@ -2125,16 +2218,21 @@
|
|
|
2125
2218
|
if (!ta) return;
|
|
2126
2219
|
const text = (ta.value || '').trim();
|
|
2127
2220
|
if (!text) {
|
|
2128
|
-
setPill('err', '
|
|
2221
|
+
setPill('err', 'vazio');
|
|
2222
|
+
setTimeout(() => setPill('ok', 'pronto'), 800);
|
|
2129
2223
|
return;
|
|
2130
2224
|
}
|
|
2131
2225
|
|
|
2226
|
+
// Update mode tag
|
|
2227
|
+
const tag = $('chatModeTag');
|
|
2228
|
+
if (tag) { tag.textContent = 'inbox'; tag.style.color = 'var(--primary)'; tag.style.borderColor = 'var(--primary)'; }
|
|
2229
|
+
|
|
2132
2230
|
chatAppend('user', text);
|
|
2133
2231
|
|
|
2134
|
-
setPill('run', '
|
|
2232
|
+
setPill('run', 'salvando…');
|
|
2135
2233
|
await api('/api/inbox/add', { dir: dirOrDefault(), text });
|
|
2136
2234
|
|
|
2137
|
-
setPill('run', '
|
|
2235
|
+
setPill('run', 'processando…');
|
|
2138
2236
|
const r = await api('/api/agents/plan', { dir: dirOrDefault(), text });
|
|
2139
2237
|
|
|
2140
2238
|
state.lastPlan = r.plan || '';
|
|
@@ -2164,9 +2262,11 @@
|
|
|
2164
2262
|
setPill('ok', 'planned');
|
|
2165
2263
|
}
|
|
2166
2264
|
|
|
2265
|
+
const tag2 = $('chatModeTag');
|
|
2266
|
+
if (tag2) setTimeout(() => { tag2.textContent = 'oracle'; tag2.style.color = ''; tag2.style.borderColor = ''; }, 1500);
|
|
2167
2267
|
setTimeout(() => setPill('ok', 'pronto'), 1200);
|
|
2168
2268
|
} catch (e) {
|
|
2169
|
-
setPill('err', '
|
|
2269
|
+
setPill('err', 'falhou');
|
|
2170
2270
|
}
|
|
2171
2271
|
}
|
|
2172
2272
|
|
|
@@ -2385,4 +2485,5 @@
|
|
|
2385
2485
|
window.exportChatObsidian = exportChatObsidian;
|
|
2386
2486
|
window.askFreya = askFreya;
|
|
2387
2487
|
window.askFreyaInline = askFreyaInline;
|
|
2488
|
+
window.askFreyaFromInput = askFreyaFromInput;
|
|
2388
2489
|
})();
|
package/cli/web.js
CHANGED
|
@@ -1207,122 +1207,135 @@ function buildHtml(safeDefault, appVersion) {
|
|
|
1207
1207
|
</div>
|
|
1208
1208
|
|
|
1209
1209
|
<div class="centerBody">
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
<
|
|
1210
|
+
<!-- Unified Input + Chat Panel -->
|
|
1211
|
+
<section style="margin-bottom: 24px; display: grid; grid-template-columns: 1fr 1.6fr; gap: 16px; height: 420px; max-height: 480px;">
|
|
1212
|
+
|
|
1213
|
+
<!-- Left: Unified Input -->
|
|
1214
|
+
<div class="promptBar" style="width: 100%; border-radius: 20px; height: 100%; display: flex; flex-direction: column; overflow: hidden;">
|
|
1215
|
+
<div class="promptMeta" style="flex-shrink:0;">
|
|
1216
|
+
<div class="promptTitle" style="display: flex; align-items: center; gap: 8px;">
|
|
1217
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color: var(--primary)"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg>
|
|
1218
|
+
<span>Freya Input</span>
|
|
1219
1219
|
</div>
|
|
1220
|
-
<
|
|
1220
|
+
<div id="status" class="small">pronto</div>
|
|
1221
1221
|
</div>
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1222
|
+
|
|
1223
|
+
<textarea id="inboxText" placeholder="Cole updates, decisões, blockers... ou faça uma pergunta à Freya. ▸ Salvar & Processar → extrai tarefas e blockers ▸ Perguntar → consulta o histórico (RAG)" style="resize:none; flex: 1; min-height: 0; 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;"
|
|
1224
|
+
onkeydown="if((event.metaKey||event.ctrlKey)&&event.key==='Enter'){event.preventDefault();window.saveAndPlan();}"></textarea>
|
|
1225
|
+
|
|
1226
|
+
<!-- Primary actions -->
|
|
1227
|
+
<div style="padding: 10px 14px 6px; display: flex; gap: 8px; flex-wrap: wrap; flex-shrink:0;">
|
|
1228
|
+
<button class="btn primary small" type="button" onclick="saveAndPlan()" style="flex:1; min-width: 120px;" title="Ctrl+Enter">
|
|
1229
|
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="display:inline;vertical-align:-2px;margin-right:5px"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg>
|
|
1230
|
+
Salvar & Processar
|
|
1231
|
+
</button>
|
|
1232
|
+
<button class="btn small" type="button" onclick="window.askFreyaFromInput()" style="flex:1; min-width: 90px;" title="Consulta semântica ao histórico">
|
|
1233
|
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display:inline;vertical-align:-2px;margin-right:5px"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
|
|
1234
|
+
Perguntar
|
|
1235
|
+
</button>
|
|
1236
|
+
</div>
|
|
1237
|
+
|
|
1238
|
+
<!-- Toggles + secondary -->
|
|
1239
|
+
<div style="padding: 0 14px 12px; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 8px; flex-shrink:0;">
|
|
1240
|
+
<div class="promptToggles" style="margin:0;">
|
|
1241
|
+
<label class="toggleRow" title="Aplica o plano automaticamente após processar">
|
|
1229
1242
|
<input id="autoApply" type="checkbox" checked style="width:auto; margin: 0;" onchange="toggleAutoApply()" />
|
|
1230
1243
|
Auto-apply
|
|
1231
1244
|
</label>
|
|
1232
|
-
<label class="toggleRow">
|
|
1245
|
+
<label class="toggleRow" title="Roda relatórios sugeridos após aplicar">
|
|
1233
1246
|
<input id="autoRunReports" type="checkbox" style="width:auto; margin: 0;" onchange="toggleAutoRunReports()" />
|
|
1234
|
-
Auto-
|
|
1247
|
+
Auto-reports
|
|
1235
1248
|
</label>
|
|
1236
1249
|
</div>
|
|
1250
|
+
<div style="display:flex; gap:6px;">
|
|
1251
|
+
<button class="btn small" type="button" onclick="runSuggestedReports()" title="Rodar relatórios sugeridos pelo último plan" style="font-size:11px; padding: 3px 8px;">📋 Sugeridos</button>
|
|
1252
|
+
<button class="btn small" type="button" onclick="exportChatObsidian()" style="font-size:11px; padding: 3px 8px;" title="Exportar conversa para Obsidian">⬆ Log</button>
|
|
1253
|
+
</div>
|
|
1237
1254
|
</div>
|
|
1238
1255
|
</div>
|
|
1239
1256
|
|
|
1257
|
+
<!-- Right: Unified Chat Thread (Oracle responses + Plan outputs) -->
|
|
1240
1258
|
<div class="panel" style="height: 100%; display: flex; flex-direction: column;">
|
|
1241
|
-
<div class="panelHead">
|
|
1242
|
-
<b style="display: flex; align-items: center; gap: 8px;">
|
|
1243
|
-
<svg width="
|
|
1244
|
-
|
|
1259
|
+
<div class="panelHead" style="flex-shrink:0;">
|
|
1260
|
+
<b style="display: flex; align-items: center; gap: 8px; font-size:13px;">
|
|
1261
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color:var(--accent)"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></svg>
|
|
1262
|
+
Conversa com a Freya
|
|
1245
1263
|
</b>
|
|
1246
1264
|
<div class="stack">
|
|
1247
|
-
<
|
|
1248
|
-
</div>
|
|
1249
|
-
</div>
|
|
1250
|
-
<div class="panelBody" style="flex:1; display:flex; flex-direction:column; padding:0; background:var(--glass-bg); min-height:0;">
|
|
1251
|
-
<div id="chatThread" style="flex:1; overflow-y:auto; overflow-x:hidden; padding:12px; display:flex; flex-direction:column; gap:8px;"></div>
|
|
1252
|
-
<div style="padding: 12px; border-top: 1px solid var(--glass-border); display:flex; gap: 8px; background: var(--paper);">
|
|
1253
|
-
<input type="text" id="oracleInput" autocomplete="off" style="flex:1;" placeholder="Pergunte algo à Freya..." onkeydown="if(event.key==='Enter') window.askFreyaInline()" />
|
|
1254
|
-
<button class="btn primary small" type="button" onclick="window.askFreyaInline()">Enviar</button>
|
|
1265
|
+
<span id="chatModeTag" style="font-size:11px; padding:2px 8px; border-radius:10px; background:var(--bg2); color:var(--muted); border:1px solid var(--border);">oracle</span>
|
|
1255
1266
|
</div>
|
|
1256
1267
|
</div>
|
|
1268
|
+
<div id="chatThread" style="flex:1; overflow-y:auto; overflow-x:hidden; padding:12px; display:flex; flex-direction:column; gap:8px; min-height:0;"></div>
|
|
1257
1269
|
</div>
|
|
1258
1270
|
</section>
|
|
1259
1271
|
|
|
1260
1272
|
<div class="centerHead">
|
|
1261
1273
|
<div>
|
|
1262
1274
|
<h1 style="margin:0">Seu dia em um painel</h1>
|
|
1263
|
-
<div class="subtitle">
|
|
1275
|
+
<div class="subtitle">Use o campo acima para capturar updates (<b>Salvar & Processar</b>) ou consultar o histórico (<b>Perguntar</b>). As respostas aparecem no chat ao lado.</div>
|
|
1264
1276
|
</div>
|
|
1265
1277
|
<div class="statusLine">
|
|
1266
1278
|
<span class="small" id="last"></span>
|
|
1267
1279
|
</div>
|
|
1268
1280
|
</div>
|
|
1269
1281
|
|
|
1270
|
-
<
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
<
|
|
1282
|
+
<section class="panel" style="display:flex; flex-direction:column; min-height:0; margin-bottom:16px;">
|
|
1283
|
+
<!-- Header with live counters -->
|
|
1284
|
+
<div class="panelHead" style="background: linear-gradient(90deg, var(--paper2), var(--paper)); border-left: 4px solid var(--accent); flex-shrink:0;">
|
|
1285
|
+
<div style="display:flex; align-items:center; gap:10px;">
|
|
1286
|
+
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="color:var(--accent); flex-shrink:0;"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>
|
|
1287
|
+
<b style="color: var(--text); font-size: 14px;">Foco de Hoje</b>
|
|
1288
|
+
<div id="focusSummaryChips" style="display:flex; gap:5px; margin-left:4px;"></div>
|
|
1277
1289
|
</div>
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
<div class="small" style="opacity:.8; font-weight: 600; text-transform: uppercase;">Insights Globais de Bloqueios</div>
|
|
1285
|
-
<button class="btn small" type="button" onclick="refreshBlockersInsights()">Atualizar insights</button>
|
|
1290
|
+
<div class="stack">
|
|
1291
|
+
<div id="focusProgressWrap" style="display:none; align-items:center; gap:6px; font-size:11px; color:var(--muted);">
|
|
1292
|
+
<div style="width:80px; height:5px; background:var(--border); border-radius:3px; overflow:hidden;">
|
|
1293
|
+
<div id="focusProgressBar" style="height:100%; background:var(--accent); border-radius:3px; transition:width 0.4s; width:0%"></div>
|
|
1294
|
+
</div>
|
|
1295
|
+
<span id="focusProgressLabel"></span>
|
|
1286
1296
|
</div>
|
|
1287
|
-
<
|
|
1297
|
+
<button class="btn small" type="button" onclick="refreshToday()">
|
|
1298
|
+
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="display:inline;vertical-align:-2px;margin-right:4px"><polyline points="23 4 23 10 17 10"></polyline><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path></svg>
|
|
1299
|
+
Atualizar
|
|
1300
|
+
</button>
|
|
1288
1301
|
</div>
|
|
1289
1302
|
</div>
|
|
1290
|
-
</section>
|
|
1291
1303
|
|
|
1292
|
-
|
|
1293
|
-
<div class="
|
|
1294
|
-
<
|
|
1295
|
-
<div class="stack">
|
|
1296
|
-
<button class="btn small" type="button" onclick="refreshReports()">Atualizar</button>
|
|
1297
|
-
<button class="btn small" type="button" onclick="window.location.href='/reports'">Ver todos →</button>
|
|
1298
|
-
</div>
|
|
1304
|
+
<!-- Scrollable swimlanes body -->
|
|
1305
|
+
<div class="panelBody" style="flex:1; overflow-y:auto; display:flex; flex-direction:column; gap:0; padding:0; min-height:0;">
|
|
1306
|
+
<div id="swimlaneContainer" style="display:flex; flex-direction: column; gap: 0; flex:1;"></div>
|
|
1299
1307
|
</div>
|
|
1300
|
-
<div class="panelBody panelScroll" style="max-height: 300px;">
|
|
1301
|
-
<input id="reportsFilter" placeholder="Filtrar relatórios..." style="width:100%; margin-bottom:10px" oninput="renderReportsList()" />
|
|
1302
|
-
<div id="reportsList" style="display:grid; gap:8px"></div>
|
|
1303
|
-
</div>
|
|
1304
|
-
</section>
|
|
1305
|
-
</div>
|
|
1306
|
-
<div class="panel">
|
|
1307
|
-
<div class="panelHead"><b>Configurações de publicação</b></div>
|
|
1308
|
-
<div class="panelBody">
|
|
1309
|
-
<label>Discord webhook URL</label>
|
|
1310
|
-
<input id="discord" placeholder="https://discord.com/api/webhooks/..." />
|
|
1311
|
-
<div style="height:10px"></div>
|
|
1312
|
-
|
|
1313
|
-
<label>Teams webhook URL</label>
|
|
1314
|
-
<input id="teams" placeholder="https://..." />
|
|
1315
|
-
<div class="help">Os webhooks ficam salvos na workspace em <code>data/settings/settings.json</code>.</div>
|
|
1316
|
-
|
|
1317
|
-
<div style="height:10px"></div>
|
|
1318
|
-
<label style="display:flex; align-items:center; gap:10px; user-select:none; margin: 6px 0 12px 0">
|
|
1319
|
-
<input id="prettyPublish" type="checkbox" checked style="width:auto" onchange="togglePrettyPublish()" />
|
|
1320
|
-
Publicação bonita (cards/embeds)
|
|
1321
|
-
</label>
|
|
1322
1308
|
|
|
1309
|
+
<!-- Insights strip (collapsed by default, expands when content is available) -->
|
|
1310
|
+
<div id="blockersInsightsWrap" style="flex-shrink:0; border-top: 1px solid var(--border); display:none;">
|
|
1311
|
+
<div style="display:flex; justify-content:space-between; align-items:center; padding: 8px 16px 4px;">
|
|
1312
|
+
<div style="font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:0.5px; color:var(--muted);">
|
|
1313
|
+
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display:inline;vertical-align:-1px;margin-right:4px"><path d="M12 2a10 10 0 1 0 0 20A10 10 0 0 0 12 2z"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>
|
|
1314
|
+
Insights de Bloqueios
|
|
1323
1315
|
</div>
|
|
1316
|
+
<button class="btn small" type="button" onclick="refreshBlockersInsights()" style="font-size:10px; padding:2px 6px;">↻</button>
|
|
1324
1317
|
</div>
|
|
1318
|
+
<div id="blockersInsights" style="padding: 4px 16px 12px; font-size:12px; color:var(--muted);"></div>
|
|
1325
1319
|
</div>
|
|
1320
|
+
</section>
|
|
1321
|
+
|
|
1322
|
+
<div class="panel">
|
|
1323
|
+
<div class="panelHead"><b>Configurações de publicação</b></div>
|
|
1324
|
+
<div class="panelBody">
|
|
1325
|
+
<label>Discord webhook URL</label>
|
|
1326
|
+
<input id="discord" placeholder="https://discord.com/api/webhooks/..." />
|
|
1327
|
+
<div style="height:10px"></div>
|
|
1328
|
+
|
|
1329
|
+
<label>Teams webhook URL</label>
|
|
1330
|
+
<input id="teams" placeholder="https://..." />
|
|
1331
|
+
<div class="help">Os webhooks ficam salvos na workspace em <code>data/settings/settings.json</code>.</div>
|
|
1332
|
+
|
|
1333
|
+
<div style="height:10px"></div>
|
|
1334
|
+
<label style="display:flex; align-items:center; gap:10px; user-select:none; margin: 6px 0 12px 0">
|
|
1335
|
+
<input id="prettyPublish" type="checkbox" checked style="width:auto" onchange="togglePrettyPublish()" />
|
|
1336
|
+
Publicação bonita (cards/embeds)
|
|
1337
|
+
</label>
|
|
1338
|
+
</div>
|
|
1326
1339
|
</div>
|
|
1327
1340
|
</div>
|
|
1328
1341
|
</main>
|
package/package.json
CHANGED