@cccarv82/freya 2.14.1 → 2.16.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.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.&#10;&#10;▸ Salvar & Processar → extrai tarefas e blockers do texto&#10;▸ 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;"
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.&#10;&#10;▸ Salvar & Processar → extrai tarefas e blockers do texto&#10;▸ 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,59 +1268,82 @@ 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: 280px; overflow-y:auto; overflow-x:hidden; padding:0 12px; display:flex; flex-direction:column; gap:8px; border-top: 1px solid var(--border);"></div>
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
 
1264
- <div class="centerHead">
1265
- <div>
1266
- <h1 style="margin:0">Seu dia em um painel</h1>
1267
- <div class="subtitle">Use o campo acima para capturar updates do dia (<b>Salvar &amp; Processar</b>) ou consultar o histórico (<b>Perguntar</b>). As respostas aparecem logo abaixo do input.</div>
1268
- </div>
1269
- <div class="statusLine">
1270
- <span class="small" id="last"></span>
1275
+ <!-- Kanban toolbar -->
1276
+ <div class="kanban-toolbar" style="display:flex; justify-content:space-between; align-items:center; padding:0 0 16px; gap:12px; flex-wrap:wrap;">
1277
+ <div style="display:flex; gap:8px; align-items:center;">
1278
+ <select id="kanbanFilterProject" class="kanban-filter" onchange="window.filterKanban()">
1279
+ <option value="">Todos os projetos</option>
1280
+ </select>
1281
+ <button class="btn small" type="button" onclick="window.loadKanban()">
1282
+ <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>
1283
+ Atualizar
1284
+ </button>
1285
+ <div class="statusLine" style="margin:0;">
1286
+ <span class="small" id="last"></span>
1287
+ </div>
1271
1288
  </div>
1289
+ <button class="btn primary small" type="button" onclick="window.openQuickAdd()">+ Nova Task</button>
1272
1290
  </div>
1273
1291
 
1274
- <section class="panel" style="display:flex; flex-direction:column; min-height:0; margin-bottom:16px;">
1275
- <!-- Header with live counters -->
1276
- <div class="panelHead" style="background: linear-gradient(90deg, var(--paper2), var(--paper)); border-left: 4px solid var(--accent); flex-shrink:0;">
1277
- <div style="display:flex; align-items:center; gap:10px;">
1278
- <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>
1279
- <b style="color: var(--text); font-size: 14px;">Foco de Hoje</b>
1280
- <div id="focusSummaryChips" style="display:flex; gap:5px; margin-left:4px;"></div>
1281
- </div>
1282
- <div class="stack">
1283
- <div id="focusProgressWrap" style="display:none; align-items:center; gap:6px; font-size:11px; color:var(--muted);">
1284
- <div style="width:80px; height:5px; background:var(--border); border-radius:3px; overflow:hidden;">
1285
- <div id="focusProgressBar" style="height:100%; background:var(--accent); border-radius:3px; transition:width 0.4s; width:0%"></div>
1286
- </div>
1287
- <span id="focusProgressLabel"></span>
1288
- </div>
1289
- <button class="btn small" type="button" onclick="refreshToday()">
1290
- <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>
1291
- Atualizar
1292
- </button>
1293
- </div>
1294
- </div>
1292
+ <!-- Delta banner -->
1293
+ <div id="kanbanDelta" style="display:none;"></div>
1295
1294
 
1296
- <!-- Scrollable swimlanes body -->
1297
- <div class="panelBody" style="flex:1; overflow-y:auto; display:flex; flex-direction:column; gap:0; padding:0; min-height:0;">
1298
- <div id="swimlaneContainer" style="display:flex; flex-direction: column; gap: 0; flex:1;"></div>
1295
+ <!-- Kanban columns -->
1296
+ <div id="kanbanBoard" class="kanban-board">
1297
+ <div class="kanban-col" data-category="DO_NOW">
1298
+ <div class="kanban-col-head do-now">
1299
+ <span class="kanban-col-title">DO NOW</span>
1300
+ <span class="kanban-col-count" id="countDoNow">0</span>
1299
1301
  </div>
