issue-flow 0.12.0 → 0.14.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/README.md +79 -274
- package/dist/agent-OX3KPHE4.js +30 -0
- package/dist/{analyze-OEET7KGE.js → analyze-WIND2SEB.js} +31 -16
- package/dist/analyze-WIND2SEB.js.map +1 -0
- package/dist/{apply-ZIA3C52S.js → apply-JNOI2LR4.js} +18 -7
- package/dist/{apply-ZIA3C52S.js.map → apply-JNOI2LR4.js.map} +1 -1
- package/dist/bench-B4VZON7E.js +910 -0
- package/dist/bench-B4VZON7E.js.map +1 -0
- package/dist/{chunk-FADTCQGM.js → chunk-2IQ6M4ZE.js} +94 -32
- package/dist/chunk-2IQ6M4ZE.js.map +1 -0
- package/dist/chunk-3BWZO4YI.js +141 -0
- package/dist/chunk-3BWZO4YI.js.map +1 -0
- package/dist/chunk-3WFZETIM.js +163 -0
- package/dist/chunk-3WFZETIM.js.map +1 -0
- package/dist/chunk-67YIU2VB.js +362 -0
- package/dist/chunk-67YIU2VB.js.map +1 -0
- package/dist/{chunk-QCPGHBSZ.js → chunk-6BENHR2I.js} +63 -21
- package/dist/chunk-6BENHR2I.js.map +1 -0
- package/dist/{chunk-BYZDPR7N.js → chunk-6QYMSNVA.js} +194 -235
- package/dist/chunk-6QYMSNVA.js.map +1 -0
- package/dist/{chunk-Y5ZDJ4PJ.js → chunk-76MMPJ53.js} +20 -10
- package/dist/chunk-76MMPJ53.js.map +1 -0
- package/dist/chunk-7Z4Y3VKM.js +26 -0
- package/dist/chunk-7Z4Y3VKM.js.map +1 -0
- package/dist/{chunk-3QHK4YQA.js → chunk-B72RQZBV.js} +346 -119
- package/dist/chunk-B72RQZBV.js.map +1 -0
- package/dist/chunk-BEZ5BA5U.js +625 -0
- package/dist/chunk-BEZ5BA5U.js.map +1 -0
- package/dist/chunk-BRCNQXYQ.js +74 -0
- package/dist/chunk-BRCNQXYQ.js.map +1 -0
- package/dist/chunk-CGKLE6WR.js +1203 -0
- package/dist/chunk-CGKLE6WR.js.map +1 -0
- package/dist/chunk-CRGIQFUQ.js +83 -0
- package/dist/chunk-CRGIQFUQ.js.map +1 -0
- package/dist/{chunk-NS6R37II.js → chunk-F6TOYSYY.js} +141 -12
- package/dist/chunk-F6TOYSYY.js.map +1 -0
- package/dist/chunk-G5EK2XRN.js +25 -0
- package/dist/chunk-G5EK2XRN.js.map +1 -0
- package/dist/chunk-HAKDLR6N.js +134 -0
- package/dist/chunk-HAKDLR6N.js.map +1 -0
- package/dist/chunk-J3SOTUVI.js +298 -0
- package/dist/chunk-J3SOTUVI.js.map +1 -0
- package/dist/chunk-JN6D3MY6.js +30 -0
- package/dist/chunk-JN6D3MY6.js.map +1 -0
- package/dist/{chunk-LAFNLLQP.js → chunk-KMRFRXIT.js} +5 -5
- package/dist/chunk-KT7JKOXB.js +72 -0
- package/dist/chunk-KT7JKOXB.js.map +1 -0
- package/dist/chunk-L6HSKTQE.js +77 -0
- package/dist/chunk-L6HSKTQE.js.map +1 -0
- package/dist/chunk-LBGHI4S2.js +1264 -0
- package/dist/chunk-LBGHI4S2.js.map +1 -0
- package/dist/chunk-LEW4QM74.js +180 -0
- package/dist/chunk-LEW4QM74.js.map +1 -0
- package/dist/{chunk-FJP3UQGD.js → chunk-LFEQIZLB.js} +115 -20
- package/dist/chunk-LFEQIZLB.js.map +1 -0
- package/dist/chunk-OOVG2KBY.js +77 -0
- package/dist/chunk-OOVG2KBY.js.map +1 -0
- package/dist/{chunk-56KS3TFQ.js → chunk-OR43364P.js} +115 -160
- package/dist/chunk-OR43364P.js.map +1 -0
- package/dist/chunk-P3LRUNC6.js +16 -0
- package/dist/chunk-P3LRUNC6.js.map +1 -0
- package/dist/{chunk-TSOLWK7Y.js → chunk-PBTA3QQX.js} +8 -7
- package/dist/{chunk-TSOLWK7Y.js.map → chunk-PBTA3QQX.js.map} +1 -1
- package/dist/{chunk-MJULIKQU.js → chunk-PHW2YV6N.js} +23 -47
- package/dist/chunk-PHW2YV6N.js.map +1 -0
- package/dist/{chunk-73ATPIYX.js → chunk-PZTWU277.js} +36 -71
- package/dist/chunk-PZTWU277.js.map +1 -0
- package/dist/{chunk-M5UCXTIT.js → chunk-QD4VOILZ.js} +22 -18
- package/dist/chunk-QD4VOILZ.js.map +1 -0
- package/dist/chunk-QJ5UZAH5.js +2718 -0
- package/dist/chunk-QJ5UZAH5.js.map +1 -0
- package/dist/chunk-QK7WUO7A.js +283 -0
- package/dist/chunk-QK7WUO7A.js.map +1 -0
- package/dist/{chunk-A4SLHOW7.js → chunk-QZVKRDJY.js} +27 -19
- package/dist/chunk-QZVKRDJY.js.map +1 -0
- package/dist/chunk-REO3PXXO.js +71 -0
- package/dist/chunk-REO3PXXO.js.map +1 -0
- package/dist/{chunk-EZATO3DW.js → chunk-RVO7L4FX.js} +594 -17
- package/dist/chunk-RVO7L4FX.js.map +1 -0
- package/dist/{chunk-XQUSIEKA.js → chunk-SZMHCEYV.js} +63 -14
- package/dist/chunk-SZMHCEYV.js.map +1 -0
- package/dist/chunk-TYRNTSPJ.js +243 -0
- package/dist/chunk-TYRNTSPJ.js.map +1 -0
- package/dist/chunk-UJ3L2MS7.js +238 -0
- package/dist/chunk-UJ3L2MS7.js.map +1 -0
- package/dist/chunk-V7MO5EDS.js +120 -0
- package/dist/chunk-V7MO5EDS.js.map +1 -0
- package/dist/chunk-WB2X37MO.js +415 -0
- package/dist/chunk-WB2X37MO.js.map +1 -0
- package/dist/{chunk-NTMMF5UD.js → chunk-X44FKNKP.js} +525 -252
- package/dist/chunk-X44FKNKP.js.map +1 -0
- package/dist/chunk-XEYH6ZZI.js +289 -0
- package/dist/chunk-XEYH6ZZI.js.map +1 -0
- package/dist/cli.js +267 -44
- package/dist/cli.js.map +1 -1
- package/dist/{config-PRJGWW7J.js → config-HMFMCY2M.js} +31 -5
- package/dist/contract-JH2WBIO6.js +8 -0
- package/dist/conventions-NJF2OPVQ.js +146 -0
- package/dist/conventions-NJF2OPVQ.js.map +1 -0
- package/dist/diagnostics-THH42ZMP.js +23 -0
- package/dist/execute-KFNNZZHI.js +35 -0
- package/dist/{generate-EGB4KV2B.js → generate-FW5C6NP7.js} +37 -20
- package/dist/{generate-EGB4KV2B.js.map → generate-FW5C6NP7.js.map} +1 -1
- package/dist/git-W72QQ6GT.js +33 -0
- package/dist/init-HTN4V67Q.js +24 -0
- package/dist/{operations-BYT626KQ.js → operations-BCFOBH42.js} +27 -15
- package/dist/{operations-BYT626KQ.js.map → operations-BCFOBH42.js.map} +1 -1
- package/dist/permissions-4AKNQZHK.js +18 -0
- package/dist/plan-SIZLGA4V.js +34 -0
- package/dist/{policy-6S4GFC76.js → policy-DT6Q6ZVM.js} +20 -6
- package/dist/policy-DT6Q6ZVM.js.map +1 -0
- package/dist/pr-OMWCGUOW.js +39 -0
- package/dist/pr-review-N24CZPKR.js +32 -0
- package/dist/pr-review-N24CZPKR.js.map +1 -0
- package/dist/prd-5OFTZAHJ.js +33 -0
- package/dist/prd-5OFTZAHJ.js.map +1 -0
- package/dist/ps-RFORTCUD.js +93 -0
- package/dist/ps-RFORTCUD.js.map +1 -0
- package/dist/recorder-KCCPMKQG.js +32 -0
- package/dist/recorder-KCCPMKQG.js.map +1 -0
- package/dist/registry-GLPXOMFG.js +15 -0
- package/dist/registry-GLPXOMFG.js.map +1 -0
- package/dist/{resume-PPCNQVVV.js → resume-M5RLKP4I.js} +55 -34
- package/dist/{resume-PPCNQVVV.js.map → resume-M5RLKP4I.js.map} +1 -1
- package/dist/review-YM4JAABY.js +35 -0
- package/dist/review-YM4JAABY.js.map +1 -0
- package/dist/routing-NNT5WKGX.js +94 -0
- package/dist/routing-NNT5WKGX.js.map +1 -0
- package/dist/run-PZUKHPQK.js +54 -0
- package/dist/run-PZUKHPQK.js.map +1 -0
- package/dist/session-publisher-T7EI3CIP.js +11 -0
- package/dist/session-publisher-T7EI3CIP.js.map +1 -0
- package/dist/{state-manager-2EDVDBAR.js → state-manager-GQH32C44.js} +4 -2
- package/dist/state-manager-GQH32C44.js.map +1 -0
- package/dist/usage-V2CHE5PY.js +177 -0
- package/dist/usage-V2CHE5PY.js.map +1 -0
- package/dist/{web-EF5WIIGG.js → web-EA3KGNFN.js} +45 -30
- package/dist/web-EA3KGNFN.js.map +1 -0
- package/package.json +6 -2
- package/prompts/execute.md +7 -8
- package/prompts/plan.md +4 -5
- package/prompts/pr.md +2 -2
- package/web/AGENTS.md +152 -13
- package/web/public/app.css +380 -109
- package/web/public/app.js +525 -25
- package/web/public/index.html +68 -1
- package/dist/analyze-OEET7KGE.js.map +0 -1
- package/dist/chunk-3QHK4YQA.js.map +0 -1
- package/dist/chunk-42PJFMAR.js +0 -605
- package/dist/chunk-42PJFMAR.js.map +0 -1
- package/dist/chunk-4JSOYE4G.js +0 -368
- package/dist/chunk-4JSOYE4G.js.map +0 -1
- package/dist/chunk-56KS3TFQ.js.map +0 -1
- package/dist/chunk-73ATPIYX.js.map +0 -1
- package/dist/chunk-A4SLHOW7.js.map +0 -1
- package/dist/chunk-BAUPQXPE.js +0 -173
- package/dist/chunk-BAUPQXPE.js.map +0 -1
- package/dist/chunk-BYZDPR7N.js.map +0 -1
- package/dist/chunk-CD5ULRVC.js +0 -615
- package/dist/chunk-CD5ULRVC.js.map +0 -1
- package/dist/chunk-DIDUVL6O.js +0 -1365
- package/dist/chunk-DIDUVL6O.js.map +0 -1
- package/dist/chunk-EZATO3DW.js.map +0 -1
- package/dist/chunk-FADTCQGM.js.map +0 -1
- package/dist/chunk-FJP3UQGD.js.map +0 -1
- package/dist/chunk-LWRSMTTZ.js +0 -18
- package/dist/chunk-LWRSMTTZ.js.map +0 -1
- package/dist/chunk-M5UCXTIT.js.map +0 -1
- package/dist/chunk-M66SJHQQ.js +0 -206
- package/dist/chunk-M66SJHQQ.js.map +0 -1
- package/dist/chunk-MJULIKQU.js.map +0 -1
- package/dist/chunk-NS6R37II.js.map +0 -1
- package/dist/chunk-NTMMF5UD.js.map +0 -1
- package/dist/chunk-QCPGHBSZ.js.map +0 -1
- package/dist/chunk-XQUSIEKA.js.map +0 -1
- package/dist/chunk-Y5ZDJ4PJ.js.map +0 -1
- package/dist/execute-XBNVL753.js +0 -18
- package/dist/init-AJHCXJBP.js +0 -10
- package/dist/plan-QOZ3RIO6.js +0 -21
- package/dist/policy-6S4GFC76.js.map +0 -1
- package/dist/pr-EBBHD2ZX.js +0 -26
- package/dist/pr-review-VVMW5PO4.js +0 -20
- package/dist/prd-JQSZF7PR.js +0 -21
- package/dist/review-3F6MARWH.js +0 -19
- package/dist/run-RXE5GDQO.js +0 -36
- package/dist/web-EF5WIIGG.js.map +0 -1
- /package/dist/{config-PRJGWW7J.js.map → agent-OX3KPHE4.js.map} +0 -0
- /package/dist/{chunk-LAFNLLQP.js.map → chunk-KMRFRXIT.js.map} +0 -0
- /package/dist/{execute-XBNVL753.js.map → config-HMFMCY2M.js.map} +0 -0
- /package/dist/{init-AJHCXJBP.js.map → contract-JH2WBIO6.js.map} +0 -0
- /package/dist/{plan-QOZ3RIO6.js.map → diagnostics-THH42ZMP.js.map} +0 -0
- /package/dist/{pr-EBBHD2ZX.js.map → execute-KFNNZZHI.js.map} +0 -0
- /package/dist/{pr-review-VVMW5PO4.js.map → git-W72QQ6GT.js.map} +0 -0
- /package/dist/{prd-JQSZF7PR.js.map → init-HTN4V67Q.js.map} +0 -0
- /package/dist/{review-3F6MARWH.js.map → permissions-4AKNQZHK.js.map} +0 -0
- /package/dist/{run-RXE5GDQO.js.map → plan-SIZLGA4V.js.map} +0 -0
- /package/dist/{state-manager-2EDVDBAR.js.map → pr-OMWCGUOW.js.map} +0 -0
package/web/AGENTS.md
CHANGED
|
@@ -10,7 +10,7 @@ vitest. Toda verificação de mudança aqui é manual, no navegador.
|
|
|
10
10
|
|
|
11
11
|
O painel consome `GET api/sessions` (lista enriquecida para o dashboard),
|
|
12
12
|
`GET api/status` (o `SessionSnapshot` serializado, opcionalmente com
|
|
13
|
-
`?session=<id>`) e `GET api/health`. Ele precisa renderizar **session.json de
|
|
13
|
+
`?session=<id>`), `GET api/events?session=<id>` (journal) e `GET api/health`. Ele precisa renderizar **session.json de
|
|
14
14
|
execuções antigas**, então todo campo pode chegar como `undefined` (não
|
|
15
15
|
existia na versão que gravou o arquivo) além do `null` (existe, não
|
|
16
16
|
informado). Os dois significam "não informado" e nunca podem virar `0`,
|
|
@@ -36,8 +36,8 @@ do snapshot.
|
|
|
36
36
|
|
|
37
37
|
## Abas, Kanban e drawer
|
|
38
38
|
|
|
39
|
-
O painel tem
|
|
40
|
-
|
|
39
|
+
O painel tem três abas ("Execução", "Kanban" e "Histórico") e um único drawer de detalhes
|
|
40
|
+
para fases e stories (inclusive cards do Kanban). Três regras seguram esse conjunto:
|
|
41
41
|
|
|
42
42
|
- **Acesso a story sempre por `getStoryById()` / `getStories()`.** Elas são a
|
|
43
43
|
camada de leitura: normalizam num lugar só o que pode faltar num
|
|
@@ -45,13 +45,13 @@ story. Três regras seguram esse conjunto:
|
|
|
45
45
|
`acceptanceCriteria` → `[]`, `description` → `''`) e são o ponto onde uma
|
|
46
46
|
futura camada de escrita entra. Nenhum consumidor varre `snapshot.stories`
|
|
47
47
|
por conta própria, ou a normalização se espalha.
|
|
48
|
-
- **Estado de UI vive em `state`** (`activeTab`, `
|
|
48
|
+
- **Estado de UI vive em `state`** (`activeTab`, `selectedDetail`), junto de
|
|
49
49
|
`logFilter`, nunca em variável solta ou em referência a nó do DOM. O drawer
|
|
50
50
|
guarda o **id** da story, não o card: `render()` recria o Kanban a cada poll,
|
|
51
51
|
então uma referência guardada na abertura apontaria para um nó fora do
|
|
52
52
|
documento. Pelo mesmo motivo o foco volta ao card via
|
|
53
53
|
`[data-story-id="…"]` no momento de fechar.
|
|
54
|
-
- **`renderKanban()` e `renderDrawer()` são chamadas incondicionalmente** dentro
|
|
54
|
+
- **`renderKanban()`, `renderHistory()` e `renderDrawer()` são chamadas incondicionalmente** dentro
|
|
55
55
|
de `render()`, não ao trocar de aba. Uma aba inativa não pode ficar defasada,
|
|
56
56
|
e é o `renderDrawer()` de cada poll que mantém o drawer aberto em dia (e o
|
|
57
57
|
fecha quando a story some do plano).
|
|
@@ -66,14 +66,153 @@ grid`/`flex` da regra base vence o atributo `hidden`. E o overlay/drawer ficam
|
|
|
66
66
|
em `z-index` 20/21 para cobrir o `.banner` de desconexão, que é `sticky` com
|
|
67
67
|
`z-index: 10`.
|
|
68
68
|
|
|
69
|
-
##
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
69
|
+
## Paleta e tema
|
|
70
|
+
|
|
71
|
+
As cores do `app.css` são **tokens nomeados por papel**, não por local de uso:
|
|
72
|
+
superfície (`--surface-page`, `--surface`, `--surface-sunken`), texto (`--text`,
|
|
73
|
+
`--text-muted`, `--text-subtle`), borda (`--border`, `--border-strong`), acento
|
|
74
|
+
(`--accent`, `--accent-text`), estado (`--state-ok|run|warn|error` e o
|
|
75
|
+
`--state-*-surface` que acompanha cada um) e `--focus-ring`. Um componente novo
|
|
76
|
+
escolhe o papel que já existe em vez de inventar uma cor.
|
|
77
|
+
|
|
78
|
+
**Regra dura: nunca defina uma cor só dentro de um `@media` ou de um
|
|
79
|
+
`[data-theme]`.** `:root` carrega a paleta clara inteira; os blocos escuros
|
|
80
|
+
apenas redefinem o que muda. Um token que só existe num deles some no outro
|
|
81
|
+
tema, e o sintoma aparece longe da causa.
|
|
82
|
+
|
|
83
|
+
O tema escuro vive em **dois blocos gêmeos** com a mesma lista de overrides:
|
|
84
|
+
`@media (prefers-color-scheme: dark) { :root:not([data-theme='light']) { … } }`
|
|
85
|
+
e `:root[data-theme='dark'] { … }`. Mexeu em um, mexa no outro. O guarda
|
|
86
|
+
`:not([data-theme='light'])` é o que faz a escolha manual vencer o sistema nos
|
|
87
|
+
dois sentidos. Cada bloco declara seu próprio `color-scheme` (e `:root`, o
|
|
88
|
+
`light`): é ele — não um `<meta name="color-scheme">`, que foi removido do
|
|
89
|
+
`index.html` justamente por isso — que faz `<select>`, `<progress>` e as barras
|
|
90
|
+
de rolagem acompanharem o tema **efetivo** em vez do tema do SO.
|
|
91
|
+
|
|
92
|
+
O antigo `--idle-bg` (badge inerte, contagem de coluna do Kanban, hover do
|
|
93
|
+
fechar do drawer, trilha da barra de progresso) virou **`--surface-sunken`**, e
|
|
94
|
+
não um `--state-neutral-surface`: metade dos usos não é badge de estado, e um
|
|
95
|
+
recesso neutro é a mesma coisa nos dois casos. "Sem estado" não é um estado.
|
|
96
|
+
|
|
97
|
+
O tema é aplicado **antes do primeiro paint** por um `<script>` inline no
|
|
98
|
+
`<head>` do `index.html`, colocado antes do `<link>` do `app.css`: ele lê
|
|
99
|
+
`issue-flow:theme` do `localStorage` e define `data-theme` na raiz. Fora dali
|
|
100
|
+
o reload piscaria a paleta do SO até o `app.js` rodar. Ele é à prova de
|
|
101
|
+
exceção (`try`/`catch`) e não referencia nada do `app.js` — que só carrega no
|
|
102
|
+
fim do `<body>`. Por isso a leitura da chave é **duplicada** entre os dois, com
|
|
103
|
+
comentário nos dois lugares; mudou o formato do valor, mude nos dois.
|
|
104
|
+
|
|
105
|
+
O controle do tema é um `<select>` de três opções (Sistema/Claro/Escuro)
|
|
106
|
+
**duplicado nos dois headers** (`theme-select` e `theme-select-dashboard`),
|
|
107
|
+
como já acontece com o de intervalo — mudar num reflete no outro via
|
|
108
|
+
`syncThemeSelects()`. As opções são estáticas no HTML (ao contrário de
|
|
109
|
+
`fillRefreshSelect`, que monta as suas); o JS só sincroniza `.value`. A
|
|
110
|
+
preferência vive em `state.theme`, nunca em variável solta, e `'system'`
|
|
111
|
+
**remove** o `data-theme` da raiz em vez de gravar `'system'`: é a ausência do
|
|
112
|
+
atributo que devolve a decisão ao `@media`.
|
|
113
|
+
|
|
114
|
+
A escolha é persistida em `issue-flow:theme` (`'system' | 'light' | 'dark'`)
|
|
115
|
+
por `readStoredTheme()` / `storeTheme()`, que copiam a forma de
|
|
116
|
+
`readStoredRefresh()` / `storeRefresh()`: leitura e escrita em `try`/`catch`,
|
|
117
|
+
valor ausente ou desconhecido caindo para `'system'`, e **nenhum wrapper
|
|
118
|
+
genérico de storage** — duas chaves não justificam uma abstração. Com o
|
|
119
|
+
armazenamento bloqueado o painel carrega no modo sistema e o `<select>`
|
|
120
|
+
continua alternando o tema na sessão; só não sobrevive ao reload.
|
|
121
|
+
|
|
122
|
+
No modo `'system'` — e **só** nele — um listener de
|
|
123
|
+
`matchMedia('(prefers-color-scheme: dark)')` fica anexado, para a troca de tema
|
|
124
|
+
do SO chegar ao painel sem reload; `setTheme()` o desanexa quando a escolha
|
|
125
|
+
passa a ser forçada e o reanexa quando volta a `'system'`. O repaint das cores
|
|
126
|
+
em si é do `@media`, que o navegador reavalia sozinho: o listener sincroniza o
|
|
127
|
+
lado JS (raiz e o `.value` dos seletores).
|
|
128
|
+
|
|
129
|
+
Consequência para o servidor: `baseHeaders()` em `src/web/server.ts` hoje não
|
|
130
|
+
define `Content-Security-Policy`. Se um CSP for adicionado, ele precisa
|
|
131
|
+
contemplar esse script inline (`'unsafe-inline'` em `script-src` ou, melhor, um
|
|
132
|
+
hash/nonce), senão o painel volta a piscar — e um `script-src` estrito sem essa
|
|
133
|
+
provisão quebra a aplicação do tema silenciosamente.
|
|
134
|
+
|
|
135
|
+
### Contraste: os pares medidos
|
|
136
|
+
|
|
137
|
+
Os valores abaixo são calculados (WCAG 2.x, luminância relativa), não estimados
|
|
138
|
+
no olho. **Trocar qualquer um destes tokens exige recalcular a linha
|
|
139
|
+
correspondente** — a maior parte da paleta clara passa com pouca folga.
|
|
140
|
+
|
|
141
|
+
| Frente | Fundo | Mínimo | Claro | Escuro |
|
|
142
|
+
| --------------- | ----------------------- | ------ | ----- | ------ |
|
|
143
|
+
| `--text` | `--surface-page` | 4,5:1 | 15,17 | 15,40 |
|
|
144
|
+
| `--text` | `--surface` | 4,5:1 | 16,55 | 14,04 |
|
|
145
|
+
| `--text` | `--surface-sunken` | 4,5:1 | 13,36 | 11,38 |
|
|
146
|
+
| `--text-muted` | `--surface-page` | 4,5:1 | 6,93 | 7,21 |
|
|
147
|
+
| `--text-muted` | `--surface` | 4,5:1 | 7,56 | 6,58 |
|
|
148
|
+
| `--text-muted` | `--surface-sunken` | 4,5:1 | 6,10 | 5,33 |
|
|
149
|
+
| `--text-subtle` | `--surface-page` | 4,5:1 | 5,24 | 6,37 |
|
|
150
|
+
| `--text-subtle` | `--surface` | 4,5:1 | 5,72 | 5,81 |
|
|
151
|
+
| `--text-subtle` | `--surface-sunken` | 4,5:1 | 4,62 | 4,71 |
|
|
152
|
+
| `--state-ok` | `--state-ok-surface` | 4,5:1 | 4,57 | 8,19 |
|
|
153
|
+
| `--state-run` | `--state-run-surface` | 4,5:1 | 5,49 | 5,68 |
|
|
154
|
+
| `--state-warn` | `--state-warn-surface` | 4,5:1 | 4,51 | 8,05 |
|
|
155
|
+
| `--state-error` | `--state-error-surface` | 4,5:1 | 5,30 | 5,63 |
|
|
156
|
+
| `--focus-ring` | `--surface-page` | 3:1 | 5,76 | 6,29 |
|
|
157
|
+
| `--focus-ring` | `--surface` | 3:1 | 6,29 | 5,73 |
|
|
158
|
+
| `--focus-ring` | `--surface-sunken` | 3:1 | 5,08 | 4,65 |
|
|
159
|
+
| `--accent-text` | `--accent` | 4,5:1 | 6,29 | 6,29 |
|
|
160
|
+
| `--accent-text` | `--state-error` | 4,5:1 | 6,47 | 6,78 |
|
|
161
|
+
|
|
162
|
+
O limiar dos badges de estado é **4,5:1 e não 3:1** porque `.badge` é
|
|
163
|
+
`font-size: 0.78rem; font-weight: 600` — abaixo do que a WCAG chama de texto
|
|
164
|
+
grande. Já `--focus-ring` é um componente gráfico, não texto: 3:1 basta.
|
|
165
|
+
|
|
166
|
+
No tema claro as quatro cores de estado ficam no nível 700 da escala — é o tom
|
|
167
|
+
mais claro que ainda atende 4,5:1 sobre a superfície do próprio badge; `--state-ok`
|
|
168
|
+
(4,57) e `--state-warn` (4,51) passam por pouco. No tema escuro os
|
|
169
|
+
preenchimentos sólidos são claros, então `--accent-text` inverte para
|
|
170
|
+
`#0f1218`: era branco sobre `--state-error` no banner de desconexão, 2,98:1.
|
|
171
|
+
|
|
172
|
+
Hover e foco por teclado precisam ser **distinguíveis um do outro**. Em
|
|
173
|
+
`.dashboard-card` o hover só acende a borda (`--accent`); o foco desenha
|
|
174
|
+
`outline: 2px solid var(--focus-ring)` com `outline-offset: 2px`, que continua
|
|
175
|
+
visível em cima do hover. É a única regra do `app.css` que mexe em `outline` —
|
|
176
|
+
antes ela o suprimia e dava a mesma aparência aos dois estados.
|
|
177
|
+
|
|
178
|
+
## Como verificar uma mudança aqui
|
|
179
|
+
|
|
180
|
+
Nada nesta pasta é coberto por teste automatizado, então a verificação é um
|
|
181
|
+
navegador com **dados de verdade**. `python3 -m http.server` dentro de
|
|
182
|
+
`web/public/` basta para inspecionar o CSS, mas o painel fica no estado
|
|
183
|
+
desconectado — não dá para exercitar alertas, Kanban, drawer nem métricas.
|
|
184
|
+
|
|
185
|
+
Para isso, sirva o servidor real de um `ISSUE_FLOW_HOME` descartável:
|
|
186
|
+
|
|
187
|
+
1. Escreva um ou mais `session.json` (o schema é `sessionSnapshotSchema` em
|
|
188
|
+
`src/schemas.ts`) em `<home>/projects/<projeto>/issues/<n>/session.json`,
|
|
189
|
+
com `events.jsonl` ao lado no formato `{ seq, event }` da aba Histórico.
|
|
190
|
+
**Duas** sessões abrem o dashboard; uma só abre direto no detalhe.
|
|
191
|
+
2. `npm run build` e `ISSUE_FLOW_HOME=<home> node dist/cli.js web serve --port
|
|
192
|
+
<p> --host 127.0.0.1`. O servidor lê os assets de `web/public/` (não uma
|
|
193
|
+
cópia em `dist/`), então basta reiniciá-lo para pegar uma edição.
|
|
194
|
+
3. Uma sessão some após **90s** sem heartbeat: `touch` periódico no
|
|
195
|
+
`session.json` a mantém viva pelo tempo da verificação.
|
|
196
|
+
|
|
197
|
+
Os estados que só aparecem sob condição se forçam do console: o `.banner` de
|
|
198
|
+
desconexão, substituindo `window.fetch` por um que rejeita (e restaurando
|
|
199
|
+
depois); o armazenamento bloqueado, com um `Object.defineProperty(window,
|
|
200
|
+
'localStorage', { get() { throw … } })` num script de inicialização; a troca de
|
|
201
|
+
tema do SO, pela emulação de `prefers-color-scheme` do DevTools, que dispara o
|
|
202
|
+
evento `change` real da media query.
|
|
203
|
+
|
|
204
|
+
Para contraste, **meça na página** (ler os tokens com
|
|
205
|
+
`getComputedStyle(document.documentElement)` e calcular a razão em JS), nunca a
|
|
206
|
+
partir dos valores no arquivo: só assim a cascata resolvida aparece, incluindo
|
|
207
|
+
o token que um tema herda do outro por engano.
|
|
208
|
+
|
|
209
|
+
## Escrita limitada a preferências futuras
|
|
210
|
+
|
|
211
|
+
O estado de execução continua somente leitura (`snapshot.readOnly === true`). A
|
|
212
|
+
única mutação é `POST /api/config/agent`, que salva preferência global para
|
|
213
|
+
execuções **futuras**, aparece via capability e só funciona em loopback. Nunca
|
|
214
|
+
inferir permissão pela versão: o client renderiza o formulário apenas quando
|
|
215
|
+
`/api/health.capabilities` contém `config:agent:write`.
|
|
77
216
|
|
|
78
217
|
Quando essa etapa chegar, o contrato esperado é: as rotas de escrita passam a
|
|
79
218
|
ser anunciadas em `capabilities` (o client decide o que renderizar a partir
|