@cccarv82/freya 2.14.1 → 2.15.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.css +303 -2
- package/cli/web-ui.js +306 -7
- package/cli/web.js +376 -10
- package/package.json +2 -2
- package/scripts/lib/DataLayer.js +10 -1
- package/scripts/lib/DataManager.js +1 -0
- package/scripts/lib/Embedder.js +5 -1
- package/templates/base/scripts/lib/DataLayer.js +11 -2
- package/templates/base/scripts/lib/DataManager.js +15 -14
- package/templates/base/scripts/lib/Embedder.js +5 -1
package/cli/web.js
CHANGED
|
@@ -624,7 +624,7 @@ function safeJson(res, code, obj) {
|
|
|
624
624
|
function looksEmptyWorkspace(dir) {
|
|
625
625
|
try {
|
|
626
626
|
if (!exists(dir)) return true;
|
|
627
|
-
const entries = fs.readdirSync(dir).filter((n) => !['.debuglogs', '.DS_Store'].includes(n));
|
|
627
|
+
const entries = fs.readdirSync(dir).filter((n) => !['.debuglogs', '.DS_Store', 'data', 'logs'].includes(n));
|
|
628
628
|
return entries.length === 0;
|
|
629
629
|
} catch {
|
|
630
630
|
return true;
|
|
@@ -983,6 +983,11 @@ function docsHtml(defaultDir) {
|
|
|
983
983
|
return buildDocsHtml(safeDefault, APP_VERSION);
|
|
984
984
|
}
|
|
985
985
|
|
|
986
|
+
function kanbanHtml(defaultDir) {
|
|
987
|
+
const safeDefault = String(defaultDir || './freya').replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
988
|
+
return buildKanbanHtml(safeDefault, APP_VERSION);
|
|
989
|
+
}
|
|
990
|
+
|
|
986
991
|
function buildDocsHtml(safeDefault, appVersion) {
|
|
987
992
|
const safeVersion = escapeHtml(appVersion || 'unknown');
|
|
988
993
|
return `<!doctype html>
|
|
@@ -1035,6 +1040,9 @@ function buildDocsHtml(safeDefault, appVersion) {
|
|
|
1035
1040
|
<button class="railBtn" id="railCompanion" type="button" title="Companion">
|
|
1036
1041
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"></polygon></svg>
|
|
1037
1042
|
</button>
|
|
1043
|
+
<button class="railBtn" id="railKanban" type="button" title="Kanban">
|
|
1044
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="5" height="18" rx="1"></rect><rect x="10" y="3" width="5" height="12" rx="1"></rect><rect x="17" y="3" width="5" height="15" rx="1"></rect></svg>
|
|
1045
|
+
</button>
|
|
1038
1046
|
<button class="railBtn" id="railProjects" type="button" title="Projects">
|
|
1039
1047
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
|
|
1040
1048
|
</button>
|
|
@@ -1174,6 +1182,9 @@ function buildHtml(safeDefault, appVersion) {
|
|
|
1174
1182
|
<button class="railBtn" id="railCompanion" type="button" title="Companion">
|
|
1175
1183
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"></polygon></svg>
|
|
1176
1184
|
</button>
|
|
1185
|
+
<button class="railBtn" id="railKanban" type="button" title="Kanban">
|
|
1186
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="5" height="18" rx="1"></rect><rect x="10" y="3" width="5" height="12" rx="1"></rect><rect x="17" y="3" width="5" height="15" rx="1"></rect></svg>
|
|
1187
|
+
</button>
|
|
1177
1188
|
<button class="railBtn" id="railProjects" type="button" title="Projects">
|
|
1178
1189
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
|
|
1179
1190
|
</button>
|
|
@@ -1209,7 +1220,7 @@ function buildHtml(safeDefault, appVersion) {
|
|
|
1209
1220
|
<div class="centerBody">
|
|
1210
1221
|
<!-- Unified Input Panel -->
|
|
1211
1222
|
<div class="promptShell">
|
|
1212
|
-
<div class="promptBar" style="border-radius: 16px; display: flex; flex-direction: column; overflow: hidden;">
|
|
1223
|
+
<div class="promptBar" style="border-radius: 16px; display: flex; flex-direction: column; overflow: hidden; flex: 1;">
|
|
1213
1224
|
<!-- Header -->
|
|
1214
1225
|
<div class="promptMeta" style="flex-shrink:0;">
|
|
1215
1226
|
<div class="promptTitle" style="display: flex; align-items: center; gap: 8px;">
|
|
@@ -1223,7 +1234,7 @@ function buildHtml(safeDefault, appVersion) {
|
|
|
1223
1234
|
</div>
|
|
1224
1235
|
|
|
1225
1236
|
<!-- Textarea -->
|
|
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:
|
|
1237
|
+
<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: 200px; flex: 1; 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
1238
|
onkeydown="if((event.metaKey||event.ctrlKey)&&event.key==='Enter'){event.preventDefault();window.saveAndPlan();}"></textarea>
|
|
1228
1239
|
|
|
1229
1240
|
<!-- Actions bar -->
|
|
@@ -1257,7 +1268,7 @@ function buildHtml(safeDefault, appVersion) {
|
|
|
1257
1268
|
</div>
|
|
1258
1269
|
|
|
1259
1270
|
<!-- Chat thread: responses appear here after actions -->
|
|
1260
|
-
<div id="chatThread" style="max-height:
|
|
1271
|
+
<div id="chatThread" style="flex: 0 1 auto; min-height: 0; max-height: 40vh; overflow-y:auto; overflow-x:hidden; padding:0 12px; display:none; flex-direction:column; gap:8px; border-top: 1px solid var(--border);"></div>
|
|
1261
1272
|
</div>
|
|
1262
1273
|
</div>
|
|
1263
1274
|
|
|
@@ -1317,6 +1328,39 @@ function buildHtml(safeDefault, appVersion) {
|
|
|
1317
1328
|
</div>
|
|
1318
1329
|
</div>
|
|
1319
1330
|
|
|
1331
|
+
<!-- Quick-add modal (Ctrl+K) -->
|
|
1332
|
+
<div id="quickAddOverlay" class="qa-overlay" style="display:none;">
|
|
1333
|
+
<div class="qa-modal">
|
|
1334
|
+
<div class="qa-header">
|
|
1335
|
+
<span style="font-weight:700; font-size:14px;">Nova Task</span>
|
|
1336
|
+
<button class="btn small" type="button" onclick="window.closeQuickAdd()" style="padding:2px 8px;">×</button>
|
|
1337
|
+
</div>
|
|
1338
|
+
<textarea id="qaDesc" class="qa-input" placeholder="Descri\u00e7\u00e3o da task..." rows="3"></textarea>
|
|
1339
|
+
<div class="qa-row">
|
|
1340
|
+
<select id="qaCat" class="qa-select">
|
|
1341
|
+
<option value="DO_NOW">DO_NOW</option>
|
|
1342
|
+
<option value="SCHEDULE">SCHEDULE</option>
|
|
1343
|
+
<option value="DELEGATE">DELEGATE</option>
|
|
1344
|
+
</select>
|
|
1345
|
+
<select id="qaPriority" class="qa-select">
|
|
1346
|
+
<option value="">Prioridade</option>
|
|
1347
|
+
<option value="critical">Cr\u00edtica</option>
|
|
1348
|
+
<option value="high">Alta</option>
|
|
1349
|
+
<option value="medium">M\u00e9dia</option>
|
|
1350
|
+
<option value="low">Baixa</option>
|
|
1351
|
+
</select>
|
|
1352
|
+
</div>
|
|
1353
|
+
<div class="qa-row">
|
|
1354
|
+
<input id="qaSlug" class="qa-input" placeholder="Projeto (slug)" style="flex:1;" />
|
|
1355
|
+
<input id="qaDue" type="date" class="qa-input" style="width:160px;" />
|
|
1356
|
+
</div>
|
|
1357
|
+
<div class="qa-row" style="justify-content:flex-end;">
|
|
1358
|
+
<button class="btn small" type="button" onclick="window.closeQuickAdd()">Cancelar</button>
|
|
1359
|
+
<button class="btn primary small" type="button" onclick="window.submitQuickAdd()">Criar Task</button>
|
|
1360
|
+
</div>
|
|
1361
|
+
</div>
|
|
1362
|
+
</div>
|
|
1363
|
+
|
|
1320
1364
|
<script>
|
|
1321
1365
|
window.__FREYA_DEFAULT_DIR = "${safeDefault}";
|
|
1322
1366
|
</script>
|
|
@@ -1354,6 +1398,9 @@ function buildReportsHtml(safeDefault, appVersion) {
|
|
|
1354
1398
|
<button class="railBtn" id="railCompanion" type="button" title="Companion">
|
|
1355
1399
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"></polygon></svg>
|
|
1356
1400
|
</button>
|
|
1401
|
+
<button class="railBtn" id="railKanban" type="button" title="Kanban">
|
|
1402
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="5" height="18" rx="1"></rect><rect x="10" y="3" width="5" height="12" rx="1"></rect><rect x="17" y="3" width="5" height="15" rx="1"></rect></svg>
|
|
1403
|
+
</button>
|
|
1357
1404
|
<button class="railBtn" id="railProjects" type="button" title="Projects">
|
|
1358
1405
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
|
|
1359
1406
|
</button>
|
|
@@ -1467,6 +1514,9 @@ function buildProjectsHtml(safeDefault, appVersion) {
|
|
|
1467
1514
|
<button class="railBtn" id="railCompanion" type="button" title="Companion">
|
|
1468
1515
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"></polygon></svg>
|
|
1469
1516
|
</button>
|
|
1517
|
+
<button class="railBtn" id="railKanban" type="button" title="Kanban">
|
|
1518
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="5" height="18" rx="1"></rect><rect x="10" y="3" width="5" height="12" rx="1"></rect><rect x="17" y="3" width="5" height="15" rx="1"></rect></svg>
|
|
1519
|
+
</button>
|
|
1470
1520
|
<button class="railBtn active" id="railProjects" type="button" title="Projects">
|
|
1471
1521
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
|
|
1472
1522
|
</button>
|
|
@@ -1560,6 +1610,9 @@ function buildTimelineHtml(safeDefault, appVersion) {
|
|
|
1560
1610
|
<button class=\"railBtn\" id=\"railCompanion\" type=\"button\" title=\"Companion\">
|
|
1561
1611
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"></polygon></svg>
|
|
1562
1612
|
</button>
|
|
1613
|
+
<button class=\"railBtn\" id=\"railKanban\" type=\"button\" title=\"Kanban\">
|
|
1614
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="5" height="18" rx="1"></rect><rect x="10" y="3" width="5" height="12" rx="1"></rect><rect x="17" y="3" width="5" height="15" rx="1"></rect></svg>
|
|
1615
|
+
</button>
|
|
1563
1616
|
<button class=\"railBtn\" id=\"railProjects\" type=\"button\" title=\"Projects\">
|
|
1564
1617
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
|
|
1565
1618
|
</button>
|
|
@@ -1669,6 +1722,9 @@ function buildGraphHtml(safeDefault, appVersion) {
|
|
|
1669
1722
|
<button class="railBtn" id="railCompanion" type="button" title="Companion">
|
|
1670
1723
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"></polygon></svg>
|
|
1671
1724
|
</button>
|
|
1725
|
+
<button class="railBtn" id="railKanban" type="button" title="Kanban">
|
|
1726
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="5" height="18" rx="1"></rect><rect x="10" y="3" width="5" height="12" rx="1"></rect><rect x="17" y="3" width="5" height="15" rx="1"></rect></svg>
|
|
1727
|
+
</button>
|
|
1672
1728
|
<button class="railBtn" id="railProjects" type="button" title="Projects">
|
|
1673
1729
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
|
|
1674
1730
|
</button>
|
|
@@ -1760,6 +1816,9 @@ function buildCompanionHtml(safeDefault, appVersion) {
|
|
|
1760
1816
|
<button class="railBtn active" id="railCompanion" type="button" title="Companion">
|
|
1761
1817
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"></polygon></svg>
|
|
1762
1818
|
</button>
|
|
1819
|
+
<button class="railBtn" id="railKanban" type="button" title="Kanban">
|
|
1820
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="5" height="18" rx="1"></rect><rect x="10" y="3" width="5" height="12" rx="1"></rect><rect x="17" y="3" width="5" height="15" rx="1"></rect></svg>
|
|
1821
|
+
</button>
|
|
1763
1822
|
<button class="railBtn" id="railProjects" type="button" title="Projects">
|
|
1764
1823
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
|
|
1765
1824
|
</button>
|
|
@@ -2322,6 +2381,14 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
2322
2381
|
return;
|
|
2323
2382
|
}
|
|
2324
2383
|
|
|
2384
|
+
if (req.method === 'GET' && req.url === '/kanban') {
|
|
2385
|
+
try { res.__freyaDebug.workspaceDir = normalizeWorkspaceDir(dir || './freya'); } catch { }
|
|
2386
|
+
const body = injectPort(kanbanHtml(dir || './freya'));
|
|
2387
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store' });
|
|
2388
|
+
res.end(body);
|
|
2389
|
+
return;
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2325
2392
|
if (req.method === 'GET' && req.url === '/app.css') {
|
|
2326
2393
|
const css = fs.readFileSync(path.join(__dirname, 'web-ui.css'), 'utf8');
|
|
2327
2394
|
res.writeHead(200, { 'Content-Type': 'text/css; charset=utf-8', 'Cache-Control': 'no-store' });
|
|
@@ -3724,15 +3791,19 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
3724
3791
|
return `\n\n---\nFILE: ${rel}\n---\n` + fs.readFileSync(p, 'utf8');
|
|
3725
3792
|
}).join('');
|
|
3726
3793
|
|
|
3727
|
-
// V2 RAG Context
|
|
3794
|
+
// V2 RAG Context (graceful fallback if embedder/sharp not available)
|
|
3728
3795
|
const dm = new DataManager(workspaceDir, path.join(workspaceDir, 'logs'));
|
|
3729
|
-
const ragResults = await dm.semanticSearch(query, 12);
|
|
3730
3796
|
let ragContext = '';
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
ragContext
|
|
3797
|
+
try {
|
|
3798
|
+
const ragResults = await dm.semanticSearch(query, 12);
|
|
3799
|
+
if (ragResults.length > 0) {
|
|
3800
|
+
ragContext = '\n\n[MEMÓRIA DE LONGO PRAZO RECUPERADA (RAG VIA SQLITE)]\n';
|
|
3801
|
+
for (const r of ragResults) {
|
|
3802
|
+
ragContext += `\n---\nFONTE: ${r.reference_type} -> ID: ${r.reference_id} (Score: ${r.score.toFixed(3)})\nCONTEÚDO:\n${r.text_chunk}\n`;
|
|
3803
|
+
}
|
|
3735
3804
|
}
|
|
3805
|
+
} catch (ragErr) {
|
|
3806
|
+
console.error('[oracle] RAG search failed (embedder/sharp unavailable), continuing without context:', ragErr.message);
|
|
3736
3807
|
}
|
|
3737
3808
|
|
|
3738
3809
|
const prompt = `Você é o agente Oracle do sistema F.R.E.Y.A.\n\nSiga estritamente os arquivos de regras abaixo.\nResponda de forma analítica e consultiva.\n${ragContext}\n\nREGRAS:${rulesText}\n\nCONSULTA DO USUÁRIO:\n${query}\n`;
|
|
@@ -3882,6 +3953,7 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
3882
3953
|
status: t.status,
|
|
3883
3954
|
createdAt: t.created_at,
|
|
3884
3955
|
completedAt: t.completed_at,
|
|
3956
|
+
dueDate: t.due_date || null,
|
|
3885
3957
|
projectSlug: t.project_slug,
|
|
3886
3958
|
priority: meta.priority,
|
|
3887
3959
|
streamSlug: meta.streamSlug,
|
|
@@ -3934,6 +4006,33 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
3934
4006
|
queryUpdates.push('category = ?');
|
|
3935
4007
|
params.push(patch.category.trim());
|
|
3936
4008
|
}
|
|
4009
|
+
if (typeof patch.dueDate === 'string') {
|
|
4010
|
+
queryUpdates.push('due_date = ?');
|
|
4011
|
+
params.push(patch.dueDate.trim() || null);
|
|
4012
|
+
}
|
|
4013
|
+
if (patch.dueDate === null) {
|
|
4014
|
+
queryUpdates.push('due_date = NULL');
|
|
4015
|
+
}
|
|
4016
|
+
if (typeof patch.status === 'string') {
|
|
4017
|
+
queryUpdates.push('status = ?');
|
|
4018
|
+
params.push(patch.status.trim());
|
|
4019
|
+
if (patch.status === 'COMPLETED') {
|
|
4020
|
+
queryUpdates.push('completed_at = ?');
|
|
4021
|
+
params.push(new Date().toISOString());
|
|
4022
|
+
}
|
|
4023
|
+
}
|
|
4024
|
+
if (typeof patch.description === 'string') {
|
|
4025
|
+
queryUpdates.push('description = ?');
|
|
4026
|
+
params.push(patch.description.trim());
|
|
4027
|
+
}
|
|
4028
|
+
if (typeof patch.priority === 'string') {
|
|
4029
|
+
const row = dl.db.prepare('SELECT metadata FROM tasks WHERE id = ?').get(id);
|
|
4030
|
+
let meta = {};
|
|
4031
|
+
try { meta = row && row.metadata ? JSON.parse(row.metadata) : {}; } catch { meta = {}; }
|
|
4032
|
+
meta.priority = patch.priority.trim().toLowerCase();
|
|
4033
|
+
queryUpdates.push('metadata = ?');
|
|
4034
|
+
params.push(JSON.stringify(meta));
|
|
4035
|
+
}
|
|
3937
4036
|
|
|
3938
4037
|
if (queryUpdates.length === 0) return safeJson(res, 200, { ok: true, task: { id } });
|
|
3939
4038
|
|
|
@@ -4085,6 +4184,100 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
4085
4184
|
return safeJson(res, 200, { ok: true, blocker: { id, ...patch } });
|
|
4086
4185
|
}
|
|
4087
4186
|
|
|
4187
|
+
// --- Quick-add: create a single task directly ---
|
|
4188
|
+
if (req.url === '/api/tasks/create') {
|
|
4189
|
+
const desc = String(payload.description || '').trim();
|
|
4190
|
+
if (!desc) return safeJson(res, 400, { error: 'Missing description' });
|
|
4191
|
+
|
|
4192
|
+
const cat = String(payload.category || 'DO_NOW').trim().toUpperCase();
|
|
4193
|
+
const validCats = new Set(['DO_NOW', 'SCHEDULE', 'DELEGATE', 'IGNORE']);
|
|
4194
|
+
if (!validCats.has(cat)) return safeJson(res, 400, { error: 'Invalid category' });
|
|
4195
|
+
|
|
4196
|
+
const slug = typeof payload.projectSlug === 'string' ? payload.projectSlug.trim() || null : null;
|
|
4197
|
+
const dueDate = typeof payload.dueDate === 'string' ? payload.dueDate.trim() || null : null;
|
|
4198
|
+
const pri = typeof payload.priority === 'string' ? payload.priority.trim().toLowerCase() : undefined;
|
|
4199
|
+
const meta = {};
|
|
4200
|
+
if (pri) meta.priority = pri;
|
|
4201
|
+
|
|
4202
|
+
const id = `task-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
|
|
4203
|
+
dl.db.prepare('INSERT INTO tasks (id, project_slug, description, category, status, due_date, metadata) VALUES (?, ?, ?, ?, ?, ?, ?)')
|
|
4204
|
+
.run(id, slug, desc, cat, 'PENDING', dueDate, Object.keys(meta).length ? JSON.stringify(meta) : null);
|
|
4205
|
+
|
|
4206
|
+
return safeJson(res, 200, { ok: true, task: { id, description: desc, category: cat, status: 'PENDING', projectSlug: slug, dueDate, priority: pri } });
|
|
4207
|
+
}
|
|
4208
|
+
|
|
4209
|
+
// --- Delta summary: what changed since yesterday ---
|
|
4210
|
+
if (req.url === '/api/summary/delta') {
|
|
4211
|
+
const now = new Date();
|
|
4212
|
+
const yesterday = new Date(now);
|
|
4213
|
+
yesterday.setDate(yesterday.getDate() - 1);
|
|
4214
|
+
const yIso = yesterday.toISOString();
|
|
4215
|
+
|
|
4216
|
+
const completed = dl.db.prepare("SELECT count(*) as c FROM tasks WHERE status = 'COMPLETED' AND completed_at >= ?").get(yIso);
|
|
4217
|
+
const newTasks = dl.db.prepare("SELECT count(*) as c FROM tasks WHERE created_at >= ?").get(yIso);
|
|
4218
|
+
const resolvedBlockers = dl.db.prepare("SELECT count(*) as c FROM blockers WHERE resolved_at >= ?").get(yIso);
|
|
4219
|
+
const newBlockers = dl.db.prepare("SELECT count(*) as c FROM blockers WHERE created_at >= ?").get(yIso);
|
|
4220
|
+
const overdue = dl.db.prepare("SELECT count(*) as c FROM tasks WHERE status = 'PENDING' AND due_date IS NOT NULL AND due_date < ?")
|
|
4221
|
+
.get(now.toISOString().slice(0, 10));
|
|
4222
|
+
|
|
4223
|
+
return safeJson(res, 200, {
|
|
4224
|
+
ok: true,
|
|
4225
|
+
delta: {
|
|
4226
|
+
completedTasks: completed ? completed.c : 0,
|
|
4227
|
+
newTasks: newTasks ? newTasks.c : 0,
|
|
4228
|
+
resolvedBlockers: resolvedBlockers ? resolvedBlockers.c : 0,
|
|
4229
|
+
newBlockers: newBlockers ? newBlockers.c : 0,
|
|
4230
|
+
overdueTasks: overdue ? overdue.c : 0
|
|
4231
|
+
}
|
|
4232
|
+
});
|
|
4233
|
+
}
|
|
4234
|
+
|
|
4235
|
+
// --- Kanban: all active tasks for board view ---
|
|
4236
|
+
if (req.url === '/api/tasks/kanban') {
|
|
4237
|
+
const rawTasks = dl.db.prepare(`
|
|
4238
|
+
SELECT * FROM tasks WHERE status != 'ARCHIVED'
|
|
4239
|
+
ORDER BY
|
|
4240
|
+
CASE JSON_EXTRACT(metadata, '$.priority')
|
|
4241
|
+
WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 ELSE 4
|
|
4242
|
+
END ASC,
|
|
4243
|
+
created_at DESC
|
|
4244
|
+
`).all();
|
|
4245
|
+
|
|
4246
|
+
const tasks = rawTasks.map(t => {
|
|
4247
|
+
let meta = {};
|
|
4248
|
+
try { meta = t.metadata ? JSON.parse(t.metadata) : {}; } catch { meta = {}; }
|
|
4249
|
+
return {
|
|
4250
|
+
id: t.id,
|
|
4251
|
+
description: t.description,
|
|
4252
|
+
category: t.category,
|
|
4253
|
+
status: t.status,
|
|
4254
|
+
createdAt: t.created_at,
|
|
4255
|
+
completedAt: t.completed_at,
|
|
4256
|
+
dueDate: t.due_date || null,
|
|
4257
|
+
projectSlug: t.project_slug,
|
|
4258
|
+
priority: meta.priority,
|
|
4259
|
+
streamSlug: meta.streamSlug
|
|
4260
|
+
};
|
|
4261
|
+
});
|
|
4262
|
+
|
|
4263
|
+
const openBlockers = dl.db.prepare(`
|
|
4264
|
+
SELECT * FROM blockers WHERE status IN ('OPEN', 'MITIGATING')
|
|
4265
|
+
ORDER BY
|
|
4266
|
+
CASE severity WHEN 'CRITICAL' THEN 0 WHEN 'HIGH' THEN 1 WHEN 'MEDIUM' THEN 2 WHEN 'LOW' THEN 3 ELSE 9 END ASC,
|
|
4267
|
+
created_at ASC
|
|
4268
|
+
`).all().map(b => ({
|
|
4269
|
+
id: b.id,
|
|
4270
|
+
title: b.title,
|
|
4271
|
+
severity: b.severity,
|
|
4272
|
+
status: b.status,
|
|
4273
|
+
projectSlug: b.project_slug,
|
|
4274
|
+
owner: b.owner,
|
|
4275
|
+
createdAt: b.created_at
|
|
4276
|
+
}));
|
|
4277
|
+
|
|
4278
|
+
return safeJson(res, 200, { ok: true, tasks, blockers: openBlockers });
|
|
4279
|
+
}
|
|
4280
|
+
|
|
4088
4281
|
if (req.url === '/api/report') {
|
|
4089
4282
|
const script = payload.script;
|
|
4090
4283
|
if (!script) return safeJson(res, 400, { error: 'Missing script' });
|
|
@@ -4211,6 +4404,9 @@ function buildSettingsHtml(safeDefault, appVersion) {
|
|
|
4211
4404
|
<button class="railBtn" id="railCompanion" type="button" title="Companion">
|
|
4212
4405
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"></polygon></svg>
|
|
4213
4406
|
</button>
|
|
4407
|
+
<button class="railBtn" id="railKanban" type="button" title="Kanban">
|
|
4408
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="5" height="18" rx="1"></rect><rect x="10" y="3" width="5" height="12" rx="1"></rect><rect x="17" y="3" width="5" height="15" rx="1"></rect></svg>
|
|
4409
|
+
</button>
|
|
4214
4410
|
<button class="railBtn" id="railProjects" type="button" title="Projects">
|
|
4215
4411
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
|
|
4216
4412
|
</button>
|
|
@@ -4326,5 +4522,175 @@ function buildSettingsHtml(safeDefault, appVersion) {
|
|
|
4326
4522
|
</html>`;
|
|
4327
4523
|
}
|
|
4328
4524
|
|
|
4525
|
+
function buildKanbanHtml(safeDefault, appVersion) {
|
|
4526
|
+
const safeVersion = escapeHtml(appVersion || 'unknown');
|
|
4527
|
+
const kanbanSvg = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="5" height="18" rx="1"></rect><rect x="10" y="3" width="5" height="12" rx="1"></rect><rect x="17" y="3" width="5" height="15" rx="1"></rect></svg>';
|
|
4528
|
+
return `<!doctype html>
|
|
4529
|
+
<html>
|
|
4530
|
+
<head>
|
|
4531
|
+
<meta charset="utf-8" />
|
|
4532
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
4533
|
+
<title>FREYA Kanban</title>
|
|
4534
|
+
<link rel="stylesheet" href="/app.css" />
|
|
4535
|
+
</head>
|
|
4536
|
+
<body data-page="kanban">
|
|
4537
|
+
<div class="app">
|
|
4538
|
+
<div class="frame">
|
|
4539
|
+
<div class="shell">
|
|
4540
|
+
|
|
4541
|
+
<aside class="rail" role="navigation" aria-label="Menu principal">
|
|
4542
|
+
<div class="railTop">
|
|
4543
|
+
<div class="railLogo">F</div>
|
|
4544
|
+
</div>
|
|
4545
|
+
<div class="railNav">
|
|
4546
|
+
<button class="railBtn" id="railDashboard" type="button" title="Dashboard">
|
|
4547
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="9"></rect><rect x="14" y="3" width="7" height="5"></rect><rect x="14" y="12" width="7" height="9"></rect><rect x="3" y="16" width="7" height="5"></rect></svg>
|
|
4548
|
+
</button>
|
|
4549
|
+
<button class="railBtn" id="railReports" type="button" title="Relat\\u00f3rios">
|
|
4550
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>
|
|
4551
|
+
</button>
|
|
4552
|
+
<button class="railBtn" id="railCompanion" type="button" title="Companion">
|
|
4553
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88 16.24 7.76"></polygon></svg>
|
|
4554
|
+
</button>
|
|
4555
|
+
<button class="railBtn active" id="railKanban" type="button" title="Kanban">
|
|
4556
|
+
${kanbanSvg}
|
|
4557
|
+
</button>
|
|
4558
|
+
<button class="railBtn" id="railProjects" type="button" title="Projects">
|
|
4559
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
|
|
4560
|
+
</button>
|
|
4561
|
+
<button class="railBtn" id="railTimeline" type="button" title="Timeline">
|
|
4562
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>
|
|
4563
|
+
</button>
|
|
4564
|
+
<button class="railBtn" id="railGraph" type="button" title="Grafo">
|
|
4565
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3"></circle><circle cx="6" cy="12" r="3"></circle><circle cx="18" cy="19" r="3"></circle><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line></svg>
|
|
4566
|
+
</button>
|
|
4567
|
+
</div>
|
|
4568
|
+
<div class="railBottom">
|
|
4569
|
+
<button id="railSettings" class="railBtn" title="Configura\\u00e7\\u00f5es" onclick="if (document.body.dataset.page !== 'settings') window.location.href='/settings';">
|
|
4570
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
|
|
4571
|
+
</button>
|
|
4572
|
+
</div>
|
|
4573
|
+
</aside>
|
|
4574
|
+
|
|
4575
|
+
<main class="center" role="main">
|
|
4576
|
+
<div class="topbar">
|
|
4577
|
+
<div class="brandLine">
|
|
4578
|
+
<span class="spark"></span>
|
|
4579
|
+
<div class="brandStack">
|
|
4580
|
+
<div class="brand">FREYA</div>
|
|
4581
|
+
<div class="brandSub">Kanban Board — Vis\\u00e3o SM</div>
|
|
4582
|
+
</div>
|
|
4583
|
+
</div>
|
|
4584
|
+
<div class="topActions">
|
|
4585
|
+
<span class="chip clickable" onclick="window.location.href='/docs'">Docs</span>
|
|
4586
|
+
<span class="chip" id="chipVersion">v${safeVersion}</span>
|
|
4587
|
+
</div>
|
|
4588
|
+
</div>
|
|
4589
|
+
|
|
4590
|
+
<div class="centerBody">
|
|
4591
|
+
<!-- Kanban toolbar -->
|
|
4592
|
+
<div class="kanban-toolbar" style="display:flex; justify-content:space-between; align-items:center; padding:0 0 16px; gap:12px; flex-wrap:wrap;">
|
|
4593
|
+
<div style="display:flex; gap:8px; align-items:center;">
|
|
4594
|
+
<select id="kanbanFilterProject" class="kanban-filter" onchange="window.filterKanban()">
|
|
4595
|
+
<option value="">Todos os projetos</option>
|
|
4596
|
+
</select>
|
|
4597
|
+
<button class="btn small" type="button" onclick="window.loadKanban()">
|
|
4598
|
+
<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>
|
|
4599
|
+
Atualizar
|
|
4600
|
+
</button>
|
|
4601
|
+
</div>
|
|
4602
|
+
<button class="btn primary small" type="button" onclick="window.openQuickAdd()">+ Nova Task</button>
|
|
4603
|
+
</div>
|
|
4604
|
+
|
|
4605
|
+
<!-- Delta banner -->
|
|
4606
|
+
<div id="kanbanDelta" style="display:none;"></div>
|
|
4607
|
+
|
|
4608
|
+
<!-- Kanban columns -->
|
|
4609
|
+
<div id="kanbanBoard" class="kanban-board">
|
|
4610
|
+
<div class="kanban-col" data-category="DO_NOW">
|
|
4611
|
+
<div class="kanban-col-head do-now">
|
|
4612
|
+
<span class="kanban-col-title">DO NOW</span>
|
|
4613
|
+
<span class="kanban-col-count" id="countDoNow">0</span>
|
|
4614
|
+
</div>
|
|
4615
|
+
<div class="kanban-col-body" id="colDoNow"></div>
|
|
4616
|
+
</div>
|
|
4617
|
+
<div class="kanban-col" data-category="SCHEDULE">
|
|
4618
|
+
<div class="kanban-col-head schedule">
|
|
4619
|
+
<span class="kanban-col-title">SCHEDULE</span>
|
|
4620
|
+
<span class="kanban-col-count" id="countSchedule">0</span>
|
|
4621
|
+
</div>
|
|
4622
|
+
<div class="kanban-col-body" id="colSchedule"></div>
|
|
4623
|
+
</div>
|
|
4624
|
+
<div class="kanban-col" data-category="DELEGATE">
|
|
4625
|
+
<div class="kanban-col-head delegate">
|
|
4626
|
+
<span class="kanban-col-title">DELEGATE</span>
|
|
4627
|
+
<span class="kanban-col-count" id="countDelegate">0</span>
|
|
4628
|
+
</div>
|
|
4629
|
+
<div class="kanban-col-body" id="colDelegate"></div>
|
|
4630
|
+
</div>
|
|
4631
|
+
<div class="kanban-col" data-category="COMPLETED">
|
|
4632
|
+
<div class="kanban-col-head done">
|
|
4633
|
+
<span class="kanban-col-title">DONE (7d)</span>
|
|
4634
|
+
<span class="kanban-col-count" id="countDone">0</span>
|
|
4635
|
+
</div>
|
|
4636
|
+
<div class="kanban-col-body" id="colDone"></div>
|
|
4637
|
+
</div>
|
|
4638
|
+
</div>
|
|
4639
|
+
|
|
4640
|
+
<!-- Blockers strip below kanban -->
|
|
4641
|
+
<div id="kanbanBlockers" style="margin-top:20px; display:none;">
|
|
4642
|
+
<div style="font-size:13px; font-weight:700; color:var(--text); margin-bottom:8px; display:flex; align-items:center; gap:6px;">
|
|
4643
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#ef4444" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
|
|
4644
|
+
Blockers Ativos
|
|
4645
|
+
</div>
|
|
4646
|
+
<div id="kanbanBlockersList" class="kanban-blockers-list"></div>
|
|
4647
|
+
</div>
|
|
4648
|
+
</div>
|
|
4649
|
+
</main>
|
|
4650
|
+
</div>
|
|
4651
|
+
</div>
|
|
4652
|
+
</div>
|
|
4653
|
+
|
|
4654
|
+
<!-- Quick-add modal -->
|
|
4655
|
+
<div id="quickAddOverlay" class="qa-overlay" style="display:none;">
|
|
4656
|
+
<div class="qa-modal">
|
|
4657
|
+
<div class="qa-header">
|
|
4658
|
+
<span style="font-weight:700; font-size:14px;">Nova Task</span>
|
|
4659
|
+
<button class="btn small" type="button" onclick="window.closeQuickAdd()" style="padding:2px 8px;">×</button>
|
|
4660
|
+
</div>
|
|
4661
|
+
<textarea id="qaDesc" class="qa-input" placeholder="Descri\\u00e7\\u00e3o da task..." rows="3"></textarea>
|
|
4662
|
+
<div class="qa-row">
|
|
4663
|
+
<select id="qaCat" class="qa-select">
|
|
4664
|
+
<option value="DO_NOW">DO_NOW</option>
|
|
4665
|
+
<option value="SCHEDULE">SCHEDULE</option>
|
|
4666
|
+
<option value="DELEGATE">DELEGATE</option>
|
|
4667
|
+
</select>
|
|
4668
|
+
<select id="qaPriority" class="qa-select">
|
|
4669
|
+
<option value="">Prioridade</option>
|
|
4670
|
+
<option value="critical">Cr\\u00edtica</option>
|
|
4671
|
+
<option value="high">Alta</option>
|
|
4672
|
+
<option value="medium">M\\u00e9dia</option>
|
|
4673
|
+
<option value="low">Baixa</option>
|
|
4674
|
+
</select>
|
|
4675
|
+
</div>
|
|
4676
|
+
<div class="qa-row">
|
|
4677
|
+
<input id="qaSlug" class="qa-input" placeholder="Projeto (slug)" style="flex:1;" />
|
|
4678
|
+
<input id="qaDue" type="date" class="qa-input" style="width:160px;" />
|
|
4679
|
+
</div>
|
|
4680
|
+
<div class="qa-row" style="justify-content:flex-end;">
|
|
4681
|
+
<button class="btn small" type="button" onclick="window.closeQuickAdd()">Cancelar</button>
|
|
4682
|
+
<button class="btn primary small" type="button" onclick="window.submitQuickAdd()">Criar Task</button>
|
|
4683
|
+
</div>
|
|
4684
|
+
</div>
|
|
4685
|
+
</div>
|
|
4686
|
+
|
|
4687
|
+
<script>
|
|
4688
|
+
window.__FREYA_DEFAULT_DIR = "${safeDefault}";
|
|
4689
|
+
</script>
|
|
4690
|
+
<script src="/app.js"></script>
|
|
4691
|
+
</body>
|
|
4692
|
+
</html>`;
|
|
4693
|
+
}
|
|
4694
|
+
|
|
4329
4695
|
module.exports = { cmdWeb };
|
|
4330
4696
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cccarv82/freya",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.15.0",
|
|
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",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
],
|
|
32
32
|
"preferGlobal": true,
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@
|
|
34
|
+
"@huggingface/transformers": "^3.8.1",
|
|
35
35
|
"pdf-lib": "^1.17.1",
|
|
36
36
|
"sql.js": "^1.12.0"
|
|
37
37
|
}
|
package/scripts/lib/DataLayer.js
CHANGED
|
@@ -319,13 +319,22 @@ class DataLayer {
|
|
|
319
319
|
CREATE TABLE IF NOT EXISTS document_embeddings (
|
|
320
320
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
321
321
|
reference_type TEXT NOT NULL, /* 'daily_log', 'task', 'blocker' */
|
|
322
|
-
reference_id TEXT NOT NULL,
|
|
322
|
+
reference_id TEXT NOT NULL,
|
|
323
323
|
chunk_index INTEGER DEFAULT 0,
|
|
324
324
|
text_chunk TEXT NOT NULL,
|
|
325
325
|
embedding BLOB NOT NULL, /* Stored as Buffer of Float32Array */
|
|
326
326
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
327
327
|
);
|
|
328
328
|
`);
|
|
329
|
+
|
|
330
|
+
// --- Migrations for existing databases ---
|
|
331
|
+
// Add due_date column to tasks (safe to run multiple times)
|
|
332
|
+
try {
|
|
333
|
+
const cols = this.db.prepare("PRAGMA table_info(tasks)").all();
|
|
334
|
+
if (!cols.some(c => c.name === 'due_date')) {
|
|
335
|
+
this.db.exec("ALTER TABLE tasks ADD COLUMN due_date TEXT");
|
|
336
|
+
}
|
|
337
|
+
} catch { /* ignore if PRAGMA not supported */ }
|
|
329
338
|
}
|
|
330
339
|
|
|
331
340
|
// Helper close method
|
package/scripts/lib/Embedder.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
class Embedder {
|
|
2
2
|
constructor() {
|
|
3
|
+
// V3: use the official HuggingFace model name (same weights, new namespace)
|
|
3
4
|
this.modelName = 'Xenova/all-MiniLM-L6-v2';
|
|
4
5
|
this.extractorInfo = null;
|
|
5
6
|
this.initPromise = null;
|
|
@@ -9,7 +10,9 @@ class Embedder {
|
|
|
9
10
|
if (this.extractorInfo) return;
|
|
10
11
|
if (!this.initPromise) {
|
|
11
12
|
this.initPromise = (async () => {
|
|
12
|
-
|
|
13
|
+
// V3: @huggingface/transformers replaces @xenova/transformers
|
|
14
|
+
// sharp 0.34+ uses prebuilt platform binaries (no node-gyp needed)
|
|
15
|
+
const { pipeline } = await import('@huggingface/transformers');
|
|
13
16
|
this.extractorInfo = await pipeline('feature-extraction', this.modelName, { quantized: true });
|
|
14
17
|
})().catch((err) => {
|
|
15
18
|
this.initPromise = null;
|
|
@@ -25,6 +28,7 @@ class Embedder {
|
|
|
25
28
|
if (!cleanText) return new Float32Array(384); // Empty zero vector for model
|
|
26
29
|
|
|
27
30
|
const output = await this.extractorInfo(cleanText, { pooling: 'mean', normalize: true });
|
|
31
|
+
// V3: output.data may be a typed array or need .tolist()
|
|
28
32
|
return new Float32Array(output.data);
|
|
29
33
|
}
|
|
30
34
|
|