1302
+ <div class="kanban-col-body" id="colDoNow"></div>
1303
+ </div>
1304
+ <div class="kanban-col" data-category="SCHEDULE">
1305
+ <div class="kanban-col-head schedule">
1306
+ <span class="kanban-col-title">SCHEDULE</span>
1307
+ <span class="kanban-col-count" id="countSchedule">0</span>
1308
+ </div>
1309
+ <div class="kanban-col-body" id="colSchedule"></div>
1310
+ </div>
1311
+ <div class="kanban-col" data-category="DELEGATE">
1312
+ <div class="kanban-col-head delegate">
1313
+ <span class="kanban-col-title">DELEGATE</span>
1314
+ <span class="kanban-col-count" id="countDelegate">0</span>
1315
+ </div>
1316
+ <div class="kanban-col-body" id="colDelegate"></div>
1317
+ </div>
1318
+ <div class="kanban-col" data-category="COMPLETED">
1319
+ <div class="kanban-col-head done">
1320
+ <span class="kanban-col-title">DONE (7d)</span>
1321
+ <span class="kanban-col-count" id="countDone">0</span>
1322
+ </div>
1323
+ <div class="kanban-col-body" id="colDone"></div>
1324
+ </div>
1325
+ </div>
1300
1326
 
1301
- <!-- Insights strip (collapsed by default, expands when content is available) -->
1302
- <div id="blockersInsightsWrap" style="flex-shrink:0; border-top: 1px solid var(--border); display:none;">
1303
- <div style="display:flex; justify-content:space-between; align-items:center; padding: 8px 16px 4px;">
1304
- <div style="font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:0.5px; color:var(--muted);">
1305
- <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>
1306
- Insights de Bloqueios
1307
- </div>
1308
- <button class="btn small" type="button" onclick="refreshBlockersInsights()" style="font-size:10px; padding:2px 6px;">↻</button>
1309
- </div>
1310
- <div id="blockersInsights" style="padding: 4px 16px 12px; font-size:12px; color:var(--muted);"></div>
1327
+ <!-- Blockers strip below kanban -->
1328
+ <div id="kanbanBlockers" style="margin-top:20px; display:none;">
1329
+ <div style="font-size:13px; font-weight:700; color:var(--text); margin-bottom:8px; display:flex; align-items:center; gap:6px;">
1330
+ <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>
1331
+ Blockers Ativos
1332
+ </div>
1333
+ <div id="kanbanBlockersList" class="kanban-blockers-list"></div>
1334
+ </div>
1335
+
1336
+ <!-- Insights de bloqueios -->
1337
+ <div id="blockersInsightsWrap" style="margin-top:16px; border-top: 1px solid var(--border); display:none;">
1338
+ <div style="display:flex; justify-content:space-between; align-items:center; padding: 8px 0 4px;">
1339
+ <div style="font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:0.5px; color:var(--muted);">
1340
+ <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>
1341
+ Insights de Bloqueios
1311
1342
  </div>
1312
- </section>
1343
+ <button class="btn small" type="button" onclick="refreshBlockersInsights()" style="font-size:10px; padding:2px 6px;">↻</button>
1344
+ </div>
1345
+ <div id="blockersInsights" style="padding: 4px 0 12px; font-size:12px; color:var(--muted);"></div>
1346
+ </div>
1313
1347
 
1314
1348
  </div>
1315
1349
  </main>
