issue-flow 0.17.0 → 0.18.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/dist/{agent-TB6W7PT7.js → agent-HMZTXCRT.js} +5 -5
- package/dist/{analyze-6M3KEOX7.js → analyze-Q4ONIWRZ.js} +10 -10
- package/dist/{apply-ADB3ZK7W.js → apply-EEFPY2W4.js} +5 -5
- package/dist/{bench-FM7I7EIV.js → bench-WWWXLTFH.js} +9 -9
- package/dist/{chunk-KZ5EY5XX.js → chunk-34OCSKOB.js} +7 -7
- package/dist/{chunk-UWDCIFTP.js → chunk-45WZ6GMI.js} +27 -43
- package/dist/chunk-45WZ6GMI.js.map +1 -0
- package/dist/{chunk-QMIQR7EE.js → chunk-62GVVZQO.js} +3 -3
- package/dist/{chunk-YII4U7ZD.js → chunk-6K2LCFG6.js} +2 -2
- package/dist/{chunk-A4H7P7T7.js → chunk-7UMMFOL2.js} +3 -3
- package/dist/chunk-7UMMFOL2.js.map +1 -0
- package/dist/{chunk-HJBF4WD2.js → chunk-CWSQVHVV.js} +59 -25
- package/dist/chunk-CWSQVHVV.js.map +1 -0
- package/dist/{chunk-SGTTR4XW.js → chunk-E4I6MMHD.js} +4 -4
- package/dist/{chunk-6YQT3SSM.js → chunk-ECU7YO7S.js} +2 -2
- package/dist/{chunk-MI5TREN3.js → chunk-EG2MGDP5.js} +6 -6
- package/dist/{chunk-RCZJQUTC.js → chunk-HOAHMERN.js} +43 -10
- package/dist/chunk-HOAHMERN.js.map +1 -0
- package/dist/{chunk-HLAPOUEM.js → chunk-KMCS76ZW.js} +3 -3
- package/dist/{chunk-KZXQWC7U.js → chunk-KRAGAUKD.js} +8 -8
- package/dist/{chunk-E6KXCV3T.js → chunk-LGHND47K.js} +2 -2
- package/dist/{chunk-DV7HUC4Z.js → chunk-LI35PIIS.js} +2 -2
- package/dist/{chunk-Z5O2XBWA.js → chunk-LTH6XYZ3.js} +5 -5
- package/dist/{chunk-2SUFAE6X.js → chunk-NEWNXZ65.js} +21 -12
- package/dist/{chunk-2SUFAE6X.js.map → chunk-NEWNXZ65.js.map} +1 -1
- package/dist/{chunk-LPWPFIIB.js → chunk-OODXFCQV.js} +7 -7
- package/dist/{chunk-ISZ54SVE.js → chunk-PMLE7KAQ.js} +17 -9
- package/dist/chunk-PMLE7KAQ.js.map +1 -0
- package/dist/{chunk-HY5HLFDU.js → chunk-QF7VEL3M.js} +115 -14
- package/dist/chunk-QF7VEL3M.js.map +1 -0
- package/dist/{chunk-B3TSS7BT.js → chunk-QJ44VG5G.js} +2 -2
- package/dist/{chunk-VE3IYNDN.js → chunk-SA6HXL4Y.js} +2 -2
- package/dist/{chunk-UK4E2FUA.js → chunk-TZYINKCC.js} +75 -53
- package/dist/chunk-TZYINKCC.js.map +1 -0
- package/dist/{chunk-32JSYXEK.js → chunk-V7QOIQYC.js} +2 -2
- package/dist/{chunk-ANI2NTR4.js → chunk-XUEMATVY.js} +6 -6
- package/dist/{chunk-D6ZBGJ3L.js → chunk-ZVZMWNCG.js} +129 -35
- package/dist/chunk-ZVZMWNCG.js.map +1 -0
- package/dist/cli.js +35 -35
- package/dist/cli.js.map +1 -1
- package/dist/{config-KGZUPAUP.js → config-LEBZZCJP.js} +4 -4
- package/dist/{conventions-RYN7OXJN.js → conventions-XOT6YW5L.js} +6 -6
- package/dist/{execute-QL3ZQIAP.js → execute-LELOSR5T.js} +14 -14
- package/dist/{generate-CX6KNHZS.js → generate-JGW5JSKB.js} +10 -10
- package/dist/{init-PDVQDRS6.js → init-TO6MUXO4.js} +7 -5
- package/dist/{operations-CAKIT2BO.js → operations-O4FORFIH.js} +4 -4
- package/dist/{plan-PRORGS3L.js → plan-OXAT344Q.js} +12 -12
- package/dist/{policy-WX6O2JID.js → policy-U5NBFAO5.js} +5 -5
- package/dist/{pr-27RMQI5F.js → pr-UZHNKKZC.js} +12 -12
- package/dist/{pr-review-VNESMAMT.js → pr-review-Q4ACAWDM.js} +9 -9
- package/dist/{prd-ZNC4T5BS.js → prd-6CYEMLDY.js} +12 -12
- package/dist/{recorder-N7U66HCO.js → recorder-2BAKPBRA.js} +2 -2
- package/dist/{resume-VLWIE264.js → resume-URCU6CIJ.js} +26 -26
- package/dist/{review-7CREJDKB.js → review-LVZT2TMW.js} +13 -13
- package/dist/{routing-VCDYHSXA.js → routing-EK6RBZM4.js} +6 -6
- package/dist/run-2RXJCJKT.js +56 -0
- package/dist/{usage-U7QNGCPK.js → usage-KTMYXAPF.js} +3 -3
- package/dist/{web-HUNDHCJ4.js → web-B5UPHHR5.js} +8 -8
- package/package.json +1 -1
- package/web/AGENTS.md +115 -7
- package/web/public/app.css +393 -245
- package/web/public/app.js +108 -38
- package/web/public/index.html +106 -78
- package/dist/chunk-A4H7P7T7.js.map +0 -1
- package/dist/chunk-D6ZBGJ3L.js.map +0 -1
- package/dist/chunk-HJBF4WD2.js.map +0 -1
- package/dist/chunk-HY5HLFDU.js.map +0 -1
- package/dist/chunk-ISZ54SVE.js.map +0 -1
- package/dist/chunk-RCZJQUTC.js.map +0 -1
- package/dist/chunk-UK4E2FUA.js.map +0 -1
- package/dist/chunk-UWDCIFTP.js.map +0 -1
- package/dist/run-3MPORLF5.js +0 -56
- /package/dist/{agent-TB6W7PT7.js.map → agent-HMZTXCRT.js.map} +0 -0
- /package/dist/{analyze-6M3KEOX7.js.map → analyze-Q4ONIWRZ.js.map} +0 -0
- /package/dist/{apply-ADB3ZK7W.js.map → apply-EEFPY2W4.js.map} +0 -0
- /package/dist/{bench-FM7I7EIV.js.map → bench-WWWXLTFH.js.map} +0 -0
- /package/dist/{chunk-KZ5EY5XX.js.map → chunk-34OCSKOB.js.map} +0 -0
- /package/dist/{chunk-QMIQR7EE.js.map → chunk-62GVVZQO.js.map} +0 -0
- /package/dist/{chunk-YII4U7ZD.js.map → chunk-6K2LCFG6.js.map} +0 -0
- /package/dist/{chunk-SGTTR4XW.js.map → chunk-E4I6MMHD.js.map} +0 -0
- /package/dist/{chunk-6YQT3SSM.js.map → chunk-ECU7YO7S.js.map} +0 -0
- /package/dist/{chunk-MI5TREN3.js.map → chunk-EG2MGDP5.js.map} +0 -0
- /package/dist/{chunk-HLAPOUEM.js.map → chunk-KMCS76ZW.js.map} +0 -0
- /package/dist/{chunk-KZXQWC7U.js.map → chunk-KRAGAUKD.js.map} +0 -0
- /package/dist/{chunk-E6KXCV3T.js.map → chunk-LGHND47K.js.map} +0 -0
- /package/dist/{chunk-DV7HUC4Z.js.map → chunk-LI35PIIS.js.map} +0 -0
- /package/dist/{chunk-Z5O2XBWA.js.map → chunk-LTH6XYZ3.js.map} +0 -0
- /package/dist/{chunk-LPWPFIIB.js.map → chunk-OODXFCQV.js.map} +0 -0
- /package/dist/{chunk-B3TSS7BT.js.map → chunk-QJ44VG5G.js.map} +0 -0
- /package/dist/{chunk-VE3IYNDN.js.map → chunk-SA6HXL4Y.js.map} +0 -0
- /package/dist/{chunk-32JSYXEK.js.map → chunk-V7QOIQYC.js.map} +0 -0
- /package/dist/{chunk-ANI2NTR4.js.map → chunk-XUEMATVY.js.map} +0 -0
- /package/dist/{config-KGZUPAUP.js.map → config-LEBZZCJP.js.map} +0 -0
- /package/dist/{conventions-RYN7OXJN.js.map → conventions-XOT6YW5L.js.map} +0 -0
- /package/dist/{execute-QL3ZQIAP.js.map → execute-LELOSR5T.js.map} +0 -0
- /package/dist/{generate-CX6KNHZS.js.map → generate-JGW5JSKB.js.map} +0 -0
- /package/dist/{init-PDVQDRS6.js.map → init-TO6MUXO4.js.map} +0 -0
- /package/dist/{operations-CAKIT2BO.js.map → operations-O4FORFIH.js.map} +0 -0
- /package/dist/{plan-PRORGS3L.js.map → plan-OXAT344Q.js.map} +0 -0
- /package/dist/{policy-WX6O2JID.js.map → policy-U5NBFAO5.js.map} +0 -0
- /package/dist/{pr-27RMQI5F.js.map → pr-UZHNKKZC.js.map} +0 -0
- /package/dist/{pr-review-VNESMAMT.js.map → pr-review-Q4ACAWDM.js.map} +0 -0
- /package/dist/{prd-ZNC4T5BS.js.map → prd-6CYEMLDY.js.map} +0 -0
- /package/dist/{recorder-N7U66HCO.js.map → recorder-2BAKPBRA.js.map} +0 -0
- /package/dist/{resume-VLWIE264.js.map → resume-URCU6CIJ.js.map} +0 -0
- /package/dist/{review-7CREJDKB.js.map → review-LVZT2TMW.js.map} +0 -0
- /package/dist/{routing-VCDYHSXA.js.map → routing-EK6RBZM4.js.map} +0 -0
- /package/dist/{run-3MPORLF5.js.map → run-2RXJCJKT.js.map} +0 -0
- /package/dist/{usage-U7QNGCPK.js.map → usage-KTMYXAPF.js.map} +0 -0
- /package/dist/{web-HUNDHCJ4.js.map → web-B5UPHHR5.js.map} +0 -0
package/web/public/app.js
CHANGED
|
@@ -24,6 +24,16 @@
|
|
|
24
24
|
failed: 'falhou',
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
+
// O resumo do dashboard conta *execuções* (feminino), então não reaproveita
|
|
28
|
+
// STATUS_LABELS, que qualifica a execução no masculino usado pelo badge.
|
|
29
|
+
const SUMMARY_STATUS_ORDER = ['running', 'idle', 'completed', 'failed'];
|
|
30
|
+
const SUMMARY_STATUS_LABELS = {
|
|
31
|
+
running: ['em execução', 'em execução'],
|
|
32
|
+
idle: ['aguardando', 'aguardando'],
|
|
33
|
+
completed: ['concluída', 'concluídas'],
|
|
34
|
+
failed: ['com falha', 'com falha'],
|
|
35
|
+
};
|
|
36
|
+
|
|
27
37
|
const PHASE_ICONS = {
|
|
28
38
|
pending: '○',
|
|
29
39
|
running: '●',
|
|
@@ -69,6 +79,7 @@
|
|
|
69
79
|
dashboardMeta: document.getElementById('dashboard-meta'),
|
|
70
80
|
backToDashboard: document.getElementById('back-to-dashboard'),
|
|
71
81
|
issueLink: document.getElementById('issue-link'),
|
|
82
|
+
issueHeadline: document.getElementById('issue-headline'),
|
|
72
83
|
branchLine: document.getElementById('branch-line'),
|
|
73
84
|
statusBadge: document.getElementById('status-badge'),
|
|
74
85
|
elapsed: document.getElementById('elapsed'),
|
|
@@ -89,6 +100,7 @@
|
|
|
89
100
|
configuration: document.getElementById('configuration'),
|
|
90
101
|
appVersion: document.getElementById('app-version'),
|
|
91
102
|
appVersionDashboard: document.getElementById('app-version-dashboard'),
|
|
103
|
+
dashboardSummary: document.getElementById('dashboard-summary'),
|
|
92
104
|
phases: document.getElementById('phases'),
|
|
93
105
|
nextSteps: document.getElementById('next-steps'),
|
|
94
106
|
stories: document.getElementById('stories'),
|
|
@@ -155,6 +167,21 @@
|
|
|
155
167
|
return node;
|
|
156
168
|
}
|
|
157
169
|
|
|
170
|
+
// Ponto pulsante único do painel — o mesmo componente em "Executando agora"
|
|
171
|
+
// e no card do dashboard. Sem texto: o rótulo adjacente carrega o significado.
|
|
172
|
+
function liveDot() {
|
|
173
|
+
const dot = el('span', 'live');
|
|
174
|
+
dot.setAttribute('aria-hidden', 'true');
|
|
175
|
+
return dot;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Ícone textual (○ ● ✓ ✗): a cor e o rótulo ao lado já dizem o estado.
|
|
179
|
+
function statusIcon(status, symbol) {
|
|
180
|
+
const icon = el('span', 'item-icon icon-' + status, symbol || PHASE_ICONS[status] || '○');
|
|
181
|
+
icon.setAttribute('aria-hidden', 'true');
|
|
182
|
+
return icon;
|
|
183
|
+
}
|
|
184
|
+
|
|
158
185
|
function link(href, text, className) {
|
|
159
186
|
const node = el('a', className, text);
|
|
160
187
|
node.href = href;
|
|
@@ -318,15 +345,34 @@
|
|
|
318
345
|
// Troca apenas visibilidade e estado ARIA. Não toca no polling: render()
|
|
319
346
|
// segue atualizando os dois painéis, então a aba inativa nunca fica defasada.
|
|
320
347
|
|
|
321
|
-
function setActiveTab(tabId) {
|
|
348
|
+
function setActiveTab(tabId, options) {
|
|
322
349
|
state.activeTab = tabId;
|
|
350
|
+
let focused = null;
|
|
323
351
|
for (const tab of els.tabs) {
|
|
324
352
|
const active = tab.id === tabId;
|
|
325
353
|
tab.setAttribute('aria-selected', active ? 'true' : 'false');
|
|
354
|
+
tab.tabIndex = active ? 0 : -1;
|
|
326
355
|
tab.classList.toggle('is-active', active);
|
|
327
356
|
const panel = document.getElementById(tab.getAttribute('aria-controls'));
|
|
328
357
|
if (panel) panel.hidden = !active;
|
|
358
|
+
if (active) focused = tab;
|
|
329
359
|
}
|
|
360
|
+
if (options && options.focus && focused) focused.focus();
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Padrão ARIA de tablist: setas movem entre as abas, Home/End vão às pontas.
|
|
364
|
+
// Só a aba ativa fica no fluxo do Tab (roving tabindex).
|
|
365
|
+
function onTabListKeydown(event) {
|
|
366
|
+
const current = els.tabs.indexOf(event.target);
|
|
367
|
+
if (current === -1) return;
|
|
368
|
+
let next = -1;
|
|
369
|
+
if (event.key === 'ArrowRight') next = (current + 1) % els.tabs.length;
|
|
370
|
+
else if (event.key === 'ArrowLeft') next = (current - 1 + els.tabs.length) % els.tabs.length;
|
|
371
|
+
else if (event.key === 'Home') next = 0;
|
|
372
|
+
else if (event.key === 'End') next = els.tabs.length - 1;
|
|
373
|
+
else return;
|
|
374
|
+
event.preventDefault();
|
|
375
|
+
setActiveTab(els.tabs[next].id, { focus: true });
|
|
330
376
|
}
|
|
331
377
|
|
|
332
378
|
// ---- Tema ------------------------------------------------------------------
|
|
@@ -701,6 +747,9 @@
|
|
|
701
747
|
function renderEmptyDetail() {
|
|
702
748
|
if (state.snapshot) return;
|
|
703
749
|
document.title = 'issue-flow';
|
|
750
|
+
els.issueLink.hidden = true;
|
|
751
|
+
els.issueHeadline.textContent = 'Nenhuma execução ativa';
|
|
752
|
+
els.branchLine.textContent = '';
|
|
704
753
|
els.statusBadge.textContent = STATUS_LABELS.idle;
|
|
705
754
|
els.statusBadge.className = 'badge status-idle';
|
|
706
755
|
els.elapsed.textContent = '—';
|
|
@@ -715,6 +764,25 @@
|
|
|
715
764
|
return normalized.slice(0, max - 1).trimEnd() + '…';
|
|
716
765
|
}
|
|
717
766
|
|
|
767
|
+
// Linha secundária do header do dashboard: quantas execuções e em que
|
|
768
|
+
// estado. Substitui a legenda estática que só repetia o h1.
|
|
769
|
+
function renderDashboardSummary(sessions) {
|
|
770
|
+
const counts = {};
|
|
771
|
+
for (const session of sessions) {
|
|
772
|
+
const status = session.status || 'idle';
|
|
773
|
+
counts[status] = (counts[status] || 0) + 1;
|
|
774
|
+
}
|
|
775
|
+
const parts = [];
|
|
776
|
+
for (const status of SUMMARY_STATUS_ORDER) {
|
|
777
|
+
const count = counts[status];
|
|
778
|
+
if (!count) continue;
|
|
779
|
+
const label = SUMMARY_STATUS_LABELS[status];
|
|
780
|
+
parts.push(count + ' ' + (count === 1 ? label[0] : label[1]));
|
|
781
|
+
}
|
|
782
|
+
const total = sessions.length + (sessions.length === 1 ? ' execução' : ' execuções');
|
|
783
|
+
els.dashboardSummary.textContent = parts.length > 0 ? total + ' · ' + parts.join(' · ') : total;
|
|
784
|
+
}
|
|
785
|
+
|
|
718
786
|
function renderDashboard(sessions) {
|
|
719
787
|
const active = document.activeElement;
|
|
720
788
|
const focusedId =
|
|
@@ -727,10 +795,13 @@
|
|
|
727
795
|
if (sessions.length === 0) {
|
|
728
796
|
els.dashboard.appendChild(el('p', 'empty', 'Nenhuma execução ativa.'));
|
|
729
797
|
els.dashboardMeta.textContent = '0 execuções';
|
|
798
|
+
els.dashboardSummary.textContent = 'Nenhuma execução ativa';
|
|
730
799
|
document.title = 'issue-flow';
|
|
731
800
|
return;
|
|
732
801
|
}
|
|
733
802
|
|
|
803
|
+
renderDashboardSummary(sessions);
|
|
804
|
+
|
|
734
805
|
for (const session of sessions) {
|
|
735
806
|
// <button> só aceita phrasing content — mesmos spans do Kanban.
|
|
736
807
|
const card = el('button', 'dashboard-card');
|
|
@@ -798,7 +869,10 @@
|
|
|
798
869
|
card.appendChild(progress);
|
|
799
870
|
|
|
800
871
|
if (session.status === 'running') {
|
|
801
|
-
|
|
872
|
+
const live = el('span', 'live-label');
|
|
873
|
+
live.appendChild(liveDot());
|
|
874
|
+
live.appendChild(document.createTextNode('ao vivo'));
|
|
875
|
+
card.appendChild(live);
|
|
802
876
|
}
|
|
803
877
|
|
|
804
878
|
card.addEventListener('click', () => {
|
|
@@ -856,8 +930,11 @@
|
|
|
856
930
|
document.title = (prefix + issue + ' · issue-flow').replace(/^ · /, '');
|
|
857
931
|
}
|
|
858
932
|
|
|
933
|
+
// O h1 é a execução — número, título, e ao lado status, tempo decorrido e
|
|
934
|
+
// branch. A marca do produto vive só no <title> do documento.
|
|
859
935
|
function renderHeader(snapshot) {
|
|
860
|
-
|
|
936
|
+
const hasIssue = snapshot.issue.number !== null && snapshot.issue.number !== undefined;
|
|
937
|
+
if (hasIssue) {
|
|
861
938
|
els.issueLink.hidden = false;
|
|
862
939
|
els.issueLink.textContent = '#' + snapshot.issue.number;
|
|
863
940
|
if (snapshot.issue.url) els.issueLink.href = snapshot.issue.url;
|
|
@@ -866,6 +943,10 @@
|
|
|
866
943
|
els.issueLink.hidden = true;
|
|
867
944
|
}
|
|
868
945
|
|
|
946
|
+
// session.json antigo pode não ter título; sem issue vinculada, nem número.
|
|
947
|
+
els.issueHeadline.textContent =
|
|
948
|
+
snapshot.issue.title || (hasIssue ? 'Sem título' : 'Execução sem issue vinculada');
|
|
949
|
+
|
|
869
950
|
const branch = snapshot.git.branch;
|
|
870
951
|
const base = snapshot.git.baseBranch;
|
|
871
952
|
const branchMode =
|
|
@@ -925,23 +1006,13 @@
|
|
|
925
1006
|
clear(els.issueSummary);
|
|
926
1007
|
const issue = snapshot.issue || {};
|
|
927
1008
|
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
el(
|
|
931
|
-
'span',
|
|
932
|
-
'mono',
|
|
933
|
-
issue.number !== null && issue.number !== undefined ? '#' + issue.number : '—',
|
|
934
|
-
),
|
|
935
|
-
);
|
|
936
|
-
heading.appendChild(document.createTextNode(' ' + (issue.title || 'Sem título')));
|
|
937
|
-
els.issueSummary.appendChild(heading);
|
|
938
|
-
|
|
1009
|
+
// Número e título saem no h1 do header (renderHeader); aqui fica só o que
|
|
1010
|
+
// o header não carrega — estado, labels e descrição.
|
|
939
1011
|
const meta = el('div', 'issue-summary-meta');
|
|
940
1012
|
const state = issue.state || null;
|
|
941
1013
|
const stateClass =
|
|
942
1014
|
state === 'open' ? 'state-open' : state === 'closed' ? 'state-closed' : 'state-unknown';
|
|
943
1015
|
meta.appendChild(el('span', 'badge ' + stateClass, state || 'estado desconhecido'));
|
|
944
|
-
meta.appendChild(el('span', 'muted', 'Prioridade: Não definida'));
|
|
945
1016
|
els.issueSummary.appendChild(meta);
|
|
946
1017
|
|
|
947
1018
|
const labels = issue.labels || [];
|
|
@@ -1008,16 +1079,16 @@
|
|
|
1008
1079
|
const messages = {
|
|
1009
1080
|
idle: 'Nenhuma execução em andamento.',
|
|
1010
1081
|
completed: 'Execução concluída.',
|
|
1011
|
-
failed: 'Execução falhou
|
|
1082
|
+
failed: 'Execução falhou. Veja os erros acima.',
|
|
1012
1083
|
};
|
|
1013
1084
|
els.now.appendChild(el('p', 'empty', messages[snapshot.status] || '—'));
|
|
1014
1085
|
return;
|
|
1015
1086
|
}
|
|
1016
1087
|
|
|
1017
1088
|
const grid = el('dl', 'now-grid');
|
|
1018
|
-
const phase = el('dd');
|
|
1089
|
+
const phase = el('dd', 'now-phase');
|
|
1019
1090
|
if (snapshot.currentPhase) {
|
|
1020
|
-
phase.appendChild(
|
|
1091
|
+
phase.appendChild(liveDot());
|
|
1021
1092
|
phase.appendChild(document.createTextNode(snapshot.currentPhase));
|
|
1022
1093
|
} else {
|
|
1023
1094
|
phase.textContent = '—';
|
|
@@ -1027,7 +1098,7 @@
|
|
|
1027
1098
|
|
|
1028
1099
|
const activity = snapshot.currentActivity;
|
|
1029
1100
|
if (activity) {
|
|
1030
|
-
if (activity.story) nowRow(grid, '
|
|
1101
|
+
if (activity.story) nowRow(grid, 'User story', activity.story);
|
|
1031
1102
|
if (activity.tool) nowRow(grid, 'Ferramenta', activity.tool);
|
|
1032
1103
|
if (activity.detail) nowRow(grid, 'Detalhe', activity.detail);
|
|
1033
1104
|
const since = el('dd', null, formatAgo(activity.since));
|
|
@@ -1299,7 +1370,14 @@
|
|
|
1299
1370
|
|
|
1300
1371
|
function fillProviderSelect(select, selected) {
|
|
1301
1372
|
clear(select);
|
|
1302
|
-
const entries = catalog().filter((entry) =>
|
|
1373
|
+
const entries = catalog().filter((entry) => {
|
|
1374
|
+
if (!entry.installed) return false;
|
|
1375
|
+
if (entry.state === 'unavailable') return false;
|
|
1376
|
+
if (entry.authentication === 'failed') return false;
|
|
1377
|
+
return (
|
|
1378
|
+
entry.authenticated !== false || entry.state === 'conditional' || entry.state === 'ready'
|
|
1379
|
+
);
|
|
1380
|
+
});
|
|
1303
1381
|
const providers = entries.length
|
|
1304
1382
|
? entries.map((entry) => entry.provider)
|
|
1305
1383
|
: ['claude', 'codex', 'cursor', 'antigravity'];
|
|
@@ -1364,9 +1442,7 @@
|
|
|
1364
1442
|
for (const phase of snapshot.phases) {
|
|
1365
1443
|
const item = el('li');
|
|
1366
1444
|
item.className = 'detail-row';
|
|
1367
|
-
item.appendChild(
|
|
1368
|
-
el('span', 'item-icon icon-' + phase.status, PHASE_ICONS[phase.status] || '○'),
|
|
1369
|
-
);
|
|
1445
|
+
item.appendChild(statusIcon(phase.status));
|
|
1370
1446
|
const main = el('div', 'item-main');
|
|
1371
1447
|
main.appendChild(el('div', 'item-title', phase.name));
|
|
1372
1448
|
if (phase.error) main.appendChild(el('div', 'item-error', phase.error));
|
|
@@ -1387,19 +1463,17 @@
|
|
|
1387
1463
|
}
|
|
1388
1464
|
}
|
|
1389
1465
|
|
|
1466
|
+
// Uma linha dentro do bloco de estado, não uma lista num cartão próprio: os
|
|
1467
|
+
// passos são poucos e curtos, e o rótulo já vem do HTML (.next-steps-label).
|
|
1390
1468
|
function renderNextSteps(snapshot) {
|
|
1391
1469
|
clear(els.nextSteps);
|
|
1392
1470
|
if (snapshot.nextSteps.length === 0) {
|
|
1393
1471
|
const message =
|
|
1394
1472
|
snapshot.status === 'completed' ? 'Pipeline concluído.' : 'Nenhum passo pendente.';
|
|
1395
|
-
els.nextSteps.appendChild(el('
|
|
1473
|
+
els.nextSteps.appendChild(el('span', 'empty', message));
|
|
1396
1474
|
return;
|
|
1397
1475
|
}
|
|
1398
|
-
|
|
1399
|
-
for (const step of snapshot.nextSteps) {
|
|
1400
|
-
list.appendChild(el('li', null, step));
|
|
1401
|
-
}
|
|
1402
|
-
els.nextSteps.appendChild(list);
|
|
1476
|
+
els.nextSteps.appendChild(el('span', null, snapshot.nextSteps.join(' · ')));
|
|
1403
1477
|
}
|
|
1404
1478
|
|
|
1405
1479
|
function renderStories(snapshot) {
|
|
@@ -1416,7 +1490,7 @@
|
|
|
1416
1490
|
const item = el('li', storyStage === 'executing' ? 'story-executing' : null);
|
|
1417
1491
|
item.classList.add('detail-row');
|
|
1418
1492
|
const status = story.passes ? 'completed' : 'pending';
|
|
1419
|
-
item.appendChild(
|
|
1493
|
+
item.appendChild(statusIcon(status, story.passes ? '✓' : '○'));
|
|
1420
1494
|
const main = el('div', 'item-main');
|
|
1421
1495
|
const title = el('div', 'item-title');
|
|
1422
1496
|
title.appendChild(el('span', 'story-id', story.id));
|
|
@@ -1475,13 +1549,7 @@
|
|
|
1475
1549
|
card.dataset.storyId = story.id;
|
|
1476
1550
|
|
|
1477
1551
|
const head = el('span', 'kanban-card-head');
|
|
1478
|
-
head.appendChild(
|
|
1479
|
-
el(
|
|
1480
|
-
'span',
|
|
1481
|
-
'item-icon icon-' + (story.passes ? 'completed' : 'pending'),
|
|
1482
|
-
story.passes ? '✓' : '○',
|
|
1483
|
-
),
|
|
1484
|
-
);
|
|
1552
|
+
head.appendChild(statusIcon(story.passes ? 'completed' : 'pending', story.passes ? '✓' : '○'));
|
|
1485
1553
|
head.appendChild(el('span', 'story-id', story.id));
|
|
1486
1554
|
card.appendChild(head);
|
|
1487
1555
|
|
|
@@ -1914,7 +1982,7 @@
|
|
|
1914
1982
|
|
|
1915
1983
|
function renderMeta(snapshot) {
|
|
1916
1984
|
const parts = [];
|
|
1917
|
-
if (snapshot.sessionId) parts.push('
|
|
1985
|
+
if (snapshot.sessionId) parts.push('execução ' + snapshot.sessionId);
|
|
1918
1986
|
if (snapshot.updatedAt) parts.push('atualizado ' + formatClock(snapshot.updatedAt));
|
|
1919
1987
|
parts.push('somente leitura');
|
|
1920
1988
|
els.sessionMeta.textContent = parts.join(' · ');
|
|
@@ -1963,6 +2031,8 @@
|
|
|
1963
2031
|
}
|
|
1964
2032
|
|
|
1965
2033
|
async function init() {
|
|
2034
|
+
const tablist = document.querySelector('[role="tablist"]');
|
|
2035
|
+
if (tablist) tablist.addEventListener('keydown', onTabListKeydown);
|
|
1966
2036
|
for (const tab of els.tabs) {
|
|
1967
2037
|
tab.addEventListener('click', () => setActiveTab(tab.id));
|
|
1968
2038
|
}
|
package/web/public/index.html
CHANGED
|
@@ -30,18 +30,18 @@
|
|
|
30
30
|
</head>
|
|
31
31
|
<body>
|
|
32
32
|
<div id="banner-disconnected" class="banner" role="alert" hidden>
|
|
33
|
-
Desconectado do servidor
|
|
33
|
+
Desconectado do servidor. Tentando reconectar…
|
|
34
34
|
</div>
|
|
35
35
|
|
|
36
36
|
<main>
|
|
37
37
|
<div id="view-dashboard" class="view" hidden>
|
|
38
38
|
<header class="card header">
|
|
39
39
|
<div class="header-main">
|
|
40
|
-
<h1>
|
|
41
|
-
|
|
40
|
+
<h1>Execuções ativas</h1>
|
|
41
|
+
<p class="header-meta">
|
|
42
|
+
<span id="dashboard-summary" class="muted"></span>
|
|
42
43
|
<span id="app-version-dashboard" class="app-version" hidden></span>
|
|
43
|
-
</
|
|
44
|
-
<p class="muted">Execuções ativas</p>
|
|
44
|
+
</p>
|
|
45
45
|
</div>
|
|
46
46
|
<div class="header-side">
|
|
47
47
|
<label class="refresh-control">
|
|
@@ -70,12 +70,16 @@
|
|
|
70
70
|
<button type="button" id="back-to-dashboard" class="back-link" hidden>
|
|
71
71
|
← Todas as execuções
|
|
72
72
|
</button>
|
|
73
|
+
<!-- A linha mais visível da tela é a execução, não a marca: o produto
|
|
74
|
+
identifica-se pelo <title> do documento. Ver web/AGENTS.md. -->
|
|
73
75
|
<h1>
|
|
74
|
-
<span class="brand">issue-flow</span>
|
|
75
|
-
<span id="app-version" class="app-version" hidden></span>
|
|
76
76
|
<a id="issue-link" class="issue-link" target="_blank" rel="noopener" hidden>#—</a>
|
|
77
|
+
<span id="issue-headline">Nenhuma execução ativa</span>
|
|
77
78
|
</h1>
|
|
78
|
-
<p
|
|
79
|
+
<p class="header-meta">
|
|
80
|
+
<span id="branch-line" class="muted mono"></span>
|
|
81
|
+
<span id="app-version" class="app-version" hidden></span>
|
|
82
|
+
</p>
|
|
79
83
|
</div>
|
|
80
84
|
<div class="header-side">
|
|
81
85
|
<span id="status-badge" class="badge status-idle">aguardando</span>
|
|
@@ -105,99 +109,124 @@
|
|
|
105
109
|
|
|
106
110
|
<div class="tabs" role="tablist" aria-label="Visualizações do painel">
|
|
107
111
|
<button type="button" class="tab" role="tab" id="tab-execution"
|
|
108
|
-
aria-controls="panel-execution" aria-selected="true">Execução</button>
|
|
112
|
+
aria-controls="panel-execution" aria-selected="true" tabindex="0">Execução</button>
|
|
109
113
|
<button type="button" class="tab" role="tab" id="tab-kanban"
|
|
110
|
-
aria-controls="panel-kanban" aria-selected="false">Kanban</button>
|
|
114
|
+
aria-controls="panel-kanban" aria-selected="false" tabindex="-1">Kanban</button>
|
|
111
115
|
<button type="button" class="tab" role="tab" id="tab-history"
|
|
112
|
-
aria-controls="panel-history" aria-selected="false">Histórico</button>
|
|
116
|
+
aria-controls="panel-history" aria-selected="false" tabindex="-1">Histórico</button>
|
|
113
117
|
</div>
|
|
114
118
|
|
|
119
|
+
<!-- Aba Execução em quatro blocos, na ordem em que a pergunta aparece:
|
|
120
|
+
o que está acontecendo agora, sobre o quê, quanto já andou e o que
|
|
121
|
+
saiu disso. Cada bloco é UM cartão; o que antes era um cartão por
|
|
122
|
+
assunto virou uma sub-seção com <h3> e sem borda própria. -->
|
|
115
123
|
<div class="tab-panel" id="panel-execution" role="tabpanel" aria-labelledby="tab-execution" tabindex="0">
|
|
116
|
-
<div class="columns">
|
|
117
|
-
<section class="card">
|
|
118
|
-
<h2>Resumo da issue</h2>
|
|
119
|
-
<div id="issue-summary"></div>
|
|
120
|
-
</section>
|
|
121
|
-
<section class="card">
|
|
122
|
-
<h2>Repositório</h2>
|
|
123
|
-
<div id="repository"></div>
|
|
124
|
-
</section>
|
|
125
|
-
</div>
|
|
126
124
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
125
|
+
<!-- Estado agora. Primeiro de propósito: é o único bloco que precisa ser
|
|
126
|
+
legível sem rolagem. Não acrescente conteúdo aqui sem checar isso. -->
|
|
127
|
+
<section class="card block block-now">
|
|
128
|
+
<h2>Estado agora</h2>
|
|
129
|
+
|
|
130
|
+
<div aria-live="polite">
|
|
131
|
+
<div class="progress-row">
|
|
132
|
+
<progress id="progress-bar" max="100" value="0"></progress>
|
|
133
|
+
<span id="progress-percent" class="progress-percent">0%</span>
|
|
134
|
+
</div>
|
|
135
|
+
<p id="progress-counters" class="muted"></p>
|
|
132
136
|
</div>
|
|
133
|
-
<p id="progress-counters" class="muted"></p>
|
|
134
|
-
</section>
|
|
135
137
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
<div class="columns">
|
|
139
|
+
<div class="block-part">
|
|
140
|
+
<h3>Executando agora</h3>
|
|
141
|
+
<div id="now" aria-live="polite"></div>
|
|
142
|
+
</div>
|
|
143
|
+
<div class="block-part">
|
|
144
|
+
<h3>Resiliência</h3>
|
|
145
|
+
<div id="resilience" aria-live="polite"></div>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
140
148
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
149
|
+
<p class="next-steps-line">
|
|
150
|
+
<span class="next-steps-label">Próximos passos:</span>
|
|
151
|
+
<span id="next-steps"></span>
|
|
152
|
+
</p>
|
|
144
153
|
</section>
|
|
145
154
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
155
|
+
<!-- Contexto. Estável durante a execução, então densidade menor: é
|
|
156
|
+
referência para quando o estado acima não bastar. -->
|
|
157
|
+
<section class="card block block-context">
|
|
158
|
+
<h2>Contexto</h2>
|
|
159
|
+
|
|
160
|
+
<div class="columns">
|
|
161
|
+
<div class="block-part">
|
|
162
|
+
<h3>Issue</h3>
|
|
163
|
+
<div id="issue-summary"></div>
|
|
164
|
+
</div>
|
|
165
|
+
<div class="block-part">
|
|
166
|
+
<h3>Repositório</h3>
|
|
167
|
+
<div id="repository"></div>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
|
|
171
|
+
<div class="block-part">
|
|
172
|
+
<h3>
|
|
173
|
+
Harnesses e configuração efetiva
|
|
174
|
+
<span class="muted block-hint">versões, preferências futuras e overrides desta execução</span>
|
|
175
|
+
</h3>
|
|
176
|
+
<div id="configuration"></div>
|
|
150
177
|
</div>
|
|
151
|
-
<div id="configuration"></div>
|
|
152
178
|
</section>
|
|
153
179
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
180
|
+
<!-- Andamento: o caminho já percorrido, fase a fase e story a story. -->
|
|
181
|
+
<section class="card block block-progress">
|
|
182
|
+
<h2>Andamento</h2>
|
|
183
|
+
|
|
184
|
+
<div class="block-part">
|
|
185
|
+
<h3>Fases</h3>
|
|
157
186
|
<ol id="phases" class="plain-list"></ol>
|
|
158
|
-
</
|
|
159
|
-
<section class="card">
|
|
160
|
-
<h2>Próximos passos</h2>
|
|
161
|
-
<div id="next-steps"></div>
|
|
162
|
-
</section>
|
|
163
|
-
</div>
|
|
187
|
+
</div>
|
|
164
188
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
189
|
+
<div class="block-part">
|
|
190
|
+
<h3>User stories</h3>
|
|
191
|
+
<ol id="stories" class="plain-list"></ol>
|
|
192
|
+
</div>
|
|
168
193
|
</section>
|
|
169
194
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
<ul id="commits" class="plain-list"></ul>
|
|
174
|
-
</section>
|
|
175
|
-
<section class="card">
|
|
176
|
-
<h2>Pull requests</h2>
|
|
177
|
-
<ul id="pull-requests" class="plain-list"></ul>
|
|
178
|
-
</section>
|
|
179
|
-
</div>
|
|
195
|
+
<!-- Saída: o que a execução produziu no repositório e no log. -->
|
|
196
|
+
<section class="card block block-output">
|
|
197
|
+
<h2>Saída</h2>
|
|
180
198
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
199
|
+
<div class="columns">
|
|
200
|
+
<div class="block-part">
|
|
201
|
+
<h3>Commits</h3>
|
|
202
|
+
<ul id="commits" class="plain-list"></ul>
|
|
203
|
+
</div>
|
|
204
|
+
<div class="block-part">
|
|
205
|
+
<h3>Pull requests</h3>
|
|
206
|
+
<ul id="pull-requests" class="plain-list"></ul>
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
|
|
210
|
+
<div class="block-part">
|
|
211
|
+
<div class="section-head">
|
|
212
|
+
<h3>Logs recentes</h3>
|
|
213
|
+
<label class="log-filter-control">
|
|
214
|
+
Nível
|
|
215
|
+
<select id="log-filter" aria-label="Filtro por nível de log">
|
|
216
|
+
<option value="all">todos</option>
|
|
217
|
+
<option value="info">info</option>
|
|
218
|
+
<option value="warn">avisos</option>
|
|
219
|
+
<option value="error">erros</option>
|
|
220
|
+
</select>
|
|
221
|
+
</label>
|
|
222
|
+
</div>
|
|
223
|
+
<ol id="logs" class="plain-list logs"></ol>
|
|
193
224
|
</div>
|
|
194
|
-
<ol id="logs" class="plain-list logs"></ol>
|
|
195
225
|
</section>
|
|
196
226
|
</div>
|
|
197
227
|
|
|
198
228
|
<div class="tab-panel" id="panel-kanban" role="tabpanel" aria-labelledby="tab-kanban" tabindex="0" hidden>
|
|
199
229
|
<section class="card">
|
|
200
|
-
<h2>Kanban de user stories</h2>
|
|
201
230
|
<div id="kanban" class="kanban"></div>
|
|
202
231
|
</section>
|
|
203
232
|
</div>
|
|
@@ -205,7 +234,6 @@
|
|
|
205
234
|
<div class="tab-panel" id="panel-history" role="tabpanel" aria-labelledby="tab-history" tabindex="0" hidden>
|
|
206
235
|
<section class="card">
|
|
207
236
|
<div class="section-head">
|
|
208
|
-
<h2>Histórico da execução</h2>
|
|
209
237
|
<label class="log-filter-control">
|
|
210
238
|
Evento
|
|
211
239
|
<select id="history-filter" aria-label="Filtro do histórico">
|
|
@@ -229,7 +257,7 @@
|
|
|
229
257
|
<aside id="drawer" class="drawer" role="dialog" aria-modal="true"
|
|
230
258
|
aria-labelledby="drawer-title" tabindex="-1" hidden>
|
|
231
259
|
<div class="drawer-head">
|
|
232
|
-
<h2 id="drawer-title"
|
|
260
|
+
<h2 id="drawer-title"></h2>
|
|
233
261
|
<button type="button" id="drawer-close" class="drawer-close" aria-label="Fechar detalhes">✕</button>
|
|
234
262
|
</div>
|
|
235
263
|
<div id="drawer-body" class="drawer-body"></div>
|