@@ -1317,6 +1351,43 @@ function buildHtml(safeDefault, appVersion) {
1317
1351
  </div>
1318
1352
  </div>
1319
1353
 
1354
+ <!-- Quick-add modal (Ctrl+K) -->
1355
+ <div id="quickAddOverlay" class="qa-overlay" style="display:none;">
1356
+ <div class="qa-modal">
1357
+ <div class="qa-header">
1358
+ <span style="font-weight:700; font-size:14px;">Nova Task</span>
1359
+ <button class="btn small" type="button" onclick="window.closeQuickAdd()" style="padding:2px 8px;">&times;</button>
1360
+ </div>
1361
+ <textarea id="qaDesc" class="qa-input" placeholder="Descri\u00e7\u00e3o da task..." rows="3"></textarea>
1362
+ <div class="qa-row">
1363
+ <select id="qaCat" class="qa-select">
1364
+ <option value="DO_NOW">DO_NOW</option>
1365
+ <option value="SCHEDULE">SCHEDULE</option>
1366
+ <option value="DELEGATE">DELEGATE</option>
1367
+ </select>
1368
+ <select id="qaPriority" class="qa-select">
1369
+ <option value="">Prioridade</option>
1370
+ <option value="critical">Cr\u00edtica</option>
1371
+ <option value="high">Alta</option>
1372
+ <option value="medium">M\u00e9dia</option>
1373
+ <option value="low">Baixa</option>
1374
+ </select>
1375
+ </div>
1376
+ <div class="qa-row">
1377
+ <input id="qaSlug" class="qa-input" placeholder="Projeto (slug)" style="flex:1;" />
1378
+ <div class="qa-date-wrap">
1379
+ <input id="qaDue" type="text" class="qa-input qa-date-text" placeholder="dd/mm/aaaa" maxlength="10" />
1380
+ <input id="qaDuePicker" type="date" class="qa-date-hidden" tabindex="-1" />
1381
+ <button type="button" class="qa-date-btn" onclick="var p=this.parentElement.querySelector('#qaDuePicker'); p.showPicker ? p.showPicker() : p.click();" title="Abrir calendario">&#128197;</button>
1382
+ </div>
1383
+ </div>
1384
+ <div class="qa-row" style="justify-content:flex-end;">
1385
+ <button class="btn small" type="button" onclick="window.closeQuickAdd()">Cancelar</button>
1386
+ <button class="btn primary small" type="button" onclick="window.submitQuickAdd()">Criar Task</button>
1387
+ </div>
1388
+ </div>
1389
+ </div>
1390
+
1320
1391
  <script>
1321
1392
  window.__FREYA_DEFAULT_DIR = "${safeDefault}";
1322
1393
  </script>
@@ -1354,6 +1425,9 @@ function buildReportsHtml(safeDefault, appVersion) {
1354
1425
  <button class="railBtn" id="railCompanion" type="button" title="Companion">
1355
1426
  <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
1427
  </button>
1428
+ <button class="railBtn" id="railKanban" type="button" title="Kanban">
1429
+ <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>
1430
+ </button>
1357
1431
  <button class="railBtn" id="railProjects" type="button" title="Projects">
1358
1432
  <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
1433
  </button>
@@ -1467,6 +1541,9 @@ function buildProjectsHtml(safeDefault, appVersion) {
1467
1541
  <button class="railBtn" id="railCompanion" type="button" title="Companion">
1468
1542
  <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
1543
  </button>
1544
+ <button class="railBtn" id="railKanban" type="button" title="Kanban">
1545
+ <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>
1546
+ </button>
1470
1547
  <button class="railBtn active" id="railProjects" type="button" title="Projects">
1471
1548
  <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
1549
  </button>
@@ -1560,6 +1637,9 @@ function buildTimelineHtml(safeDefault, appVersion) {
1560
1637
  <button class=\"railBtn\" id=\"railCompanion\" type=\"button\" title=\"Companion\">
1561
1638
  <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
1639
  </button>
1640
+ <button class=\"railBtn\" id=\"railKanban\" type=\"button\" title=\"Kanban\">
1641
+ <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>
1642
+ </button>
1563
1643
  <button class=\"railBtn\" id=\"railProjects\" type=\"button\" title=\"Projects\">
1564
1644
  <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
1645
  </button>
@@ -1669,6 +1749,9 @@ function buildGraphHtml(safeDefault, appVersion) {
1669
1749
  <button class="railBtn" id="railCompanion" type="button" title="Companion">
1670
1750
  <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
1751
  </button>
1752
+ <button class="railBtn" id="railKanban" type="button" title="Kanban">
1753
+ <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>
1754
+ </button>
1672
1755
  <button class="railBtn" id="railProjects" type="button" title="Projects">
1673
1756
  <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
1757
  </button>
@@ -1760,6 +1843,9 @@ function buildCompanionHtml(safeDefault, appVersion) {
1760
1843
  <button class="railBtn active" id="railCompanion" type="button" title="Companion">
1761
1844
  <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
1845
  </button>
1846
+ <button class="railBtn" id="railKanban" type="button" title="Kanban">
1847
+ <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>
1848
+ </button>
1763
1849
  <button class="railBtn" id="railProjects" type="button" title="Projects">
1764
1850
  <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
1851
  </button>
@@ -2322,6 +2408,14 @@ async function cmdWeb({ port, dir, open, dev }) {
2322
2408
  return;
2323
2409
  }
2324
2410
 
2411
+ if (req.method === 'GET' && req.url === '/kanban') {
2412
+ try { res.__freyaDebug.workspaceDir = normalizeWorkspaceDir(dir || './freya'); } catch { }
2413
+ const body = injectPort(kanbanHtml(dir || './freya'));
2414
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store' });
2415
+ res.end(body);
2416
+ return;
2417
+ }
2418
+
2325
2419
  if (req.method === 'GET' && req.url === '/app.css') {
2326
2420
  const css = fs.readFileSync(path.join(__dirname, 'web-ui.css'), 'utf8');
2327
2421
  res.writeHead(200, { 'Content-Type': 'text/css; charset=utf-8', 'Cache-Control': 'no-store' });
@@ -3724,15 +3818,19 @@ async function cmdWeb({ port, dir, open, dev }) {
3724
3818
  return `\n\n---\nFILE: ${rel}\n---\n` + fs.readFileSync(p, 'utf8');
3725
3819
  }).join('');
3726
3820
 
3727
- // V2 RAG Context
3821
+ // V2 RAG Context (graceful fallback if embedder/sharp not available)
3728
3822
  const dm = new DataManager(workspaceDir, path.join(workspaceDir, 'logs'));
3729
- const ragResults = await dm.semanticSearch(query, 12);
3730
3823
  let ragContext = '';
3731
- if (ragResults.length > 0) {
3732
- ragContext = '\n\n[MEMÓRIA DE LONGO PRAZO RECUPERADA (RAG VIA SQLITE)]\n';
3733
- for (const r of ragResults) {
3734
- ragContext += `\n---\nFONTE: ${r.reference_type} -> ID: ${r.reference_id} (Score: ${r.score.toFixed(3)})\nCONTEÚDO:\n${r.text_chunk}\n`;
3824
+ try {
3825
+ const ragResults = await dm.semanticSearch(query, 12);
3826
+ if (ragResults.length > 0) {
3827
+ ragContext = '\n\n[MEMÓRIA DE LONGO PRAZO RECUPERADA (RAG VIA SQLITE)]\n';
3828
+ for (const r of ragResults) {
3829
+ ragContext += `\n---\nFONTE: ${r.reference_type} -> ID: ${r.reference_id} (Score: ${r.score.toFixed(3)})\nCONTEÚDO:\n${r.text_chunk}\n`;
3830
+ }
3735
3831
  }
3832
+ } catch (ragErr) {
3833
+ console.error('[oracle] RAG search failed (embedder/sharp unavailable), continuing without context:', ragErr.message);
3736
3834
  }
3737
3835
 
3738
3836
  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 +3980,7 @@ async function cmdWeb({ port, dir, open, dev }) {
3882
3980
  status: t.status,
3883
3981
  createdAt: t.created_at,
3884
3982
  completedAt: t.completed_at,
3983
+ dueDate: t.due_date || null,
3885
3984
  projectSlug: t.project_slug,
3886
3985
  priority: meta.priority,
3887
3986
  streamSlug: meta.streamSlug,
@@ -3934,6 +4033,33 @@ async function cmdWeb({ port, dir, open, dev }) {
3934
4033
  queryUpdates.push('category = ?');
3935
4034
  params.push(patch.category.trim());
3936
4035
  }
4036
+ if (typeof patch.dueDate === 'string') {
4037
+ queryUpdates.push('due_date = ?');
4038
+ params.push(patch.dueDate.trim() || null);
4039
+ }
4040
+ if (patch.dueDate === null) {
4041
+ queryUpdates.push('due_date = NULL');
4042
+ }
4043
+ if (typeof patch.status === 'string') {
4044
+ queryUpdates.push('status = ?');
4045
+ params.push(patch.status.trim());
4046
+ if (patch.status === 'COMPLETED') {
4047
+ queryUpdates.push('completed_at = ?');
4048
+ params.push(new Date().toISOString());
4049
+ }
4050
+ }
4051
+ if (typeof patch.description === 'string') {
4052
+ queryUpdates.push('description = ?');
4053
+ params.push(patch.description.trim());
4054
+ }
4055
+ if (typeof patch.priority === 'string') {
4056
+ const row = dl.db.prepare('SELECT metadata FROM tasks WHERE id = ?').get(id);
4057
+ let meta = {};
4058
+ try { meta = row && row.metadata ? JSON.parse(row.metadata) : {}; } catch { meta = {}; }
4059
+ meta.priority = patch.priority.trim().toLowerCase();
4060
+ queryUpdates.push('metadata = ?');
4061
+ params.push(JSON.stringify(meta));
4062
+ }
3937
4063
 
3938
4064
  if (queryUpdates.length === 0) return safeJson(res, 200, { ok: true, task: { id } });
3939
4065
 
@@ -4085,6 +4211,111 @@ async function cmdWeb({ port, dir, open, dev }) {
4085
4211
  return safeJson(res, 200, { ok: true, blocker: { id, ...patch } });
4086
4212
  }
4087
4213
 
4214
+ // --- Quick-add: create a single task directly ---
4215
+ if (req.url === '/api/tasks/create') {
4216
+ const desc = String(payload.description || '').trim();
4217
+ if (!desc) return safeJson(res, 400, { error: 'Missing description' });
4218
+
4219
+ const cat = String(payload.category || 'DO_NOW').trim().toUpperCase();
4220
+ const validCats = new Set(['DO_NOW', 'SCHEDULE', 'DELEGATE', 'IGNORE']);
4221
+ if (!validCats.has(cat)) return safeJson(res, 400, { error: 'Invalid category' });
4222
+
4223
+ const slug = typeof payload.projectSlug === 'string' ? payload.projectSlug.trim() || null : null;
4224
+ const dueDate = typeof payload.dueDate === 'string' ? payload.dueDate.trim() || null : null;
4225
+ const pri = typeof payload.priority === 'string' ? payload.priority.trim().toLowerCase() : undefined;
4226
+ const meta = {};
4227
+ if (pri) meta.priority = pri;
4228
+
4229
+ const id = `task-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
4230
+ dl.db.prepare('INSERT INTO tasks (id, project_slug, description, category, status, due_date, metadata) VALUES (?, ?, ?, ?, ?, ?, ?)')
4231
+ .run(id, slug, desc, cat, 'PENDING', dueDate, Object.keys(meta).length ? JSON.stringify(meta) : null);
4232
+
4233
+ return safeJson(res, 200, { ok: true, task: { id, description: desc, category: cat, status: 'PENDING', projectSlug: slug, dueDate, priority: pri } });
4234
+ }
4235
+
4236
+ // --- Delta summary: what changed since yesterday ---
4237
+ if (req.url === '/api/summary/delta') {
4238
+ const now = new Date();
4239
+ const yesterday = new Date(now);
4240
+ yesterday.setDate(yesterday.getDate() - 1);
4241
+ const yIso = yesterday.toISOString();
4242
+
4243
+ const completed = dl.db.prepare("SELECT count(*) as c FROM tasks WHERE status = 'COMPLETED' AND completed_at >= ?").get(yIso);
4244
+ const newTasks = dl.db.prepare("SELECT count(*) as c FROM tasks WHERE created_at >= ?").get(yIso);
4245
+ const resolvedBlockers = dl.db.prepare("SELECT count(*) as c FROM blockers WHERE resolved_at >= ?").get(yIso);
4246
+ const newBlockers = dl.db.prepare("SELECT count(*) as c FROM blockers WHERE created_at >= ?").get(yIso);
4247
+ const overdue = dl.db.prepare("SELECT count(*) as c FROM tasks WHERE status = 'PENDING' AND due_date IS NOT NULL AND due_date < ?")
4248
+ .get(now.toISOString().slice(0, 10));
4249
+
4250
+ return safeJson(res, 200, {
4251
+ ok: true,
4252
+ delta: {
4253
+ completedTasks: completed ? completed.c : 0,
4254
+ newTasks: newTasks ? newTasks.c : 0,
4255
+ resolvedBlockers: resolvedBlockers ? resolvedBlockers.c : 0,
4256
+ newBlockers: newBlockers ? newBlockers.c : 0,
4257
+ overdueTasks: overdue ? overdue.c : 0
4258
+ }
4259
+ });
4260
+ }
4261
+
4262
+ // --- Kanban: all active tasks for board view ---
4263
+ if (req.url === '/api/tasks/kanban') {
4264
+ const rawTasks = dl.db.prepare(`
4265
+ SELECT * FROM tasks WHERE status != 'ARCHIVED'
4266
+ ORDER BY
4267
+ CASE JSON_EXTRACT(metadata, '$.priority')
4268
+ WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 ELSE 4
4269
+ END ASC,
4270
+ created_at DESC
4271
+ `).all();
4272
+
4273
+ const tasks = rawTasks.map(t => {
4274
+ let meta = {};
4275
+ try { meta = t.metadata ? JSON.parse(t.metadata) : {}; } catch { meta = {}; }
4276
+ return {
4277
+ id: t.id,
4278
+ description: t.description,
4279
+ category: t.category,
4280
+ status: t.status,
4281
+ createdAt: t.created_at,
4282
+ completedAt: t.completed_at,
4283
+ dueDate: t.due_date || null,
4284
+ projectSlug: t.project_slug,
4285
+ priority: meta.priority,
4286
+ streamSlug: meta.streamSlug,
4287
+ comments: meta.comments || [],
4288
+ source: meta.source || null,
4289
+ metadata: meta
4290
+ };
4291
+ });
4292
+
4293
+ const openBlockers = dl.db.prepare(`
4294
+ SELECT * FROM blockers WHERE status IN ('OPEN', 'MITIGATING')
4295
+ ORDER BY
4296
+ CASE severity WHEN 'CRITICAL' THEN 0 WHEN 'HIGH' THEN 1 WHEN 'MEDIUM' THEN 2 WHEN 'LOW' THEN 3 ELSE 9 END ASC,
4297
+ created_at ASC
4298
+ `).all().map(b => {
4299
+ let meta = {};
4300
+ try { meta = b.metadata ? JSON.parse(b.metadata) : {}; } catch { meta = {}; }
4301
+ return {
4302
+ id: b.id,
4303
+ title: b.title,
4304
+ severity: b.severity,
4305
+ status: b.status,
4306
+ projectSlug: b.project_slug,
4307
+ owner: b.owner,
4308
+ nextAction: b.next_action,
4309
+ createdAt: b.created_at,
4310
+ resolvedAt: b.resolved_at,
4311
+ source: meta.source || null,
4312
+ metadata: meta
4313
+ };
4314
+ });
4315
+
4316
+ return safeJson(res, 200, { ok: true, tasks, blockers: openBlockers });
4317
+ }
4318
+
4088
4319
  if (req.url === '/api/report') {
4089
4320
  const script = payload.script;
4090
4321
  if (!script) return safeJson(res, 400, { error: 'Missing script' });
@@ -4211,6 +4442,9 @@ function buildSettingsHtml(safeDefault, appVersion) {
4211
4442
  <button class="railBtn" id="railCompanion" type="button" title="Companion">
4212
4443
  <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
4444
  </button>
4445
+ <button class="railBtn" id="railKanban" type="button" title="Kanban">
4446
+ <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>
4447
+ </button>
4214
4448
  <button class="railBtn" id="railProjects" type="button" title="Projects">
4215
4449
  <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
4450
  </button>
@@ -4326,5 +4560,179 @@ function buildSettingsHtml(safeDefault, appVersion) {
4326
4560
  </html>`;
4327
4561
  }
4328
4562
 
4563
+ function buildKanbanHtml(safeDefault, appVersion) {
4564
+ const safeVersion = escapeHtml(appVersion || 'unknown');
4565
+ 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>';
4566
+ return `<!doctype html>
4567
+ <html>
4568
+ <head>
4569
+ <meta charset="utf-8" />
4570
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
4571
+ <title>FREYA Kanban</title>
4572
+ <link rel="stylesheet" href="/app.css" />
4573
+ </head>
4574
+ <body data-page="kanban">
4575
+ <div class="app">
4576
+ <div class="frame">
4577
+ <div class="shell">
4578
+
4579
+ <aside class="rail" role="navigation" aria-label="Menu principal">
4580
+ <div class="railTop">
4581
+ <div class="railLogo">F</div>
4582
+ </div>
4583
+ <div class="railNav">
4584
+ <button class="railBtn" id="railDashboard" type="button" title="Dashboard">
4585
+ <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>
4586
+ </button>
4587
+ <button class="railBtn" id="railReports" type="button" title="Relat\\u00f3rios">
4588
+ <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>
4589
+ </button>
4590
+ <button class="railBtn" id="railCompanion" type="button" title="Companion">
4591
+ <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>
4592
+ </button>
4593
+ <button class="railBtn active" id="railKanban" type="button" title="Kanban">
4594
+ ${kanbanSvg}
4595
+ </button>
4596
+ <button class="railBtn" id="railProjects" type="button" title="Projects">
4597
+ <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>
4598
+ </button>
4599
+ <button class="railBtn" id="railTimeline" type="button" title="Timeline">
4600
+ <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>
4601
+ </button>
4602
+ <button class="railBtn" id="railGraph" type="button" title="Grafo">
4603
+ <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>
4604
+ </button>
4605
+ </div>
4606
+ <div class="railBottom">
4607
+ <button id="railSettings" class="railBtn" title="Configura\\u00e7\\u00f5es" onclick="if (document.body.dataset.page !== 'settings') window.location.href='/settings';">
4608
+ <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>
4609
+ </button>
4610
+ </div>
4611
+ </aside>
4612
+
4613
+ <main class="center" role="main">
4614
+ <div class="topbar">
4615
+ <div class="brandLine">
4616
+ <span class="spark"></span>
4617
+ <div class="brandStack">
4618
+ <div class="brand">FREYA</div>
4619
+ <div class="brandSub">Kanban Board &mdash; Vis\\u00e3o SM</div>
4620
+ </div>
4621
+ </div>
4622
+ <div class="topActions">
4623
+ <span class="chip clickable" onclick="window.location.href='/docs'">Docs</span>
4624
+ <span class="chip" id="chipVersion">v${safeVersion}</span>
4625
+ </div>
4626
+ </div>
4627
+
4628
+ <div class="centerBody">
4629
+ <!-- Kanban toolbar -->
4630
+ <div class="kanban-toolbar" style="display:flex; justify-content:space-between; align-items:center; padding:0 0 16px; gap:12px; flex-wrap:wrap;">
4631
+ <div style="display:flex; gap:8px; align-items:center;">
4632
+ <select id="kanbanFilterProject" class="kanban-filter" onchange="window.filterKanban()">
4633
+ <option value="">Todos os projetos</option>
4634
+ </select>
4635
+ <button class="btn small" type="button" onclick="window.loadKanban()">
4636
+ <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>
4637
+ Atualizar
4638
+ </button>
4639
+ </div>
4640
+ <button class="btn primary small" type="button" onclick="window.openQuickAdd()">+ Nova Task</button>
4641
+ </div>
4642
+
4643
+ <!-- Delta banner -->
4644
+ <div id="kanbanDelta" style="display:none;"></div>
4645
+
4646
+ <!-- Kanban columns -->
4647
+ <div id="kanbanBoard" class="kanban-board">
4648
+ <div class="kanban-col" data-category="DO_NOW">
4649
+ <div class="kanban-col-head do-now">
4650
+ <span class="kanban-col-title">DO NOW</span>
4651
+ <span class="kanban-col-count" id="countDoNow">0</span>
4652
+ </div>
4653
+ <div class="kanban-col-body" id="colDoNow"></div>
4654
+ </div>
4655
+ <div class="kanban-col" data-category="SCHEDULE">
4656
+ <div class="kanban-col-head schedule">
4657
+ <span class="kanban-col-title">SCHEDULE</span>
4658
+ <span class="kanban-col-count" id="countSchedule">0</span>
4659
+ </div>
4660
+ <div class="kanban-col-body" id="colSchedule"></div>
4661
+ </div>
4662
+ <div class="kanban-col" data-category="DELEGATE">
4663
+ <div class="kanban-col-head delegate">
4664
+ <span class="kanban-col-title">DELEGATE</span>
4665
+ <span class="kanban-col-count" id="countDelegate">0</span>
4666
+ </div>
4667
+ <div class="kanban-col-body" id="colDelegate"></div>
4668
+ </div>
4669
+ <div class="kanban-col" data-category="COMPLETED">
4670
+ <div class="kanban-col-head done">
4671
+ <span class="kanban-col-title">DONE (7d)</span>
4672
+ <span class="kanban-col-count" id="countDone">0</span>
4673
+ </div>
4674
+ <div class="kanban-col-body" id="colDone"></div>
4675
+ </div>
4676
+ </div>
4677
+
4678
+ <!-- Blockers strip below kanban -->
4679
+ <div id="kanbanBlockers" style="margin-top:20px; display:none;">
4680
+ <div style="font-size:13px; font-weight:700; color:var(--text); margin-bottom:8px; display:flex; align-items:center; gap:6px;">
4681
+ <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>
4682
+ Blockers Ativos
4683
+ </div>
4684
+ <div id="kanbanBlockersList" class="kanban-blockers-list"></div>
4685
+ </div>
4686
+ </div>
4687
+ </main>
4688
+ </div>
4689
+ </div>
4690
+ </div>
4691
+
4692
+ <!-- Quick-add modal -->
4693
+ <div id="quickAddOverlay" class="qa-overlay" style="display:none;">
4694
+ <div class="qa-modal">
4695
+ <div class="qa-header">
4696
+ <span style="font-weight:700; font-size:14px;">Nova Task</span>
4697
+ <button class="btn small" type="button" onclick="window.closeQuickAdd()" style="padding:2px 8px;">&times;</button>
4698
+ </div>
4699
+ <textarea id="qaDesc" class="qa-input" placeholder="Descri\\u00e7\\u00e3o da task..." rows="3"></textarea>
4700
+ <div class="qa-row">
4701
+ <select id="qaCat" class="qa-select">
4702
+ <option value="DO_NOW">DO_NOW</option>
4703
+ <option value="SCHEDULE">SCHEDULE</option>
4704
+ <option value="DELEGATE">DELEGATE</option>
4705
+ </select>
4706
+ <select id="qaPriority" class="qa-select">
4707
+ <option value="">Prioridade</option>
4708
+ <option value="critical">Cr\\u00edtica</option>
4709
+ <option value="high">Alta</option>
4710
+ <option value="medium">M\\u00e9dia</option>
4711
+ <option value="low">Baixa</option>
4712
+ </select>
4713
+ </div>
4714
+ <div class="qa-row">
4715
+ <input id="qaSlug" class="qa-input" placeholder="Projeto (slug)" style="flex:1;" />
4716
+ <div class="qa-date-wrap">
4717
+ <input id="qaDue" type="text" class="qa-input qa-date-text" placeholder="dd/mm/aaaa" maxlength="10" />
4718
+ <input id="qaDuePicker" type="date" class="qa-date-hidden" tabindex="-1" />
4719
+ <button type="button" class="qa-date-btn" onclick="var p=this.parentElement.querySelector('#qaDuePicker'); p.showPicker ? p.showPicker() : p.click();" title="Abrir calendario">&#128197;</button>
4720
+ </div>
4721
+ </div>
4722
+ <div class="qa-row" style="justify-content:flex-end;">
4723
+ <button class="btn small" type="button" onclick="window.closeQuickAdd()">Cancelar</button>
4724
+ <button class="btn primary small" type="button" onclick="window.submitQuickAdd()">Criar Task</button>
4725
+ </div>
4726
+ </div>
4727
+ </div>
4728
+
4729
+ <script>
4730
+ window.__FREYA_DEFAULT_DIR = "${safeDefault}";
4731
+ </script>
4732
+ <script src="/app.js"></script>
4733
+ </body>
4734
+ </html>`;
4735
+ }
4736
+
4329
4737
  module.exports = { cmdWeb };
4330
4738