Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36178df130 | ||
|
|
be281d1d04 | ||
|
|
ed69db4d59 | ||
|
|
cfea07a179 | ||
|
|
9f1bc289ee | ||
|
|
e20a52d3b2 | ||
|
|
3d3ec9d47c | ||
|
|
0abbbf5abd | ||
|
|
9598172174 | ||
|
|
68943221c3 | ||
|
|
d27c0f9567 | ||
|
|
0658f7007e | ||
|
|
14b342c3a2 | ||
|
|
4010a9bba5 | ||
|
|
caaa1de8a0 | ||
|
|
6ba1a6a77f | ||
|
|
66f3172bfb | ||
|
|
cf63aa6d22 | ||
|
|
5d8c5d487d | ||
|
|
b13129b7d8 | ||
|
|
975885753a |
@@ -126,8 +126,7 @@ Para detalhes completos sobre arquitetura, protocolos e manuais técnicos, acess
|
|||||||
* 🩺 **[Diagnóstico e Troubleshooting](docs/diagnostics.md):** Auto-teste e guia de resolução de problemas.
|
* 🩺 **[Diagnóstico e Troubleshooting](docs/diagnostics.md):** Auto-teste e guia de resolução de problemas.
|
||||||
* 💿 **[Instalação e Empacotamento Nativo](docs/installation.md):** Distribuição via `jpackage` sem JRE pré-instalado.
|
* 💿 **[Instalação e Empacotamento Nativo](docs/installation.md):** Distribuição via `jpackage` sem JRE pré-instalado.
|
||||||
* ⚖️ **[Limitações Técnicas](docs/limitations.md):** Declaração de dependências conforme a Regra 23.
|
* ⚖️ **[Limitações Técnicas](docs/limitations.md):** Declaração de dependências conforme a Regra 23.
|
||||||
* 🛠️ **[Modo de Desenvolvimento](docs/development-mode.md):** Parâmetro `--dev` e certificados de teste.
|
* 🎨 **[Layout e Assinatura em Lote](docs/ui-layout.md):** Design da UI padronizada e operações em lote.
|
||||||
* 🎨 **[Layout Dual-Theme e Assinatura em Lote](docs/serpro-layout-and-batch.md):** Design da UI e operações em lote.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,6 @@ assets/
|
|||||||
│ ├── tutorial.svg # Ícone Tutorial e Ajuda (monitor com vídeo/play)
|
│ ├── tutorial.svg # Ícone Tutorial e Ajuda (monitor com vídeo/play)
|
||||||
│ ├── cert.svg # Ícone Certificados Digitais (pasta com cadeado seguro)
|
│ ├── cert.svg # Ícone Certificados Digitais (pasta com cadeado seguro)
|
||||||
│ ├── about.svg # Ícone Sobre o Sistema (círculo azul com 'i')
|
│ ├── about.svg # Ícone Sobre o Sistema (círculo azul com 'i')
|
||||||
│ ├── sun.svg # Ícone Tema Claro (sol dourado com raios vetoriais)
|
|
||||||
│ ├── moon.svg # Ícone Tema Escuro (lua crescente azul/índigo)
|
|
||||||
│ ├── shield.svg # Ícone Brasão de Segurança (CyberShield com chave interna)
|
│ ├── shield.svg # Ícone Brasão de Segurança (CyberShield com chave interna)
|
||||||
│ ├── seal.svg # Ícone Designer e Gerenciador de Selos Notariais
|
│ ├── seal.svg # Ícone Designer e Gerenciador de Selos Notariais
|
||||||
│ ├── check.svg # Ícone Checkmark de sucesso e validação
|
│ ├── check.svg # Ícone Checkmark de sucesso e validação
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<!-- Círculo azul e letra 'i' informativa centrada em (24, 24) -->
|
<circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" stroke-width="2"/>
|
||||||
<circle cx="24" cy="24" r="18" fill="#0288d1"/>
|
<circle cx="12" cy="7.5" r="1.2" fill="currentColor"/>
|
||||||
<circle cx="24" cy="15" r="2.5" fill="#ffffff"/>
|
<line x1="12" y1="11" x2="12" y2="16.5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||||
<line x1="24" y1="21" x2="24" y2="33" stroke="#ffffff" stroke-width="4.5" stroke-linecap="round"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 362 B After Width: | Height: | Size: 338 B |
@@ -1,3 +1,3 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<path d="M 8 5 L 15 12 L 8 19" fill="none" stroke="#ffffff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
<path d="M 9 5 L 16 12 L 9 19" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 221 B After Width: | Height: | Size: 224 B |
@@ -1,12 +1,4 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<!-- Folha de documento com dobra -->
|
<path d="M 16.5 6.5 L 9.5 13.5 C 8.1 14.9 8.1 17.1 9.5 18.5 C 10.9 19.9 13.1 19.9 14.5 18.5 L 20 13 C 22.2 10.8 22.2 7.2 20 5 C 17.8 2.8 14.2 2.8 12 5 L 6.5 10.5 C 3.7 13.3 3.7 17.7 6.5 20.5 C 9.3 23.3 13.7 23.3 16.5 20.5 L 20 17"
|
||||||
<path d="M 10 4 L 34 4 L 42 12 L 42 44 L 10 44 Z" fill="#ffffff" stroke="#0288d1" stroke-width="2.5" stroke-linejoin="round"/>
|
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
<path d="M 34 4 L 34 12 L 42 12 Z" fill="#b3e5fc" stroke="#0288d1" stroke-width="2"/>
|
|
||||||
<!-- Linhas de texto representativas -->
|
|
||||||
<line x1="16" y1="20" x2="36" y2="20" stroke="#90caf9" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="16" y1="26" x2="36" y2="26" stroke="#90caf9" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="16" y1="32" x2="30" y2="32" stroke="#90caf9" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<!-- Clipe de papel metálico prendendo a folha -->
|
|
||||||
<path d="M 12 2 C 8 2 6 5 6 9 L 6 25 C 6 29 9 32 14 32 C 18 32 21 29 21 25 L 21 11 C 21 8 19 6 16 6 C 13 6 12 8 12 11 L 12 24"
|
|
||||||
fill="none" stroke="#455a64" stroke-width="2.8" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 976 B After Width: | Height: | Size: 433 B |
@@ -1,13 +1,6 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<!-- Folha traseira (3) -->
|
<path d="M 3.5 7.5 H 16.5 V 20.5 H 3.5 Z" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
||||||
<path d="M 16 4 L 38 4 L 44 10 L 44 32 L 16 32 Z" fill="#e1f5fe" stroke="#4fc3f7" stroke-width="2" stroke-linejoin="round"/>
|
<path d="M 7.5 4.5 H 20.5 V 17.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
||||||
<!-- Folha do meio (2) -->
|
<line x1="7" y1="11.5" x2="13" y2="11.5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
|
||||||
<path d="M 10 10 L 32 10 L 38 16 L 38 38 L 10 38 Z" fill="#b3e5fc" stroke="#0288d1" stroke-width="2" stroke-linejoin="round"/>
|
<line x1="7" y1="15.5" x2="11" y2="15.5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
|
||||||
<!-- Folha frontal (1) -->
|
|
||||||
<path d="M 4 16 L 26 16 L 32 22 L 32 44 L 4 44 Z" fill="#ffffff" stroke="#01579b" stroke-width="2.2" stroke-linejoin="round"/>
|
|
||||||
<!-- Dobra folha frontal -->
|
|
||||||
<path d="M 26 16 L 26 22 L 32 22 Z" fill="#e1f5fe" stroke="#01579b" stroke-width="1.8"/>
|
|
||||||
<!-- Checkmark de execução/lote concluído -->
|
|
||||||
<circle cx="34" cy="34" r="10" fill="#00c853"/>
|
|
||||||
<path d="M 29 34 L 33 38 L 39 30" fill="none" stroke="#ffffff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 920 B After Width: | Height: | Size: 543 B |
@@ -1,11 +1,7 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<!-- Pasta de arquivos de certificados -->
|
<rect x="3.5" y="2.5" width="17" height="19" rx="2.5" fill="none" stroke="currentColor" stroke-width="2"/>
|
||||||
<path d="M 4 10 L 16 10 L 20 14 L 44 14 L 44 40 L 4 40 Z" fill="#e1f5fe" stroke="#0288d1" stroke-width="2.5" stroke-linejoin="round"/>
|
<line x1="7" y1="6.5" x2="13" y2="6.5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
|
||||||
<!-- Aba superior da pasta -->
|
<line x1="7" y1="9.5" x2="11" y2="9.5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" opacity="0.6"/>
|
||||||
<path d="M 4 18 L 44 18" stroke="#0288d1" stroke-width="2"/>
|
<circle cx="14.5" cy="14.5" r="3.5" fill="none" stroke="#0288d1" stroke-width="1.8"/>
|
||||||
<!-- Cadeado de segurança dourado/azul -->
|
<path d="M 13 14.5 L 14.2 15.7 L 16.5 13.2" fill="none" stroke="#0288d1" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
<rect x="26" y="24" width="14" height="12" rx="3" fill="#0288d1"/>
|
|
||||||
<!-- Arco do cadeado -->
|
|
||||||
<path d="M 29 24 L 29 20 C 29 17 31 15 33 15 C 35 15 37 17 37 20 L 37 24" fill="none" stroke="#01579b" stroke-width="2.5"/>
|
|
||||||
<circle cx="33" cy="30" r="1.8" fill="#ffffff"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 688 B After Width: | Height: | Size: 659 B |
@@ -1,4 +1,4 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<circle cx="24" cy="24" r="20" fill="#10b981"/>
|
<circle cx="12" cy="12" r="9" fill="#10b981"/>
|
||||||
<path d="M 14 24 L 21 31 L 34 17" fill="none" stroke="#ffffff" stroke-width="4.5" stroke-linecap="round" stroke-linejoin="round"/>
|
<path d="M 8 12 L 11 15 L 16 9" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 274 B After Width: | Height: | Size: 269 B |
@@ -1,3 +1,3 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<path d="M 6 6 L 18 18 M 18 6 L 6 18" fill="none" stroke="#64748b" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
<path d="M 6 6 L 18 18 M 18 6 L 6 18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 228 B After Width: | Height: | Size: 231 B |
@@ -1,12 +1,6 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<!-- Folha secundária -->
|
<path d="M 13 3.5 H 5 C 4.4 3.5 4 3.9 4 4.5 V 20.5 C 4 21.1 4.4 21.5 5 21.5 H 17 C 17.6 21.5 18 21.1 18 20.5 V 13"
|
||||||
<path d="M 6 10 L 26 10 L 32 16 L 32 40 L 6 40 Z" fill="#ffffff" stroke="#1976d2" stroke-width="2.2" stroke-linejoin="round"/>
|
fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
||||||
<!-- Folha primária -->
|
<path d="M 12 18 C 14 18 16 17 18 15" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
|
||||||
<path d="M 16 4 L 36 4 L 42 10 L 42 34 L 16 34 Z" fill="#e3f2fd" stroke="#0288d1" stroke-width="2.2" stroke-linejoin="round"/>
|
<path d="M 18.5 3.5 L 21 6 L 14 13 L 11.5 13.5 L 12 11 Z" fill="none" stroke="#0288d1" stroke-width="1.8" stroke-linejoin="round"/>
|
||||||
<path d="M 36 4 L 36 10 L 42 10 Z" fill="#bbdefb" stroke="#0288d1" stroke-width="1.8"/>
|
|
||||||
<!-- Ícone de dois usuários / parceiros de assinatura -->
|
|
||||||
<circle cx="16" cy="24" r="4" fill="#0d47a1"/>
|
|
||||||
<path d="M 10 36 C 10 31 13 30 16 30 C 19 30 22 31 22 36 Z" fill="#0d47a1"/>
|
|
||||||
<circle cx="26" cy="22" r="3.5" fill="#0288d1"/>
|
|
||||||
<path d="M 21 34 C 21 30 24 29 26 29 C 29 29 32 30 32 34 Z" fill="#0288d1"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 815 B After Width: | Height: | Size: 546 B |
@@ -1,5 +1,5 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<circle cx="24" cy="24" r="20" fill="#ef4444"/>
|
<circle cx="12" cy="12" r="9" fill="#ef4444"/>
|
||||||
<line x1="16" y1="16" x2="32" y2="32" stroke="#ffffff" stroke-width="4.5" stroke-linecap="round"/>
|
<line x1="8.5" y1="8.5" x2="15.5" y2="15.5" stroke="#ffffff" stroke-width="2" stroke-linecap="round"/>
|
||||||
<line x1="32" y1="16" x2="16" y2="32" stroke="#ffffff" stroke-width="4.5" stroke-linecap="round"/>
|
<line x1="15.5" y1="8.5" x2="8.5" y2="15.5" stroke="#ffffff" stroke-width="2" stroke-linecap="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 343 B After Width: | Height: | Size: 350 B |
@@ -1,8 +1,6 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<!-- Porta de saída / batente -->
|
<path d="M 10 3.5 H 5.5 C 4.4 3.5 3.5 4.4 3.5 5.5 V 18.5 C 3.5 19.6 4.4 20.5 5.5 20.5 H 10"
|
||||||
<path d="M 24 8 L 12 8 C 9.8 8 8 9.8 8 12 L 8 36 C 8 38.2 9.8 40 12 40 L 24 40"
|
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
fill="none" stroke="#546e7a" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
<line x1="8.5" y1="12" x2="20.5" y2="12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/>
|
||||||
<!-- Seta vermelha apontando para fora -->
|
<polyline points="16.5 8, 20.5 12, 16.5 16" fill="none" stroke="#ef4444" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
<line x1="18" y1="24" x2="40" y2="24" stroke="#d32f2f" stroke-width="3.2" stroke-linecap="round"/>
|
|
||||||
<polyline points="32 16, 40 24, 32 32" fill="none" stroke="#d32f2f" stroke-width="3.2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 598 B After Width: | Height: | Size: 537 B |
@@ -1,13 +1,7 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<!-- Folha de documento com dobra -->
|
<path d="M 5 3.5 H 14 L 19 8.5 V 20.5 C 19 21.1 18.5 21.5 17.9 21.5 H 5 C 4.4 21.5 4 21.1 4 20.5 V 4.5 C 4 3.9 4.4 3.5 5 3.5 Z"
|
||||||
<path d="M 8 4 L 32 4 L 42 14 L 42 44 L 8 44 Z" fill="#ffffff" stroke="#0288d1" stroke-width="2.5" stroke-linejoin="round"/>
|
fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
||||||
<!-- Dobra de canto -->
|
<path d="M 14 3.5 V 8.5 H 19" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
||||||
<path d="M 32 4 L 32 14 L 42 14 Z" fill="#b3e5fc" stroke="#0288d1" stroke-width="2" stroke-linejoin="round"/>
|
<line x1="8" y1="13" x2="15" y2="13" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
|
||||||
<!-- Linhas de texto representativas -->
|
<line x1="8" y1="17" x2="13" y2="17" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
|
||||||
<line x1="14" y1="20" x2="34" y2="20" stroke="#cfd8dc" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="14" y1="26" x2="34" y2="26" stroke="#cfd8dc" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<!-- Caneta / Pena de assinatura eletrônica -->
|
|
||||||
<path d="M 16 38 L 30 24 L 35 29 L 21 43 L 14 44 Z" fill="#0288d1"/>
|
|
||||||
<path d="M 30 24 L 34 20 L 39 25 L 35 29 Z" fill="#01579b"/>
|
|
||||||
<circle cx="16" cy="42" r="1.5" fill="#ffffff"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 873 B After Width: | Height: | Size: 626 B |
@@ -1,13 +1,6 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<!-- Janela de terminal / console -->
|
<rect x="2.5" y="3.5" width="19" height="17" rx="3" fill="none" stroke="currentColor" stroke-width="2"/>
|
||||||
<rect x="4" y="6" width="40" height="36" rx="6" fill="#1e293b" stroke="#334155" stroke-width="2"/>
|
<path d="M 6.5 8.5 L 9.5 11 L 6.5 13.5" fill="none" stroke="#0288d1" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
<!-- Barra de título do terminal com botões -->
|
<line x1="11.5" y1="13.5" x2="16.5" y2="13.5" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||||
<circle cx="10" cy="12" r="2" fill="#ef4444"/>
|
<line x1="6.5" y1="16.5" x2="14" y2="16.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
<circle cx="16" cy="12" r="2" fill="#f59e0b"/>
|
|
||||||
<circle cx="22" cy="12" r="2" fill="#10b981"/>
|
|
||||||
<!-- Linha divisória -->
|
|
||||||
<line x1="4" y1="18" x2="44" y2="18" stroke="#334155" stroke-width="1.5"/>
|
|
||||||
<!-- Texto e prompt de código </> -->
|
|
||||||
<path d="M 12 24 L 18 29 L 12 34" fill="none" stroke="#38bdf8" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
<line x1="22" y1="34" x2="32" y2="34" stroke="#94a3b8" stroke-width="2.5" stroke-linecap="round"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 811 B After Width: | Height: | Size: 572 B |
@@ -1,11 +1,6 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<!-- Folha de documento com dobra -->
|
<path d="M 5 3.5 H 14 L 19 8.5 V 20.5 C 19 21.1 18.5 21.5 17.9 21.5 H 5 C 4.4 21.5 4 21.1 4 20.5 V 4.5 C 4 3.9 4.4 3.5 5 3.5 Z"
|
||||||
<path d="M 8 4 L 30 4 L 38 12 L 38 34 L 8 34 Z" fill="#ffffff" stroke="#00838f" stroke-width="2.5" stroke-linejoin="round"/>
|
fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
||||||
<path d="M 30 4 L 30 12 L 38 12 Z" fill="#b2dfdb" stroke="#00838f" stroke-width="2"/>
|
<circle cx="14" cy="15" r="4.5" fill="none" stroke="#0288d1" stroke-width="1.8"/>
|
||||||
<!-- Linhas no documento -->
|
<polyline points="14 12.5, 14 15, 16 15" fill="none" stroke="#0288d1" stroke-width="1.8" stroke-linecap="round"/>
|
||||||
<line x1="14" y1="18" x2="30" y2="18" stroke="#cfd8dc" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="14" y1="24" x2="24" y2="24" stroke="#cfd8dc" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<!-- Selo LTV com fita de proteção permanente -->
|
|
||||||
<circle cx="32" cy="32" r="12" fill="#e0f2f1" stroke="#00897b" stroke-width="2.5"/>
|
|
||||||
<text x="32" y="36.5" font-family="Segoe UI, Arial, sans-serif" font-size="9" font-weight="bold" fill="#00695c" text-anchor="middle">LTV</text>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 861 B After Width: | Height: | Size: 507 B |
@@ -1,6 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
|
||||||
<!-- Lua crescente ciano/índigo perfeitamente centrada em (24, 24) -->
|
|
||||||
<path d="M 24 8 A 16 16 0 1 0 40 24 A 14 14 0 0 1 24 8 Z" fill="#38bdf8"/>
|
|
||||||
<circle cx="34" cy="14" r="1.8" fill="#818cf8"/>
|
|
||||||
<circle cx="38" cy="22" r="1.2" fill="#818cf8"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 344 B |
@@ -1,13 +1,7 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<!-- Folha de documento com dobra -->
|
<path d="M 5 3.5 H 14 L 19 8.5 V 20.5 C 19 21.1 18.5 21.5 17.9 21.5 H 5 C 4.4 21.5 4 21.1 4 20.5 V 4.5 C 4 3.9 4.4 3.5 5 3.5 Z"
|
||||||
<path d="M 8 4 L 32 4 L 42 14 L 42 44 L 8 44 Z" fill="#ffffff" stroke="#d32f2f" stroke-width="2.5" stroke-linejoin="round"/>
|
fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
||||||
<!-- Dobra de canto -->
|
<path d="M 14 3.5 V 8.5 H 19" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
||||||
<path d="M 32 4 L 32 14 L 42 14 Z" fill="#ffcdd2" stroke="#d32f2f" stroke-width="2" stroke-linejoin="round"/>
|
<rect x="7" y="12" width="10" height="6" rx="1.5" fill="#ef4444"/>
|
||||||
<!-- Linhas de texto representativas -->
|
<path d="M 9 14 H 10.5 C 11 14 11.5 14.3 11.5 14.8 C 11.5 15.3 11 15.6 10.5 15.6 H 9 V 17 M 9 15.6 H 10.5" stroke="#ffffff" stroke-width="1" stroke-linecap="round" fill="none"/>
|
||||||
<line x1="13" y1="18" x2="27" y2="18" stroke="#b0bec5" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="13" y1="22" x2="35" y2="22" stroke="#b0bec5" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="13" y1="26" x2="35" y2="26" stroke="#b0bec5" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<!-- Etiqueta vermelha com sigla PDF perfeitamente centralizada -->
|
|
||||||
<rect x="11" y="31" width="28" height="11" rx="3" fill="#d32f2f"/>
|
|
||||||
<text x="25" y="36.5" font-family="'Segoe UI', Arial, sans-serif" font-size="8.5px" font-weight="bold" fill="#ffffff" text-anchor="middle" dominant-baseline="central">PDF</text>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 665 B |
@@ -1,5 +1,5 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<path d="M 20 4 L 20 9 L 15 9" fill="none" stroke="#0284c7" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
|
<path d="M 19 4 L 19 9 L 14 9" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
<path d="M 4 20 L 4 15 L 9 15" fill="none" stroke="#0284c7" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
|
<path d="M 5 20 L 5 15 L 10 15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
<path d="M 5.8 9 A 7 7 0 0 1 18.5 7.5 L 20 9 M 4 15 L 5.5 16.5 A 7 7 0 0 0 18.2 15" fill="none" stroke="#0284c7" stroke-width="2.2" stroke-linecap="round"/>
|
<path d="M 6.3 8.7 A 8 8 0 0 1 18.5 7.5 L 19 9 M 5 15 L 5.5 16.5 A 8 8 0 0 0 17.7 15.3" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 510 B After Width: | Height: | Size: 524 B |
@@ -0,0 +1,7 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
|
<path d="M 12 2.5 L 4.5 5.5 V 11 C 4.5 16 8 20 12 21.5 C 16 20 19.5 16 19.5 11 V 5.5 Z"
|
||||||
|
fill="none" stroke="#0288d1" stroke-width="2" stroke-linejoin="round"/>
|
||||||
|
<rect x="8.5" y="11" width="7" height="5.5" rx="1.2" fill="#0288d1"/>
|
||||||
|
<path d="M 10 11 V 8.5 C 10 7.4 10.9 6.5 12 6.5 C 13.1 6.5 14 7.4 14 8.5 V 11" fill="none" stroke="#0288d1" stroke-width="1.8" stroke-linecap="round"/>
|
||||||
|
<circle cx="12" cy="13.5" r="0.8" fill="#ffffff"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 541 B |
@@ -1,11 +1,7 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<!-- Fita pendente do selo -->
|
<rect x="2.5" y="4.5" width="19" height="15" rx="3" fill="none" stroke="currentColor" stroke-width="2"/>
|
||||||
<polygon points="18,30 24,44 30,30" fill="#f59e0b"/>
|
<path d="M 6 13.5 C 7.5 10 9 10 10.5 13 C 11.5 15 13 11 15 12" fill="none" stroke="#0288d1" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
<polygon points="12,30 16,42 20,30" fill="#d97706"/>
|
<circle cx="17.5" cy="8.5" r="1.5" fill="#f59e0b"/>
|
||||||
<polygon points="28,30 32,42 36,30" fill="#d97706"/>
|
<line x1="6" y1="8.5" x2="13" y2="8.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.5"/>
|
||||||
<!-- Medalha / Selo circular -->
|
<line x1="6" y1="16" x2="18" y2="16" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.4"/>
|
||||||
<circle cx="24" cy="20" r="16" fill="#d97706" stroke="#fbbf24" stroke-width="2.5"/>
|
|
||||||
<circle cx="24" cy="20" r="13" fill="none" stroke="#fbbf24" stroke-width="1.2" stroke-dasharray="2,2"/>
|
|
||||||
<!-- Estrela de 5 pontas central -->
|
|
||||||
<polygon points="24,9 26.5,16.5 34,16.5 28,21 30.5,28.5 24,24 17.5,28.5 20,21 14,16.5 21.5,16.5" fill="#ffffff"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 671 B After Width: | Height: | Size: 654 B |
@@ -1,4 +1,5 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<!-- Engrenagem corporativa simétrica centrada em (24, 24) -->
|
<circle cx="12" cy="12" r="3" fill="none" stroke="currentColor" stroke-width="2"/>
|
||||||
<path fill="#0288d1" fill-rule="evenodd" d="M 21.5 5 L 26.5 5 L 27.2 10.2 C 29 10.9 30.7 11.9 32.2 13.1 L 36.6 10.6 L 40.1 14.1 L 37.6 18.5 C 38.8 20 39.8 21.7 40.5 23.5 L 45.7 24.2 L 45.7 29.2 L 40.5 29.9 C 39.8 31.7 38.8 33.4 37.6 34.9 L 40.1 39.3 L 36.6 42.8 L 32.2 40.3 C 30.7 41.5 29 42.5 27.2 43.2 L 26.5 48.4 L 21.5 48.4 L 20.8 43.2 C 19 42.5 17.3 41.5 15.8 40.3 L 11.4 42.8 L 7.9 39.3 L 10.4 34.9 C 9.2 33.4 8.2 31.7 7.5 29.9 L 2.3 29.2 L 2.3 24.2 L 7.5 23.5 C 8.2 21.7 9.2 20 10.4 18.5 L 7.9 14.1 L 11.4 10.6 L 15.8 13.1 C 17.3 11.9 19 10.9 20.8 10.2 Z M 24 17.5 A 6.5 6.5 0 1 0 24 30.5 A 6.5 6.5 0 1 0 24 17.5 Z"/>
|
<path d="M 10.7 2.5 L 13.3 2.5 L 13.7 4.8 C 14.4 5.1 15.1 5.5 15.8 6.1 L 17.9 5.2 L 19.8 7.1 L 18.9 9.2 C 19.4 9.9 19.8 10.6 20.1 11.3 L 22.5 11.7 L 22.5 14.3 L 20.1 14.7 C 19.8 15.4 19.4 16.1 18.9 16.8 L 19.8 18.9 L 17.9 20.8 L 15.8 19.9 C 15.1 20.5 14.4 20.9 13.7 21.2 L 13.3 23.5 L 10.7 23.5 L 10.3 21.2 C 9.6 20.9 8.9 20.5 8.2 19.9 L 6.1 20.8 L 4.2 18.9 L 5.1 16.8 C 4.6 16.1 4.2 15.4 3.9 14.7 L 1.5 14.3 L 1.5 11.7 L 3.9 11.3 C 4.2 10.6 4.6 9.9 5.1 9.2 L 4.2 7.1 L 6.1 5.2 L 8.2 6.1 C 8.9 5.5 9.6 5.1 10.3 4.8 Z"
|
||||||
|
fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 784 B After Width: | Height: | Size: 784 B |
@@ -1,6 +1,5 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<!-- Escudo CyberShield -->
|
<path d="M 12 2.5 L 4 5.5 V 11.5 C 4 16.5 7.5 21 12 22.5 C 16.5 21 20 16.5 20 11.5 V 5.5 Z"
|
||||||
<path d="M 24 4 L 8 11 L 8 24 C 8 34.5 15 42.5 24 45 C 33 42.5 40 34.5 40 24 L 40 11 Z" fill="#0284c7" stroke="#38bdf8" stroke-width="2.5" stroke-linejoin="round"/>
|
fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
||||||
<!-- Chave de segurança criptográfica no centro -->
|
<path d="M 8.5 11.5 L 11 14 L 15.5 9.5" fill="none" stroke="#0288d1" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
<path d="M 24 14 C 20.7 14 18 16.7 18 20 C 18 22.4 19.5 24.5 22 25.4 L 22 34 L 26 34 L 26 30 L 28 30 L 28 27 L 26 27 L 26 25.4 C 28.5 24.5 30 22.4 30 20 C 30 16.7 27.3 14 24 14 Z M 24 18 C 25.1 18 26 18.9 26 20 C 26 21.1 25.1 22 24 22 C 22.9 22 22 21.1 22 20 C 22 18.9 22.9 18 24 18 Z" fill="#ffffff"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 649 B After Width: | Height: | Size: 408 B |
@@ -1,4 +1,4 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<polygon points="24,4 29.5,16.5 43,17.5 32.5,26.5 35.8,40 24,32.5 12.2,40 15.5,26.5 5,17.5 18.5,16.5"
|
<polygon points="12,2.5 14.8,8.2 21.1,9.1 16.5,13.6 17.6,19.9 12,16.9 6.4,19.9 7.5,13.6 2.9,9.1 9.2,8.2"
|
||||||
fill="#f59e0b" stroke="#d97706" stroke-width="2" stroke-linejoin="round"/>
|
fill="#f59e0b" stroke="#d97706" stroke-width="1.2" stroke-linejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 287 B |
@@ -1,15 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
|
||||||
<!-- Núcleo solar brilhante -->
|
|
||||||
<circle cx="24" cy="24" r="10" fill="#f59e0b"/>
|
|
||||||
<!-- Raios solares -->
|
|
||||||
<g stroke="#f59e0b" stroke-width="3" stroke-linecap="round">
|
|
||||||
<line x1="24" y1="4" x2="24" y2="9"/>
|
|
||||||
<line x1="24" y1="39" x2="24" y2="44"/>
|
|
||||||
<line x1="4" y1="24" x2="9" y2="24"/>
|
|
||||||
<line x1="39" y1="24" x2="44" y2="24"/>
|
|
||||||
<line x1="9.8" y1="9.8" x2="13.3" y2="13.3"/>
|
|
||||||
<line x1="34.7" y1="34.7" x2="38.2" y2="38.2"/>
|
|
||||||
<line x1="9.8" y1="38.2" x2="13.3" y2="34.7"/>
|
|
||||||
<line x1="34.7" y1="13.3" x2="38.2" y2="9.8"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 647 B |
@@ -1,13 +1,5 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<!-- Folha de documento com dobra -->
|
<circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" stroke-width="2"/>
|
||||||
<path d="M 8 4 L 30 4 L 38 12 L 38 34 L 8 34 Z" fill="#ffffff" stroke="#0288d1" stroke-width="2.5" stroke-linejoin="round"/>
|
<polyline points="12 7, 12 12, 16 14" fill="none" stroke="#0288d1" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
<path d="M 30 4 L 30 12 L 38 12 Z" fill="#b3e5fc" stroke="#0288d1" stroke-width="2"/>
|
<line x1="12" y1="1" x2="12" y2="3" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||||
<!-- Linhas no documento -->
|
|
||||||
<line x1="14" y1="18" x2="30" y2="18" stroke="#cfd8dc" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="14" y1="24" x2="24" y2="24" stroke="#cfd8dc" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<!-- Relógio de precisão temporal TSA -->
|
|
||||||
<circle cx="32" cy="32" r="12" fill="#e1f5fe" stroke="#0288d1" stroke-width="2.5"/>
|
|
||||||
<circle cx="32" cy="32" r="2" fill="#01579b"/>
|
|
||||||
<line x1="32" y1="24" x2="32" y2="32" stroke="#01579b" stroke-width="2.5" stroke-linecap="round"/>
|
|
||||||
<line x1="32" y1="32" x2="38" y2="32" stroke="#01579b" stroke-width="2.5" stroke-linecap="round"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 958 B After Width: | Height: | Size: 413 B |
@@ -1,8 +1,5 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<!-- Monitor de computador -->
|
<path d="M 3 6.5 C 3 5 4.5 4 6 4 C 8.5 4 11 5.5 12 7 C 13 5.5 15.5 4 18 4 C 19.5 4 21 5 21 6.5 V 18 C 21 19 19.5 19.5 18 19.5 C 15.5 19.5 13 18 12 17 C 11 18 8.5 19.5 6 19.5 C 4.5 19.5 3 19 3 18 Z"
|
||||||
<rect x="4" y="6" width="40" height="28" rx="4" fill="#ffffff" stroke="#0288d1" stroke-width="2.5"/>
|
fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/>
|
||||||
<!-- Base do monitor -->
|
<line x1="12" y1="7" x2="12" y2="17" stroke="currentColor" stroke-width="1.8"/>
|
||||||
<path d="M 16 34 L 12 42 L 36 42 L 32 34" fill="none" stroke="#0288d1" stroke-width="2.5" stroke-linejoin="round"/>
|
|
||||||
<!-- Ícone de play/vídeo tutorial central -->
|
|
||||||
<polygon points="20,14 32,20 20,26" fill="#0288d1"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 477 B After Width: | Height: | Size: 461 B |
@@ -1,10 +1,6 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<!-- Círculo de relógio base -->
|
<circle cx="10" cy="10" r="7.5" fill="none" stroke="currentColor" stroke-width="2"/>
|
||||||
<circle cx="24" cy="24" r="20" fill="#f1f8e9" stroke="#558b2f" stroke-width="2.5"/>
|
<polyline points="10 6, 10 10, 13 11" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
|
||||||
<!-- Ponteiros do relógio -->
|
<circle cx="16.5" cy="16.5" r="5" fill="none" stroke="#10b981" stroke-width="2"/>
|
||||||
<line x1="24" y1="12" x2="24" y2="24" stroke="#33691e" stroke-width="2" stroke-linecap="round"/>
|
<polyline points="14.8 16.5, 16 17.7, 18.5 15.2" fill="none" stroke="#10b981" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
<line x1="24" y1="24" x2="32" y2="24" stroke="#33691e" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<!-- Checkmark verde de validação de tempo -->
|
|
||||||
<circle cx="34" cy="34" r="10" fill="#2e7d32"/>
|
|
||||||
<path d="M 29 34 L 33 38 L 39 30" fill="none" stroke="#ffffff" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 679 B After Width: | Height: | Size: 528 B |
@@ -1,11 +1,6 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<!-- Folha de documento com dobra -->
|
<path d="M 5 3.5 H 13 L 18 8.5 V 13" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
||||||
<path d="M 8 4 L 32 4 L 42 14 L 42 44 L 8 44 Z" fill="#ffffff" stroke="#0288d1" stroke-width="2.5" stroke-linejoin="round"/>
|
<path d="M 5 3.5 V 20.5 H 10" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
||||||
<path d="M 32 4 L 32 14 L 42 14 Z" fill="#b3e5fc" stroke="#0288d1" stroke-width="2"/>
|
<circle cx="15.5" cy="16.5" r="5" fill="none" stroke="#10b981" stroke-width="2"/>
|
||||||
<!-- Linhas no documento -->
|
<polyline points="13.8 16.5, 15 17.7, 17.5 15.2" fill="none" stroke="#10b981" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
<line x1="14" y1="20" x2="34" y2="20" stroke="#cfd8dc" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="14" y1="26" x2="28" y2="26" stroke="#cfd8dc" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<!-- Emblema de validação com Checkmark verde -->
|
|
||||||
<circle cx="30" cy="34" r="11" fill="#e8f5e9" stroke="#2e7d32" stroke-width="2.5"/>
|
|
||||||
<path d="M 24 34 L 28 38 L 36 29" fill="none" stroke="#2e7d32" stroke-width="3.2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 848 B After Width: | Height: | Size: 548 B |
@@ -1,13 +1,8 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<!-- Folha de documento com dobra -->
|
<path d="M 5 3.5 H 14 L 19 8.5 V 20.5 C 19 21.1 18.5 21.5 17.9 21.5 H 5 C 4.4 21.5 4 21.1 4 20.5 V 4.5 C 4 3.9 4.4 3.5 5 3.5 Z"
|
||||||
<path d="M 8 4 L 32 4 L 42 14 L 42 44 L 8 44 Z" fill="#ffffff" stroke="#0288d1" stroke-width="2.5" stroke-linejoin="round"/>
|
fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
||||||
<!-- Dobra de canto -->
|
<path d="M 14 3.5 V 8.5 H 19" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
||||||
<path d="M 32 4 L 32 14 L 42 14 Z" fill="#b3e5fc" stroke="#0288d1" stroke-width="2" stroke-linejoin="round"/>
|
<path d="M 8.5 13 L 6.5 15 L 8.5 17" fill="none" stroke="#0288d1" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
<!-- Linhas de texto representativas -->
|
<path d="M 15.5 13 L 17.5 15 L 15.5 17" fill="none" stroke="#0288d1" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
<line x1="13" y1="18" x2="27" y2="18" stroke="#b0bec5" stroke-width="2" stroke-linecap="round"/>
|
<line x1="13" y1="12.5" x2="11" y2="17.5" stroke="#0288d1" stroke-width="1.8" stroke-linecap="round"/>
|
||||||
<line x1="13" y1="22" x2="35" y2="22" stroke="#b0bec5" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<line x1="13" y1="26" x2="35" y2="26" stroke="#b0bec5" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
<!-- Etiqueta azul com sigla XML perfeitamente centralizada -->
|
|
||||||
<rect x="11" y="31" width="28" height="11" rx="3" fill="#0288d1"/>
|
|
||||||
<text x="25" y="36.5" font-family="'Segoe UI', Arial, sans-serif" font-size="8px" font-weight="bold" fill="#ffffff" text-anchor="middle" dominant-baseline="central"><XML></text>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 796 B |
@@ -1,3 +1,6 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<path d="M 4 9 L 4 4 L 9 4 M 15 4 L 20 4 L 20 9 M 20 15 L 20 20 L 15 20 M 9 20 L 4 20 L 4 15" fill="none" stroke="#0284c7" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
|
<path d="M 4 8 L 4 4 L 8 4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M 16 4 L 20 4 L 20 8" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M 4 16 L 4 20 L 8 20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M 16 20 L 20 20 L 20 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 284 B After Width: | Height: | Size: 623 B |
@@ -1,5 +1,5 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<circle cx="10" cy="10" r="7" fill="none" stroke="#0284c7" stroke-width="2.2"/>
|
<circle cx="10.5" cy="10.5" r="6.5" fill="none" stroke="currentColor" stroke-width="2"/>
|
||||||
<path d="M 15 15 L 21 21" fill="none" stroke="#0284c7" stroke-width="2.5" stroke-linecap="round"/>
|
<path d="M 15.5 15.5 L 20.5 20.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||||
<path d="M 10 7 L 10 13 M 7 10 L 13 10" fill="none" stroke="#0284c7" stroke-width="2" stroke-linecap="round"/>
|
<path d="M 10.5 7.5 L 10.5 13.5 M 7.5 10.5 L 13.5 10.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 387 B After Width: | Height: | Size: 428 B |
@@ -1,5 +1,5 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||||
<circle cx="10" cy="10" r="7" fill="none" stroke="#0284c7" stroke-width="2.2"/>
|
<circle cx="10.5" cy="10.5" r="6.5" fill="none" stroke="currentColor" stroke-width="2"/>
|
||||||
<path d="M 15 15 L 21 21" fill="none" stroke="#0284c7" stroke-width="2.5" stroke-linecap="round"/>
|
<path d="M 15.5 15.5 L 20.5 20.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||||
<path d="M 7 10 L 13 10" fill="none" stroke="#0284c7" stroke-width="2" stroke-linecap="round"/>
|
<path d="M 7.5 10.5 L 13.5 10.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 372 B After Width: | Height: | Size: 405 B |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 665 B |
|
After Width: | Height: | Size: 873 B |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
@@ -27,7 +27,7 @@ Se você atua no desenvolvimento, manutenção ou auditoria do código-fonte do
|
|||||||
* **[Certificados A1, A3 e PKCS#11](a1-a3-certificates.md):** Funcionamento dos provedores `Pkcs12CertificateProvider`, `WindowsCertificateStoreProvider` (SunMSCAPI/CNG) e `Pkcs11CertificateProvider` (SunPKCS11), monitoramento dinâmico PC/SC (`SmartCardWatcher`) e extração de OIDs ICP-Brasil.
|
* **[Certificados A1, A3 e PKCS#11](a1-a3-certificates.md):** Funcionamento dos provedores `Pkcs12CertificateProvider`, `WindowsCertificateStoreProvider` (SunMSCAPI/CNG) e `Pkcs11CertificateProvider` (SunPKCS11), monitoramento dinâmico PC/SC (`SmartCardWatcher`) e extração de OIDs ICP-Brasil.
|
||||||
* **[Assinaturas PDF (PAdES) e Selos Visuais SVG](pades.md):** Motor de assinatura PDFBox + Bouncy Castle, salvamento incremental, renderização de alta resolução (300 DPI Java2D) e gerenciamento de selos digitais.
|
* **[Assinaturas PDF (PAdES) e Selos Visuais SVG](pades.md):** Motor de assinatura PDFBox + Bouncy Castle, salvamento incremental, renderização de alta resolução (300 DPI Java2D) e gerenciamento de selos digitais.
|
||||||
* **[Assinaturas XML (XAdES-BES e XMLDSig)](xml-xmldsig.md):** Conformidade com o padrão ETSI TS 101 903 / DOC-ICP-15.03 e suporte a notas fiscais eletrônicas (NF-e, CT-e, MDF-e, eSocial).
|
* **[Assinaturas XML (XAdES-BES e XMLDSig)](xml-xmldsig.md):** Conformidade com o padrão ETSI TS 101 903 / DOC-ICP-15.03 e suporte a notas fiscais eletrônicas (NF-e, CT-e, MDF-e, eSocial).
|
||||||
* **[Layout Dual-Theme e Assinatura em Lote](serpro-layout-and-batch.md):** Especificação de interface gráfica moderna, alternância dinâmica de tema (Claro/Escuro) e fila de processamento em lote.
|
* **[Layout de Interface e Assinatura em Lote](ui-layout.md):** Especificação de interface gráfica padronizada, acessibilidade WCAG AA e processamento em lote.
|
||||||
* **[Modo de Desenvolvimento](development-mode.md):** Guia de inicialização em modo `--dev`, políticas de não utilização de mocks em produção e geração de certificados A1 de teste locais.
|
* **[Modo de Desenvolvimento](development-mode.md):** Guia de inicialização em modo `--dev`, políticas de não utilização de mocks em produção e geração de certificados A1 de teste locais.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -66,7 +66,7 @@ Se você precisa implantar, homologar ou validar os aspectos de conformidade e s
|
|||||||
| [`browser-compatibility.md`](browser-compatibility.md) | Compatibilidade de navegadores, HTTPS para loopback e Private Network Access (PNA) | Integradores Web & Segurança |
|
| [`browser-compatibility.md`](browser-compatibility.md) | Compatibilidade de navegadores, HTTPS para loopback e Private Network Access (PNA) | Integradores Web & Segurança |
|
||||||
| [`ccsp-v1.schema.json`](ccsp-v1.schema.json) | JSON Schema formal e versionado do protocolo CCSP/1 | Integradores & QA |
|
| [`ccsp-v1.schema.json`](ccsp-v1.schema.json) | JSON Schema formal e versionado do protocolo CCSP/1 | Integradores & QA |
|
||||||
| [`sdk-release-process.md`](sdk-release-process.md) | Processo de publicação e release do SDK no registry npm privado | DevOps & Engenharia |
|
| [`sdk-release-process.md`](sdk-release-process.md) | Processo de publicação e release do SDK no registry npm privado | DevOps & Engenharia |
|
||||||
| [`serpro-layout-and-batch.md`](serpro-layout-and-batch.md) | Design de interface e processamento em lote | UI/UX & Desenvolvedores |
|
| [`ui-layout.md`](ui-layout.md) | Design de interface e processamento em lote | UI/UX & Desenvolvedores |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
# Sistema de Ícones — CyberCore Signer
|
||||||
|
|
||||||
|
Este documento estabelece as especificações de design, gramática visual, convenções de código e diretrizes de renderização do sistema de ícones do **CyberCore Signer**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Gramática Visual e Grid Base
|
||||||
|
|
||||||
|
O sistema de ícones do CyberCore Signer foi projetado para oferecer clareza, consistência corporativa e alta fidelidade tanto no **Modo Claro** quanto no **Modo Escuro**, além de suporte a telas com alta densidade de pixels (HiDPI / Retina).
|
||||||
|
|
||||||
|
| Parâmetro | Especificação | Descrição |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| **ViewBox Canônico** | `0 0 24 24` | Todos os ícones são desenhados em um canvas quadrado padronizado de 24×24 pixels. |
|
||||||
|
| **Safe Area** | `2px` em todas as bordas | Os grafismos principais ficam contidos na área útil de `20×20px` (coordenadas de `2` a `22`). |
|
||||||
|
| **Stroke (Espessura)** | `1.75px` a `2.0px` | Traços consistentes entre todos os glifos vetoriais, proporcionando peso uniforme. |
|
||||||
|
| **Cantos e Terminações** | `round` | `stroke-linecap="round"` e `stroke-linejoin="round"` para um visual moderno e suave. |
|
||||||
|
| **Texto Embutido** | **Nenhum** (`Proibido`) | Ícones não devem incorporar elementos `<text>`. Toda semântica é transmitida por formas puras e labels da interface. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Paleta de Cores e Tintas Semânticas
|
||||||
|
|
||||||
|
Os ícones utilizam `currentColor` como tinta principal, permitindo repintura dinâmica e herança das cores dos tokens do tema ativo:
|
||||||
|
|
||||||
|
| Papel | Token / Cor Modo Claro | Token / Cor Modo Escuro | Uso |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| **Tinta Principal (`currentColor`)** | `#1E293B` (`-fx-color-text`) | `#F1F5F9` (`-fx-color-text`) | Linhas de contorno, silhuetas e glifos primários. |
|
||||||
|
| **Acento Primário** | `#0288D1` (`-fx-color-primary`) | `#38BDF8` (`-fx-color-primary`) | Elementos de destaque, dobras de documentos, escudos. |
|
||||||
|
| **Sucesso** | `#10B981` (`-fx-color-success`) | `#34D399` (`-fx-color-success`) | Confirmações, validações concluídas (`check`, `validate`). |
|
||||||
|
| **Alerta / Atenção** | `#F59E0B` (`-fx-color-warning`) | `#FBBF24` (`-fx-color-warning`) | Avisos de validade próxima, selos e certificações (`seal`). |
|
||||||
|
| **Erro / Perigo** | `#EF4444` (`-fx-color-error`) | `#F87171` (`-fx-color-error`) | Ações destrutivas, falhas de assinatura (`error`, `exit`). |
|
||||||
|
| **Atenuado / Muted** | `#64748B` (`-fx-color-text-muted`) | `#94A3B8` (`-fx-color-text-muted`) | Guias internas, linhas secundárias, fechar (`close`). |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Escala de Tamanhos Padronizados
|
||||||
|
|
||||||
|
Para manter a consistência da interface, os ícones devem ser consumidos exclusivamente através dos 5 tamanhos canônicos definidos na fachada `Icons`:
|
||||||
|
|
||||||
|
```
|
||||||
|
16px 20px 24px 32px 40px
|
||||||
|
[ Micro ] [ Pequeno ] [ Padrão ] [ Médio ] [ Grande ]
|
||||||
|
```
|
||||||
|
|
||||||
|
- **`SIZE_16` (16×16 px):** Badges de status de certificado, botões auxiliares pequenos de janelas de diálogo (ex.: fechar, atualizar).
|
||||||
|
- **`SIZE_20` (20×20 px):** Menus secundários, botões de ação secundária, itens compactos em listas.
|
||||||
|
- **`SIZE_24` (24×24 px):** Tamanho padrão de componentes, botões de barra lateral do Hub, campos com ícone prefixado.
|
||||||
|
- **`SIZE_32` (32×32 px):** Cabeçalhos de diálogos de aprovação CCSP, títulos de assistentes.
|
||||||
|
- **`SIZE_40` (40×40 px):** Cartões e botões do painel principal (Hub de Ações Rápidas).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Catálogo dos Ícones Vetoriais
|
||||||
|
|
||||||
|
| Arquivo SVG | Glifo / Nome | Propósito e Uso na Aplicação |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| `pdf.svg` | `Icons.pdf()` | Assinatura de documentos PDF e fluxos PAdES. |
|
||||||
|
| `xml.svg` | `Icons.xml()` | Assinatura de documentos XML e notas fiscais (XMLDSig). |
|
||||||
|
| `file.svg` | `Icons.file()` | Assinatura de arquivos e digests genéricos. |
|
||||||
|
| `attached.svg` | `Icons.attached()` | Assinatura atachada / envelopada (CAdES Attached). |
|
||||||
|
| `batch.svg` | `Icons.batch()` | Assinatura de múltiplos documentos em lote. |
|
||||||
|
| `co-sign.svg` | `Icons.coSign()` | Adição de co-assinatura em documento já assinado. |
|
||||||
|
| `timestamp.svg` | `Icons.timestamp()` | Aplicação de carimbo do tempo (TSA). |
|
||||||
|
| `ltv.svg` | `Icons.ltv()` | Assinatura com validação de longo prazo (LTV). |
|
||||||
|
| `validate.svg` | `Icons.validate()` | Validador de integridade e assinaturas digitais. |
|
||||||
|
| `validate-timestamp.svg` | `Icons.validateTimestamp()` | Validador específico de carimbos de tempo. |
|
||||||
|
| `seal.svg` | `Icons.seal()` | Gerenciamento e aplicação de selos visuais. |
|
||||||
|
| `seal-icp-brasil.svg` | `Icons.sealIcpBrasil()` | Selo vetorial oficial padrão ICP-Brasil. |
|
||||||
|
| `cert.svg` | `Icons.certificates()` | Gerenciamento e seleção de certificados A1 e A3. |
|
||||||
|
| `shield.svg` | `Icons.shield()` | Segurança, proteção e integridade do agente. |
|
||||||
|
| `settings.svg` | `Icons.settings()` | Painel de configurações e preferências locais. |
|
||||||
|
| `logs.svg` | `Icons.logs()` | Visualizador de registros de auditoria e logs locais. |
|
||||||
|
| `tutorial.svg` | `Icons.tutorial()` | Guia interativo e tutorial de primeiros passos. |
|
||||||
|
| `about.svg` | `Icons.about()` | Diálogo sobre o CyberCore Signer, versão e licença. |
|
||||||
|
| `exit.svg` | `Icons.exit()` | Confirmação e encerramento do agente desktop. |
|
||||||
|
| `refresh.svg` | `Icons.refresh()` | Atualizar lista de certificados e recarregar dados. |
|
||||||
|
| `sun.svg` | `Icons.sun()` | Alternador para Modo Claro. |
|
||||||
|
| `moon.svg` | `Icons.moon()` | Alternador para Modo Escuro. |
|
||||||
|
| `arrow-right.svg` | `Icons.arrowRight()` | Navegação para próximo passo ou expansão. |
|
||||||
|
| `close.svg` | `Icons.close()` | Fechar diálogo, dispensar notificação ou cancelar. |
|
||||||
|
| `check.svg` | `Icons.check()` | Operação concluída com sucesso ou item selecionado. |
|
||||||
|
| `error.svg` | `Icons.error()` | Alerta de falha, cancelamento ou erro de hardware. |
|
||||||
|
| `star.svg` | `Icons.star()` | Certificado padrão ou favorito selecionado. |
|
||||||
|
| `zoom-in.svg` | `Icons.zoomIn()` | Aumentar zoom na visualização de documento ou selo. |
|
||||||
|
| `zoom-out.svg` | `Icons.zoomOut()` | Reduzir zoom na visualização de documento ou selo. |
|
||||||
|
| `zoom-fit.svg` | `Icons.zoomFit()` | Ajustar visualização à tela ou página inteira. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Implementação Técnica
|
||||||
|
|
||||||
|
### Fachada `Icons`
|
||||||
|
A classe `br.com.cybercore.signer.ui.components.Icons` centraliza a instanciação:
|
||||||
|
```java
|
||||||
|
// Tamanho padrão (24px) com cor do tema ativo:
|
||||||
|
Node icon = Icons.settings();
|
||||||
|
|
||||||
|
// Tamanho específico:
|
||||||
|
Node refresh = Icons.refresh(Icons.SIZE_16);
|
||||||
|
|
||||||
|
// Tinta customizada:
|
||||||
|
Node custom = Icons.of("pdf", Icons.SIZE_40, Color.web("#0288D1"));
|
||||||
|
```
|
||||||
|
|
||||||
|
### Repintura Dinâmica
|
||||||
|
Ao registrar alteração de tema em `ThemeManager`, os nós de ícone baseados em `currentColor` recebem automaticamente o listener e atualizam suas cores em tempo real sem necessidade de recriar a janela.
|
||||||
|
Before Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 45 KiB |
|
After Width: | Height: | Size: 45 KiB |
@@ -1,32 +1,32 @@
|
|||||||
# Layout CyberCore Signer — Design Dual-Theme & Assinatura em Lote
|
# Layout e Interface Gráfica — CyberCore Signer
|
||||||
|
|
||||||
O **CyberCore Signer** adota uma interface gráfica que une a ergonomia de hub consolidada no mercado nacional com uma identidade visual moderna, autoral e com suporte dinâmico a **Tema Claro (Light)** e **Tema Escuro (Dark)**.
|
O **CyberCore Signer** adota uma interface gráfica profissional e moderna, com foco em usabilidade, segurança visual e conformidade rigorosa com os padrões de acessibilidade WCAG 2.1 (AA).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Identidade Visual e Filosofia de Design
|
## 1. Identidade Visual e Filosofia de Design
|
||||||
|
|
||||||
Em conformidade com os requisitos do projeto, a interface **não é uma cópia datada**, mas sim uma evolução:
|
A interface foi estruturada com foco na clareza operacional e consistência ergonômica:
|
||||||
1. **Identidade CyberCore:** Logotipo central em cristal tridimensional e escudo criptográfico vetorial (`SvgIcons`), subtítulo "Agente Criptográfico Local • ICP-Brasil" e indicador de segurança em tempo real.
|
1. **Identidade CyberCore:** Logotipo central em cristal e escudo criptográfico vetorial (`SvgIcons`), subtítulo "Agente Criptográfico Local • ICP-Brasil" e indicador de segurança em tempo real.
|
||||||
2. **Badge de Status Ativo:** Exibe o status da porta local (`● Agente Ativo • 127.0.0.1:21325`) com verde esmeralda no modo claro e verde neon no modo escuro.
|
2. **Badge de Status Ativo:** Exibe o status do agente e da porta local (`● Agente Ativo • 127.0.0.1:21325`) com indicação em verde esmeralda.
|
||||||
3. **Alternador Dinâmico de Temas:** Botão pílula localizado no cabeçalho superior que permite ao usuário alternar instantaneamente entre **Modo Claro** e **Modo Escuro**:
|
3. **Tema Único Padronizado:** Sistema visual unificado baseado em tokens CSS declarados em `.root`, garantindo contraste nítido, tipografia escalável e ausência de inconsistências de renderização.
|
||||||
- Ícone do Sol ☀️ para comutar para o tema Claro.
|
|
||||||
- Ícone da Lua 🌙 para comutar para o tema Escuro.
|
|
||||||
- Classe gerenciadora centralizada: [`ThemeManager.java`](file:///var/www/cybercore/cybercore-signer/modules/cybercore-ui/src/main/java/br/com/cybercore/signer/ui/theme/ThemeManager.java), que replica a escolha de tema para todos os diálogos secundários.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 2. Paleta de Cores dos Temas
|
## 2. Paleta de Cores e Tokens Semânticos
|
||||||
|
|
||||||
| Componente | Tema Claro (Light) | Tema Escuro (Dark) |
|
| Componente / Token | Valor Canônico | Descrição |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **Fundo da Janela** | `#f1f5f9` (Slate suave) | `#0b0f17` (Dark profundo) |
|
| **Fundo da Janela** (`-fx-color-background`) | `#f1f5f9` | Tom slate neutro e suave para leitura prolongada |
|
||||||
| **Header Superior** | `#ffffff`, borda `#e2e8f0` | `#121824`, borda `#1e293b` |
|
| **Superfície de Cartões** (`-fx-color-surface`) | `#ffffff` | Branco puro para cartões, caixas e painéis |
|
||||||
| **Cartões de Ação** | `#ffffff`, borda `#e2e8f0`, hover `#0284c7` | `#131c2e`, borda `#1e293b`, hover `#38bdf8` |
|
| **Bordas Principais** (`-fx-color-border`) | `#cbd5e1` | Linhas de divisão estrutural com contraste seguro |
|
||||||
| **Texto dos Botões** | `#1e293b` (Cinza escuro de alto contraste) | `#f8fafc` (Branco gelo nítido) |
|
| **Bordas Suaves** (`-fx-color-border-subtle`) | `#e2e8f0` | Divisórias e contornos secundários |
|
||||||
| **Painel Lateral** | `#ffffff`, borda `#e2e8f0` | `#121824`, borda `#1e293b` |
|
| **Texto Primário** (`-fx-color-text`) | `#0f172a` | Texto de alto contraste (WCAG ratio ~ 15.8:1) |
|
||||||
| **Badge de Status** | Fundo `#ecfdf5`, texto `#047857`, borda `#10b981` | Fundo `rgba(16, 185, 129, 0.15)`, texto `#34d399` |
|
| **Texto Atenuado** (`-fx-color-text-muted`) | `#475569` | Rótulos secundários e metadados (WCAG ratio ~ 6.8:1) |
|
||||||
| **Alternador de Tema** | Fundo `#ffffff`, borda `#cbd5e1`, hover `#0284c7` | Fundo `#1e293b`, borda `#334155`, hover `#38bdf8` |
|
| **Cor Primária** (`-fx-color-primary`) | `#0284c7` | Azul característico da marca CyberCore para ações padrão |
|
||||||
|
| **Sucesso** (`-fx-color-success`) | `#16a34a` | Indicadores de assinaturas válidas e status operacional |
|
||||||
|
| **Aviso** (`-fx-color-warning`) | `#d97706` | Alertas de atenção e certificados próximos ao vencimento |
|
||||||
|
| **Erro** (`-fx-color-error`) | `#dc2626` | Notificações de erro ou falha criptográfica |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ A interface principal organiza as operações em 6 cartões modernos com cantos
|
|||||||
|
|
||||||
| Posição | Operação | Descrição |
|
| Posição | Operação | Descrição |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| (0, 0) | **Assinar PDF** | Assinatura digital de documentos PDF com cálculo de hash SHA-256 e gravação de assinatura. |
|
| (0, 0) | **Assinar PDF** | Assinatura digital de documentos PDF (PAdES) com visualização e posicionamento interativo de selos. |
|
||||||
| (1, 0) | **Assinar XML** | Assinatura de documentos XML (estruturas fiscais, NF-e, XMLDSig). |
|
| (1, 0) | **Assinar XML** | Assinatura de documentos XML (estruturas fiscais, NF-e, XMLDSig). |
|
||||||
| (2, 0) | **Assinatura Atachada** | Assinatura embutida/anexada ao documento original (CAdES enveloped). |
|
| (2, 0) | **Assinatura Atachada** | Assinatura embutida/anexada ao documento original (CAdES enveloped). |
|
||||||
| (0, 1) | **Gerenciar Selos** | Abertura do Designer e Gerenciador de Selos Digitais visuais para PAdES. |
|
| (0, 1) | **Gerenciar Selos** | Abertura do Designer e Gerenciador de Selos Digitais visuais para PAdES. |
|
||||||
@@ -52,7 +52,7 @@ Na coluna lateral direita, o usuário dispõe de atalhos rápidos para operaçõ
|
|||||||
1. **`</> Logs`:** Visualizador de auditoria em tempo real, monitorando requisições locais CCSP/1 e eventos criptográficos.
|
1. **`</> Logs`:** Visualizador de auditoria em tempo real, monitorando requisições locais CCSP/1 e eventos criptográficos.
|
||||||
2. **`⚙ Configurações`:** Gerenciador de origens web autorizadas, revogação de acessos e opções de PIN.
|
2. **`⚙ Configurações`:** Gerenciador de origens web autorizadas, revogação de acessos e opções de PIN.
|
||||||
3. **`💻 Tutorial`:** Guia passo a passo interativo de uso do assinador e integração web.
|
3. **`💻 Tutorial`:** Guia passo a passo interativo de uso do assinador e integração web.
|
||||||
4. **`🗂 Certificados`:** Lista de certificados ICP-Brasil instalados (Windows-MY, A1 e A3) e ferramenta para importação de certificados A1 (`.pfx` / `.p12`).
|
4. **`🗂 Certificados`:** Lista de certificados ICP-Brasil disponíveis (Windows Certificate Store, A1 e A3) e ferramenta para importação de certificados A1 (`.pfx` / `.p12`).
|
||||||
5. **`ℹ Sobre`:** Informações de versão, engine JCA/JCE e garantias de privacidade local.
|
5. **`ℹ Sobre`:** Informações de versão, engine JCA/JCE e garantias de privacidade local.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -65,7 +65,6 @@ A funcionalidade de assinatura em lote permite:
|
|||||||
- Seleção do certificado digital ICP-Brasil desejado (com exibição de titular, CPF/CNPJ, tipo A1/A3 e validade).
|
- Seleção do certificado digital ICP-Brasil desejado (com exibição de titular, CPF/CNPJ, tipo A1/A3 e validade).
|
||||||
- Escolha da pasta de destino para os arquivos assinados (salvando arquivos `.p7s` correspondentes).
|
- Escolha da pasta de destino para os arquivos assinados (salvando arquivos `.p7s` correspondentes).
|
||||||
- Execução assíncrona em thread isolada para manter a interface responsiva durante todo o lote.
|
- Execução assíncrona em thread isolada para manter a interface responsiva durante todo o lote.
|
||||||
- Compatibilidade automática com o tema ativo (Claro ou Escuro).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
# UI Troubleshooting & Resolução de Estilos (JavaFX CSS)
|
||||||
|
|
||||||
|
Este documento registra a investigação da causa raiz de problemas visuais na interface gráfica do CyberCore Signer, em especial a falha de renderização de textos com cor preta (`Color.BLACK`) via `setStyle()`, a eliminação definitiva de estilos inline de cor e a transição para o tema claro unificado.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. O Problema: Texto Renderizado em Preto (`#000000`) via `setStyle`
|
||||||
|
|
||||||
|
### Sintoma
|
||||||
|
Em diálogos modais como `ExitConfirmationDialog`, `PdfSignDialog` e `PdfCertificateSignDialog`, chamadas como:
|
||||||
|
```java
|
||||||
|
subtitle.setStyle("-fx-text-fill: -fx-text-secondary;");
|
||||||
|
lblDocName.setStyle("-fx-text-fill: -fx-text-primary;");
|
||||||
|
dragTip.setStyle("-fx-text-fill: -fx-text-secondary;");
|
||||||
|
```
|
||||||
|
resultavam em texto renderizado com a cor preta opaca (`#000000`), mesmo quando o token `-fx-text-secondary` já se encontrava definido no arquivo `theme.css`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Investigação Técnica e Causa Raiz
|
||||||
|
|
||||||
|
### 2.1 Por que `setStyle("-fx-text-fill: -fx-text-secondary")` renderiza PRETO?
|
||||||
|
No motor de estilização do JavaFX (`com.sun.javafx.css.StyleManager` / `CssStyleHelper`):
|
||||||
|
|
||||||
|
1. **Momento da Avaliação (Timing) de Nós Fora da Árvore da Scene:**
|
||||||
|
Quando um nó (como um `Label` ou `Text`) é instanciado no código Java dentro do construtor de um diálogo e recebe `.setStyle("-fx-text-fill: -fx-text-secondary;")`, ele ainda **não foi anexado ao grafo de cena** (`Scene`).
|
||||||
|
2. **Escopo de Resolução de Lookups Ausente:**
|
||||||
|
Como `-fx-text-secondary` é uma cor pesquisada (*looked-up color*) definida no seletor `.root` da folha de estilo da aplicação, a engine de CSS do JavaFX tenta resolver essa variável subindo a cadeia de nós pais (`parent chain`) até alcançar `.root`. Como o nó está solto na memória (sem parent e sem Scene vinculada no instante da chamada), o lookup não encontra nenhuma definição válida.
|
||||||
|
3. **Mecanismo de Fallback Silencioso do Modena (`Color.BLACK`):**
|
||||||
|
O analisador do JavaFX não lança exceção ao falhar na resolução de uma cor de lookup em estilo inline. Em vez disso, seu conversor (`PaintConverter` / `ColorConverter`) assume o valor padrão de salvaguarda da especificação Modena: **`Color.BLACK` (`rgba(0, 0, 0, 1.0)`)**.
|
||||||
|
4. **Imutabilidade e Prioridade do Estilo Inline:**
|
||||||
|
O estilo inline atribuído via `setStyle(...)` possui a especificidade mais alta na hierarquia CSS do JavaFX. O valor pré-resolvido como `Color.BLACK` fica gravado diretamente no cache de estilo do nó. Quando a Scene finalmente executa a passagem formal de CSS (`applyCss()` / `layout()`), a engine prioriza o estilo inline e não reavalia o lookup que falhou previamente.
|
||||||
|
|
||||||
|
### 2.2 Por que o problema não ocorre com Classes CSS?
|
||||||
|
Quando utilizamos classes semânticas declaradas em folha de estilo (ex.: `lbl.getStyleClass().add("text-muted");`):
|
||||||
|
```css
|
||||||
|
.text-muted {
|
||||||
|
-fx-text-fill: -fx-text-secondary;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
A resolução da regra é postergada até que o nó pertença à hierarquia da Scene e a passagem de CSS do JavaFX seja executada. Nesse momento, o nó tem acesso direto às definições de `.root`, resolvendo perfeitamente o token para a cor correta da paleta (`#475569`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Diretriz Arquitetural: Proibição de Cores em `setStyle()`
|
||||||
|
|
||||||
|
Fica estabelecido como regra obrigatória de desenvolvimento no `cybercore-ui`:
|
||||||
|
|
||||||
|
1. **`setStyle()` restrito exclusivamente à Geometria:**
|
||||||
|
- Permitido: dimensões dinâmicas, larguras mínimas, alturas, alinhamentos e paddings específicos de cálculo em tempo de execução (`-fx-min-width`, `-fx-pref-width`, `-fx-padding`, `-fx-alignment`).
|
||||||
|
- **Terminantemente proibido em `setStyle()`:**
|
||||||
|
- `-fx-text-fill`
|
||||||
|
- `-fx-background-color`
|
||||||
|
- `-fx-border-color`
|
||||||
|
- `-fx-fill` / `-fx-stroke`
|
||||||
|
- Códigos hexadecimais (`#...`) ou funções cromáticas (`rgb(...)`, `rgba(...)`)
|
||||||
|
- Nomes de lookups de cor (`-fx-accent`, `-fx-color-*`, etc.)
|
||||||
|
|
||||||
|
2. **Uso Obrigatório de Classes CSS Semânticas:**
|
||||||
|
| Classe CSS | Finalidade | Propriedade Aplicada |
|
||||||
|
|---|---|---|
|
||||||
|
| `.text-primary` | Texto principal | `-fx-text-fill: -fx-text-primary;` |
|
||||||
|
| `.text-secondary`, `.text-muted` | Texto auxiliar / metadados | `-fx-text-fill: -fx-text-secondary;` |
|
||||||
|
| `.text-accent` | Destaque primário | `-fx-text-fill: -fx-accent;` |
|
||||||
|
| `.text-bold` | Tipografia em negrito | `-fx-font-weight: bold;` |
|
||||||
|
| `.text-small` | Texto compacto | `-fx-font-size: 11px;` |
|
||||||
|
| `.text-mono` | Fonte monoespaçada para hashes | `-fx-font-family: monospace;` |
|
||||||
|
| `.surface-box` | Container de superfície branca com borda | `-fx-background-color: -fx-color-surface;` |
|
||||||
|
| `.toolbar-box` | Barra de ferramentas compacta | `-fx-background-color: -fx-color-surface;` |
|
||||||
|
| `.scroll-transparent` | ScrollPane sem fundo cinza | `-fx-background-color: transparent;` |
|
||||||
|
| `.status-ok` / `.status-warn` / `.status-error` | Indicadores de status | Cores semânticas da paleta |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Salvaguardas Automatizadas
|
||||||
|
|
||||||
|
O teste de arquitetura [`DesignTokenGuardrailTest.java`](file:///var/www/cybercore/cybercore-signer/modules/cybercore-ui/src/test/java/br/com/cybercore/signer/ui/theme/DesignTokenGuardrailTest.java) atua como barreira de compilação contínua:
|
||||||
|
- Varre todos os arquivos `.java` no módulo `cybercore-ui` e reprova automaticamente o build se qualquer chamada `setStyle()` contiver propriedades ou valores de cor.
|
||||||
|
- Valida matematicamente a relação de contraste WCAG AA (mínimo de 4.5:1 para texto normal) em todas as combinações de tokens de texto sobre superfície/fundo.
|
||||||
|
- Assegura ausência de seletores ou classes residuais de temas escuros (`.theme-dark`, `AppTheme`, `isDark()`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Comportamento da Barra de Título Nativa no Windows (DWM em Modo Escuro)
|
||||||
|
|
||||||
|
### Diagnóstico Operacional
|
||||||
|
Quando o sistema operacional Windows 10/11 está configurado no "Modo Escuro do Sistema" (*Personalização > Cores > Escolher o modo: Escuro*), o gerenciador de janelas do sistema operacional (**Desktop Window Manager — DWM**) aplica automaticamente o atributo `DWMWA_USE_IMMERSIVE_DARK_MODE` na moldura não-cliente da janela nativa (`HWND`).
|
||||||
|
|
||||||
|
Como o CyberCore Signer utiliza decoração nativa padrão (`StageStyle.DECORATED`) para preservar o comportamento esperado pelo usuário empresarial — incluindo cantos arredondados, atalhos de teclado nativos, suporte a múltiplos monitores com DPIs distintos e os *Snap Layouts* do Windows 11 — a barra de título nativa é desenhada pelo Windows em tom escuro, enquanto o conteúdo da janela exibe o tema claro unificado da aplicação.
|
||||||
|
|
||||||
|
### Diretriz de Arquitetura (Não Forçar)
|
||||||
|
Em estrita conformidade com as diretrizes do projeto (Regras 2 e 23):
|
||||||
|
1. **Não forçar a barra de título:** Evita-se o uso de hacks de baixo nível via JNA/JNI para sobrescrever o DWM do Windows ou janelas não decoradas (`UNDECORATED`), o que causaria quebra de acessibilidade e perda de recursos nativos do sistema.
|
||||||
|
2. **Harmonia Visual:** A moldura do Windows atua como moldura de sistema e o conteúdo claro do CyberCore Signer permanece legível, íntegro e em total conformidade com a especificação de interface.
|
||||||
|
|
||||||
@@ -95,6 +95,10 @@ public class CyberCoreDesktopApplication extends Application {
|
|||||||
developmentMode = devMode;
|
developmentMode = devMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isDevelopmentMode() {
|
||||||
|
return developmentMode;
|
||||||
|
}
|
||||||
|
|
||||||
public static MainAppView getMainAppView() {
|
public static MainAppView getMainAppView() {
|
||||||
return mainAppView;
|
return mainAppView;
|
||||||
}
|
}
|
||||||
@@ -224,7 +228,7 @@ public class CyberCoreDesktopApplication extends Application {
|
|||||||
|
|
||||||
ScrollPane scrollPane = new ScrollPane(settingsView);
|
ScrollPane scrollPane = new ScrollPane(settingsView);
|
||||||
scrollPane.setFitToWidth(true);
|
scrollPane.setFitToWidth(true);
|
||||||
scrollPane.setStyle("-fx-background: transparent; -fx-background-color: transparent;");
|
scrollPane.getStyleClass().add("scroll-transparent");
|
||||||
VBox.setVgrow(scrollPane, Priority.ALWAYS);
|
VBox.setVgrow(scrollPane, Priority.ALWAYS);
|
||||||
|
|
||||||
Button btnClose = new Button(" Fechar", SvgIcons.closeIcon(13));
|
Button btnClose = new Button(" Fechar", SvgIcons.closeIcon(13));
|
||||||
|
|||||||
@@ -39,14 +39,14 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import br.com.cybercore.signer.ui.CyberCoreDesktopApplication;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gerenciador centralizado de recursos visuais (ícones e imagens).
|
* Gerenciador centralizado de recursos visuais (ícones e imagens).
|
||||||
* Permite que o usuário customize ou substitua facilmente qualquer ícone ou imagem (SVG/PNG)
|
* Centraliza o carregamento de recursos do classpath em produção e permite
|
||||||
* do CyberCore Signer simplesmente colocando os arquivos na pasta 'assets/'
|
* substituição via pasta externa 'assets/' exclusivamente em modo de desenvolvimento.
|
||||||
* na raiz do projeto ou no diretório ~/.cybercore/assets/.
|
|
||||||
*/
|
*/
|
||||||
public final class AssetManager {
|
public final class AssetManager {
|
||||||
|
|
||||||
@@ -54,9 +54,33 @@ public final class AssetManager {
|
|||||||
|
|
||||||
private static final String[] RASTER_EXTENSIONS = {".png", ".jpg", ".jpeg"};
|
private static final String[] RASTER_EXTENSIONS = {".png", ".jpg", ".jpeg"};
|
||||||
|
|
||||||
|
private static volatile Boolean externalOverridesOverride = null;
|
||||||
|
|
||||||
private AssetManager() {
|
private AssetManager() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define explicitamente se o carregamento de assets externos está habilitado (útil para testes).
|
||||||
|
*/
|
||||||
|
public static void setExternalOverridesEnabled(Boolean enabled) {
|
||||||
|
externalOverridesOverride = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indica se o carregamento de assets externos (./assets ou ~/.cybercore/assets) é permitido.
|
||||||
|
* Em ambiente de produção, este método retorna false por padrão para garantir
|
||||||
|
* integridade e isolamento estrito de recursos empacotados.
|
||||||
|
*/
|
||||||
|
public static boolean isExternalOverridesAllowed() {
|
||||||
|
if (externalOverridesOverride != null) {
|
||||||
|
return externalOverridesOverride;
|
||||||
|
}
|
||||||
|
if (CyberCoreDesktopApplication.isDevelopmentMode()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return Boolean.getBoolean("cybercore.dev") || Boolean.getBoolean("cybercore.assets.external");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retorna o caminho do diretório externo prioritário de assets.
|
* Retorna o caminho do diretório externo prioritário de assets.
|
||||||
*/
|
*/
|
||||||
@@ -81,31 +105,33 @@ public final class AssetManager {
|
|||||||
|
|
||||||
String cleanName = cleanBaseName(baseName);
|
String cleanName = cleanBaseName(baseName);
|
||||||
|
|
||||||
// 1. Procura no diretório local ./assets/{category}/
|
if (isExternalOverridesAllowed()) {
|
||||||
Path localDir = Path.of("assets", category);
|
// 1. Procura no diretório local ./assets/{category}/
|
||||||
for (String ext : RASTER_EXTENSIONS) {
|
Path localDir = Path.of("assets", category);
|
||||||
Path file = localDir.resolve(cleanName + ext);
|
for (String ext : RASTER_EXTENSIONS) {
|
||||||
if (Files.isRegularFile(file)) {
|
Path file = localDir.resolve(cleanName + ext);
|
||||||
try (InputStream in = new FileInputStream(file.toFile())) {
|
if (Files.isRegularFile(file)) {
|
||||||
logger.debug("Carregando imagem bitmap do diretório local: {}", file);
|
try (InputStream in = new FileInputStream(file.toFile())) {
|
||||||
return Optional.of(new Image(in, width, height, true, true));
|
logger.debug("Carregando imagem bitmap do diretório local: {}", file);
|
||||||
} catch (Exception e) {
|
return Optional.of(new Image(in, width, height, true, true));
|
||||||
logger.warn("Falha ao ler imagem customizada {}: {}", file, e.getMessage());
|
} catch (Exception e) {
|
||||||
|
logger.warn("Falha ao ler imagem customizada {}: {}", file, e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Procura em ~/.cybercore/assets/{category}/
|
// 2. Procura em ~/.cybercore/assets/{category}/
|
||||||
String userHome = System.getProperty("user.home", ".");
|
String userHome = System.getProperty("user.home", ".");
|
||||||
Path userDir = Path.of(userHome, ".cybercore", "assets", category);
|
Path userDir = Path.of(userHome, ".cybercore", "assets", category);
|
||||||
for (String ext : RASTER_EXTENSIONS) {
|
for (String ext : RASTER_EXTENSIONS) {
|
||||||
Path file = userDir.resolve(cleanName + ext);
|
Path file = userDir.resolve(cleanName + ext);
|
||||||
if (Files.isRegularFile(file)) {
|
if (Files.isRegularFile(file)) {
|
||||||
try (InputStream in = new FileInputStream(file.toFile())) {
|
try (InputStream in = new FileInputStream(file.toFile())) {
|
||||||
logger.debug("Carregando imagem bitmap de ~/.cybercore: {}", file);
|
logger.debug("Carregando imagem bitmap de ~/.cybercore: {}", file);
|
||||||
return Optional.of(new Image(in, width, height, true, true));
|
return Optional.of(new Image(in, width, height, true, true));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.warn("Falha ao ler imagem do usuário {}: {}", file, e.getMessage());
|
logger.warn("Falha ao ler imagem do usuário {}: {}", file, e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,20 +156,29 @@ public final class AssetManager {
|
|||||||
* Retorna um Node de ícone customizado (prioritariamente SVG, com fallback para PNG ou supplier vetorial).
|
* Retorna um Node de ícone customizado (prioritariamente SVG, com fallback para PNG ou supplier vetorial).
|
||||||
*/
|
*/
|
||||||
public static Node getIconOrDefault(String iconName, double size, Supplier<Node> fallbackSupplier) {
|
public static Node getIconOrDefault(String iconName, double size, Supplier<Node> fallbackSupplier) {
|
||||||
|
return getIconOrDefault(iconName, size, null, fallbackSupplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retorna um Node de ícone customizado com suporte a repintura ou tinta explícita.
|
||||||
|
*/
|
||||||
|
public static Node getIconOrDefault(String iconName, double size, Color tint, Supplier<Node> fallbackSupplier) {
|
||||||
if (iconName != null && !iconName.isBlank()) {
|
if (iconName != null && !iconName.isBlank()) {
|
||||||
String cleanName = cleanBaseName(iconName);
|
String cleanName = cleanBaseName(iconName);
|
||||||
|
|
||||||
// 1. Prioridade para SVG em ./assets/icons/
|
if (isExternalOverridesAllowed()) {
|
||||||
Optional<Node> localSvg = loadSvgIconFromPath(Path.of("assets", "icons", cleanName + ".svg"), size);
|
// 1. Prioridade para SVG em ./assets/icons/
|
||||||
if (localSvg.isPresent()) {
|
Optional<Node> localSvg = loadSvgIconFromPath(Path.of("assets", "icons", cleanName + ".svg"), size, tint);
|
||||||
return localSvg.get();
|
if (localSvg.isPresent()) {
|
||||||
}
|
return localSvg.get();
|
||||||
|
}
|
||||||
|
|
||||||
// 2. Procura SVG em ~/.cybercore/assets/icons/
|
// 2. Procura SVG em ~/.cybercore/assets/icons/
|
||||||
String userHome = System.getProperty("user.home", ".");
|
String userHome = System.getProperty("user.home", ".");
|
||||||
Optional<Node> userSvg = loadSvgIconFromPath(Path.of(userHome, ".cybercore", "assets", "icons", cleanName + ".svg"), size);
|
Optional<Node> userSvg = loadSvgIconFromPath(Path.of(userHome, ".cybercore", "assets", "icons", cleanName + ".svg"), size, tint);
|
||||||
if (userSvg.isPresent()) {
|
if (userSvg.isPresent()) {
|
||||||
return userSvg.get();
|
return userSvg.get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Procura SVG no Classpath interno
|
// 3. Procura SVG no Classpath interno
|
||||||
@@ -151,7 +186,7 @@ public final class AssetManager {
|
|||||||
InputStream resStream = AssetManager.class.getResourceAsStream(resSvgPath);
|
InputStream resStream = AssetManager.class.getResourceAsStream(resSvgPath);
|
||||||
if (resStream != null) {
|
if (resStream != null) {
|
||||||
try (resStream) {
|
try (resStream) {
|
||||||
Node node = parseSvgToNode(resStream, size, size);
|
Node node = parseSvgToNode(resStream, size, size, tint);
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@@ -180,32 +215,34 @@ public final class AssetManager {
|
|||||||
if (imageName != null && !imageName.isBlank()) {
|
if (imageName != null && !imageName.isBlank()) {
|
||||||
String cleanName = cleanBaseName(imageName);
|
String cleanName = cleanBaseName(imageName);
|
||||||
|
|
||||||
// 1. Prioridade para SVG em ./assets/images/
|
if (isExternalOverridesAllowed()) {
|
||||||
Path localSvg = Path.of("assets", "images", cleanName + ".svg");
|
// 1. Prioridade para SVG em ./assets/images/
|
||||||
if (Files.isRegularFile(localSvg)) {
|
Path localSvg = Path.of("assets", "images", cleanName + ".svg");
|
||||||
try (InputStream in = new FileInputStream(localSvg.toFile())) {
|
if (Files.isRegularFile(localSvg)) {
|
||||||
Node node = parseSvgToNode(in, width, height);
|
try (InputStream in = new FileInputStream(localSvg.toFile())) {
|
||||||
if (node != null) {
|
Node node = parseSvgToNode(in, width, height, null);
|
||||||
logger.debug("Carregada imagem SVG local: {}", localSvg);
|
if (node != null) {
|
||||||
return node;
|
logger.debug("Carregada imagem SVG local: {}", localSvg);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("Falha ao ler imagem SVG local {}: {}", localSvg, e.getMessage());
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
logger.warn("Falha ao ler imagem SVG local {}: {}", localSvg, e.getMessage());
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Procura SVG em ~/.cybercore/assets/images/
|
// 2. Procura SVG em ~/.cybercore/assets/images/
|
||||||
String userHome = System.getProperty("user.home", ".");
|
String userHome = System.getProperty("user.home", ".");
|
||||||
Path userSvg = Path.of(userHome, ".cybercore", "assets", "images", cleanName + ".svg");
|
Path userSvg = Path.of(userHome, ".cybercore", "assets", "images", cleanName + ".svg");
|
||||||
if (Files.isRegularFile(userSvg)) {
|
if (Files.isRegularFile(userSvg)) {
|
||||||
try (InputStream in = new FileInputStream(userSvg.toFile())) {
|
try (InputStream in = new FileInputStream(userSvg.toFile())) {
|
||||||
Node node = parseSvgToNode(in, width, height);
|
Node node = parseSvgToNode(in, width, height, null);
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
logger.debug("Carregada imagem SVG do usuário: {}", userSvg);
|
logger.debug("Carregada imagem SVG do usuário: {}", userSvg);
|
||||||
return node;
|
return node;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("Falha ao ler imagem SVG do usuário {}: {}", userSvg, e.getMessage());
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
logger.warn("Falha ao ler imagem SVG do usuário {}: {}", userSvg, e.getMessage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,7 +251,7 @@ public final class AssetManager {
|
|||||||
InputStream resStream = AssetManager.class.getResourceAsStream(resPath);
|
InputStream resStream = AssetManager.class.getResourceAsStream(resPath);
|
||||||
if (resStream != null) {
|
if (resStream != null) {
|
||||||
try (resStream) {
|
try (resStream) {
|
||||||
Node node = parseSvgToNode(resStream, width, height);
|
Node node = parseSvgToNode(resStream, width, height, null);
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@@ -244,6 +281,59 @@ public final class AssetManager {
|
|||||||
return getImageOrDefault("app-logo", width, height, fallbackSupplier);
|
return getImageOrDefault("app-logo", width, height, fallbackSupplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final int[] ICON_SIZES = {16, 24, 32, 48, 64, 128, 256};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retorna a lista de imagens multi-resolução do ícone da aplicação
|
||||||
|
* para configuração do Stage JavaFX principal (16, 24, 32, 48, 64, 128, 256 px).
|
||||||
|
*/
|
||||||
|
public static List<Image> getApplicationIcons() {
|
||||||
|
List<Image> icons = new ArrayList<>();
|
||||||
|
for (int size : ICON_SIZES) {
|
||||||
|
String path = "/assets/images/app-icon-" + size + ".png";
|
||||||
|
InputStream in = AssetManager.class.getResourceAsStream(path);
|
||||||
|
if (in != null) {
|
||||||
|
try (in) {
|
||||||
|
icons.add(new Image(in));
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.debug("Falha ao carregar ícone {}: {}", path, e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
InputStream fallback = AssetManager.class.getResourceAsStream("/assets/images/app-icon.png");
|
||||||
|
if (fallback != null) {
|
||||||
|
try (fallback) {
|
||||||
|
icons.add(new Image(fallback, size, size, true, true));
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return icons;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Carrega a imagem da bandeja do sistema nativa (AWT TrayIcon) em alta fidelidade.
|
||||||
|
*/
|
||||||
|
public static java.awt.Image getTrayIconImage() {
|
||||||
|
String[] preferredPaths = {
|
||||||
|
"/assets/images/app-icon-32.png",
|
||||||
|
"/assets/images/app-icon-16.png",
|
||||||
|
"/assets/images/app-icon.png"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (String path : preferredPaths) {
|
||||||
|
InputStream in = AssetManager.class.getResourceAsStream(path);
|
||||||
|
if (in != null) {
|
||||||
|
try (in) {
|
||||||
|
return javax.imageio.ImageIO.read(in);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.debug("Falha ao ler recurso AWT {}: {}", path, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private static String cleanBaseName(String name) {
|
private static String cleanBaseName(String name) {
|
||||||
String clean = name;
|
String clean = name;
|
||||||
int dotIdx = clean.lastIndexOf('.');
|
int dotIdx = clean.lastIndexOf('.');
|
||||||
@@ -253,10 +343,10 @@ public final class AssetManager {
|
|||||||
return clean;
|
return clean;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Optional<Node> loadSvgIconFromPath(Path path, double size) {
|
private static Optional<Node> loadSvgIconFromPath(Path path, double size, Color tint) {
|
||||||
if (Files.isRegularFile(path)) {
|
if (Files.isRegularFile(path)) {
|
||||||
try (InputStream in = new FileInputStream(path.toFile())) {
|
try (InputStream in = new FileInputStream(path.toFile())) {
|
||||||
Node node = parseSvgToNode(in, size, size);
|
Node node = parseSvgToNode(in, size, size, tint);
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
logger.debug("Carregado ícone SVG customizado de: {}", path);
|
logger.debug("Carregado ícone SVG customizado de: {}", path);
|
||||||
return Optional.of(node);
|
return Optional.of(node);
|
||||||
@@ -272,13 +362,18 @@ public final class AssetManager {
|
|||||||
* Parser analítico de SVG para nós gráficos JavaFX com suporte a dimensões quadradas.
|
* Parser analítico de SVG para nós gráficos JavaFX com suporte a dimensões quadradas.
|
||||||
*/
|
*/
|
||||||
public static Node parseSvgToNode(InputStream in, double targetSize) {
|
public static Node parseSvgToNode(InputStream in, double targetSize) {
|
||||||
return parseSvgToNode(in, targetSize, targetSize);
|
return parseSvgToNode(in, targetSize, targetSize, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Node parseSvgToNode(InputStream in, double targetSize, Color tint) {
|
||||||
|
return parseSvgToNode(in, targetSize, targetSize, tint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Parser analítico completo de SVG para nós gráficos JavaFX com dimensões proporcionais e suporte a defs/gradientes.
|
|
||||||
*/
|
|
||||||
public static Node parseSvgToNode(InputStream in, double targetWidth, double targetHeight) {
|
public static Node parseSvgToNode(InputStream in, double targetWidth, double targetHeight) {
|
||||||
|
return parseSvgToNode(in, targetWidth, targetHeight, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Node parseSvgToNode(InputStream in, double targetWidth, double targetHeight, Color tint) {
|
||||||
try {
|
try {
|
||||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||||
dbf.setNamespaceAware(false);
|
dbf.setNamespaceAware(false);
|
||||||
@@ -326,8 +421,13 @@ public final class AssetManager {
|
|||||||
Map<String, Paint> defsMap = new HashMap<>();
|
Map<String, Paint> defsMap = new HashMap<>();
|
||||||
parseDefs(svg, defsMap);
|
parseDefs(svg, defsMap);
|
||||||
|
|
||||||
|
Color effectiveCurrentColor = (tint != null) ? tint : Color.web("#1E293B");
|
||||||
|
List<Shape> dynamicStrokeShapes = new ArrayList<>();
|
||||||
|
List<Shape> dynamicFillShapes = new ArrayList<>();
|
||||||
|
RenderContext ctx = new RenderContext(defsMap, effectiveCurrentColor, dynamicStrokeShapes, dynamicFillShapes);
|
||||||
|
|
||||||
Group contentGroup = new Group();
|
Group contentGroup = new Group();
|
||||||
renderChildren(svg, contentGroup, defsMap);
|
renderChildren(svg, contentGroup, ctx);
|
||||||
|
|
||||||
double scaleX = targetWidth / vbWidth;
|
double scaleX = targetWidth / vbWidth;
|
||||||
double scaleY = targetHeight / vbHeight;
|
double scaleY = targetHeight / vbHeight;
|
||||||
@@ -348,9 +448,11 @@ public final class AssetManager {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Pane container = new Pane(contentGroup);
|
Pane container = new Pane(contentGroup);
|
||||||
|
container.getStyleClass().add("cyber-icon");
|
||||||
container.setPrefSize(targetWidth, targetHeight);
|
container.setPrefSize(targetWidth, targetHeight);
|
||||||
container.setMaxSize(targetWidth, targetHeight);
|
container.setMaxSize(targetWidth, targetHeight);
|
||||||
container.setMinSize(targetWidth, targetHeight);
|
container.setMinSize(targetWidth, targetHeight);
|
||||||
|
|
||||||
return container;
|
return container;
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -444,7 +546,14 @@ public final class AssetManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void renderChildren(Element parent, Group targetGroup, Map<String, Paint> defsMap) {
|
private record RenderContext(
|
||||||
|
Map<String, Paint> defsMap,
|
||||||
|
Color currentColor,
|
||||||
|
List<Shape> dynamicStrokeShapes,
|
||||||
|
List<Shape> dynamicFillShapes
|
||||||
|
) {}
|
||||||
|
|
||||||
|
private static void renderChildren(Element parent, Group targetGroup, RenderContext ctx) {
|
||||||
NodeList children = parent.getChildNodes();
|
NodeList children = parent.getChildNodes();
|
||||||
for (int i = 0; i < children.getLength(); i++) {
|
for (int i = 0; i < children.getLength(); i++) {
|
||||||
org.w3c.dom.Node n = children.item(i);
|
org.w3c.dom.Node n = children.item(i);
|
||||||
@@ -452,7 +561,7 @@ public final class AssetManager {
|
|||||||
if ("defs".equalsIgnoreCase(el.getTagName())) {
|
if ("defs".equalsIgnoreCase(el.getTagName())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Node jfxNode = createElementNode(el, defsMap);
|
Node jfxNode = createElementNode(el, ctx);
|
||||||
if (jfxNode != null) {
|
if (jfxNode != null) {
|
||||||
targetGroup.getChildren().add(jfxNode);
|
targetGroup.getChildren().add(jfxNode);
|
||||||
}
|
}
|
||||||
@@ -460,7 +569,7 @@ public final class AssetManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Node createElementNode(Element el, Map<String, Paint> defsMap) {
|
private static Node createElementNode(Element el, RenderContext ctx) {
|
||||||
String tagName = el.getTagName().toLowerCase();
|
String tagName = el.getTagName().toLowerCase();
|
||||||
Shape shape = null;
|
Shape shape = null;
|
||||||
|
|
||||||
@@ -527,7 +636,7 @@ public final class AssetManager {
|
|||||||
Map<String, String> inlineStyle = el.hasAttribute("style") ? parseStyleAttribute(el.getAttribute("style")) : Map.of();
|
Map<String, String> inlineStyle = el.hasAttribute("style") ? parseStyleAttribute(el.getAttribute("style")) : Map.of();
|
||||||
String fillVal = inlineStyle.getOrDefault("fill", el.hasAttribute("fill") ? el.getAttribute("fill") : null);
|
String fillVal = inlineStyle.getOrDefault("fill", el.hasAttribute("fill") ? el.getAttribute("fill") : null);
|
||||||
if (fillVal != null) {
|
if (fillVal != null) {
|
||||||
t.setFill(parsePaint(fillVal, defsMap, Color.BLACK));
|
t.setFill(parsePaint(fillVal, ctx, Color.BLACK));
|
||||||
}
|
}
|
||||||
|
|
||||||
String anchor = inlineStyle.getOrDefault("text-anchor", el.getAttribute("text-anchor"));
|
String anchor = inlineStyle.getOrDefault("text-anchor", el.getAttribute("text-anchor"));
|
||||||
@@ -555,9 +664,9 @@ public final class AssetManager {
|
|||||||
}
|
}
|
||||||
case "g" -> {
|
case "g" -> {
|
||||||
Group g = new Group();
|
Group g = new Group();
|
||||||
renderChildren(el, g, defsMap);
|
renderChildren(el, g, ctx);
|
||||||
if (el.hasAttribute("stroke")) {
|
if (el.hasAttribute("stroke")) {
|
||||||
Paint strokePaint = parsePaint(el.getAttribute("stroke"), defsMap, null);
|
Paint strokePaint = parsePaint(el.getAttribute("stroke"), ctx, null);
|
||||||
double sw = parseDouble(el.getAttribute("stroke-width"), 1.0);
|
double sw = parseDouble(el.getAttribute("stroke-width"), 1.0);
|
||||||
for (Node child : g.getChildren()) {
|
for (Node child : g.getChildren()) {
|
||||||
if (child instanceof Shape s) {
|
if (child instanceof Shape s) {
|
||||||
@@ -572,7 +681,8 @@ public final class AssetManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (shape != null) {
|
if (shape != null) {
|
||||||
applyShapeStyles(el, shape, defsMap);
|
shape.getStyleClass().add("cyber-icon-glyph");
|
||||||
|
applyShapeStyles(el, shape, ctx);
|
||||||
applyElementTransform(el, shape);
|
applyElementTransform(el, shape);
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
@@ -597,19 +707,25 @@ public final class AssetManager {
|
|||||||
return styleMap;
|
return styleMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void applyShapeStyles(Element el, Shape shape, Map<String, Paint> defsMap) {
|
private static void applyShapeStyles(Element el, Shape shape, RenderContext ctx) {
|
||||||
Map<String, String> inlineStyle = el.hasAttribute("style") ? parseStyleAttribute(el.getAttribute("style")) : Map.of();
|
Map<String, String> inlineStyle = el.hasAttribute("style") ? parseStyleAttribute(el.getAttribute("style")) : Map.of();
|
||||||
|
|
||||||
String fillVal = inlineStyle.getOrDefault("fill", el.hasAttribute("fill") ? el.getAttribute("fill") : null);
|
String fillVal = inlineStyle.getOrDefault("fill", el.hasAttribute("fill") ? el.getAttribute("fill") : null);
|
||||||
if (fillVal != null) {
|
if (fillVal != null) {
|
||||||
shape.setFill(parsePaint(fillVal, defsMap, Color.TRANSPARENT));
|
if ("currentColor".equalsIgnoreCase(fillVal.trim()) && ctx != null) {
|
||||||
|
ctx.dynamicFillShapes().add(shape);
|
||||||
|
}
|
||||||
|
shape.setFill(parsePaint(fillVal, ctx, Color.TRANSPARENT));
|
||||||
} else if (shape instanceof Line) {
|
} else if (shape instanceof Line) {
|
||||||
shape.setFill(Color.TRANSPARENT);
|
shape.setFill(Color.TRANSPARENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
String strokeVal = inlineStyle.getOrDefault("stroke", el.hasAttribute("stroke") ? el.getAttribute("stroke") : null);
|
String strokeVal = inlineStyle.getOrDefault("stroke", el.hasAttribute("stroke") ? el.getAttribute("stroke") : null);
|
||||||
if (strokeVal != null) {
|
if (strokeVal != null) {
|
||||||
shape.setStroke(parsePaint(strokeVal, defsMap, Color.TRANSPARENT));
|
if ("currentColor".equalsIgnoreCase(strokeVal.trim()) && ctx != null) {
|
||||||
|
ctx.dynamicStrokeShapes().add(shape);
|
||||||
|
}
|
||||||
|
shape.setStroke(parsePaint(strokeVal, ctx, Color.TRANSPARENT));
|
||||||
}
|
}
|
||||||
|
|
||||||
String strokeWidthVal = inlineStyle.getOrDefault("stroke-width", el.hasAttribute("stroke-width") ? el.getAttribute("stroke-width") : null);
|
String strokeWidthVal = inlineStyle.getOrDefault("stroke-width", el.hasAttribute("stroke-width") ? el.getAttribute("stroke-width") : null);
|
||||||
@@ -629,20 +745,20 @@ public final class AssetManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Paint parsePaint(String str, Map<String, Paint> defsMap, Paint fallback) {
|
private static Paint parsePaint(String str, RenderContext ctx, Paint fallback) {
|
||||||
if (str == null || str.isBlank() || "none".equalsIgnoreCase(str)) {
|
if (str == null || str.isBlank() || "none".equalsIgnoreCase(str)) {
|
||||||
return Color.TRANSPARENT;
|
return Color.TRANSPARENT;
|
||||||
}
|
}
|
||||||
String trimmed = str.trim();
|
String trimmed = str.trim();
|
||||||
if (trimmed.startsWith("url(#") && trimmed.endsWith(")")) {
|
if (trimmed.startsWith("url(#") && trimmed.endsWith(")")) {
|
||||||
String id = trimmed.substring(5, trimmed.length() - 1).trim();
|
String id = trimmed.substring(5, trimmed.length() - 1).trim();
|
||||||
Paint defPaint = defsMap.get(id);
|
Paint defPaint = ctx != null && ctx.defsMap() != null ? ctx.defsMap().get(id) : null;
|
||||||
if (defPaint != null) {
|
if (defPaint != null) {
|
||||||
return defPaint;
|
return defPaint;
|
||||||
}
|
}
|
||||||
return Color.web("#0288d1");
|
return Color.web("#0288d1");
|
||||||
}
|
}
|
||||||
return parseColor(trimmed, fallback instanceof Color ? (Color) fallback : Color.BLACK);
|
return parseColor(trimmed, fallback instanceof Color ? (Color) fallback : Color.BLACK, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void parsePoints(String ptsStr, java.util.List<Double> pointsList) {
|
private static void parsePoints(String ptsStr, java.util.List<Double> pointsList) {
|
||||||
@@ -658,12 +774,19 @@ public final class AssetManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Color parseColor(String str, Color fallback) {
|
private static Color parseColor(String str, Color fallback) {
|
||||||
|
return parseColor(str, fallback, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Color parseColor(String str, Color fallback, RenderContext ctx) {
|
||||||
if (str == null || str.isBlank() || "none".equalsIgnoreCase(str)) {
|
if (str == null || str.isBlank() || "none".equalsIgnoreCase(str)) {
|
||||||
return Color.TRANSPARENT;
|
return Color.TRANSPARENT;
|
||||||
}
|
}
|
||||||
String trimmed = str.trim();
|
String trimmed = str.trim();
|
||||||
if ("currentColor".equalsIgnoreCase(trimmed)) {
|
if ("currentColor".equalsIgnoreCase(trimmed)) {
|
||||||
return Color.web("#0288d1");
|
if (ctx != null && ctx.currentColor() != null) {
|
||||||
|
return ctx.currentColor();
|
||||||
|
}
|
||||||
|
return Color.web("#1E293B");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return Color.web(trimmed);
|
return Color.web(trimmed);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package br.com.cybercore.signer.ui.components;
|
|||||||
import br.com.cybercore.signer.core.domain.CertificateStatus;
|
import br.com.cybercore.signer.core.domain.CertificateStatus;
|
||||||
import br.com.cybercore.signer.core.domain.CertificateType;
|
import br.com.cybercore.signer.core.domain.CertificateType;
|
||||||
import br.com.cybercore.signer.core.domain.ManagedCertificate;
|
import br.com.cybercore.signer.core.domain.ManagedCertificate;
|
||||||
|
import javafx.scene.Node;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
@@ -34,24 +35,31 @@ public final class CertificateCardNode extends VBox {
|
|||||||
setCursor(javafx.scene.Cursor.HAND);
|
setCursor(javafx.scene.Cursor.HAND);
|
||||||
setSpacing(8);
|
setSpacing(8);
|
||||||
|
|
||||||
// Linha 1: Nome do Titular e Badge A1/A3
|
// Linha 1: Ícone + Nome do Titular e Badge A1/A3
|
||||||
HBox topRow = new HBox(10);
|
HBox topRow = new HBox(10);
|
||||||
topRow.setAlignment(Pos.CENTER_LEFT);
|
topRow.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
|
Node certIcon = Icons.certificates(Icons.SIZE_20);
|
||||||
|
|
||||||
String rawName = certificate.commonName() != null ? certificate.commonName() : certificate.alias();
|
String rawName = certificate.commonName() != null ? certificate.commonName() : certificate.alias();
|
||||||
String cleanTitle = rawName != null && rawName.contains(":") ? rawName.split(":")[0].trim() : (rawName != null ? rawName.trim() : "Certificado Digital");
|
String cleanTitle = rawName != null && rawName.contains(":") ? rawName.split(":")[0].trim() : (rawName != null ? rawName.trim() : "Certificado Digital");
|
||||||
Label titleLabel = new Label(cleanTitle);
|
Label titleLabel = new Label(cleanTitle);
|
||||||
titleLabel.getStyleClass().add("cert-title");
|
titleLabel.getStyleClass().add("cert-title");
|
||||||
HBox.setHgrow(titleLabel, Priority.ALWAYS);
|
HBox.setHgrow(titleLabel, Priority.ALWAYS);
|
||||||
|
|
||||||
Label badgeLabel = new Label("[" + certificate.type().getCode() + "]");
|
HBox titleBox = new HBox(8, certIcon, titleLabel);
|
||||||
|
titleBox.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
HBox.setHgrow(titleBox, Priority.ALWAYS);
|
||||||
|
|
||||||
|
String badgeText = certificate.type() == CertificateType.A1 ? "A1 • SOFTWARE" : "A3 • TOKEN";
|
||||||
|
Label badgeLabel = new Label(badgeText);
|
||||||
if (certificate.type() == CertificateType.A1) {
|
if (certificate.type() == CertificateType.A1) {
|
||||||
badgeLabel.getStyleClass().add("cert-badge-a1");
|
badgeLabel.getStyleClass().add("cert-badge-a1");
|
||||||
} else {
|
} else {
|
||||||
badgeLabel.getStyleClass().add("cert-badge-a3");
|
badgeLabel.getStyleClass().add("cert-badge-a3");
|
||||||
}
|
}
|
||||||
|
|
||||||
topRow.getChildren().addAll(titleLabel, badgeLabel);
|
topRow.getChildren().addAll(titleBox, badgeLabel);
|
||||||
|
|
||||||
// Linha 2: Documento (CPF / CNPJ) e Emissor
|
// Linha 2: Documento (CPF / CNPJ) e Emissor
|
||||||
HBox midRow = new HBox(12);
|
HBox midRow = new HBox(12);
|
||||||
|
|||||||
@@ -0,0 +1,159 @@
|
|||||||
|
package br.com.cybercore.signer.ui.components;
|
||||||
|
|
||||||
|
import br.com.cybercore.signer.ui.assets.AssetManager;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fachada centralizada e moderna para obtenção de ícones vetoriais padronizados no CyberCore Signer.
|
||||||
|
* <p>
|
||||||
|
* Segue a gramática visual estipulada em {@code docs/icon-system.md}:
|
||||||
|
* grid canônico de 24x24 px, safe area de 2px, stroke 1.75-2.0px, sem textos embutidos
|
||||||
|
* e repintura dinâmica orientada a temas (currentColor).
|
||||||
|
*/
|
||||||
|
public final class Icons {
|
||||||
|
|
||||||
|
/** Tamanho micro (16x16 px) para badges, tags e botões compactos. */
|
||||||
|
public static final int SIZE_16 = 16;
|
||||||
|
|
||||||
|
/** Tamanho pequeno (20x20 px) para botões auxiliares e itens compactos. */
|
||||||
|
public static final int SIZE_20 = 20;
|
||||||
|
|
||||||
|
/** Tamanho padrão canônico (24x24 px) para botões de diálogo e barra lateral. */
|
||||||
|
public static final int SIZE_24 = 24;
|
||||||
|
|
||||||
|
/** Tamanho médio (32x32 px) para cabeçalhos e diálogos de aprovação CCSP. */
|
||||||
|
public static final int SIZE_32 = 32;
|
||||||
|
|
||||||
|
/** Tamanho grande (40x40 px) para cards e botões do painel principal (Hub). */
|
||||||
|
public static final int SIZE_40 = 40;
|
||||||
|
|
||||||
|
private Icons() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retorna um ícone vetorial pelo nome no tamanho especificado, com repintura dinâmica de tema.
|
||||||
|
*/
|
||||||
|
public static Node of(String name, int size) {
|
||||||
|
return of(name, size, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retorna um ícone vetorial pelo nome no tamanho especificado, aplicando uma tinta (tint) fixa.
|
||||||
|
*/
|
||||||
|
public static Node of(String name, int size, Color tint) {
|
||||||
|
return of(name, size, tint, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retorna um ícone vetorial com fallback customizado.
|
||||||
|
*/
|
||||||
|
public static Node of(String name, int size, Color tint, Supplier<Node> fallbackSupplier) {
|
||||||
|
return AssetManager.getIconOrDefault(name, size, tint, fallbackSupplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// MÉTODOS DE CONVENIÊNCIA POR SEMÂNTICA VISUAL
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
public static Node pdf() { return pdf(SIZE_24); }
|
||||||
|
public static Node pdf(int size) { return of("pdf", size); }
|
||||||
|
public static Node pdf(int size, Color tint) { return of("pdf", size, tint); }
|
||||||
|
|
||||||
|
public static Node xml() { return xml(SIZE_24); }
|
||||||
|
public static Node xml(int size) { return of("xml", size); }
|
||||||
|
|
||||||
|
public static Node file() { return file(SIZE_24); }
|
||||||
|
public static Node file(int size) { return of("file", size); }
|
||||||
|
|
||||||
|
public static Node attached() { return attached(SIZE_24); }
|
||||||
|
public static Node attached(int size) { return of("attached", size); }
|
||||||
|
|
||||||
|
public static Node batch() { return batch(SIZE_24); }
|
||||||
|
public static Node batch(int size) { return of("batch", size); }
|
||||||
|
|
||||||
|
public static Node coSign() { return coSign(SIZE_24); }
|
||||||
|
public static Node coSign(int size) { return of("co-sign", size); }
|
||||||
|
|
||||||
|
public static Node timestamp() { return timestamp(SIZE_24); }
|
||||||
|
public static Node timestamp(int size) { return of("timestamp", size); }
|
||||||
|
|
||||||
|
public static Node ltv() { return ltv(SIZE_24); }
|
||||||
|
public static Node ltv(int size) { return of("ltv", size); }
|
||||||
|
|
||||||
|
public static Node validate() { return validate(SIZE_24); }
|
||||||
|
public static Node validate(int size) { return of("validate", size); }
|
||||||
|
|
||||||
|
public static Node validateTimestamp() { return validateTimestamp(SIZE_24); }
|
||||||
|
public static Node validateTimestamp(int size) { return of("validate-timestamp", size); }
|
||||||
|
|
||||||
|
public static Node seal() { return seal(SIZE_24); }
|
||||||
|
public static Node seal(int size) { return of("seal", size); }
|
||||||
|
|
||||||
|
public static Node sealIcpBrasil() { return sealIcpBrasil(SIZE_24); }
|
||||||
|
public static Node sealIcpBrasil(int size) { return of("seal-icp-brasil", size); }
|
||||||
|
|
||||||
|
public static Node certificates() { return certificates(SIZE_24); }
|
||||||
|
public static Node certificates(int size) { return of("cert", size); }
|
||||||
|
|
||||||
|
public static Node shield() { return shield(SIZE_24); }
|
||||||
|
public static Node shield(int size) { return of("shield", size); }
|
||||||
|
|
||||||
|
public static Node settings() { return settings(SIZE_24); }
|
||||||
|
public static Node settings(int size) { return of("settings", size); }
|
||||||
|
|
||||||
|
public static Node logs() { return logs(SIZE_24); }
|
||||||
|
public static Node logs(int size) { return of("logs", size); }
|
||||||
|
|
||||||
|
public static Node tutorial() { return tutorial(SIZE_24); }
|
||||||
|
public static Node tutorial(int size) { return of("tutorial", size); }
|
||||||
|
|
||||||
|
public static Node about() { return about(SIZE_24); }
|
||||||
|
public static Node about(int size) { return of("about", size); }
|
||||||
|
|
||||||
|
public static Node exit() { return exit(SIZE_24); }
|
||||||
|
public static Node exit(int size) { return of("exit", size); }
|
||||||
|
|
||||||
|
public static Node refresh() { return refresh(SIZE_16); }
|
||||||
|
public static Node refresh(int size) { return of("refresh", size); }
|
||||||
|
|
||||||
|
public static Node arrowRight() { return arrowRight(SIZE_16); }
|
||||||
|
public static Node arrowRight(int size) { return of("arrow-right", size); }
|
||||||
|
|
||||||
|
public static Node close() { return close(SIZE_16); }
|
||||||
|
public static Node close(int size) { return of("close", size); }
|
||||||
|
|
||||||
|
public static Node check() { return check(SIZE_16); }
|
||||||
|
public static Node check(int size) { return of("check", size); }
|
||||||
|
|
||||||
|
public static Node error() { return error(SIZE_16); }
|
||||||
|
public static Node error(int size) { return of("error", size); }
|
||||||
|
|
||||||
|
public static Node star() { return star(SIZE_16); }
|
||||||
|
public static Node star(int size) { return of("star", size); }
|
||||||
|
|
||||||
|
public static Node zoomIn() { return zoomIn(SIZE_16); }
|
||||||
|
public static Node zoomIn(int size) { return of("zoom-in", size); }
|
||||||
|
|
||||||
|
public static Node zoomOut() { return zoomOut(SIZE_16); }
|
||||||
|
public static Node zoomOut(int size) { return of("zoom-out", size); }
|
||||||
|
|
||||||
|
public static Node zoomFit() { return zoomFit(SIZE_16); }
|
||||||
|
public static Node zoomFit(int size) { return of("zoom-fit", size); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logotipo institucional principal do CyberCore Signer.
|
||||||
|
*/
|
||||||
|
public static Node appLogo(double width, double height) {
|
||||||
|
return AssetManager.getLogoOrDefault(width, height, () -> SvgIcons.buildAppCrystalLogo(Math.min(width, height)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logotipo de cristal CyberCore.
|
||||||
|
*/
|
||||||
|
public static Node appCrystal(double size) {
|
||||||
|
return SvgIcons.createAppCrystalLogo(size);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,12 +14,14 @@ import javafx.scene.shape.Circle;
|
|||||||
import javafx.scene.shape.Polygon;
|
import javafx.scene.shape.Polygon;
|
||||||
import javafx.scene.shape.Rectangle;
|
import javafx.scene.shape.Rectangle;
|
||||||
import javafx.scene.shape.SVGPath;
|
import javafx.scene.shape.SVGPath;
|
||||||
|
import javafx.scene.shape.StrokeLineCap;
|
||||||
|
import javafx.scene.shape.StrokeLineJoin;
|
||||||
import javafx.scene.text.Font;
|
import javafx.scene.text.Font;
|
||||||
import javafx.scene.text.FontWeight;
|
import javafx.scene.text.FontWeight;
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fábrica de ícones e grafismos vetoriais SVG nítidos e escaláveis para a interface Serpro do CyberCore Signer.
|
* Fábrica de ícones e grafismos vetoriais SVG nítidos e escaláveis para a interface do CyberCore Signer.
|
||||||
* Integra-se ao AssetManager para carregar dinamicamente arquivos .svg ou .png da pasta 'assets/icons/'.
|
* Integra-se ao AssetManager para carregar dinamicamente arquivos .svg ou .png da pasta 'assets/icons/'.
|
||||||
*/
|
*/
|
||||||
public final class SvgIcons {
|
public final class SvgIcons {
|
||||||
@@ -477,11 +479,21 @@ public final class SvgIcons {
|
|||||||
StackPane box = new StackPane();
|
StackPane box = new StackPane();
|
||||||
box.setPrefSize(24, 24);
|
box.setPrefSize(24, 24);
|
||||||
|
|
||||||
Text code = new Text("</>");
|
SVGPath frame = new SVGPath();
|
||||||
code.setFont(Font.font("Segoe UI", FontWeight.BOLD, 12));
|
frame.setContent("M 3 5 C 3 3.9 3.9 3 5 3 L 19 3 C 20.1 3 21 3.9 21 5 L 21 19 C 21 20.1 20.1 21 19 21 L 5 21 C 3.9 21 3 20.1 3 19 Z");
|
||||||
code.setFill(Color.web("#0288d1"));
|
frame.setFill(Color.TRANSPARENT);
|
||||||
|
frame.setStroke(Color.web("#1e293b"));
|
||||||
|
frame.setStrokeWidth(1.8);
|
||||||
|
|
||||||
box.getChildren().add(code);
|
SVGPath prompt = new SVGPath();
|
||||||
|
prompt.setContent("M 7 9 L 10 12 L 7 15 M 12 15 L 16 15");
|
||||||
|
prompt.setFill(Color.TRANSPARENT);
|
||||||
|
prompt.setStroke(Color.web("#0288d1"));
|
||||||
|
prompt.setStrokeWidth(1.8);
|
||||||
|
prompt.setStrokeLineCap(StrokeLineCap.ROUND);
|
||||||
|
prompt.setStrokeLineJoin(StrokeLineJoin.ROUND);
|
||||||
|
|
||||||
|
box.getChildren().addAll(frame, prompt);
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -538,20 +550,21 @@ public final class SvgIcons {
|
|||||||
StackPane box = new StackPane();
|
StackPane box = new StackPane();
|
||||||
box.setPrefSize(24, 24);
|
box.setPrefSize(24, 24);
|
||||||
|
|
||||||
SVGPath folder = new SVGPath();
|
SVGPath certFrame = new SVGPath();
|
||||||
folder.setContent("M 2 5 L 8 5 L 10 7 L 22 7 L 22 20 L 2 20 Z");
|
certFrame.setContent("M 4 4 C 4 3 5 2 6 2 L 18 2 C 19 2 20 3 20 4 L 20 20 C 20 21 19 22 18 22 L 6 22 C 5 22 4 21 4 20 Z M 7 6 L 14 6 M 7 9 L 12 9");
|
||||||
folder.setFill(Color.web("#e1f5fe"));
|
certFrame.setFill(Color.TRANSPARENT);
|
||||||
folder.setStroke(Color.web("#0288d1"));
|
certFrame.setStroke(Color.web("#1e293b"));
|
||||||
folder.setStrokeWidth(1.5);
|
certFrame.setStrokeWidth(1.8);
|
||||||
|
|
||||||
Rectangle lock = new Rectangle(7, 6);
|
SVGPath badge = new SVGPath();
|
||||||
lock.setArcWidth(2);
|
badge.setContent("M 15 13 A 3.2 3.2 0 1 1 15 13.01 M 13.5 15 L 14.8 16.2 L 17 14");
|
||||||
lock.setArcHeight(2);
|
badge.setFill(Color.TRANSPARENT);
|
||||||
lock.setFill(Color.web("#0288d1"));
|
badge.setStroke(Color.web("#0288d1"));
|
||||||
lock.setTranslateX(3);
|
badge.setStrokeWidth(1.6);
|
||||||
lock.setTranslateY(2);
|
badge.setStrokeLineCap(StrokeLineCap.ROUND);
|
||||||
|
badge.setStrokeLineJoin(StrokeLineJoin.ROUND);
|
||||||
|
|
||||||
box.getChildren().addAll(folder, lock);
|
box.getChildren().addAll(certFrame, badge);
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -577,63 +590,6 @@ public final class SvgIcons {
|
|||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Ícone de Sol (Light Mode).
|
|
||||||
*/
|
|
||||||
public static Node sunIcon(double size) {
|
|
||||||
return AssetManager.getIconOrDefault("sun", size, () -> buildSunIcon(size));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Node buildSunIcon(double size) {
|
|
||||||
StackPane box = new StackPane();
|
|
||||||
box.setPrefSize(size, size);
|
|
||||||
box.setMaxSize(size, size);
|
|
||||||
|
|
||||||
Circle sunCore = new Circle(size * 0.5, size * 0.5, size * 0.24);
|
|
||||||
sunCore.setFill(new LinearGradient(
|
|
||||||
0, 0, 1, 1, true, CycleMethod.NO_CYCLE,
|
|
||||||
new Stop(0, Color.web("#f59e0b")),
|
|
||||||
new Stop(1, Color.web("#d97706"))
|
|
||||||
));
|
|
||||||
|
|
||||||
SVGPath rays = new SVGPath();
|
|
||||||
rays.setContent("M 12 2 L 12 5 M 12 19 L 12 22 M 2 12 L 5 12 M 19 12 L 22 12 " +
|
|
||||||
"M 4.93 4.93 L 7.05 7.05 M 16.95 16.95 L 19.07 19.07 " +
|
|
||||||
"M 4.93 19.07 L 7.05 16.95 M 16.95 7.05 L 19.07 4.93");
|
|
||||||
rays.setStroke(Color.web("#f59e0b"));
|
|
||||||
rays.setStrokeWidth(2.0);
|
|
||||||
rays.setScaleX(size / 24.0);
|
|
||||||
rays.setScaleY(size / 24.0);
|
|
||||||
|
|
||||||
box.getChildren().addAll(rays, sunCore);
|
|
||||||
return box;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ícone de Lua (Dark Mode).
|
|
||||||
*/
|
|
||||||
public static Node moonIcon(double size) {
|
|
||||||
return AssetManager.getIconOrDefault("moon", size, () -> buildMoonIcon(size));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Node buildMoonIcon(double size) {
|
|
||||||
StackPane box = new StackPane();
|
|
||||||
box.setPrefSize(size, size);
|
|
||||||
box.setMaxSize(size, size);
|
|
||||||
|
|
||||||
SVGPath moon = new SVGPath();
|
|
||||||
moon.setContent("M 21 12.79 A 9 9 0 1 1 11.21 3 A 7 7 0 0 0 21 12.79 Z");
|
|
||||||
moon.setFill(new LinearGradient(
|
|
||||||
0, 0, 1, 1, true, CycleMethod.NO_CYCLE,
|
|
||||||
new Stop(0, Color.web("#38bdf8")),
|
|
||||||
new Stop(1, Color.web("#818cf8"))
|
|
||||||
));
|
|
||||||
moon.setScaleX(size / 24.0);
|
|
||||||
moon.setScaleY(size / 24.0);
|
|
||||||
|
|
||||||
box.getChildren().add(moon);
|
|
||||||
return box;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logotipo vetorial CyberShield.
|
* Logotipo vetorial CyberShield.
|
||||||
@@ -681,23 +637,31 @@ public final class SvgIcons {
|
|||||||
StackPane box = new StackPane();
|
StackPane box = new StackPane();
|
||||||
box.setPrefSize(38, 38);
|
box.setPrefSize(38, 38);
|
||||||
|
|
||||||
SVGPath ribbon = new SVGPath();
|
SVGPath frame = new SVGPath();
|
||||||
ribbon.setContent("M 14 26 L 19 36 L 24 26 Z M 9 26 L 12 34 L 15 26 Z");
|
frame.setContent("M 6 8 C 4.9 8 4 8.9 4 10 L 4 28 C 4 29.1 4.9 30 6 30 L 32 30 C 33.1 30 34 29.1 34 28 L 34 10 C 34 8.9 33.1 8 32 8 Z");
|
||||||
ribbon.setFill(Color.web("#f59e0b"));
|
frame.setFill(Color.TRANSPARENT);
|
||||||
|
frame.setStroke(Color.web("#1e293b"));
|
||||||
|
frame.setStrokeWidth(1.8);
|
||||||
|
|
||||||
Circle sealCircle = new Circle(13);
|
SVGPath lines = new SVGPath();
|
||||||
sealCircle.setFill(Color.web("#d97706"));
|
lines.setContent("M 8 13 L 20 13 M 8 25 L 30 25");
|
||||||
sealCircle.setStroke(Color.web("#fbbf24"));
|
lines.setFill(Color.TRANSPARENT);
|
||||||
sealCircle.setStrokeWidth(1.6);
|
lines.setStroke(Color.web("#64748b"));
|
||||||
sealCircle.setTranslateY(-4);
|
lines.setStrokeWidth(1.4);
|
||||||
|
lines.setStrokeLineCap(StrokeLineCap.ROUND);
|
||||||
|
|
||||||
SVGPath star = new SVGPath();
|
Circle dot = new Circle(28, 13, 2);
|
||||||
star.setContent("M 19 8 L 21 13 L 26 13 L 22 16 L 24 21 L 19 18 L 14 21 L 16 16 L 12 13 L 17 13 Z");
|
dot.setFill(Color.web("#f59e0b"));
|
||||||
star.setFill(Color.web("#ffffff"));
|
|
||||||
star.setTranslateX(-9);
|
|
||||||
star.setTranslateY(-14);
|
|
||||||
|
|
||||||
box.getChildren().addAll(ribbon, sealCircle, star);
|
SVGPath signature = new SVGPath();
|
||||||
|
signature.setContent("M 8 20 C 11 16 13 16 15 20 C 17 23 20 17 23 18");
|
||||||
|
signature.setFill(Color.TRANSPARENT);
|
||||||
|
signature.setStroke(Color.web("#0288d1"));
|
||||||
|
signature.setStrokeWidth(2.0);
|
||||||
|
signature.setStrokeLineCap(StrokeLineCap.ROUND);
|
||||||
|
signature.setStrokeLineJoin(StrokeLineJoin.ROUND);
|
||||||
|
|
||||||
|
box.getChildren().addAll(frame, lines, dot, signature);
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,70 +4,19 @@ import br.com.cybercore.signer.ui.CyberCoreUiConstants;
|
|||||||
import javafx.scene.Parent;
|
import javafx.scene.Parent;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gerenciador centralizado de temas visuais do CyberCore Signer.
|
* Utilitário para aplicação da folha de estilo global do CyberCore Signer à Scene.
|
||||||
* Permite alternar dinamicamente entre Tema Claro (Light) e Tema Escuro (Dark)
|
* Gerencia a folha de estilo padrão única do aplicativo.
|
||||||
* aplicando as classes CSS e folhas de estilo correspondentes em qualquer Scene.
|
|
||||||
*/
|
*/
|
||||||
public final class ThemeManager {
|
public final class ThemeManager {
|
||||||
|
|
||||||
public enum AppTheme {
|
|
||||||
LIGHT("theme-light", "Modo Claro"),
|
|
||||||
DARK("theme-dark", "Modo Escuro");
|
|
||||||
|
|
||||||
private final String cssClass;
|
|
||||||
private final String displayName;
|
|
||||||
|
|
||||||
AppTheme(String cssClass, String displayName) {
|
|
||||||
this.cssClass = cssClass;
|
|
||||||
this.displayName = displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCssClass() {
|
|
||||||
return cssClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDisplayName() {
|
|
||||||
return displayName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static AppTheme currentTheme = AppTheme.LIGHT;
|
|
||||||
private static final List<Consumer<AppTheme>> listeners = new ArrayList<>();
|
|
||||||
|
|
||||||
private ThemeManager() {
|
private ThemeManager() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AppTheme getCurrentTheme() {
|
|
||||||
return currentTheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isDark() {
|
|
||||||
return currentTheme == AppTheme.DARK;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setCurrentTheme(AppTheme theme) {
|
|
||||||
if (theme == null || theme == currentTheme) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
currentTheme = theme;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AppTheme toggleTheme() {
|
|
||||||
currentTheme = (currentTheme == AppTheme.LIGHT) ? AppTheme.DARK : AppTheme.LIGHT;
|
|
||||||
notifyListeners();
|
|
||||||
return currentTheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aplica o tema atualmente ativo a uma Scene, adicionando a folha de estilo global
|
* Aplica a folha de estilo padrão única a uma Scene.
|
||||||
* e ajustando a classe CSS raiz (theme-light / theme-dark).
|
|
||||||
*/
|
*/
|
||||||
public static void applyTheme(Scene scene) {
|
public static void applyTheme(Scene scene) {
|
||||||
if (scene == null) {
|
if (scene == null) {
|
||||||
@@ -81,40 +30,12 @@ public final class ThemeManager {
|
|||||||
if (!scene.getStylesheets().contains(cssUrl)) {
|
if (!scene.getStylesheets().contains(cssUrl)) {
|
||||||
scene.getStylesheets().add(cssUrl);
|
scene.getStylesheets().add(cssUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
Parent root = scene.getRoot();
|
|
||||||
if (root != null) {
|
|
||||||
applyThemeToNode(root);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aplica a classe de estilo do tema ativo no nó raiz fornecido.
|
* Mantido para compatibilidade com chamadas de estilização de nós raiz.
|
||||||
*/
|
*/
|
||||||
public static void applyThemeToNode(Parent node) {
|
public static void applyThemeToNode(Parent node) {
|
||||||
if (node == null) {
|
// No tema padrão único definido em .root, a vinculação da folha na Scene é suficiente.
|
||||||
return;
|
|
||||||
}
|
|
||||||
node.getStyleClass().removeAll(AppTheme.LIGHT.getCssClass(), AppTheme.DARK.getCssClass());
|
|
||||||
node.getStyleClass().add(currentTheme.getCssClass());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void addListener(Consumer<AppTheme> listener) {
|
|
||||||
if (listener != null && !listeners.contains(listener)) {
|
|
||||||
listeners.add(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void removeListener(Consumer<AppTheme> listener) {
|
|
||||||
listeners.remove(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void notifyListeners() {
|
|
||||||
for (Consumer<AppTheme> listener : new ArrayList<>(listeners)) {
|
|
||||||
try {
|
|
||||||
listener.accept(currentTheme);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,9 +189,15 @@ public class SystemTrayManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gera dinamicamente um ícone vetorial de 32x32 com o padrão de cores CyberCore (#38bdf8).
|
* Obtém o ícone oficial da marca CyberCore em alta resolução para a bandeja do sistema,
|
||||||
|
* com fallback vetorial dinâmico caso o recurso não esteja disponível.
|
||||||
*/
|
*/
|
||||||
private Image createTrayIconImage() {
|
private Image createTrayIconImage() {
|
||||||
|
Image trayAsset = br.com.cybercore.signer.ui.assets.AssetManager.getTrayIconImage();
|
||||||
|
if (trayAsset != null) {
|
||||||
|
return trayAsset;
|
||||||
|
}
|
||||||
|
|
||||||
int size = 32;
|
int size = 32;
|
||||||
BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
|
BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
|
||||||
Graphics2D g = image.createGraphics();
|
Graphics2D g = image.createGraphics();
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ public class A1PasswordDialog {
|
|||||||
VBox root = new VBox(16);
|
VBox root = new VBox(16);
|
||||||
root.getStyleClass().add("dialog-container");
|
root.getStyleClass().add("dialog-container");
|
||||||
root.setPadding(new Insets(24));
|
root.setPadding(new Insets(24));
|
||||||
root.setPrefWidth(440);
|
|
||||||
|
|
||||||
Label header = new Label("IMPORTAR CERTIFICADO A1");
|
Label header = new Label("IMPORTAR CERTIFICADO A1");
|
||||||
header.getStyleClass().add("dialog-header");
|
header.getStyleClass().add("dialog-header");
|
||||||
@@ -70,14 +69,19 @@ public class A1PasswordDialog {
|
|||||||
actionsRow.setAlignment(Pos.CENTER_RIGHT);
|
actionsRow.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
|
||||||
Button btnCancel = new Button("Cancelar");
|
Button btnCancel = new Button("Cancelar");
|
||||||
btnCancel.getStyleClass().add("btn-secondary");
|
btnCancel.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
|
btnCancel.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnCancel.setOnAction(e -> {
|
btnCancel.setOnAction(e -> {
|
||||||
passwordFuture.complete(null);
|
passwordFuture.complete(null);
|
||||||
stage.close();
|
stage.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
HBox spacer = new HBox();
|
||||||
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
|
|
||||||
Button btnConfirm = new Button("Importar");
|
Button btnConfirm = new Button("Importar");
|
||||||
btnConfirm.getStyleClass().add("btn-primary");
|
btnConfirm.getStyleClass().addAll("btn-primary", "btn-dialog");
|
||||||
|
btnConfirm.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnConfirm.setDefaultButton(true);
|
btnConfirm.setDefaultButton(true);
|
||||||
btnConfirm.setOnAction(e -> {
|
btnConfirm.setOnAction(e -> {
|
||||||
String text = passwordField.getText();
|
String text = passwordField.getText();
|
||||||
@@ -85,7 +89,7 @@ public class A1PasswordDialog {
|
|||||||
stage.close();
|
stage.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
actionsRow.getChildren().addAll(btnCancel, btnConfirm);
|
actionsRow.getChildren().addAll(btnCancel, spacer, btnConfirm);
|
||||||
|
|
||||||
root.getChildren().addAll(header, desc, fileBox, inputGroup, actionsRow);
|
root.getChildren().addAll(header, desc, fileBox, inputGroup, actionsRow);
|
||||||
|
|
||||||
@@ -93,6 +97,7 @@ public class A1PasswordDialog {
|
|||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
|
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
stage.setOnCloseRequest(e -> passwordFuture.complete(null));
|
stage.setOnCloseRequest(e -> passwordFuture.complete(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,4 +105,8 @@ public class A1PasswordDialog {
|
|||||||
stage.show();
|
stage.show();
|
||||||
return passwordFuture;
|
return passwordFuture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,15 +69,14 @@ public class ApplicationApprovalDialog {
|
|||||||
Label originTitle = new Label("ORIGEM WEB (URL)");
|
Label originTitle = new Label("ORIGEM WEB (URL)");
|
||||||
originTitle.getStyleClass().add("dialog-field-title");
|
originTitle.getStyleClass().add("dialog-field-title");
|
||||||
Label originValue = new Label(origin != null ? origin : "Origem desconhecida");
|
Label originValue = new Label(origin != null ? origin : "Origem desconhecida");
|
||||||
originValue.setStyle("-fx-text-fill: -fx-accent; -fx-font-size: 13px; -fx-font-family: monospace;");
|
originValue.getStyleClass().addAll("text-accent", "text-mono");
|
||||||
originRow.getChildren().addAll(originTitle, originValue);
|
originRow.getChildren().addAll(originTitle, originValue);
|
||||||
|
|
||||||
VBox idRow = new VBox(2);
|
VBox idRow = new VBox(2);
|
||||||
Label idTitle = new Label("IDENTIFICADOR DA APLICAÇÃO");
|
Label idTitle = new Label("IDENTIFICADOR DA APLICAÇÃO");
|
||||||
idTitle.getStyleClass().add("dialog-field-title");
|
idTitle.getStyleClass().add("dialog-field-title");
|
||||||
Label idValue = new Label(applicationId);
|
Label idValue = new Label(applicationId);
|
||||||
idValue.getStyleClass().add("dialog-field-value");
|
idValue.getStyleClass().addAll("dialog-field-value", "text-mono", "text-small");
|
||||||
idValue.setStyle("-fx-font-size: 12px; -fx-font-family: monospace;");
|
|
||||||
idRow.getChildren().addAll(idTitle, idValue);
|
idRow.getChildren().addAll(idTitle, idValue);
|
||||||
|
|
||||||
detailsBox.getChildren().addAll(appRow, originRow, idRow);
|
detailsBox.getChildren().addAll(appRow, originRow, idRow);
|
||||||
@@ -88,28 +87,33 @@ public class ApplicationApprovalDialog {
|
|||||||
"instalados e solicitar assinaturas. Nenhuma assinatura será realizada sem a sua confirmação explícita na tela."
|
"instalados e solicitar assinaturas. Nenhuma assinatura será realizada sem a sua confirmação explícita na tela."
|
||||||
);
|
);
|
||||||
secWarning.setWrapText(true);
|
secWarning.setWrapText(true);
|
||||||
secWarning.setStyle("-fx-text-fill: -fx-text-secondary; -fx-font-size: 11px; -fx-line-spacing: 2px;");
|
secWarning.getStyleClass().addAll("text-muted", "text-small");
|
||||||
|
|
||||||
// Botões de Ação
|
// Botões de Ação
|
||||||
HBox actionsRow = new HBox(12);
|
HBox actionsRow = new HBox(12);
|
||||||
actionsRow.setAlignment(Pos.CENTER_RIGHT);
|
actionsRow.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
|
||||||
Button btnReject = new Button("Recusar Acesso");
|
Button btnReject = new Button("Recusar Acesso");
|
||||||
btnReject.getStyleClass().add("btn-secondary");
|
btnReject.getStyleClass().addAll("btn-danger-outline", "btn-dialog");
|
||||||
|
btnReject.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnReject.setOnAction(e -> {
|
btnReject.setOnAction(e -> {
|
||||||
approvalFuture.complete(false);
|
approvalFuture.complete(false);
|
||||||
stage.close();
|
stage.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
HBox spacer = new HBox();
|
||||||
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
|
|
||||||
Button btnAuthorize = new Button("Autorizar Conexão");
|
Button btnAuthorize = new Button("Autorizar Conexão");
|
||||||
btnAuthorize.getStyleClass().add("btn-primary");
|
btnAuthorize.getStyleClass().addAll("btn-primary", "btn-dialog");
|
||||||
|
btnAuthorize.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnAuthorize.setDefaultButton(true);
|
btnAuthorize.setDefaultButton(true);
|
||||||
btnAuthorize.setOnAction(e -> {
|
btnAuthorize.setOnAction(e -> {
|
||||||
approvalFuture.complete(true);
|
approvalFuture.complete(true);
|
||||||
stage.close();
|
stage.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
actionsRow.getChildren().addAll(btnReject, btnAuthorize);
|
actionsRow.getChildren().addAll(btnReject, spacer, btnAuthorize);
|
||||||
|
|
||||||
root.getChildren().addAll(header, detailsBox, secWarning, actionsRow);
|
root.getChildren().addAll(header, detailsBox, secWarning, actionsRow);
|
||||||
|
|
||||||
@@ -117,6 +121,7 @@ public class ApplicationApprovalDialog {
|
|||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
|
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
stage.setOnCloseRequest(e -> approvalFuture.complete(false));
|
stage.setOnCloseRequest(e -> approvalFuture.complete(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,4 +130,8 @@ public class ApplicationApprovalDialog {
|
|||||||
stage.toFront();
|
stage.toFront();
|
||||||
return approvalFuture;
|
return approvalFuture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -245,6 +245,7 @@ public final class BatchSigningDialog {
|
|||||||
|
|
||||||
btnChooseOutputDir = new Button("Selecionar...");
|
btnChooseOutputDir = new Button("Selecionar...");
|
||||||
btnChooseOutputDir.getStyleClass().add("btn-secondary");
|
btnChooseOutputDir.getStyleClass().add("btn-secondary");
|
||||||
|
btnChooseOutputDir.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnChooseOutputDir.setOnAction(e -> handleChooseOutputDir());
|
btnChooseOutputDir.setOnAction(e -> handleChooseOutputDir());
|
||||||
|
|
||||||
outputRow.getChildren().addAll(lblOutput, txtOutputDir, btnChooseOutputDir);
|
outputRow.getChildren().addAll(lblOutput, txtOutputDir, btnChooseOutputDir);
|
||||||
@@ -266,16 +267,21 @@ public final class BatchSigningDialog {
|
|||||||
footer.setAlignment(Pos.CENTER_RIGHT);
|
footer.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
|
||||||
Button btnClose = new Button("Fechar");
|
Button btnClose = new Button("Fechar");
|
||||||
btnClose.getStyleClass().add("btn-secondary");
|
btnClose.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
|
btnClose.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnClose.setOnAction(e -> {
|
btnClose.setOnAction(e -> {
|
||||||
if (!isRunning) stage.close();
|
if (!isRunning) stage.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
HBox spacer = new HBox();
|
||||||
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
|
|
||||||
btnStart = new Button("Iniciar Assinatura em Lote");
|
btnStart = new Button("Iniciar Assinatura em Lote");
|
||||||
btnStart.getStyleClass().add("btn-primary");
|
btnStart.getStyleClass().addAll("btn-primary", "btn-dialog");
|
||||||
|
btnStart.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnStart.setOnAction(e -> startBatchSigning());
|
btnStart.setOnAction(e -> startBatchSigning());
|
||||||
|
|
||||||
footer.getChildren().addAll(btnClose, btnStart);
|
footer.getChildren().addAll(btnClose, spacer, btnStart);
|
||||||
|
|
||||||
root.getChildren().addAll(header, fileActions, tableView, configBox, progressBox, footer);
|
root.getChildren().addAll(header, fileActions, tableView, configBox, progressBox, footer);
|
||||||
|
|
||||||
@@ -283,6 +289,7 @@ public final class BatchSigningDialog {
|
|||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
|
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleAddFiles() {
|
private void handleAddFiles() {
|
||||||
|
|||||||
@@ -74,8 +74,7 @@ public class CertificateDetailsDialog {
|
|||||||
// Nome do Titular em destaque
|
// Nome do Titular em destaque
|
||||||
VBox titleBox = new VBox(4);
|
VBox titleBox = new VBox(4);
|
||||||
Label nameLabel = new Label(cert.commonName() != null ? cert.commonName() : cert.alias());
|
Label nameLabel = new Label(cert.commonName() != null ? cert.commonName() : cert.alias());
|
||||||
nameLabel.getStyleClass().add("cert-title");
|
nameLabel.getStyleClass().addAll("dialog-header-title", "text-bold");
|
||||||
nameLabel.setStyle("-fx-font-size: 16px; -fx-font-weight: bold;");
|
|
||||||
|
|
||||||
String docInfo = "";
|
String docInfo = "";
|
||||||
if (cert.cpf() != null) {
|
if (cert.cpf() != null) {
|
||||||
@@ -121,7 +120,7 @@ public class CertificateDetailsDialog {
|
|||||||
|
|
||||||
ScrollPane scroll = new ScrollPane(detailsBox);
|
ScrollPane scroll = new ScrollPane(detailsBox);
|
||||||
scroll.setFitToWidth(true);
|
scroll.setFitToWidth(true);
|
||||||
scroll.setStyle("-fx-background: transparent; -fx-background-color: transparent;");
|
scroll.getStyleClass().add("scroll-transparent");
|
||||||
VBox.setVgrow(scroll, Priority.ALWAYS);
|
VBox.setVgrow(scroll, Priority.ALWAYS);
|
||||||
|
|
||||||
// Botões de Ação
|
// Botões de Ação
|
||||||
@@ -129,9 +128,8 @@ public class CertificateDetailsDialog {
|
|||||||
actionsRow.setAlignment(Pos.CENTER_RIGHT);
|
actionsRow.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
|
||||||
Button btnClose = new Button(" Fechar", SvgIcons.closeIcon(13));
|
Button btnClose = new Button(" Fechar", SvgIcons.closeIcon(13));
|
||||||
btnClose.getStyleClass().addAll("btn-secondary", "btn-dialog");
|
btnClose.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
btnClose.setPrefWidth(110);
|
btnClose.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnClose.setMinWidth(110);
|
|
||||||
btnClose.setOnAction(e -> stage.close());
|
btnClose.setOnAction(e -> stage.close());
|
||||||
|
|
||||||
actionsRow.getChildren().add(btnClose);
|
actionsRow.getChildren().add(btnClose);
|
||||||
@@ -142,12 +140,17 @@ public class CertificateDetailsDialog {
|
|||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
|
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void show() {
|
public void show() {
|
||||||
stage.show();
|
stage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
|
|
||||||
private void addField(GridPane grid, int row, String label, String value) {
|
private void addField(GridPane grid, int row, String label, String value) {
|
||||||
Label lbl = new Label(label);
|
Label lbl = new Label(label);
|
||||||
lbl.getStyleClass().add("dialog-field-title");
|
lbl.getStyleClass().add("dialog-field-title");
|
||||||
|
|||||||
@@ -75,10 +75,12 @@ public final class CertificatesManagerDialog {
|
|||||||
|
|
||||||
Button btnAddA1 = new Button("Importar A1 (.pfx / .p12)...");
|
Button btnAddA1 = new Button("Importar A1 (.pfx / .p12)...");
|
||||||
btnAddA1.getStyleClass().add("btn-primary");
|
btnAddA1.getStyleClass().add("btn-primary");
|
||||||
|
btnAddA1.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnAddA1.setOnAction(e -> handleImportA1());
|
btnAddA1.setOnAction(e -> handleImportA1());
|
||||||
|
|
||||||
Button btnRefresh = new Button(" Atualizar Lista", SvgIcons.refreshIcon(14));
|
Button btnRefresh = new Button(" Atualizar Lista", SvgIcons.refreshIcon(14));
|
||||||
btnRefresh.getStyleClass().add("btn-secondary");
|
btnRefresh.getStyleClass().add("btn-secondary");
|
||||||
|
btnRefresh.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnRefresh.setOnAction(e -> refreshList());
|
btnRefresh.setOnAction(e -> refreshList());
|
||||||
|
|
||||||
actions.getChildren().addAll(btnAddA1, btnRefresh);
|
actions.getChildren().addAll(btnAddA1, btnRefresh);
|
||||||
@@ -86,7 +88,7 @@ public final class CertificatesManagerDialog {
|
|||||||
// Container com Scroll
|
// Container com Scroll
|
||||||
ScrollPane scroll = new ScrollPane(certificatesContainer);
|
ScrollPane scroll = new ScrollPane(certificatesContainer);
|
||||||
scroll.setFitToWidth(true);
|
scroll.setFitToWidth(true);
|
||||||
scroll.setStyle("-fx-background: transparent; -fx-background-color: transparent;");
|
scroll.getStyleClass().add("scroll-transparent");
|
||||||
VBox.setVgrow(scroll, Priority.ALWAYS);
|
VBox.setVgrow(scroll, Priority.ALWAYS);
|
||||||
|
|
||||||
refreshList();
|
refreshList();
|
||||||
@@ -95,6 +97,7 @@ public final class CertificatesManagerDialog {
|
|||||||
footer.setAlignment(Pos.CENTER_RIGHT);
|
footer.setAlignment(Pos.CENTER_RIGHT);
|
||||||
Button btnClose = new Button(" Fechar", SvgIcons.closeIcon(13));
|
Button btnClose = new Button(" Fechar", SvgIcons.closeIcon(13));
|
||||||
btnClose.getStyleClass().add("btn-secondary");
|
btnClose.getStyleClass().add("btn-secondary");
|
||||||
|
btnClose.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnClose.setOnAction(e -> stage.close());
|
btnClose.setOnAction(e -> stage.close());
|
||||||
footer.getChildren().add(btnClose);
|
footer.getChildren().add(btnClose);
|
||||||
|
|
||||||
@@ -104,6 +107,7 @@ public final class CertificatesManagerDialog {
|
|||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
|
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshList() {
|
private void refreshList() {
|
||||||
@@ -189,4 +193,8 @@ public final class CertificatesManagerDialog {
|
|||||||
public void show() {
|
public void show() {
|
||||||
stage.show();
|
stage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ public final class DiagnosticsView extends VBox {
|
|||||||
// Container de Itens
|
// Container de Itens
|
||||||
ScrollPane scrollPane = new ScrollPane(itemsContainer);
|
ScrollPane scrollPane = new ScrollPane(itemsContainer);
|
||||||
scrollPane.setFitToWidth(true);
|
scrollPane.setFitToWidth(true);
|
||||||
scrollPane.setStyle("-fx-background: transparent; -fx-background-color: transparent;");
|
scrollPane.getStyleClass().add("scroll-transparent");
|
||||||
VBox.setVgrow(scrollPane, Priority.ALWAYS);
|
VBox.setVgrow(scrollPane, Priority.ALWAYS);
|
||||||
|
|
||||||
getChildren().addAll(headerBox, actionsRow, scrollPane);
|
getChildren().addAll(headerBox, actionsRow, scrollPane);
|
||||||
|
|||||||
@@ -37,8 +37,6 @@ public final class ExitConfirmationDialog {
|
|||||||
}
|
}
|
||||||
this.stage.setTitle("CyberCore Signer — Encerrar ou Manter na Bandeja");
|
this.stage.setTitle("CyberCore Signer — Encerrar ou Manter na Bandeja");
|
||||||
this.stage.setResizable(false);
|
this.stage.setResizable(false);
|
||||||
this.stage.setMinWidth(520);
|
|
||||||
this.stage.setMinHeight(190);
|
|
||||||
|
|
||||||
initLayout();
|
initLayout();
|
||||||
}
|
}
|
||||||
@@ -54,13 +52,13 @@ public final class ExitConfirmationDialog {
|
|||||||
|
|
||||||
VBox titleBox = new VBox(4);
|
VBox titleBox = new VBox(4);
|
||||||
Label title = new Label("Como você deseja proceder?");
|
Label title = new Label("Como você deseja proceder?");
|
||||||
title.setStyle("-fx-font-size: 15px; -fx-font-weight: bold;");
|
title.getStyleClass().addAll("dialog-header-title", "text-bold");
|
||||||
|
|
||||||
Label subtitle = new Label(
|
Label subtitle = new Label(
|
||||||
"Você pode manter o CyberCore Signer ativo na bandeja do sistema\n" +
|
"Você pode manter o CyberCore Signer ativo na bandeja do sistema\n" +
|
||||||
"para atender assinaturas de aplicações web, ou encerrá-lo por completo."
|
"para atender assinaturas de aplicações web, ou encerrá-lo por completo."
|
||||||
);
|
);
|
||||||
subtitle.setStyle("-fx-text-fill: -fx-text-secondary; -fx-font-size: 12px;");
|
subtitle.getStyleClass().addAll("dialog-subtitle", "text-muted");
|
||||||
|
|
||||||
titleBox.getChildren().addAll(title, subtitle);
|
titleBox.getChildren().addAll(title, subtitle);
|
||||||
header.getChildren().addAll(SvgIcons.cyberShieldLogo(38), titleBox);
|
header.getChildren().addAll(SvgIcons.cyberShieldLogo(38), titleBox);
|
||||||
@@ -70,7 +68,8 @@ public final class ExitConfirmationDialog {
|
|||||||
actionsRow.setAlignment(Pos.CENTER_RIGHT);
|
actionsRow.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
|
||||||
Button btnCancel = new Button("Cancelar");
|
Button btnCancel = new Button("Cancelar");
|
||||||
btnCancel.getStyleClass().addAll("btn-cancel", "btn-dialog");
|
btnCancel.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
|
btnCancel.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnCancel.setOnAction(e -> {
|
btnCancel.setOnAction(e -> {
|
||||||
chosenAction = ExitChoice.CANCEL;
|
chosenAction = ExitChoice.CANCEL;
|
||||||
stage.close();
|
stage.close();
|
||||||
@@ -80,7 +79,8 @@ public final class ExitConfirmationDialog {
|
|||||||
HBox.setHgrow(spacer, Priority.ALWAYS);
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
|
|
||||||
Button btnExit = new Button("Encerrar Definitivamente");
|
Button btnExit = new Button("Encerrar Definitivamente");
|
||||||
btnExit.getStyleClass().addAll("btn-danger", "btn-dialog");
|
btnExit.getStyleClass().addAll("btn-danger-outline", "btn-dialog");
|
||||||
|
btnExit.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnExit.setOnAction(e -> {
|
btnExit.setOnAction(e -> {
|
||||||
chosenAction = ExitChoice.EXIT_COMPLETELY;
|
chosenAction = ExitChoice.EXIT_COMPLETELY;
|
||||||
stage.close();
|
stage.close();
|
||||||
@@ -88,6 +88,7 @@ public final class ExitConfirmationDialog {
|
|||||||
|
|
||||||
Button btnMinimize = new Button("Minimizar para a Bandeja");
|
Button btnMinimize = new Button("Minimizar para a Bandeja");
|
||||||
btnMinimize.getStyleClass().addAll("btn-primary", "btn-dialog");
|
btnMinimize.getStyleClass().addAll("btn-primary", "btn-dialog");
|
||||||
|
btnMinimize.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnMinimize.setDefaultButton(true);
|
btnMinimize.setDefaultButton(true);
|
||||||
btnMinimize.setOnAction(e -> {
|
btnMinimize.setOnAction(e -> {
|
||||||
chosenAction = ExitChoice.MINIMIZE_TO_TRAY;
|
chosenAction = ExitChoice.MINIMIZE_TO_TRAY;
|
||||||
@@ -101,9 +102,14 @@ public final class ExitConfirmationDialog {
|
|||||||
Scene scene = new Scene(root);
|
Scene scene = new Scene(root);
|
||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
stage.setOnCloseRequest(e -> chosenAction = ExitChoice.CANCEL);
|
stage.setOnCloseRequest(e -> chosenAction = ExitChoice.CANCEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return stage;
|
||||||
|
}
|
||||||
|
|
||||||
public ExitChoice showAndGetChoice() {
|
public ExitChoice showAndGetChoice() {
|
||||||
stage.showAndWait();
|
stage.showAndWait();
|
||||||
return chosenAction;
|
return chosenAction;
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ public final class FileSignDialog {
|
|||||||
|
|
||||||
Button btnBrowse = new Button("Procurar...");
|
Button btnBrowse = new Button("Procurar...");
|
||||||
btnBrowse.getStyleClass().add("btn-secondary");
|
btnBrowse.getStyleClass().add("btn-secondary");
|
||||||
|
btnBrowse.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnBrowse.setOnAction(e -> handleBrowseFile());
|
btnBrowse.setOnAction(e -> handleBrowseFile());
|
||||||
|
|
||||||
fileRow.getChildren().addAll(txtFilePath, btnBrowse);
|
fileRow.getChildren().addAll(txtFilePath, btnBrowse);
|
||||||
@@ -159,15 +160,20 @@ public final class FileSignDialog {
|
|||||||
footer.setAlignment(Pos.CENTER_RIGHT);
|
footer.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
|
||||||
Button btnCancel = new Button("Cancelar");
|
Button btnCancel = new Button("Cancelar");
|
||||||
btnCancel.getStyleClass().add("btn-secondary");
|
btnCancel.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
|
btnCancel.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnCancel.setOnAction(e -> stage.close());
|
btnCancel.setOnAction(e -> stage.close());
|
||||||
|
|
||||||
|
HBox spacer = new HBox();
|
||||||
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
|
|
||||||
Button btnSign = new Button("Assinar Documento");
|
Button btnSign = new Button("Assinar Documento");
|
||||||
btnSign.getStyleClass().add("btn-primary");
|
btnSign.getStyleClass().addAll("btn-primary", "btn-dialog");
|
||||||
|
btnSign.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnSign.setDefaultButton(true);
|
btnSign.setDefaultButton(true);
|
||||||
btnSign.setOnAction(e -> executeSignature());
|
btnSign.setOnAction(e -> executeSignature());
|
||||||
|
|
||||||
footer.getChildren().addAll(btnCancel, btnSign);
|
footer.getChildren().addAll(btnCancel, spacer, btnSign);
|
||||||
|
|
||||||
root.getChildren().addAll(header, fileBox, certBox, footer);
|
root.getChildren().addAll(header, fileBox, certBox, footer);
|
||||||
|
|
||||||
@@ -175,6 +181,7 @@ public final class FileSignDialog {
|
|||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
|
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleBrowseFile() {
|
private void handleBrowseFile() {
|
||||||
@@ -257,4 +264,8 @@ public final class FileSignDialog {
|
|||||||
public void show() {
|
public void show() {
|
||||||
stage.show();
|
stage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ public final class LogsViewerDialog {
|
|||||||
|
|
||||||
Button btnCopy = new Button("Copiar");
|
Button btnCopy = new Button("Copiar");
|
||||||
btnCopy.getStyleClass().addAll("btn-secondary", "btn-dialog");
|
btnCopy.getStyleClass().addAll("btn-secondary", "btn-dialog");
|
||||||
btnCopy.setPrefWidth(90);
|
btnCopy.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnCopy.setOnAction(e -> {
|
btnCopy.setOnAction(e -> {
|
||||||
javafx.scene.input.ClipboardContent content = new javafx.scene.input.ClipboardContent();
|
javafx.scene.input.ClipboardContent content = new javafx.scene.input.ClipboardContent();
|
||||||
content.putString(txtLogs.getText());
|
content.putString(txtLogs.getText());
|
||||||
@@ -77,7 +77,7 @@ public final class LogsViewerDialog {
|
|||||||
|
|
||||||
Button btnClear = new Button("Limpar");
|
Button btnClear = new Button("Limpar");
|
||||||
btnClear.getStyleClass().addAll("btn-secondary", "btn-dialog");
|
btnClear.getStyleClass().addAll("btn-secondary", "btn-dialog");
|
||||||
btnClear.setPrefWidth(90);
|
btnClear.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnClear.setOnAction(e -> {
|
btnClear.setOnAction(e -> {
|
||||||
br.com.cybercore.signer.core.log.AppLogBuffer.clear();
|
br.com.cybercore.signer.core.log.AppLogBuffer.clear();
|
||||||
loadLogs();
|
loadLogs();
|
||||||
@@ -85,15 +85,18 @@ public final class LogsViewerDialog {
|
|||||||
|
|
||||||
Button btnRefresh = new Button("Atualizar");
|
Button btnRefresh = new Button("Atualizar");
|
||||||
btnRefresh.getStyleClass().addAll("btn-secondary", "btn-dialog");
|
btnRefresh.getStyleClass().addAll("btn-secondary", "btn-dialog");
|
||||||
btnRefresh.setPrefWidth(100);
|
btnRefresh.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnRefresh.setOnAction(e -> loadLogs());
|
btnRefresh.setOnAction(e -> loadLogs());
|
||||||
|
|
||||||
|
HBox spacer = new HBox();
|
||||||
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
|
|
||||||
Button btnClose = new Button(" Fechar", SvgIcons.closeIcon(13));
|
Button btnClose = new Button(" Fechar", SvgIcons.closeIcon(13));
|
||||||
btnClose.getStyleClass().addAll("btn-secondary", "btn-dialog");
|
btnClose.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
btnClose.setPrefWidth(100);
|
btnClose.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnClose.setOnAction(e -> stage.close());
|
btnClose.setOnAction(e -> stage.close());
|
||||||
|
|
||||||
footer.getChildren().addAll(btnCopy, btnClear, btnRefresh, btnClose);
|
footer.getChildren().addAll(btnCopy, btnClear, btnRefresh, spacer, btnClose);
|
||||||
|
|
||||||
root.getChildren().addAll(header, txtLogs, footer);
|
root.getChildren().addAll(header, txtLogs, footer);
|
||||||
|
|
||||||
@@ -101,6 +104,7 @@ public final class LogsViewerDialog {
|
|||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
|
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadLogs() {
|
private void loadLogs() {
|
||||||
@@ -127,4 +131,8 @@ public final class LogsViewerDialog {
|
|||||||
public void show() {
|
public void show() {
|
||||||
stage.show();
|
stage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package br.com.cybercore.signer.ui.view;
|
package br.com.cybercore.signer.ui.view;
|
||||||
|
|
||||||
import br.com.cybercore.signer.ui.CyberCoreUiConstants;
|
import br.com.cybercore.signer.ui.CyberCoreUiConstants;
|
||||||
|
import br.com.cybercore.signer.ui.components.Icons;
|
||||||
import br.com.cybercore.signer.ui.components.SvgIcons;
|
import br.com.cybercore.signer.ui.components.SvgIcons;
|
||||||
import br.com.cybercore.signer.ui.theme.ThemeManager;
|
import br.com.cybercore.signer.ui.theme.ThemeManager;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
@@ -9,6 +10,7 @@ import javafx.scene.Node;
|
|||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.layout.ColumnConstraints;
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
||||||
@@ -33,7 +35,6 @@ public final class MainAppView {
|
|||||||
|
|
||||||
private final Stage stage;
|
private final Stage stage;
|
||||||
private final VBox root = new VBox(16);
|
private final VBox root = new VBox(16);
|
||||||
private final Button themeToggleBtn = new Button();
|
|
||||||
|
|
||||||
private Runnable onSignPdf;
|
private Runnable onSignPdf;
|
||||||
private Runnable onSignXml;
|
private Runnable onSignXml;
|
||||||
@@ -57,7 +58,7 @@ public final class MainAppView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initLayout() {
|
private void initLayout() {
|
||||||
root.getStyleClass().addAll("cyber-root", "serpro-root");
|
root.getStyleClass().add("cyber-root");
|
||||||
root.setAlignment(Pos.TOP_CENTER);
|
root.setAlignment(Pos.TOP_CENTER);
|
||||||
|
|
||||||
// 1. Barra Superior Moderna com Identidade CyberCore, Status do Agente e Alternador de Tema
|
// 1. Barra Superior Moderna com Identidade CyberCore, Status do Agente e Alternador de Tema
|
||||||
@@ -69,37 +70,54 @@ public final class MainAppView {
|
|||||||
mainContent.setAlignment(Pos.CENTER);
|
mainContent.setAlignment(Pos.CENTER);
|
||||||
VBox.setVgrow(mainContent, Priority.ALWAYS);
|
VBox.setVgrow(mainContent, Priority.ALWAYS);
|
||||||
|
|
||||||
// --- Hub Central de Ações (Grid 3 colunas x 2 linhas) ---
|
// --- Hub Central de Ações (Grid 3 colunas x 2 linhas responsivo) ---
|
||||||
GridPane actionGrid = new GridPane();
|
GridPane actionGrid = new GridPane();
|
||||||
actionGrid.setHgap(12);
|
actionGrid.setHgap(12);
|
||||||
actionGrid.setVgap(12);
|
actionGrid.setVgap(12);
|
||||||
actionGrid.setAlignment(Pos.CENTER);
|
actionGrid.setAlignment(Pos.CENTER);
|
||||||
HBox.setHgrow(actionGrid, Priority.ALWAYS);
|
HBox.setHgrow(actionGrid, Priority.ALWAYS);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
ColumnConstraints col = new ColumnConstraints();
|
||||||
|
col.setHgrow(Priority.ALWAYS);
|
||||||
|
col.setPercentWidth(33.33);
|
||||||
|
col.setFillWidth(true);
|
||||||
|
actionGrid.getColumnConstraints().add(col);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
javafx.scene.layout.RowConstraints row = new javafx.scene.layout.RowConstraints();
|
||||||
|
row.setVgrow(Priority.NEVER);
|
||||||
|
row.setMinHeight(68);
|
||||||
|
row.setPrefHeight(Region.USE_COMPUTED_SIZE);
|
||||||
|
row.setFillHeight(true);
|
||||||
|
actionGrid.getRowConstraints().add(row);
|
||||||
|
}
|
||||||
|
|
||||||
// Linha 0: Operações de Assinatura
|
// Linha 0: Operações de Assinatura
|
||||||
actionGrid.add(createActionButton(SvgIcons.pdfIcon(), "Assinar\nPDF", () -> trigger(onSignPdf)), 0, 0);
|
actionGrid.add(createActionButton(Icons.pdf(Icons.SIZE_40), "Assinar\nPDF", () -> trigger(onSignPdf)), 0, 0);
|
||||||
actionGrid.add(createActionButton(SvgIcons.xmlIcon(), "Assinar\nXML", () -> trigger(onSignXml)), 1, 0);
|
actionGrid.add(createActionButton(Icons.xml(Icons.SIZE_40), "Assinar\nXML", () -> trigger(onSignXml)), 1, 0);
|
||||||
actionGrid.add(createActionButton(SvgIcons.attachedIcon(), "Assinatura\nAtachada", () -> trigger(onAttachedSign)), 2, 0);
|
actionGrid.add(createActionButton(Icons.attached(Icons.SIZE_40), "Assinatura\nAtachada", () -> trigger(onAttachedSign)), 2, 0);
|
||||||
|
|
||||||
// Linha 1: Identidade Visual, Auditoria e Saída
|
// Linha 1: Identidade Visual, Auditoria e Saída
|
||||||
actionGrid.add(createActionButton(SvgIcons.sealIcon(), "Gerenciar\nSelos", () -> trigger(onManageSeals)), 0, 1);
|
actionGrid.add(createActionButton(Icons.seal(Icons.SIZE_40), "Gerenciar\nSelos", () -> trigger(onManageSeals)), 0, 1);
|
||||||
actionGrid.add(createActionButton(SvgIcons.validateIcon(), "Validar\nAssinatura", () -> trigger(onValidateSignature)), 1, 1);
|
actionGrid.add(createActionButton(Icons.validate(Icons.SIZE_40), "Validar\nAssinatura", () -> trigger(onValidateSignature)), 1, 1);
|
||||||
actionGrid.add(createActionButton(SvgIcons.exitIcon(), "Sair do\nAgente", () -> trigger(onExit)), 2, 1);
|
actionGrid.add(createActionButton(Icons.exit(Icons.SIZE_40), "Sair do\nAgente", () -> trigger(onExit)), 2, 1);
|
||||||
|
|
||||||
// --- Painel Lateral Direito (Ferramentas, Certificados e Diagnóstico) ---
|
// --- Painel Lateral Direito (Ferramentas, Certificados e Diagnóstico) ---
|
||||||
VBox sideColumn = new VBox(10);
|
VBox sideColumn = new VBox(10);
|
||||||
sideColumn.getStyleClass().add("cyber-side-column");
|
sideColumn.getStyleClass().add("cyber-side-column");
|
||||||
sideColumn.setPrefWidth(195);
|
sideColumn.setPrefWidth(210);
|
||||||
sideColumn.setMinWidth(190);
|
sideColumn.setMinWidth(180);
|
||||||
sideColumn.setMaxWidth(210);
|
sideColumn.setMaxWidth(230);
|
||||||
sideColumn.setAlignment(Pos.TOP_CENTER);
|
sideColumn.setAlignment(Pos.TOP_CENTER);
|
||||||
|
|
||||||
sideColumn.getChildren().addAll(
|
sideColumn.getChildren().addAll(
|
||||||
createSideButton(SvgIcons.logsIcon(), "Logs", () -> trigger(onLogs)),
|
createSideButton(Icons.logs(Icons.SIZE_24), "Logs", () -> trigger(onLogs)),
|
||||||
createSideButton(SvgIcons.settingsIcon(), "Configurações", () -> trigger(onSettings)),
|
createSideButton(Icons.settings(Icons.SIZE_24), "Configurações", () -> trigger(onSettings)),
|
||||||
createSideButton(SvgIcons.tutorialIcon(), "Tutorial", () -> trigger(onTutorial)),
|
createSideButton(Icons.tutorial(Icons.SIZE_24), "Tutorial", () -> trigger(onTutorial)),
|
||||||
createSideButton(SvgIcons.certificatesIcon(), "Certificados", () -> trigger(onCertificates)),
|
createSideButton(Icons.certificates(Icons.SIZE_24), "Certificados", () -> trigger(onCertificates)),
|
||||||
createSideButton(SvgIcons.aboutIcon(), "Sobre", () -> trigger(onAbout))
|
createSideButton(Icons.about(Icons.SIZE_24), "Sobre", () -> trigger(onAbout))
|
||||||
);
|
);
|
||||||
|
|
||||||
// Espaçador elástico e Rodapé de status local
|
// Espaçador elástico e Rodapé de status local
|
||||||
@@ -112,13 +130,13 @@ public final class MainAppView {
|
|||||||
|
|
||||||
HBox brandFooter = new HBox(6);
|
HBox brandFooter = new HBox(6);
|
||||||
brandFooter.setAlignment(Pos.CENTER);
|
brandFooter.setAlignment(Pos.CENTER);
|
||||||
Node smallLogo = SvgIcons.cyberShieldLogo(16);
|
Node smallLogo = Icons.shield(Icons.SIZE_16);
|
||||||
Label lblBrandFoot = new Label("CyberCore");
|
Label lblBrandFoot = new Label("CyberCore");
|
||||||
lblBrandFoot.setStyle("-fx-font-weight: bold; -fx-font-size: 11.5px;");
|
lblBrandFoot.getStyleClass().addAll("cyber-version-label", "text-bold");
|
||||||
brandFooter.getChildren().addAll(smallLogo, lblBrandFoot);
|
brandFooter.getChildren().addAll(smallLogo, lblBrandFoot);
|
||||||
|
|
||||||
Label lblVersion = new Label("v" + CyberCoreUiConstants.APP_VERSION);
|
Label lblVersion = new Label("v" + CyberCoreUiConstants.APP_VERSION);
|
||||||
lblVersion.getStyleClass().addAll("cyber-version-label", "serpro-version-label");
|
lblVersion.getStyleClass().add("cyber-version-label");
|
||||||
|
|
||||||
footerBox.getChildren().addAll(brandFooter, lblVersion);
|
footerBox.getChildren().addAll(brandFooter, lblVersion);
|
||||||
sideColumn.getChildren().add(footerBox);
|
sideColumn.getChildren().add(footerBox);
|
||||||
@@ -139,13 +157,11 @@ public final class MainAppView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configuração e inicialização do Tema
|
// Configuração e inicialização da folha de estilos global
|
||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
updateThemeToggleButton();
|
|
||||||
|
|
||||||
ThemeManager.addListener(theme -> updateThemeToggleButton());
|
|
||||||
|
|
||||||
stage.setTitle(CyberCoreUiConstants.APP_NAME);
|
stage.setTitle(CyberCoreUiConstants.APP_NAME);
|
||||||
|
stage.getIcons().addAll(br.com.cybercore.signer.ui.assets.AssetManager.getApplicationIcons());
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
|
||||||
if (Boolean.getBoolean("cybercore.diag.plain-stage")) {
|
if (Boolean.getBoolean("cybercore.diag.plain-stage")) {
|
||||||
@@ -174,15 +190,13 @@ public final class MainAppView {
|
|||||||
brandRow.setAlignment(Pos.CENTER_LEFT);
|
brandRow.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
Label lblAssinador = new Label("ASSINADOR");
|
Label lblAssinador = new Label("ASSINADOR");
|
||||||
lblAssinador.getStyleClass().addAll("cyber-brand-title", "serpro-header-title");
|
lblAssinador.getStyleClass().addAll("cyber-brand-title", "cyber-brand-prefix");
|
||||||
lblAssinador.setStyle("-fx-font-weight: 300; -fx-opacity: 0.85;");
|
|
||||||
|
|
||||||
Label lblSep = new Label("|");
|
Label lblSep = new Label("|");
|
||||||
lblSep.setStyle("-fx-font-size: 16px; -fx-opacity: 0.4;");
|
lblSep.getStyleClass().add("cyber-brand-separator");
|
||||||
|
|
||||||
Label lblBrand = new Label("CyberCore");
|
Label lblBrand = new Label("CyberCore");
|
||||||
lblBrand.getStyleClass().addAll("cyber-brand-title", "serpro-header-brand");
|
lblBrand.getStyleClass().addAll("cyber-brand-title", "text-bold");
|
||||||
lblBrand.setStyle("-fx-font-weight: bold;");
|
|
||||||
|
|
||||||
brandRow.getChildren().addAll(lblAssinador, lblSep, lblBrand);
|
brandRow.getChildren().addAll(lblAssinador, lblSep, lblBrand);
|
||||||
|
|
||||||
@@ -199,51 +213,22 @@ public final class MainAppView {
|
|||||||
Label statusBadge = new Label("● Agente Ativo • 127.0.0.1:21325");
|
Label statusBadge = new Label("● Agente Ativo • 127.0.0.1:21325");
|
||||||
statusBadge.getStyleClass().add("cyber-status-badge");
|
statusBadge.getStyleClass().add("cyber-status-badge");
|
||||||
|
|
||||||
// Botão Alternador de Tema (Claro / Escuro)
|
headerBar.getChildren().addAll(brandLogo, titleBox, leftSpacer, statusBadge);
|
||||||
themeToggleBtn.getStyleClass().add("theme-toggle-btn");
|
|
||||||
themeToggleBtn.setOnAction(e -> {
|
|
||||||
ThemeManager.toggleTheme();
|
|
||||||
if (stage.getScene() != null) {
|
|
||||||
ThemeManager.applyTheme(stage.getScene());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
headerBar.getChildren().addAll(brandLogo, titleBox, leftSpacer, statusBadge, themeToggleBtn);
|
|
||||||
return headerBar;
|
return headerBar;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateThemeToggleButton() {
|
|
||||||
boolean dark = ThemeManager.isDark();
|
|
||||||
HBox content = new HBox(8);
|
|
||||||
content.setAlignment(Pos.CENTER);
|
|
||||||
|
|
||||||
Node icon = dark ? SvgIcons.sunIcon(15) : SvgIcons.moonIcon(15);
|
|
||||||
StackPane iconBox = new StackPane(icon);
|
|
||||||
iconBox.setPrefSize(18, 18);
|
|
||||||
iconBox.setMinSize(18, 18);
|
|
||||||
iconBox.setMaxSize(18, 18);
|
|
||||||
iconBox.setAlignment(Pos.CENTER);
|
|
||||||
|
|
||||||
Label label = new Label(dark ? "Modo Claro" : "Modo Escuro");
|
|
||||||
label.setStyle("-fx-font-size: 11.5px; -fx-font-weight: bold;");
|
|
||||||
|
|
||||||
content.getChildren().addAll(iconBox, label);
|
|
||||||
themeToggleBtn.setGraphic(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Button createActionButton(Node icon, String text, Runnable onAction) {
|
private Button createActionButton(Node icon, String text, Runnable onAction) {
|
||||||
Button btn = new Button();
|
Button btn = new Button();
|
||||||
btn.getStyleClass().addAll("cyber-action-card", "serpro-action-btn");
|
btn.getStyleClass().add("cyber-action-card");
|
||||||
btn.setPrefWidth(162);
|
btn.setMaxWidth(Double.MAX_VALUE);
|
||||||
btn.setMinWidth(158);
|
|
||||||
btn.setPrefHeight(68);
|
btn.setPrefHeight(68);
|
||||||
btn.setMinHeight(68);
|
btn.setMinHeight(Region.USE_PREF_SIZE);
|
||||||
|
|
||||||
HBox content = new HBox(12);
|
HBox content = new HBox(12);
|
||||||
content.setAlignment(Pos.CENTER_LEFT);
|
content.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
Label lbl = new Label(text);
|
Label lbl = new Label(text);
|
||||||
lbl.getStyleClass().addAll("cyber-action-text", "serpro-action-text");
|
lbl.getStyleClass().add("cyber-action-text");
|
||||||
lbl.setWrapText(true);
|
lbl.setWrapText(true);
|
||||||
lbl.setAlignment(Pos.CENTER_LEFT);
|
lbl.setAlignment(Pos.CENTER_LEFT);
|
||||||
HBox.setHgrow(lbl, Priority.ALWAYS);
|
HBox.setHgrow(lbl, Priority.ALWAYS);
|
||||||
@@ -268,16 +253,16 @@ public final class MainAppView {
|
|||||||
|
|
||||||
private Button createSideButton(Node icon, String text, Runnable onAction) {
|
private Button createSideButton(Node icon, String text, Runnable onAction) {
|
||||||
Button btn = new Button();
|
Button btn = new Button();
|
||||||
btn.getStyleClass().addAll("cyber-side-card", "serpro-side-btn");
|
btn.getStyleClass().add("cyber-side-card");
|
||||||
btn.setPrefHeight(42);
|
btn.setPrefHeight(42);
|
||||||
btn.setMinHeight(42);
|
btn.setMinHeight(40);
|
||||||
btn.setMaxWidth(Double.MAX_VALUE);
|
btn.setMaxWidth(Double.MAX_VALUE);
|
||||||
|
|
||||||
HBox content = new HBox(12);
|
HBox content = new HBox(12);
|
||||||
content.setAlignment(Pos.CENTER_LEFT);
|
content.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
Label lbl = new Label(text);
|
Label lbl = new Label(text);
|
||||||
lbl.getStyleClass().addAll("cyber-side-text", "serpro-side-text");
|
lbl.getStyleClass().add("cyber-side-text");
|
||||||
lbl.setWrapText(false);
|
lbl.setWrapText(false);
|
||||||
lbl.setAlignment(Pos.CENTER_LEFT);
|
lbl.setAlignment(Pos.CENTER_LEFT);
|
||||||
HBox.setHgrow(lbl, Priority.ALWAYS);
|
HBox.setHgrow(lbl, Priority.ALWAYS);
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public final class MainDashboardView extends VBox {
|
|||||||
certsContainer.setPadding(new Insets(4, 0, 4, 0));
|
certsContainer.setPadding(new Insets(4, 0, 4, 0));
|
||||||
ScrollPane scrollPane = new ScrollPane(certsContainer);
|
ScrollPane scrollPane = new ScrollPane(certsContainer);
|
||||||
scrollPane.setFitToWidth(true);
|
scrollPane.setFitToWidth(true);
|
||||||
scrollPane.setStyle("-fx-background: transparent; -fx-background-color: transparent;");
|
scrollPane.getStyleClass().add("scroll-transparent");
|
||||||
VBox.setVgrow(scrollPane, Priority.ALWAYS);
|
VBox.setVgrow(scrollPane, Priority.ALWAYS);
|
||||||
|
|
||||||
getChildren().addAll(statusBox, certsHeader, scrollPane);
|
getChildren().addAll(statusBox, certsHeader, scrollPane);
|
||||||
|
|||||||
@@ -167,15 +167,15 @@ public final class PdfCertificateSignDialog {
|
|||||||
summaryGrid.setVgap(6);
|
summaryGrid.setVgap(6);
|
||||||
|
|
||||||
Label lblDocTitle = new Label("Arquivo:");
|
Label lblDocTitle = new Label("Arquivo:");
|
||||||
lblDocTitle.setStyle("-fx-font-weight: bold; -fx-text-fill: -fx-text-secondary; -fx-font-size: 11.5px;");
|
lblDocTitle.getStyleClass().addAll("form-label-muted", "text-bold");
|
||||||
Label lblDocValue = new Label(sourcePdfFile.getName() + " (" + (sourcePdfFile.length() / 1024) + " KB)");
|
Label lblDocValue = new Label(sourcePdfFile.getName() + " (" + (sourcePdfFile.length() / 1024) + " KB)");
|
||||||
lblDocValue.setStyle("-fx-font-weight: bold; -fx-text-fill: -fx-text-primary; -fx-font-size: 11.5px;");
|
lblDocValue.getStyleClass().addAll("form-label", "text-bold");
|
||||||
lblDocValue.setWrapText(true);
|
lblDocValue.setWrapText(true);
|
||||||
|
|
||||||
Label lblSealTitle = new Label("Selo Visual:");
|
Label lblSealTitle = new Label("Selo Visual:");
|
||||||
lblSealTitle.setStyle("-fx-font-weight: bold; -fx-text-fill: -fx-text-secondary; -fx-font-size: 11.5px;");
|
lblSealTitle.getStyleClass().addAll("form-label-muted", "text-bold");
|
||||||
Label lblSealValue = new Label(sealSummaryText);
|
Label lblSealValue = new Label(sealSummaryText);
|
||||||
lblSealValue.setStyle("-fx-text-fill: #0284c7; -fx-font-weight: bold; -fx-font-size: 11.5px;");
|
lblSealValue.getStyleClass().addAll("text-primary", "text-bold");
|
||||||
lblSealValue.setWrapText(true);
|
lblSealValue.setWrapText(true);
|
||||||
|
|
||||||
summaryGrid.add(lblDocTitle, 0, 0);
|
summaryGrid.add(lblDocTitle, 0, 0);
|
||||||
@@ -237,13 +237,12 @@ public final class PdfCertificateSignDialog {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Banner informativo para certificados da Loja do Windows (sem PIN)
|
|
||||||
windowsStoreBox = new HBox(8);
|
windowsStoreBox = new HBox(8);
|
||||||
windowsStoreBox.setAlignment(Pos.CENTER_LEFT);
|
windowsStoreBox.setAlignment(Pos.CENTER_LEFT);
|
||||||
windowsStoreBox.setPadding(new Insets(10, 12, 10, 12));
|
windowsStoreBox.setPadding(new Insets(10, 12, 10, 12));
|
||||||
windowsStoreBox.setStyle("-fx-background-color: #ecfdf5; -fx-border-color: #a7f3d0; -fx-border-radius: 6px; -fx-background-radius: 6px;");
|
windowsStoreBox.getStyleClass().add("status-badge-valid");
|
||||||
Label lblWinNotice = new Label("✓ Certificado instalado no computador — Gerenciado nativamente pelo Windows (PIN não necessário)");
|
Label lblWinNotice = new Label("✓ Certificado instalado no computador — Gerenciado nativamente pelo Windows (PIN não necessário)");
|
||||||
lblWinNotice.setStyle("-fx-text-fill: #065f46; -fx-font-weight: bold; -fx-font-size: 11.5px;");
|
lblWinNotice.getStyleClass().addAll("text-success", "text-bold");
|
||||||
lblWinNotice.setWrapText(true);
|
lblWinNotice.setWrapText(true);
|
||||||
HBox.setHgrow(lblWinNotice, Priority.ALWAYS);
|
HBox.setHgrow(lblWinNotice, Priority.ALWAYS);
|
||||||
windowsStoreBox.getChildren().add(lblWinNotice);
|
windowsStoreBox.getChildren().add(lblWinNotice);
|
||||||
@@ -260,7 +259,6 @@ public final class PdfCertificateSignDialog {
|
|||||||
|
|
||||||
btnClearRememberedPin = new Button("Alterar PIN");
|
btnClearRememberedPin = new Button("Alterar PIN");
|
||||||
btnClearRememberedPin.getStyleClass().addAll("btn-secondary", "btn-compact");
|
btnClearRememberedPin.getStyleClass().addAll("btn-secondary", "btn-compact");
|
||||||
btnClearRememberedPin.setStyle("-fx-font-size: 11px;");
|
|
||||||
btnClearRememberedPin.setVisible(false);
|
btnClearRememberedPin.setVisible(false);
|
||||||
btnClearRememberedPin.setOnAction(e -> {
|
btnClearRememberedPin.setOnAction(e -> {
|
||||||
ManagedCertificate sel = certCombo.getValue();
|
ManagedCertificate sel = certCombo.getValue();
|
||||||
@@ -271,20 +269,20 @@ public final class PdfCertificateSignDialog {
|
|||||||
});
|
});
|
||||||
|
|
||||||
lblPinNotice = new Label();
|
lblPinNotice = new Label();
|
||||||
lblPinNotice.setStyle("-fx-font-size: 11.5px;");
|
lblPinNotice.getStyleClass().addAll("dialog-subtitle", "text-muted");
|
||||||
lblPinNotice.setWrapText(true);
|
lblPinNotice.setWrapText(true);
|
||||||
HBox.setHgrow(lblPinNotice, Priority.ALWAYS);
|
HBox.setHgrow(lblPinNotice, Priority.ALWAYS);
|
||||||
pinRow.getChildren().addAll(lblPinTitle, pinField, btnClearRememberedPin, lblPinNotice);
|
pinRow.getChildren().addAll(lblPinTitle, pinField, btnClearRememberedPin, lblPinNotice);
|
||||||
|
|
||||||
chkRememberPin = new CheckBox("Lembrar PIN/senha com segurança neste computador");
|
chkRememberPin = new CheckBox("Lembrar PIN/senha com segurança neste computador");
|
||||||
chkRememberPin.setStyle("-fx-font-size: 11.5px; -fx-text-fill: -fx-text-primary;");
|
chkRememberPin.getStyleClass().addAll("text-primary", "text-small");
|
||||||
chkRememberPin.setSelected(true);
|
chkRememberPin.setSelected(true);
|
||||||
|
|
||||||
VBox tipBox = new VBox(2);
|
VBox tipBox = new VBox(2);
|
||||||
tipBox.setPadding(new Insets(8, 10, 8, 10));
|
tipBox.setPadding(new Insets(8, 10, 8, 10));
|
||||||
tipBox.setStyle("-fx-background-color: -fx-bg-primary; -fx-border-color: -fx-border-light; -fx-border-radius: 6px; -fx-background-radius: 6px;");
|
tipBox.getStyleClass().add("dialog-box");
|
||||||
Label lblTip = new Label("🔒 Segurança: O PIN é criptografado localmente com AES-256-GCM vinculado à máquina e nunca é transmitido na rede.");
|
Label lblTip = new Label("🔒 Segurança: O PIN é criptografado localmente com AES-256-GCM vinculado à máquina e nunca é transmitido na rede.");
|
||||||
lblTip.setStyle("-fx-font-size: 11px; -fx-text-fill: -fx-text-secondary;");
|
lblTip.getStyleClass().add("text-muted");
|
||||||
lblTip.setWrapText(true);
|
lblTip.setWrapText(true);
|
||||||
lblTip.setMaxWidth(Double.MAX_VALUE);
|
lblTip.setMaxWidth(Double.MAX_VALUE);
|
||||||
tipBox.getChildren().add(lblTip);
|
tipBox.getChildren().add(lblTip);
|
||||||
@@ -301,18 +299,17 @@ public final class PdfCertificateSignDialog {
|
|||||||
progressIndicator.setVisible(false);
|
progressIndicator.setVisible(false);
|
||||||
|
|
||||||
lblStatus = new Label();
|
lblStatus = new Label();
|
||||||
lblStatus.setStyle("-fx-font-size: 12px;");
|
lblStatus.getStyleClass().addAll("text-muted", "text-small");
|
||||||
HBox.setHgrow(lblStatus, Priority.ALWAYS);
|
HBox.setHgrow(lblStatus, Priority.ALWAYS);
|
||||||
|
|
||||||
btnCancel = new Button("Cancelar");
|
btnCancel = new Button("Cancelar");
|
||||||
btnCancel.getStyleClass().addAll("btn-secondary", "btn-dialog");
|
btnCancel.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
btnCancel.setMinWidth(110);
|
btnCancel.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnCancel.setOnAction(e -> stage.close());
|
btnCancel.setOnAction(e -> stage.close());
|
||||||
|
|
||||||
btnSign = new Button("Assinar Documento PDF");
|
btnSign = new Button("Assinar Documento PDF");
|
||||||
btnSign.getStyleClass().addAll("btn-primary", "btn-dialog-wide");
|
btnSign.getStyleClass().addAll("btn-primary", "btn-dialog-wide");
|
||||||
btnSign.setMinWidth(220);
|
btnSign.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnSign.setPrefWidth(Region.USE_COMPUTED_SIZE);
|
|
||||||
btnSign.setDefaultButton(true);
|
btnSign.setDefaultButton(true);
|
||||||
btnSign.setOnAction(e -> handleSign());
|
btnSign.setOnAction(e -> handleSign());
|
||||||
|
|
||||||
@@ -320,9 +317,10 @@ public final class PdfCertificateSignDialog {
|
|||||||
|
|
||||||
root.getChildren().addAll(header, summaryBox, certSection, footer);
|
root.getChildren().addAll(header, summaryBox, certSection, footer);
|
||||||
|
|
||||||
Scene scene = new Scene(root, 720, 500);
|
Scene scene = new Scene(root);
|
||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
|
|
||||||
updatePinFieldVisibility();
|
updatePinFieldVisibility();
|
||||||
}
|
}
|
||||||
@@ -381,7 +379,7 @@ public final class PdfCertificateSignDialog {
|
|||||||
chkRememberPin.setManaged(false);
|
chkRememberPin.setManaged(false);
|
||||||
btnClearRememberedPin.setVisible(true);
|
btnClearRememberedPin.setVisible(true);
|
||||||
lblPinNotice.setText("✓ PIN lembrado com segurança neste computador");
|
lblPinNotice.setText("✓ PIN lembrado com segurança neste computador");
|
||||||
lblPinNotice.setStyle("-fx-text-fill: #10b981; -fx-font-weight: bold; -fx-font-size: 11.5px;");
|
lblPinNotice.getStyleClass().setAll("label", "text-success", "text-bold");
|
||||||
} else {
|
} else {
|
||||||
pinField.clear();
|
pinField.clear();
|
||||||
pinField.setDisable(false);
|
pinField.setDisable(false);
|
||||||
@@ -390,7 +388,7 @@ public final class PdfCertificateSignDialog {
|
|||||||
btnClearRememberedPin.setVisible(false);
|
btnClearRememberedPin.setVisible(false);
|
||||||
boolean isA3 = cert.type() == CertificateType.A3;
|
boolean isA3 = cert.type() == CertificateType.A3;
|
||||||
lblPinNotice.setText(isA3 ? "Requer PIN do token" : "Requer senha do certificado");
|
lblPinNotice.setText(isA3 ? "Requer PIN do token" : "Requer senha do certificado");
|
||||||
lblPinNotice.setStyle("-fx-text-fill: -fx-text-secondary; -fx-font-size: 11.5px;");
|
lblPinNotice.getStyleClass().setAll("label", "text-muted");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,7 +467,7 @@ public final class PdfCertificateSignDialog {
|
|||||||
setFormDisabled(true);
|
setFormDisabled(true);
|
||||||
progressIndicator.setVisible(true);
|
progressIndicator.setVisible(true);
|
||||||
lblStatus.setText("Assinando documento PDF com envelope PAdES...");
|
lblStatus.setText("Assinando documento PDF com envelope PAdES...");
|
||||||
lblStatus.setStyle("-fx-text-fill: -fx-accent;");
|
lblStatus.getStyleClass().setAll("text-accent", "text-bold");
|
||||||
|
|
||||||
Task<File> signTask = new Task<>() {
|
Task<File> signTask = new Task<>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -483,7 +481,7 @@ public final class PdfCertificateSignDialog {
|
|||||||
setFormDisabled(false);
|
setFormDisabled(false);
|
||||||
progressIndicator.setVisible(false);
|
progressIndicator.setVisible(false);
|
||||||
lblStatus.setText("✓ Documento assinado com sucesso!");
|
lblStatus.setText("✓ Documento assinado com sucesso!");
|
||||||
lblStatus.setStyle("-fx-text-fill: #10b981; -fx-font-weight: bold;");
|
lblStatus.getStyleClass().setAll("label", "text-success", "text-bold");
|
||||||
|
|
||||||
// Salva o PIN de forma segura se solicitado pelo usuário
|
// Salva o PIN de forma segura se solicitado pelo usuário
|
||||||
if (!isWindowsStore && chkRememberPin.isSelected() && enteredPin != null && enteredPin.length > 0) {
|
if (!isWindowsStore && chkRememberPin.isSelected() && enteredPin != null && enteredPin.length > 0) {
|
||||||
@@ -508,7 +506,7 @@ public final class PdfCertificateSignDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lblStatus.setText("✕ Falha na assinatura");
|
lblStatus.setText("✕ Falha na assinatura");
|
||||||
lblStatus.setStyle("-fx-text-fill: #ef4444; -fx-font-weight: bold;");
|
lblStatus.getStyleClass().setAll("label", "text-danger", "text-bold");
|
||||||
|
|
||||||
showAlert(Alert.AlertType.ERROR, "Erro na Assinatura do PDF",
|
showAlert(Alert.AlertType.ERROR, "Erro na Assinatura do PDF",
|
||||||
"Não foi possível concluir a assinatura digital:\n" + (ex != null ? ex.getMessage() : "Erro desconhecido"));
|
"Não foi possível concluir a assinatura digital:\n" + (ex != null ? ex.getMessage() : "Erro desconhecido"));
|
||||||
@@ -587,4 +585,8 @@ public final class PdfCertificateSignDialog {
|
|||||||
stage.show();
|
stage.show();
|
||||||
stage.toFront();
|
stage.toFront();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -228,9 +228,9 @@ public final class PdfSignDialog {
|
|||||||
|
|
||||||
VBox docDetails = new VBox(2);
|
VBox docDetails = new VBox(2);
|
||||||
lblDocName = new Label("Nenhum arquivo selecionado");
|
lblDocName = new Label("Nenhum arquivo selecionado");
|
||||||
lblDocName.setStyle("-fx-font-weight: bold; -fx-font-size: 12.5px; -fx-text-fill: -fx-text-primary;");
|
lblDocName.getStyleClass().addAll("text-primary", "text-bold");
|
||||||
lblDocMeta = new Label("Selecione um arquivo PDF para pré-visualização");
|
lblDocMeta = new Label("Selecione um arquivo PDF para pré-visualização");
|
||||||
lblDocMeta.setStyle("-fx-font-size: 11px; -fx-text-fill: -fx-text-secondary;");
|
lblDocMeta.getStyleClass().addAll("text-muted", "text-small");
|
||||||
docDetails.getChildren().addAll(lblDocName, lblDocMeta);
|
docDetails.getChildren().addAll(lblDocName, lblDocMeta);
|
||||||
|
|
||||||
Region docSpacer = new Region();
|
Region docSpacer = new Region();
|
||||||
@@ -252,7 +252,7 @@ public final class PdfSignDialog {
|
|||||||
leftScroll.setPrefWidth(330);
|
leftScroll.setPrefWidth(330);
|
||||||
leftScroll.setMinWidth(310);
|
leftScroll.setMinWidth(310);
|
||||||
leftScroll.setMaxWidth(360);
|
leftScroll.setMaxWidth(360);
|
||||||
leftScroll.setStyle("-fx-background-color: transparent; -fx-background: transparent; -fx-border-color: transparent;");
|
leftScroll.getStyleClass().add("scroll-transparent");
|
||||||
|
|
||||||
VBox sealSection = buildSealOptionsSection();
|
VBox sealSection = buildSealOptionsSection();
|
||||||
leftScroll.setContent(sealSection);
|
leftScroll.setContent(sealSection);
|
||||||
@@ -269,21 +269,14 @@ public final class PdfSignDialog {
|
|||||||
footer.setPadding(new Insets(6, 0, 0, 0));
|
footer.setPadding(new Insets(6, 0, 0, 0));
|
||||||
|
|
||||||
btnCancel = new Button("Cancelar");
|
btnCancel = new Button("Cancelar");
|
||||||
btnCancel.getStyleClass().addAll("btn-secondary", "btn-dialog");
|
btnCancel.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
btnCancel.setMinWidth(110);
|
btnCancel.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnCancel.setOnAction(e -> stage.close());
|
btnCancel.setOnAction(e -> stage.close());
|
||||||
|
|
||||||
HBox btnNextContent = new HBox(8);
|
btnNextToCert = new Button("Avançar", SvgIcons.arrowRightIcon(12));
|
||||||
btnNextContent.setAlignment(Pos.CENTER);
|
btnNextToCert.setContentDisplay(javafx.scene.control.ContentDisplay.RIGHT);
|
||||||
Label lblBtnNextText = new Label("Avançar");
|
|
||||||
lblBtnNextText.setStyle("-fx-font-weight: bold; -fx-font-size: 12px; -fx-text-fill: #ffffff;");
|
|
||||||
btnNextContent.getChildren().addAll(lblBtnNextText, SvgIcons.arrowRightIcon(12));
|
|
||||||
|
|
||||||
btnNextToCert = new Button();
|
|
||||||
btnNextToCert.setGraphic(btnNextContent);
|
|
||||||
btnNextToCert.getStyleClass().addAll("btn-primary", "btn-dialog-wide");
|
btnNextToCert.getStyleClass().addAll("btn-primary", "btn-dialog-wide");
|
||||||
btnNextToCert.setMinWidth(240);
|
btnNextToCert.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnNextToCert.setPrefWidth(250);
|
|
||||||
btnNextToCert.setDefaultButton(true);
|
btnNextToCert.setDefaultButton(true);
|
||||||
btnNextToCert.setOnAction(e -> handleAdvanceToCertificate());
|
btnNextToCert.setOnAction(e -> handleAdvanceToCertificate());
|
||||||
|
|
||||||
@@ -297,6 +290,7 @@ public final class PdfSignDialog {
|
|||||||
Scene scene = new Scene(root, 1020, 680);
|
Scene scene = new Scene(root, 1020, 680);
|
||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
|
|
||||||
updateSealPreview();
|
updateSealPreview();
|
||||||
}
|
}
|
||||||
@@ -325,11 +319,10 @@ public final class PdfSignDialog {
|
|||||||
HBox themeHeader = new HBox(8);
|
HBox themeHeader = new HBox(8);
|
||||||
themeHeader.setAlignment(Pos.CENTER_LEFT);
|
themeHeader.setAlignment(Pos.CENTER_LEFT);
|
||||||
Label lblDefaultBadge = new Label("★ PADRÃO");
|
Label lblDefaultBadge = new Label("★ PADRÃO");
|
||||||
lblDefaultBadge.setStyle("-fx-background-color: #0284c7; -fx-text-fill: #ffffff; -fx-padding: 2px 6px; -fx-background-radius: 4px; -fx-font-weight: bold; -fx-font-size: 10px;");
|
lblDefaultBadge.getStyleClass().add("badge-primary");
|
||||||
|
|
||||||
lblSealThemeName = new Label();
|
lblSealThemeName = new Label();
|
||||||
lblSealThemeName.getStyleClass().add("cert-title");
|
lblSealThemeName.getStyleClass().addAll("cert-title", "text-bold");
|
||||||
lblSealThemeName.setStyle("-fx-font-size: 12.5px; -fx-font-weight: bold;");
|
|
||||||
themeHeader.getChildren().addAll(lblDefaultBadge, lblSealThemeName);
|
themeHeader.getChildren().addAll(lblDefaultBadge, lblSealThemeName);
|
||||||
|
|
||||||
lblSealThemeDesc = new Label();
|
lblSealThemeDesc = new Label();
|
||||||
@@ -349,7 +342,7 @@ public final class PdfSignDialog {
|
|||||||
// Preview em miniatura do SVG
|
// Preview em miniatura do SVG
|
||||||
VBox previewBox = new VBox(4);
|
VBox previewBox = new VBox(4);
|
||||||
previewBox.setAlignment(Pos.CENTER);
|
previewBox.setAlignment(Pos.CENTER);
|
||||||
previewBox.setStyle("-fx-background-color: -fx-bg-primary; -fx-border-color: -fx-border-light; -fx-border-radius: 6px; -fx-padding: 8px;");
|
previewBox.getStyleClass().add("surface-box");
|
||||||
sealPreviewImageView = new ImageView();
|
sealPreviewImageView = new ImageView();
|
||||||
sealPreviewImageView.setPreserveRatio(true);
|
sealPreviewImageView.setPreserveRatio(true);
|
||||||
sealPreviewImageView.setFitWidth(260);
|
sealPreviewImageView.setFitWidth(260);
|
||||||
@@ -391,9 +384,9 @@ public final class PdfSignDialog {
|
|||||||
// Dica amigável de posicionamento livre
|
// Dica amigável de posicionamento livre
|
||||||
VBox dragTipBox = new VBox(2);
|
VBox dragTipBox = new VBox(2);
|
||||||
dragTipBox.setPadding(new Insets(8, 10, 8, 10));
|
dragTipBox.setPadding(new Insets(8, 10, 8, 10));
|
||||||
dragTipBox.setStyle("-fx-background-color: -fx-bg-primary; -fx-border-color: -fx-border-light; -fx-border-radius: 6px; -fx-background-radius: 6px;");
|
dragTipBox.getStyleClass().add("surface-box");
|
||||||
Label lblDragTip = new Label("💡 Arraste o selo livremente sobre a folha para posicioná-lo onde desejar.");
|
Label lblDragTip = new Label("💡 Arraste o selo livremente sobre a folha para posicioná-lo onde desejar.");
|
||||||
lblDragTip.setStyle("-fx-font-size: 11px; -fx-text-fill: -fx-text-secondary;");
|
lblDragTip.getStyleClass().addAll("text-muted", "text-small");
|
||||||
lblDragTip.setWrapText(true);
|
lblDragTip.setWrapText(true);
|
||||||
dragTipBox.getChildren().add(lblDragTip);
|
dragTipBox.getChildren().add(lblDragTip);
|
||||||
|
|
||||||
@@ -436,6 +429,7 @@ public final class PdfSignDialog {
|
|||||||
|
|
||||||
btnPrevPage = new Button("◀");
|
btnPrevPage = new Button("◀");
|
||||||
btnPrevPage.getStyleClass().addAll("btn-secondary", "btn-compact");
|
btnPrevPage.getStyleClass().addAll("btn-secondary", "btn-compact");
|
||||||
|
btnPrevPage.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnPrevPage.setTooltip(new Tooltip("Página Anterior"));
|
btnPrevPage.setTooltip(new Tooltip("Página Anterior"));
|
||||||
btnPrevPage.setDisable(true);
|
btnPrevPage.setDisable(true);
|
||||||
btnPrevPage.setOnAction(e -> navigatePage(-1));
|
btnPrevPage.setOnAction(e -> navigatePage(-1));
|
||||||
@@ -445,6 +439,7 @@ public final class PdfSignDialog {
|
|||||||
|
|
||||||
btnNextPage = new Button("▶");
|
btnNextPage = new Button("▶");
|
||||||
btnNextPage.getStyleClass().addAll("btn-secondary", "btn-compact");
|
btnNextPage.getStyleClass().addAll("btn-secondary", "btn-compact");
|
||||||
|
btnNextPage.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnNextPage.setTooltip(new Tooltip("Próxima Página"));
|
btnNextPage.setTooltip(new Tooltip("Próxima Página"));
|
||||||
btnNextPage.setDisable(true);
|
btnNextPage.setDisable(true);
|
||||||
btnNextPage.setOnAction(e -> navigatePage(1));
|
btnNextPage.setOnAction(e -> navigatePage(1));
|
||||||
@@ -458,11 +453,12 @@ public final class PdfSignDialog {
|
|||||||
// Grupo de Zoom da Folha
|
// Grupo de Zoom da Folha
|
||||||
HBox zoomBox = new HBox(5);
|
HBox zoomBox = new HBox(5);
|
||||||
zoomBox.setAlignment(Pos.CENTER_LEFT);
|
zoomBox.setAlignment(Pos.CENTER_LEFT);
|
||||||
zoomBox.setStyle("-fx-background-color: -fx-bg-primary; -fx-border-color: -fx-border-light; -fx-border-radius: 6px; -fx-padding: 2px 6px;");
|
zoomBox.getStyleClass().add("toolbar-box");
|
||||||
|
|
||||||
btnZoomOut = new Button();
|
btnZoomOut = new Button();
|
||||||
btnZoomOut.setGraphic(SvgIcons.zoomOutIcon(13));
|
btnZoomOut.setGraphic(SvgIcons.zoomOutIcon(13));
|
||||||
btnZoomOut.getStyleClass().addAll("btn-secondary", "btn-compact");
|
btnZoomOut.getStyleClass().addAll("btn-secondary", "btn-compact");
|
||||||
|
btnZoomOut.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnZoomOut.setTooltip(new Tooltip("Diminuir Zoom"));
|
btnZoomOut.setTooltip(new Tooltip("Diminuir Zoom"));
|
||||||
btnZoomOut.setOnAction(e -> adjustZoom(-ZOOM_STEP));
|
btnZoomOut.setOnAction(e -> adjustZoom(-ZOOM_STEP));
|
||||||
|
|
||||||
@@ -472,12 +468,14 @@ public final class PdfSignDialog {
|
|||||||
btnZoomIn = new Button();
|
btnZoomIn = new Button();
|
||||||
btnZoomIn.setGraphic(SvgIcons.zoomInIcon(13));
|
btnZoomIn.setGraphic(SvgIcons.zoomInIcon(13));
|
||||||
btnZoomIn.getStyleClass().addAll("btn-secondary", "btn-compact");
|
btnZoomIn.getStyleClass().addAll("btn-secondary", "btn-compact");
|
||||||
|
btnZoomIn.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnZoomIn.setTooltip(new Tooltip("Aumentar Zoom"));
|
btnZoomIn.setTooltip(new Tooltip("Aumentar Zoom"));
|
||||||
btnZoomIn.setOnAction(e -> adjustZoom(ZOOM_STEP));
|
btnZoomIn.setOnAction(e -> adjustZoom(ZOOM_STEP));
|
||||||
|
|
||||||
btnZoomFit = new Button("Ajustar");
|
btnZoomFit = new Button("Ajustar");
|
||||||
btnZoomFit.setGraphic(SvgIcons.zoomFitIcon(12));
|
btnZoomFit.setGraphic(SvgIcons.zoomFitIcon(12));
|
||||||
btnZoomFit.getStyleClass().addAll("btn-secondary", "btn-compact");
|
btnZoomFit.getStyleClass().addAll("btn-secondary", "btn-compact");
|
||||||
|
btnZoomFit.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnZoomFit.setTooltip(new Tooltip("Ajustar à janela"));
|
btnZoomFit.setTooltip(new Tooltip("Ajustar à janela"));
|
||||||
btnZoomFit.setOnAction(e -> setZoom(1.0));
|
btnZoomFit.setOnAction(e -> setZoom(1.0));
|
||||||
|
|
||||||
@@ -486,10 +484,11 @@ public final class PdfSignDialog {
|
|||||||
// Grupo de Tamanho do Selo
|
// Grupo de Tamanho do Selo
|
||||||
HBox resizeToolBox = new HBox(6);
|
HBox resizeToolBox = new HBox(6);
|
||||||
resizeToolBox.setAlignment(Pos.CENTER_LEFT);
|
resizeToolBox.setAlignment(Pos.CENTER_LEFT);
|
||||||
resizeToolBox.setStyle("-fx-background-color: -fx-bg-primary; -fx-border-color: -fx-border-light; -fx-border-radius: 6px; -fx-padding: 2px 6px;");
|
resizeToolBox.getStyleClass().add("toolbar-box");
|
||||||
|
|
||||||
btnToggleResize = new Button(" Redimensionar", SvgIcons.zoomFitIcon(13));
|
btnToggleResize = new Button(" Redimensionar", SvgIcons.zoomFitIcon(13));
|
||||||
btnToggleResize.getStyleClass().addAll("btn-secondary", "btn-compact");
|
btnToggleResize.getStyleClass().addAll("btn-secondary", "btn-compact");
|
||||||
|
btnToggleResize.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnToggleResize.setTooltip(new Tooltip("Ativar/desativar alça interativa para alterar o tamanho do selo"));
|
btnToggleResize.setTooltip(new Tooltip("Ativar/desativar alça interativa para alterar o tamanho do selo"));
|
||||||
btnToggleResize.setOnAction(e -> toggleResizeMode());
|
btnToggleResize.setOnAction(e -> toggleResizeMode());
|
||||||
|
|
||||||
@@ -498,6 +497,7 @@ public final class PdfSignDialog {
|
|||||||
|
|
||||||
btnResetSealScale = new Button(" 100%", SvgIcons.refreshIcon(12));
|
btnResetSealScale = new Button(" 100%", SvgIcons.refreshIcon(12));
|
||||||
btnResetSealScale.getStyleClass().addAll("btn-secondary", "btn-compact");
|
btnResetSealScale.getStyleClass().addAll("btn-secondary", "btn-compact");
|
||||||
|
btnResetSealScale.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnResetSealScale.setTooltip(new Tooltip("Restaurar tamanho padrão original (100%)"));
|
btnResetSealScale.setTooltip(new Tooltip("Restaurar tamanho padrão original (100%)"));
|
||||||
btnResetSealScale.setVisible(false);
|
btnResetSealScale.setVisible(false);
|
||||||
btnResetSealScale.setOnAction(e -> applySealScale(1.0));
|
btnResetSealScale.setOnAction(e -> applySealScale(1.0));
|
||||||
@@ -508,7 +508,7 @@ public final class PdfSignDialog {
|
|||||||
HBox.setHgrow(tbSpacer, Priority.ALWAYS);
|
HBox.setHgrow(tbSpacer, Priority.ALWAYS);
|
||||||
|
|
||||||
lblPositionFeedback = new Label("Clique na folha para posicionar");
|
lblPositionFeedback = new Label("Clique na folha para posicionar");
|
||||||
lblPositionFeedback.setStyle("-fx-font-size: 11.5px; -fx-text-fill: -fx-accent; -fx-font-weight: bold;");
|
lblPositionFeedback.getStyleClass().addAll("text-accent", "text-bold");
|
||||||
|
|
||||||
toolbar.getChildren().addAll(pageNavBox, zoomBox, resizeToolBox, tbSpacer, lblPositionFeedback);
|
toolbar.getChildren().addAll(pageNavBox, zoomBox, resizeToolBox, tbSpacer, lblPositionFeedback);
|
||||||
|
|
||||||
@@ -517,7 +517,7 @@ public final class PdfSignDialog {
|
|||||||
sheetScroll.setFitToWidth(false);
|
sheetScroll.setFitToWidth(false);
|
||||||
sheetScroll.setFitToHeight(false);
|
sheetScroll.setFitToHeight(false);
|
||||||
sheetScroll.setPannable(false);
|
sheetScroll.setPannable(false);
|
||||||
sheetScroll.setStyle("-fx-background-color: transparent; -fx-background: transparent; -fx-border-color: transparent;");
|
sheetScroll.getStyleClass().add("scroll-transparent");
|
||||||
VBox.setVgrow(sheetScroll, Priority.ALWAYS);
|
VBox.setVgrow(sheetScroll, Priority.ALWAYS);
|
||||||
|
|
||||||
// Permite atalho de Zoom com Ctrl + Rolagem do Mouse
|
// Permite atalho de Zoom com Ctrl + Rolagem do Mouse
|
||||||
@@ -538,7 +538,7 @@ public final class PdfSignDialog {
|
|||||||
|
|
||||||
pageSheetContainer = new StackPane();
|
pageSheetContainer = new StackPane();
|
||||||
pageSheetContainer.setAlignment(Pos.TOP_LEFT);
|
pageSheetContainer.setAlignment(Pos.TOP_LEFT);
|
||||||
pageSheetContainer.setStyle("-fx-background-color: #ffffff; -fx-border-color: #94a3b8; -fx-border-width: 1px;");
|
pageSheetContainer.getStyleClass().add("preview-box");
|
||||||
DropShadow sheetShadow = new DropShadow(16, 0, 4, Color.rgb(0, 0, 0, 0.30));
|
DropShadow sheetShadow = new DropShadow(16, 0, 4, Color.rgb(0, 0, 0, 0.30));
|
||||||
pageSheetContainer.setEffect(sheetShadow);
|
pageSheetContainer.setEffect(sheetShadow);
|
||||||
updateContainerZoomDimensions();
|
updateContainerZoomDimensions();
|
||||||
@@ -684,11 +684,11 @@ public final class PdfSignDialog {
|
|||||||
if (btnToggleResize != null) {
|
if (btnToggleResize != null) {
|
||||||
if (isResizeModeActive) {
|
if (isResizeModeActive) {
|
||||||
btnToggleResize.setText("✓ Concluir");
|
btnToggleResize.setText("✓ Concluir");
|
||||||
btnToggleResize.setStyle("-fx-background-color: #0284c7; -fx-text-fill: white; -fx-font-weight: bold;");
|
btnToggleResize.getStyleClass().setAll("button", "btn-primary", "btn-compact");
|
||||||
lblPositionFeedback.setText("👉 Puxe a alça azul no canto do selo para ajustar o tamanho");
|
lblPositionFeedback.setText("👉 Puxe a alça azul no canto do selo para ajustar o tamanho");
|
||||||
} else {
|
} else {
|
||||||
btnToggleResize.setText("📏 Redimensionar");
|
btnToggleResize.setText("📏 Redimensionar");
|
||||||
btnToggleResize.setStyle("");
|
btnToggleResize.getStyleClass().setAll("button", "btn-secondary", "btn-compact");
|
||||||
updateFeedbackLabel();
|
updateFeedbackLabel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1250,4 +1250,8 @@ public final class PdfSignDialog {
|
|||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ public final class SealDesignerDialog {
|
|||||||
ScrollPane scrollForm = new ScrollPane(formPane);
|
ScrollPane scrollForm = new ScrollPane(formPane);
|
||||||
scrollForm.setFitToWidth(true);
|
scrollForm.setFitToWidth(true);
|
||||||
VBox.setVgrow(scrollForm, Priority.ALWAYS);
|
VBox.setVgrow(scrollForm, Priority.ALWAYS);
|
||||||
scrollForm.setStyle("-fx-background: transparent; -fx-background-color: transparent;");
|
scrollForm.getStyleClass().add("scroll-transparent");
|
||||||
HBox.setHgrow(scrollForm, Priority.ALWAYS);
|
HBox.setHgrow(scrollForm, Priority.ALWAYS);
|
||||||
|
|
||||||
// 1. Identificação do Selo e Campos Adicionais
|
// 1. Identificação do Selo e Campos Adicionais
|
||||||
@@ -276,7 +276,7 @@ public final class SealDesignerDialog {
|
|||||||
logoBtnRow.setAlignment(Pos.CENTER_LEFT);
|
logoBtnRow.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
lblLogoStatus = new Label("Nenhum logotipo carregado (usando emblema vetorial)");
|
lblLogoStatus = new Label("Nenhum logotipo carregado (usando emblema vetorial)");
|
||||||
lblLogoStatus.setStyle("-fx-font-size: 11px; -fx-text-fill: #64748b;");
|
lblLogoStatus.getStyleClass().add("text-muted");
|
||||||
|
|
||||||
Label lblFallbackIcon = new Label("Emblema Vetorial Alternativo (caso sem logo própria):");
|
Label lblFallbackIcon = new Label("Emblema Vetorial Alternativo (caso sem logo própria):");
|
||||||
lblFallbackIcon.getStyleClass().add("form-label");
|
lblFallbackIcon.getStyleClass().add("form-label");
|
||||||
@@ -341,7 +341,7 @@ public final class SealDesignerDialog {
|
|||||||
lblPreviewTitle.getStyleClass().add("dialog-field-title");
|
lblPreviewTitle.getStyleClass().add("dialog-field-title");
|
||||||
|
|
||||||
Label lblPreviewNotice = new Label("O selo sempre exibe o nome completo do signatário com destaque e máxima legibilidade.");
|
Label lblPreviewNotice = new Label("O selo sempre exibe o nome completo do signatário com destaque e máxima legibilidade.");
|
||||||
lblPreviewNotice.setStyle("-fx-font-size: 11px; -fx-text-fill: #94a3b8;");
|
lblPreviewNotice.getStyleClass().add("text-muted");
|
||||||
lblPreviewNotice.setWrapText(true);
|
lblPreviewNotice.setWrapText(true);
|
||||||
|
|
||||||
previewImageView = new ImageView();
|
previewImageView = new ImageView();
|
||||||
@@ -351,7 +351,7 @@ public final class SealDesignerDialog {
|
|||||||
HBox previewContainer = new HBox(previewImageView);
|
HBox previewContainer = new HBox(previewImageView);
|
||||||
previewContainer.setAlignment(Pos.CENTER);
|
previewContainer.setAlignment(Pos.CENTER);
|
||||||
previewContainer.setPadding(new Insets(16, 8, 16, 8));
|
previewContainer.setPadding(new Insets(16, 8, 16, 8));
|
||||||
previewContainer.setStyle("-fx-background-color: #090d16; -fx-background-radius: 8px; -fx-border-color: #334155; -fx-border-radius: 8px;");
|
previewContainer.getStyleClass().add("preview-box");
|
||||||
|
|
||||||
VBox previewContentBox = new VBox(10);
|
VBox previewContentBox = new VBox(10);
|
||||||
previewContentBox.setAlignment(Pos.CENTER);
|
previewContentBox.setAlignment(Pos.CENTER);
|
||||||
@@ -383,24 +383,33 @@ public final class SealDesignerDialog {
|
|||||||
footer.setAlignment(Pos.CENTER_RIGHT);
|
footer.setAlignment(Pos.CENTER_RIGHT);
|
||||||
footer.setPadding(new Insets(6, 0, 0, 0));
|
footer.setPadding(new Insets(6, 0, 0, 0));
|
||||||
|
|
||||||
|
Region spacer = new Region();
|
||||||
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
|
|
||||||
Button btnCancel = new Button("Cancelar");
|
Button btnCancel = new Button("Cancelar");
|
||||||
btnCancel.getStyleClass().add("btn-secondary");
|
btnCancel.getStyleClass().add("btn-ghost");
|
||||||
|
btnCancel.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnCancel.setPrefHeight(34);
|
btnCancel.setPrefHeight(34);
|
||||||
btnCancel.setOnAction(e -> stage.close());
|
btnCancel.setOnAction(e -> stage.close());
|
||||||
|
|
||||||
Button btnInstall = new Button("💾 Instalar Selo no Sistema");
|
Button btnInstall = new Button("💾 Instalar Selo no Sistema");
|
||||||
btnInstall.getStyleClass().add("btn-primary");
|
btnInstall.getStyleClass().add("btn-primary");
|
||||||
|
btnInstall.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnInstall.setPrefHeight(34);
|
btnInstall.setPrefHeight(34);
|
||||||
btnInstall.setDefaultButton(true);
|
btnInstall.setDefaultButton(true);
|
||||||
btnInstall.setOnAction(e -> handleInstallSeal());
|
btnInstall.setOnAction(e -> handleInstallSeal());
|
||||||
|
|
||||||
footer.getChildren().addAll(btnCancel, btnInstall);
|
btnImport.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
|
btnExport.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
|
|
||||||
|
footer.getChildren().addAll(spacer, btnCancel, btnInstall);
|
||||||
|
|
||||||
root.getChildren().addAll(header, mainSplit, footer);
|
root.getChildren().addAll(header, mainSplit, footer);
|
||||||
|
|
||||||
Scene scene = new Scene(root, 880, 580);
|
Scene scene = new Scene(root, 880, 580);
|
||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePreview() {
|
private void updatePreview() {
|
||||||
@@ -442,7 +451,7 @@ public final class SealDesignerDialog {
|
|||||||
byte[] bytes = Files.readAllBytes(file.toPath());
|
byte[] bytes = Files.readAllBytes(file.toPath());
|
||||||
customLogoBase64 = Base64.getEncoder().encodeToString(bytes);
|
customLogoBase64 = Base64.getEncoder().encodeToString(bytes);
|
||||||
lblLogoStatus.setText("✓ Imagem carregada: " + file.getName());
|
lblLogoStatus.setText("✓ Imagem carregada: " + file.getName());
|
||||||
lblLogoStatus.setStyle("-fx-font-size: 11px; -fx-text-fill: #16a34a; -fx-font-weight: bold;");
|
lblLogoStatus.getStyleClass().setAll("label", "text-success", "text-bold");
|
||||||
btnClearLogo.setDisable(false);
|
btnClearLogo.setDisable(false);
|
||||||
updatePreview();
|
updatePreview();
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
@@ -454,7 +463,7 @@ public final class SealDesignerDialog {
|
|||||||
private void handleClearLogo() {
|
private void handleClearLogo() {
|
||||||
customLogoBase64 = null;
|
customLogoBase64 = null;
|
||||||
lblLogoStatus.setText("Nenhum logotipo carregado (usando emblema vetorial)");
|
lblLogoStatus.setText("Nenhum logotipo carregado (usando emblema vetorial)");
|
||||||
lblLogoStatus.setStyle("-fx-font-size: 11px; -fx-text-fill: #64748b;");
|
lblLogoStatus.getStyleClass().setAll("label", "text-muted");
|
||||||
btnClearLogo.setDisable(true);
|
btnClearLogo.setDisable(true);
|
||||||
updatePreview();
|
updatePreview();
|
||||||
}
|
}
|
||||||
@@ -524,12 +533,12 @@ public final class SealDesignerDialog {
|
|||||||
if (theme.customLogoBase64() != null && !theme.customLogoBase64().isBlank()) {
|
if (theme.customLogoBase64() != null && !theme.customLogoBase64().isBlank()) {
|
||||||
this.customLogoBase64 = theme.customLogoBase64();
|
this.customLogoBase64 = theme.customLogoBase64();
|
||||||
lblLogoStatus.setText("✓ Logotipo carregado com sucesso!");
|
lblLogoStatus.setText("✓ Logotipo carregado com sucesso!");
|
||||||
lblLogoStatus.setStyle("-fx-font-size: 11px; -fx-text-fill: #16a34a; -fx-font-weight: bold;");
|
lblLogoStatus.getStyleClass().setAll("label", "text-success", "text-bold");
|
||||||
btnClearLogo.setDisable(false);
|
btnClearLogo.setDisable(false);
|
||||||
} else {
|
} else {
|
||||||
this.customLogoBase64 = null;
|
this.customLogoBase64 = null;
|
||||||
lblLogoStatus.setText("Nenhum logotipo carregado (usando emblema vetorial)");
|
lblLogoStatus.setText("Nenhum logotipo carregado (usando emblema vetorial)");
|
||||||
lblLogoStatus.setStyle("-fx-font-size: 11px; -fx-text-fill: #64748b;");
|
lblLogoStatus.getStyleClass().setAll("label", "text-muted");
|
||||||
btnClearLogo.setDisable(true);
|
btnClearLogo.setDisable(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -640,4 +649,8 @@ public final class SealDesignerDialog {
|
|||||||
stage.centerOnScreen();
|
stage.centerOnScreen();
|
||||||
stage.show();
|
stage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ public final class SealManagementDialog {
|
|||||||
|
|
||||||
listViewSeals = new ListView<>();
|
listViewSeals = new ListView<>();
|
||||||
VBox.setVgrow(listViewSeals, Priority.ALWAYS);
|
VBox.setVgrow(listViewSeals, Priority.ALWAYS);
|
||||||
listViewSeals.setStyle("-fx-background-color: transparent; -fx-background-radius: 6px;");
|
listViewSeals.getStyleClass().add("cyber-scroll-pane");
|
||||||
listViewSeals.setCellFactory(lv -> new SealListCell());
|
listViewSeals.setCellFactory(lv -> new SealListCell());
|
||||||
listViewSeals.getSelectionModel().selectedItemProperty().addListener((obs, oldV, newV) -> onThemeSelected(newV));
|
listViewSeals.getSelectionModel().selectedItemProperty().addListener((obs, oldV, newV) -> onThemeSelected(newV));
|
||||||
|
|
||||||
@@ -153,10 +153,10 @@ public final class SealManagementDialog {
|
|||||||
lblPreviewHeader.getStyleClass().add("dialog-field-title");
|
lblPreviewHeader.getStyleClass().add("dialog-field-title");
|
||||||
|
|
||||||
lblSelectedName = new Label("Selecione um selo para visualizar");
|
lblSelectedName = new Label("Selecione um selo para visualizar");
|
||||||
lblSelectedName.setStyle("-fx-font-weight: bold; -fx-font-size: 14px;");
|
lblSelectedName.getStyleClass().add("dialog-header-title");
|
||||||
|
|
||||||
lblSelectedDesc = new Label("");
|
lblSelectedDesc = new Label("");
|
||||||
lblSelectedDesc.setStyle("-fx-font-size: 11.5px; -fx-text-fill: #64748b;");
|
lblSelectedDesc.getStyleClass().add("text-muted");
|
||||||
lblSelectedDesc.setWrapText(true);
|
lblSelectedDesc.setWrapText(true);
|
||||||
|
|
||||||
previewImageView = new ImageView();
|
previewImageView = new ImageView();
|
||||||
@@ -166,14 +166,14 @@ public final class SealManagementDialog {
|
|||||||
HBox previewContainer = new HBox(previewImageView);
|
HBox previewContainer = new HBox(previewImageView);
|
||||||
previewContainer.setAlignment(Pos.CENTER);
|
previewContainer.setAlignment(Pos.CENTER);
|
||||||
previewContainer.setPadding(new Insets(14, 10, 14, 10));
|
previewContainer.setPadding(new Insets(14, 10, 14, 10));
|
||||||
previewContainer.setStyle("-fx-background-color: #090d16; -fx-background-radius: 8px; -fx-border-color: #334155; -fx-border-radius: 8px;");
|
previewContainer.getStyleClass().add("preview-box");
|
||||||
VBox.setVgrow(previewContainer, Priority.ALWAYS);
|
VBox.setVgrow(previewContainer, Priority.ALWAYS);
|
||||||
|
|
||||||
lblActiveBadge = new Label("");
|
lblActiveBadge = new Label("");
|
||||||
lblActiveBadge.setStyle("-fx-font-weight: bold; -fx-font-size: 12px;");
|
lblActiveBadge.getStyleClass().add("text-bold");
|
||||||
|
|
||||||
lblFeedback = new Label("");
|
lblFeedback = new Label("");
|
||||||
lblFeedback.setStyle("-fx-font-size: 12px; -fx-font-weight: bold; -fx-text-fill: #16a34a;");
|
lblFeedback.getStyleClass().addAll("text-success", "text-bold");
|
||||||
|
|
||||||
// Botões de Ação do Selo Selecionado
|
// Botões de Ação do Selo Selecionado
|
||||||
VBox sealActions = new VBox(8);
|
VBox sealActions = new VBox(8);
|
||||||
@@ -222,9 +222,8 @@ public final class SealManagementDialog {
|
|||||||
footer.setAlignment(Pos.CENTER_RIGHT);
|
footer.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
|
||||||
Button btnClose = new Button(" Fechar", SvgIcons.closeIcon(13));
|
Button btnClose = new Button(" Fechar", SvgIcons.closeIcon(13));
|
||||||
btnClose.getStyleClass().addAll("btn-secondary", "btn-dialog");
|
btnClose.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
btnClose.setPrefWidth(110);
|
btnClose.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnClose.setMinWidth(110);
|
|
||||||
btnClose.setOnAction(e -> stage.close());
|
btnClose.setOnAction(e -> stage.close());
|
||||||
|
|
||||||
footer.getChildren().add(btnClose);
|
footer.getChildren().add(btnClose);
|
||||||
@@ -234,6 +233,7 @@ public final class SealManagementDialog {
|
|||||||
Scene scene = new Scene(root, 880, 560);
|
Scene scene = new Scene(root, 880, 560);
|
||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshSealsList() {
|
private void refreshSealsList() {
|
||||||
@@ -271,16 +271,14 @@ public final class SealManagementDialog {
|
|||||||
boolean isDefault = theme.id().equalsIgnoreCase(currentDefaultThemeId);
|
boolean isDefault = theme.id().equalsIgnoreCase(currentDefaultThemeId);
|
||||||
if (isDefault) {
|
if (isDefault) {
|
||||||
lblActiveBadge.setText("★ ESTE É O SELO PADRÃO ATIVO PARA TODAS AS ASSINATURAS");
|
lblActiveBadge.setText("★ ESTE É O SELO PADRÃO ATIVO PARA TODAS AS ASSINATURAS");
|
||||||
lblActiveBadge.setStyle("-fx-text-fill: #38bdf8; -fx-font-weight: bold; -fx-font-size: 11.5px;");
|
lblActiveBadge.getStyleClass().setAll("label", "text-primary", "text-bold");
|
||||||
btnSetDefault.setText("★ Selo Padrão Ativo no Sistema");
|
btnSetDefault.setText("★ Selo Padrão Ativo no Sistema");
|
||||||
btnSetDefault.setDisable(true);
|
btnSetDefault.setDisable(true);
|
||||||
btnSetDefault.setStyle("-fx-opacity: 0.95; -fx-background-color: #0369a1; -fx-text-fill: #ffffff; -fx-font-weight: bold;");
|
|
||||||
} else {
|
} else {
|
||||||
lblActiveBadge.setText("Selo disponível para uso.");
|
lblActiveBadge.setText("Selo disponível para uso.");
|
||||||
lblActiveBadge.setStyle("-fx-text-fill: #94a3b8; -fx-font-size: 11.5px;");
|
lblActiveBadge.getStyleClass().setAll("label", "text-muted");
|
||||||
btnSetDefault.setText("✔ Definir como Selo Padrão Ativo");
|
btnSetDefault.setText("✔ Definir como Selo Padrão Ativo");
|
||||||
btnSetDefault.setDisable(false);
|
btnSetDefault.setDisable(false);
|
||||||
btnSetDefault.setStyle("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
btnEditClone.setDisable(false);
|
btnEditClone.setDisable(false);
|
||||||
@@ -443,6 +441,10 @@ public final class SealManagementDialog {
|
|||||||
stage.showAndWait();
|
stage.showAndWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Célula customizada para a listagem visual dos selos.
|
* Célula customizada para a listagem visual dos selos.
|
||||||
*/
|
*/
|
||||||
@@ -460,14 +462,14 @@ public final class SealManagementDialog {
|
|||||||
|
|
||||||
VBox textBox = new VBox(2);
|
VBox textBox = new VBox(2);
|
||||||
Label lblName = new Label(item.name());
|
Label lblName = new Label(item.name());
|
||||||
lblName.setStyle("-fx-font-weight: bold; -fx-font-size: 12.5px;");
|
lblName.getStyleClass().add("cert-title");
|
||||||
lblName.maxWidthProperty().bind(listViewSeals.widthProperty().subtract(120));
|
lblName.maxWidthProperty().bind(listViewSeals.widthProperty().subtract(120));
|
||||||
lblName.setWrapText(true);
|
lblName.setWrapText(true);
|
||||||
|
|
||||||
String desc = (item.description() != null && !item.description().isBlank())
|
String desc = (item.description() != null && !item.description().isBlank())
|
||||||
? item.description() : "Identidade visual de assinatura";
|
? item.description() : "Identidade visual de assinatura";
|
||||||
Label lblDesc = new Label(desc);
|
Label lblDesc = new Label(desc);
|
||||||
lblDesc.setStyle("-fx-font-size: 11px; -fx-text-fill: #64748b;");
|
lblDesc.getStyleClass().add("cert-detail");
|
||||||
lblDesc.maxWidthProperty().bind(listViewSeals.widthProperty().subtract(120));
|
lblDesc.maxWidthProperty().bind(listViewSeals.widthProperty().subtract(120));
|
||||||
lblDesc.setWrapText(true);
|
lblDesc.setWrapText(true);
|
||||||
|
|
||||||
@@ -480,14 +482,12 @@ public final class SealManagementDialog {
|
|||||||
boolean isDefault = item.id().equalsIgnoreCase(currentDefaultThemeId);
|
boolean isDefault = item.id().equalsIgnoreCase(currentDefaultThemeId);
|
||||||
if (isDefault) {
|
if (isDefault) {
|
||||||
Label badgeDefault = new Label("★ PADRÃO");
|
Label badgeDefault = new Label("★ PADRÃO");
|
||||||
badgeDefault.setStyle("-fx-background-color: #0284c7; -fx-text-fill: #ffffff; -fx-font-size: 9.5px; -fx-font-weight: bold; -fx-padding: 2px 6px; -fx-background-radius: 4px;");
|
badgeDefault.getStyleClass().add("badge-primary");
|
||||||
badgesBox.getChildren().add(badgeDefault);
|
badgesBox.getChildren().add(badgeDefault);
|
||||||
}
|
}
|
||||||
|
|
||||||
Label badgeType = new Label(item.isCustom() ? "Personalizado" : "Sistema");
|
Label badgeType = new Label(item.isCustom() ? "Personalizado" : "Sistema");
|
||||||
badgeType.setStyle(item.isCustom()
|
badgeType.getStyleClass().add(item.isCustom() ? "cert-badge-a3" : "cert-badge-a1");
|
||||||
? "-fx-background-color: #fef3c7; -fx-text-fill: #b45309; -fx-font-size: 9.5px; -fx-padding: 2px 6px; -fx-background-radius: 4px;"
|
|
||||||
: "-fx-background-color: #e0f2fe; -fx-text-fill: #0369a1; -fx-font-size: 9.5px; -fx-padding: 2px 6px; -fx-background-radius: 4px;");
|
|
||||||
badgesBox.getChildren().add(badgeType);
|
badgesBox.getChildren().add(badgeType);
|
||||||
|
|
||||||
card.getChildren().addAll(textBox, badgesBox);
|
card.getChildren().addAll(textBox, badgesBox);
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public final class SettingsView extends VBox {
|
|||||||
Label subtitleLabel = new Label("Parâmetros de segurança local, identidade visual dos selos e aplicações autorizadas.");
|
Label subtitleLabel = new Label("Parâmetros de segurança local, identidade visual dos selos e aplicações autorizadas.");
|
||||||
subtitleLabel.getStyleClass().add("brand-subtitle");
|
subtitleLabel.getStyleClass().add("brand-subtitle");
|
||||||
|
|
||||||
saveFeedbackLabel.setStyle("-fx-text-fill: #10b981; -fx-font-size: 11.5px; -fx-font-weight: bold;");
|
saveFeedbackLabel.getStyleClass().addAll("text-success", "text-bold");
|
||||||
|
|
||||||
headerBox.getChildren().addAll(titleLabel, subtitleLabel, saveFeedbackLabel);
|
headerBox.getChildren().addAll(titleLabel, subtitleLabel, saveFeedbackLabel);
|
||||||
|
|
||||||
@@ -126,10 +126,9 @@ public final class SettingsView extends VBox {
|
|||||||
|
|
||||||
Button btnClearPins = new Button("Limpar PINs Salvos (Sessão e Computador)");
|
Button btnClearPins = new Button("Limpar PINs Salvos (Sessão e Computador)");
|
||||||
btnClearPins.getStyleClass().add("btn-secondary");
|
btnClearPins.getStyleClass().add("btn-secondary");
|
||||||
btnClearPins.setStyle("-fx-padding: 6px 14px; -fx-font-size: 12px;");
|
|
||||||
|
|
||||||
Label pinStatusLabel = new Label();
|
Label pinStatusLabel = new Label();
|
||||||
pinStatusLabel.setStyle("-fx-text-fill: #34d399; -fx-font-size: 12px;");
|
pinStatusLabel.getStyleClass().add("text-success");
|
||||||
|
|
||||||
btnClearPins.setOnAction(e -> {
|
btnClearPins.setOnAction(e -> {
|
||||||
PinSessionManager.getInstance().clearAll();
|
PinSessionManager.getInstance().clearAll();
|
||||||
@@ -152,12 +151,11 @@ public final class SettingsView extends VBox {
|
|||||||
sealHeaderRow.setAlignment(Pos.CENTER_LEFT);
|
sealHeaderRow.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
Label lblSealsDesc = new Label("Gerencie os temas e identidades gráficas instaladas para assinaturas PDF:");
|
Label lblSealsDesc = new Label("Gerencie os temas e identidades gráficas instaladas para assinaturas PDF:");
|
||||||
lblSealsDesc.setStyle("-fx-text-fill: #94a3b8; -fx-font-size: 12px;");
|
lblSealsDesc.getStyleClass().add("text-muted");
|
||||||
HBox.setHgrow(lblSealsDesc, Priority.ALWAYS);
|
HBox.setHgrow(lblSealsDesc, Priority.ALWAYS);
|
||||||
|
|
||||||
Button btnCreateSeal = new Button("🎨 Criar ou Instalar Selo...");
|
Button btnCreateSeal = new Button("🎨 Criar ou Instalar Selo...");
|
||||||
btnCreateSeal.getStyleClass().add("btn-primary");
|
btnCreateSeal.getStyleClass().add("btn-primary");
|
||||||
btnCreateSeal.setStyle("-fx-padding: 6px 14px; -fx-font-size: 12px;");
|
|
||||||
btnCreateSeal.setOnAction(e -> openSealDesigner());
|
btnCreateSeal.setOnAction(e -> openSealDesigner());
|
||||||
|
|
||||||
sealHeaderRow.getChildren().addAll(lblSealsDesc, btnCreateSeal);
|
sealHeaderRow.getChildren().addAll(lblSealsDesc, btnCreateSeal);
|
||||||
@@ -176,7 +174,7 @@ public final class SettingsView extends VBox {
|
|||||||
ScrollPane scrollApps = new ScrollPane(appsContainer);
|
ScrollPane scrollApps = new ScrollPane(appsContainer);
|
||||||
scrollApps.setFitToWidth(true);
|
scrollApps.setFitToWidth(true);
|
||||||
scrollApps.setPrefHeight(150);
|
scrollApps.setPrefHeight(150);
|
||||||
scrollApps.setStyle("-fx-background: transparent; -fx-background-color: transparent;");
|
scrollApps.getStyleClass().add("cyber-scroll-pane");
|
||||||
|
|
||||||
appsBox.getChildren().addAll(appsTitle, scrollApps);
|
appsBox.getChildren().addAll(appsTitle, scrollApps);
|
||||||
|
|
||||||
@@ -222,12 +220,11 @@ public final class SettingsView extends VBox {
|
|||||||
|
|
||||||
if (isDefault) {
|
if (isDefault) {
|
||||||
Label badgeDefault = new Label("★ PADRÃO");
|
Label badgeDefault = new Label("★ PADRÃO");
|
||||||
badgeDefault.setStyle("-fx-background-color: #0284c7; -fx-text-fill: #ffffff; -fx-padding: 3px 8px; -fx-background-radius: 4px; -fx-font-weight: bold; -fx-font-size: 11px;");
|
badgeDefault.getStyleClass().add("badge-primary");
|
||||||
item.getChildren().add(badgeDefault);
|
item.getChildren().add(badgeDefault);
|
||||||
} else {
|
} else {
|
||||||
Button btnSetDefault = new Button("★ Definir Padrão");
|
Button btnSetDefault = new Button("★ Definir Padrão");
|
||||||
btnSetDefault.getStyleClass().addAll("btn-secondary", "btn-compact");
|
btnSetDefault.getStyleClass().addAll("btn-secondary", "btn-compact");
|
||||||
btnSetDefault.setStyle("-fx-padding: 3px 8px; -fx-font-size: 11px;");
|
|
||||||
btnSetDefault.setOnAction(e -> {
|
btnSetDefault.setOnAction(e -> {
|
||||||
AgentConfiguration current = configService.getConfig();
|
AgentConfiguration current = configService.getConfig();
|
||||||
configService.updateConfig(new AgentConfiguration(
|
configService.updateConfig(new AgentConfiguration(
|
||||||
@@ -247,8 +244,7 @@ public final class SettingsView extends VBox {
|
|||||||
|
|
||||||
if (theme.isCustom()) {
|
if (theme.isCustom()) {
|
||||||
Button btnDelete = new Button("Excluir");
|
Button btnDelete = new Button("Excluir");
|
||||||
btnDelete.getStyleClass().add("btn-secondary");
|
btnDelete.getStyleClass().add("btn-danger-sm");
|
||||||
btnDelete.setStyle("-fx-padding: 3px 8px; -fx-font-size: 11px; -fx-text-fill: #f87171;");
|
|
||||||
btnDelete.setOnAction(e -> {
|
btnDelete.setOnAction(e -> {
|
||||||
try {
|
try {
|
||||||
PdfSealThemeRegistry.uninstallCustomTheme(theme.id());
|
PdfSealThemeRegistry.uninstallCustomTheme(theme.id());
|
||||||
@@ -310,8 +306,7 @@ public final class SettingsView extends VBox {
|
|||||||
statusLabel.getStyleClass().add(app.enabled() ? "diagnostic-value-ok" : "diagnostic-value-warn");
|
statusLabel.getStyleClass().add(app.enabled() ? "diagnostic-value-ok" : "diagnostic-value-warn");
|
||||||
|
|
||||||
Button btnRevoke = new Button("Revogar");
|
Button btnRevoke = new Button("Revogar");
|
||||||
btnRevoke.getStyleClass().add("btn-secondary");
|
btnRevoke.getStyleClass().add("btn-danger-sm");
|
||||||
btnRevoke.setStyle("-fx-padding: 4px 10px; -fx-font-size: 11px; -fx-text-fill: #f87171;");
|
|
||||||
btnRevoke.setOnAction(e -> {
|
btnRevoke.setOnAction(e -> {
|
||||||
if (onRevokeApplication != null) {
|
if (onRevokeApplication != null) {
|
||||||
onRevokeApplication.accept(app.applicationId());
|
onRevokeApplication.accept(app.applicationId());
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ public class SignaturePromptDialog {
|
|||||||
VBox root = new VBox(16);
|
VBox root = new VBox(16);
|
||||||
root.getStyleClass().add("dialog-container");
|
root.getStyleClass().add("dialog-container");
|
||||||
root.setPadding(new Insets(20, 24, 20, 24));
|
root.setPadding(new Insets(20, 24, 20, 24));
|
||||||
root.setPrefWidth(560);
|
|
||||||
|
|
||||||
HBox headerBox = new HBox(14);
|
HBox headerBox = new HBox(14);
|
||||||
headerBox.setAlignment(Pos.CENTER_LEFT);
|
headerBox.setAlignment(Pos.CENTER_LEFT);
|
||||||
@@ -174,14 +173,13 @@ public class SignaturePromptDialog {
|
|||||||
if (isWindowsStore) {
|
if (isWindowsStore) {
|
||||||
VBox winBox = new VBox(6);
|
VBox winBox = new VBox(6);
|
||||||
Label winTitle = new Label("🔐 Autenticação nativa pelo Windows / Driver do Token");
|
Label winTitle = new Label("🔐 Autenticação nativa pelo Windows / Driver do Token");
|
||||||
winTitle.setStyle("-fx-text-fill: -fx-accent; -fx-font-weight: bold; -fx-font-size: 13px;");
|
winTitle.getStyleClass().addAll("text-accent", "text-bold");
|
||||||
|
|
||||||
Label winDesc = new Label(
|
Label winDesc = new Label(
|
||||||
"O PIN deste dispositivo é gerenciado diretamente pelo driver nativo (SafeSign / Windows Security). " +
|
"O PIN deste dispositivo é gerenciado diretamente pelo driver nativo (SafeSign / Windows Security). " +
|
||||||
"Caso o token ainda não esteja autenticado, a janela segura do driver será aberta automaticamente."
|
"Caso o token ainda não esteja autenticado, a janela segura do driver será aberta automaticamente."
|
||||||
);
|
);
|
||||||
winDesc.getStyleClass().add("dialog-subtitle");
|
winDesc.getStyleClass().add("dialog-subtitle");
|
||||||
winDesc.setStyle("-fx-font-size: 12px;");
|
|
||||||
winDesc.setWrapText(true);
|
winDesc.setWrapText(true);
|
||||||
|
|
||||||
winBox.getChildren().addAll(winTitle, winDesc);
|
winBox.getChildren().addAll(winTitle, winDesc);
|
||||||
@@ -194,11 +192,10 @@ public class SignaturePromptDialog {
|
|||||||
rememberedBox.setAlignment(Pos.CENTER_LEFT);
|
rememberedBox.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
Label rememberedBadge = new Label("● PIN salvo nesta sessão ativa");
|
Label rememberedBadge = new Label("● PIN salvo nesta sessão ativa");
|
||||||
rememberedBadge.setStyle("-fx-text-fill: #10b981; -fx-font-weight: bold; -fx-font-size: 12px;");
|
rememberedBadge.getStyleClass().addAll("text-success", "text-bold");
|
||||||
|
|
||||||
Button btnChangePin = new Button("Alterar PIN");
|
Button btnChangePin = new Button("Alterar PIN");
|
||||||
btnChangePin.getStyleClass().add("btn-secondary");
|
btnChangePin.getStyleClass().add("btn-secondary");
|
||||||
btnChangePin.setStyle("-fx-padding: 4px 10px; -fx-font-size: 11px;");
|
|
||||||
|
|
||||||
VBox inputWrapper = new VBox(8);
|
VBox inputWrapper = new VBox(8);
|
||||||
inputWrapper.setVisible(false);
|
inputWrapper.setVisible(false);
|
||||||
@@ -222,7 +219,6 @@ public class SignaturePromptDialog {
|
|||||||
VBox inputWrapper = new VBox(8);
|
VBox inputWrapper = new VBox(8);
|
||||||
Label pinPromptLabel = new Label("Informe o PIN do dispositivo para autorizar a chave privada:");
|
Label pinPromptLabel = new Label("Informe o PIN do dispositivo para autorizar a chave privada:");
|
||||||
pinPromptLabel.getStyleClass().add("dialog-subtitle");
|
pinPromptLabel.getStyleClass().add("dialog-subtitle");
|
||||||
pinPromptLabel.setStyle("-fx-font-size: 12px;");
|
|
||||||
|
|
||||||
cbSavePin.setSelected(false);
|
cbSavePin.setSelected(false);
|
||||||
inputWrapper.getChildren().addAll(pinPromptLabel, pinField, cbSavePin);
|
inputWrapper.getChildren().addAll(pinPromptLabel, pinField, cbSavePin);
|
||||||
@@ -233,7 +229,7 @@ public class SignaturePromptDialog {
|
|||||||
|
|
||||||
// Label de Alerta de Validação
|
// Label de Alerta de Validação
|
||||||
Label validationLabel = new Label();
|
Label validationLabel = new Label();
|
||||||
validationLabel.setStyle("-fx-text-fill: #f87171; -fx-font-size: 12px;");
|
validationLabel.getStyleClass().addAll("text-danger", "text-bold");
|
||||||
validationLabel.setVisible(false);
|
validationLabel.setVisible(false);
|
||||||
validationLabel.setManaged(false);
|
validationLabel.setManaged(false);
|
||||||
|
|
||||||
@@ -243,14 +239,19 @@ public class SignaturePromptDialog {
|
|||||||
actionsRow.setPadding(new Insets(4, 0, 0, 0));
|
actionsRow.setPadding(new Insets(4, 0, 0, 0));
|
||||||
|
|
||||||
Button btnCancel = new Button("Cancelar");
|
Button btnCancel = new Button("Cancelar");
|
||||||
btnCancel.getStyleClass().add("btn-cancel");
|
btnCancel.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
|
btnCancel.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnCancel.setOnAction(e -> {
|
btnCancel.setOnAction(e -> {
|
||||||
approvalFuture.complete(false);
|
approvalFuture.complete(false);
|
||||||
stage.close();
|
stage.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
HBox spacer = new HBox();
|
||||||
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
|
|
||||||
Button btnConfirm = new Button(isAuthentication ? "Confirmar e Entrar" : "Confirmar e Assinar");
|
Button btnConfirm = new Button(isAuthentication ? "Confirmar e Entrar" : "Confirmar e Assinar");
|
||||||
btnConfirm.getStyleClass().add("btn-primary");
|
btnConfirm.getStyleClass().addAll("btn-primary", "btn-dialog");
|
||||||
|
btnConfirm.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnConfirm.setDefaultButton(true);
|
btnConfirm.setDefaultButton(true);
|
||||||
btnConfirm.setOnAction(e -> {
|
btnConfirm.setOnAction(e -> {
|
||||||
if (isA3 && !isWindowsStore) {
|
if (isA3 && !isWindowsStore) {
|
||||||
@@ -280,7 +281,7 @@ public class SignaturePromptDialog {
|
|||||||
stage.close();
|
stage.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
actionsRow.getChildren().addAll(btnCancel, btnConfirm);
|
actionsRow.getChildren().addAll(btnCancel, spacer, btnConfirm);
|
||||||
|
|
||||||
if (isA3) {
|
if (isA3) {
|
||||||
root.getChildren().addAll(headerBox, detailsBox, pinContainer, validationLabel, actionsRow);
|
root.getChildren().addAll(headerBox, detailsBox, pinContainer, validationLabel, actionsRow);
|
||||||
@@ -338,4 +339,8 @@ public class SignaturePromptDialog {
|
|||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import javafx.scene.control.TextField;
|
|||||||
import javafx.scene.control.ToggleGroup;
|
import javafx.scene.control.ToggleGroup;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
||||||
|
import javafx.scene.layout.Region;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
import javafx.stage.Modality;
|
import javafx.stage.Modality;
|
||||||
@@ -120,6 +121,7 @@ public final class SignatureValidationDialog {
|
|||||||
|
|
||||||
Button btnBrowseSig = new Button("Procurar...");
|
Button btnBrowseSig = new Button("Procurar...");
|
||||||
btnBrowseSig.getStyleClass().add("btn-secondary");
|
btnBrowseSig.getStyleClass().add("btn-secondary");
|
||||||
|
btnBrowseSig.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnBrowseSig.setOnAction(e -> handleBrowseSig());
|
btnBrowseSig.setOnAction(e -> handleBrowseSig());
|
||||||
|
|
||||||
sigRow.getChildren().addAll(txtSigPath, btnBrowseSig);
|
sigRow.getChildren().addAll(txtSigPath, btnBrowseSig);
|
||||||
@@ -139,6 +141,7 @@ public final class SignatureValidationDialog {
|
|||||||
|
|
||||||
Button btnBrowseDoc = new Button("Procurar...");
|
Button btnBrowseDoc = new Button("Procurar...");
|
||||||
btnBrowseDoc.getStyleClass().add("btn-secondary");
|
btnBrowseDoc.getStyleClass().add("btn-secondary");
|
||||||
|
btnBrowseDoc.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnBrowseDoc.setOnAction(e -> handleBrowseDoc());
|
btnBrowseDoc.setOnAction(e -> handleBrowseDoc());
|
||||||
|
|
||||||
docRow.getChildren().addAll(txtDocPath, btnBrowseDoc);
|
docRow.getChildren().addAll(txtDocPath, btnBrowseDoc);
|
||||||
@@ -152,7 +155,7 @@ public final class SignatureValidationDialog {
|
|||||||
|
|
||||||
ScrollPane scrollResult = new ScrollPane(resultBox);
|
ScrollPane scrollResult = new ScrollPane(resultBox);
|
||||||
scrollResult.setFitToWidth(true);
|
scrollResult.setFitToWidth(true);
|
||||||
scrollResult.setStyle("-fx-background: transparent; -fx-background-color: transparent;");
|
scrollResult.getStyleClass().add("cyber-scroll-pane");
|
||||||
VBox.setVgrow(scrollResult, Priority.ALWAYS);
|
VBox.setVgrow(scrollResult, Priority.ALWAYS);
|
||||||
|
|
||||||
// Rodapé
|
// Rodapé
|
||||||
@@ -161,28 +164,35 @@ public final class SignatureValidationDialog {
|
|||||||
|
|
||||||
btnSaveExtracted = new Button("Salvar Conteúdo Extraído...");
|
btnSaveExtracted = new Button("Salvar Conteúdo Extraído...");
|
||||||
btnSaveExtracted.getStyleClass().add("btn-secondary");
|
btnSaveExtracted.getStyleClass().add("btn-secondary");
|
||||||
|
btnSaveExtracted.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnSaveExtracted.setVisible(false);
|
btnSaveExtracted.setVisible(false);
|
||||||
btnSaveExtracted.setManaged(false);
|
btnSaveExtracted.setManaged(false);
|
||||||
btnSaveExtracted.setOnAction(e -> handleSaveExtracted());
|
btnSaveExtracted.setOnAction(e -> handleSaveExtracted());
|
||||||
|
|
||||||
HBox.setHgrow(btnSaveExtracted, Priority.NEVER);
|
HBox.setHgrow(btnSaveExtracted, Priority.NEVER);
|
||||||
|
|
||||||
|
Region spacer = new Region();
|
||||||
|
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||||
|
|
||||||
Button btnClose = new Button("Fechar");
|
Button btnClose = new Button("Fechar");
|
||||||
btnClose.getStyleClass().add("btn-secondary");
|
btnClose.getStyleClass().add("btn-ghost");
|
||||||
|
btnClose.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnClose.setOnAction(e -> stage.close());
|
btnClose.setOnAction(e -> stage.close());
|
||||||
|
|
||||||
Button btnValidate = new Button("Validar Agora");
|
Button btnValidate = new Button("Validar Agora");
|
||||||
btnValidate.getStyleClass().add("btn-primary");
|
btnValidate.getStyleClass().add("btn-primary");
|
||||||
|
btnValidate.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnValidate.setDefaultButton(true);
|
btnValidate.setDefaultButton(true);
|
||||||
btnValidate.setOnAction(e -> handleValidate());
|
btnValidate.setOnAction(e -> handleValidate());
|
||||||
|
|
||||||
footer.getChildren().addAll(btnSaveExtracted, btnClose, btnValidate);
|
footer.getChildren().addAll(btnSaveExtracted, spacer, btnClose, btnValidate);
|
||||||
|
|
||||||
root.getChildren().addAll(header, typeBox, sigBox, docBox, scrollResult, footer);
|
root.getChildren().addAll(header, typeBox, sigBox, docBox, scrollResult, footer);
|
||||||
|
|
||||||
Scene scene = new Scene(root);
|
Scene scene = new Scene(root);
|
||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
|
|
||||||
updateModeView();
|
updateModeView();
|
||||||
}
|
}
|
||||||
@@ -277,27 +287,26 @@ public final class SignatureValidationDialog {
|
|||||||
statusBadge.setPadding(new Insets(10, 14, 10, 14));
|
statusBadge.setPadding(new Insets(10, 14, 10, 14));
|
||||||
|
|
||||||
Label lblBadge = new Label(result.status().getDescription());
|
Label lblBadge = new Label(result.status().getDescription());
|
||||||
lblBadge.setStyle("-fx-font-weight: bold; -fx-font-size: 14px;");
|
|
||||||
|
|
||||||
if (result.status() == ValidationStatus.VALID) {
|
if (result.status() == ValidationStatus.VALID) {
|
||||||
statusBadge.setStyle("-fx-background-color: rgba(16, 185, 129, 0.15); -fx-background-radius: 6px; -fx-border-color: #10b981; -fx-border-radius: 6px;");
|
statusBadge.getStyleClass().add("status-badge-valid");
|
||||||
lblBadge.setStyle(lblBadge.getStyle() + " -fx-text-fill: #10b981;");
|
lblBadge.getStyleClass().addAll("text-success", "text-bold");
|
||||||
} else if (result.status() == ValidationStatus.INVALID) {
|
} else if (result.status() == ValidationStatus.INVALID) {
|
||||||
statusBadge.setStyle("-fx-background-color: rgba(239, 68, 68, 0.15); -fx-background-radius: 6px; -fx-border-color: #ef4444; -fx-border-radius: 6px;");
|
statusBadge.getStyleClass().add("status-badge-invalid");
|
||||||
lblBadge.setStyle(lblBadge.getStyle() + " -fx-text-fill: #ef4444;");
|
lblBadge.getStyleClass().addAll("text-danger", "text-bold");
|
||||||
} else {
|
} else {
|
||||||
statusBadge.setStyle("-fx-background-color: rgba(245, 158, 11, 0.15); -fx-background-radius: 6px; -fx-border-color: #f59e0b; -fx-border-radius: 6px;");
|
statusBadge.getStyleClass().add("status-badge-warning");
|
||||||
lblBadge.setStyle(lblBadge.getStyle() + " -fx-text-fill: #f59e0b;");
|
lblBadge.getStyleClass().addAll("text-warning", "text-bold");
|
||||||
}
|
}
|
||||||
|
|
||||||
Label lblType = new Label("• Formato: " + result.type().getDescription());
|
Label lblType = new Label("• Formato: " + result.type().getDescription());
|
||||||
lblType.setStyle("-fx-font-size: 12px; -fx-text-fill: #cbd5e1;");
|
lblType.getStyleClass().add("text-muted");
|
||||||
|
|
||||||
statusBadge.getChildren().addAll(lblBadge, lblType);
|
statusBadge.getChildren().addAll(lblBadge, lblType);
|
||||||
|
|
||||||
Label lblMsg = new Label(result.summaryMessage());
|
Label lblMsg = new Label(result.summaryMessage());
|
||||||
lblMsg.setWrapText(true);
|
lblMsg.setWrapText(true);
|
||||||
lblMsg.setStyle("-fx-font-size: 12.5px; -fx-font-weight: bold;");
|
lblMsg.getStyleClass().add("text-bold");
|
||||||
|
|
||||||
resultBox.getChildren().addAll(statusBadge, lblMsg);
|
resultBox.getChildren().addAll(statusBadge, lblMsg);
|
||||||
|
|
||||||
@@ -324,7 +333,7 @@ public final class SignatureValidationDialog {
|
|||||||
lblTime.getStyleClass().add("cert-detail");
|
lblTime.getStyleClass().add("cert-detail");
|
||||||
|
|
||||||
Label lblIntegrity = new Label(s.isValid() ? "✓ Assinatura íntegra e chave pública conferida." : "✗ Falha na integridade da assinatura.");
|
Label lblIntegrity = new Label(s.isValid() ? "✓ Assinatura íntegra e chave pública conferida." : "✗ Falha na integridade da assinatura.");
|
||||||
lblIntegrity.setStyle(s.isValid() ? "-fx-text-fill: #10b981; -fx-font-weight: bold; -fx-font-size: 11.5px;" : "-fx-text-fill: #ef4444; -fx-font-weight: bold; -fx-font-size: 11.5px;");
|
lblIntegrity.getStyleClass().addAll(s.isValid() ? "text-success" : "text-danger", "text-bold");
|
||||||
|
|
||||||
card.getChildren().addAll(lblSignerName, lblCert, lblTime, lblIntegrity);
|
card.getChildren().addAll(lblSignerName, lblCert, lblTime, lblIntegrity);
|
||||||
resultBox.getChildren().add(card);
|
resultBox.getChildren().add(card);
|
||||||
@@ -383,4 +392,8 @@ public final class SignatureValidationDialog {
|
|||||||
public void show() {
|
public void show() {
|
||||||
stage.show();
|
stage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,15 +71,14 @@ public final class TutorialDialog {
|
|||||||
|
|
||||||
ScrollPane scroll = new ScrollPane(content);
|
ScrollPane scroll = new ScrollPane(content);
|
||||||
scroll.setFitToWidth(true);
|
scroll.setFitToWidth(true);
|
||||||
scroll.setStyle("-fx-background: transparent; -fx-background-color: transparent;");
|
scroll.getStyleClass().add("scroll-transparent");
|
||||||
VBox.setVgrow(scroll, Priority.ALWAYS);
|
VBox.setVgrow(scroll, Priority.ALWAYS);
|
||||||
|
|
||||||
HBox footer = new HBox();
|
HBox footer = new HBox();
|
||||||
footer.setAlignment(Pos.CENTER_RIGHT);
|
footer.setAlignment(Pos.CENTER_RIGHT);
|
||||||
Button btnClose = new Button(" Entendi", SvgIcons.closeIcon(13));
|
Button btnClose = new Button(" Entendi", SvgIcons.closeIcon(13));
|
||||||
btnClose.getStyleClass().addAll("btn-secondary", "btn-dialog");
|
btnClose.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
btnClose.setPrefWidth(110);
|
btnClose.setMinWidth(javafx.scene.layout.Region.USE_PREF_SIZE);
|
||||||
btnClose.setMinWidth(110);
|
|
||||||
btnClose.setOnAction(e -> stage.close());
|
btnClose.setOnAction(e -> stage.close());
|
||||||
footer.getChildren().add(btnClose);
|
footer.getChildren().add(btnClose);
|
||||||
|
|
||||||
@@ -89,6 +88,7 @@ public final class TutorialDialog {
|
|||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
|
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addStep(VBox container, String title, String description) {
|
private void addStep(VBox container, String title, String description) {
|
||||||
@@ -116,4 +116,8 @@ public final class TutorialDialog {
|
|||||||
public void show() {
|
public void show() {
|
||||||
stage.show();
|
stage.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ public final class XmlSignDialog {
|
|||||||
// ScrollPane
|
// ScrollPane
|
||||||
ScrollPane scrollPane = new ScrollPane();
|
ScrollPane scrollPane = new ScrollPane();
|
||||||
scrollPane.setFitToWidth(true);
|
scrollPane.setFitToWidth(true);
|
||||||
scrollPane.setStyle("-fx-background-color: transparent; -fx-background: transparent; -fx-border-color: transparent;");
|
scrollPane.getStyleClass().add("cyber-scroll-pane");
|
||||||
|
|
||||||
VBox formContent = new VBox(16);
|
VBox formContent = new VBox(16);
|
||||||
formContent.setPadding(new Insets(4, 8, 4, 0));
|
formContent.setPadding(new Insets(4, 8, 4, 0));
|
||||||
@@ -194,7 +194,7 @@ public final class XmlSignDialog {
|
|||||||
pinField.setPrefWidth(260);
|
pinField.setPrefWidth(260);
|
||||||
|
|
||||||
lblPinNotice = new Label();
|
lblPinNotice = new Label();
|
||||||
lblPinNotice.setStyle("-fx-font-size: 11px;");
|
lblPinNotice.getStyleClass().addAll("dialog-subtitle", "text-muted");
|
||||||
pinRow.getChildren().addAll(lblPin, pinField, lblPinNotice);
|
pinRow.getChildren().addAll(lblPin, pinField, lblPinNotice);
|
||||||
|
|
||||||
certSection.getChildren().addAll(lblCertTitle, certCombo, pinRow);
|
certSection.getChildren().addAll(lblCertTitle, certCombo, pinRow);
|
||||||
@@ -289,11 +289,12 @@ public final class XmlSignDialog {
|
|||||||
progressIndicator.setVisible(false);
|
progressIndicator.setVisible(false);
|
||||||
|
|
||||||
lblStatus = new Label();
|
lblStatus = new Label();
|
||||||
lblStatus.setStyle("-fx-font-size: 12px;");
|
lblStatus.getStyleClass().addAll("text-muted", "text-small");
|
||||||
HBox.setHgrow(lblStatus, Priority.ALWAYS);
|
HBox.setHgrow(lblStatus, Priority.ALWAYS);
|
||||||
|
|
||||||
btnCancel = new Button("Cancelar");
|
btnCancel = new Button("Cancelar");
|
||||||
btnCancel.getStyleClass().addAll("btn-secondary", "btn-dialog");
|
btnCancel.getStyleClass().addAll("btn-ghost", "btn-dialog");
|
||||||
|
btnCancel.setMinWidth(Region.USE_PREF_SIZE);
|
||||||
btnCancel.setOnAction(e -> stage.close());
|
btnCancel.setOnAction(e -> stage.close());
|
||||||
|
|
||||||
btnSign = new Button("Assinar Documento XML");
|
btnSign = new Button("Assinar Documento XML");
|
||||||
@@ -309,6 +310,7 @@ public final class XmlSignDialog {
|
|||||||
Scene scene = new Scene(root);
|
Scene scene = new Scene(root);
|
||||||
ThemeManager.applyTheme(scene);
|
ThemeManager.applyTheme(scene);
|
||||||
stage.setScene(scene);
|
stage.setScene(scene);
|
||||||
|
stage.sizeToScene();
|
||||||
|
|
||||||
updatePinFieldVisibility();
|
updatePinFieldVisibility();
|
||||||
}
|
}
|
||||||
@@ -340,16 +342,16 @@ public final class XmlSignDialog {
|
|||||||
pinField.setDisable(true);
|
pinField.setDisable(true);
|
||||||
pinField.clear();
|
pinField.clear();
|
||||||
lblPinNotice.setText("✓ PIN memorizado na sessão segura");
|
lblPinNotice.setText("✓ PIN memorizado na sessão segura");
|
||||||
lblPinNotice.setStyle("-fx-text-fill: #10b981; -fx-font-weight: bold;");
|
lblPinNotice.getStyleClass().setAll("label", "text-success", "text-bold");
|
||||||
} else if (isA3 && "provider-win-store".equalsIgnoreCase(cert.providerId())) {
|
} else if (isA3 && "provider-win-store".equalsIgnoreCase(cert.providerId())) {
|
||||||
pinField.setDisable(true);
|
pinField.setDisable(true);
|
||||||
pinField.clear();
|
pinField.clear();
|
||||||
lblPinNotice.setText("Autenticação nativa pelo Windows/Driver");
|
lblPinNotice.setText("Autenticação nativa pelo Windows/Driver");
|
||||||
lblPinNotice.setStyle("-fx-text-fill: -fx-accent;");
|
lblPinNotice.getStyleClass().setAll("label", "text-primary");
|
||||||
} else {
|
} else {
|
||||||
pinField.setDisable(false);
|
pinField.setDisable(false);
|
||||||
lblPinNotice.setText(isA3 ? "Requer PIN do Smart Card" : "Requer senha do certificado A1");
|
lblPinNotice.setText(isA3 ? "Requer PIN do Smart Card" : "Requer senha do certificado A1");
|
||||||
lblPinNotice.setStyle("-fx-text-fill: -fx-text-secondary;");
|
lblPinNotice.getStyleClass().setAll("label", "text-muted");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -435,7 +437,7 @@ public final class XmlSignDialog {
|
|||||||
setFormDisabled(true);
|
setFormDisabled(true);
|
||||||
progressIndicator.setVisible(true);
|
progressIndicator.setVisible(true);
|
||||||
lblStatus.setText("Assinando documento XML (" + profile.getDisplayName() + ")...");
|
lblStatus.setText("Assinando documento XML (" + profile.getDisplayName() + ")...");
|
||||||
lblStatus.setStyle("-fx-text-fill: -fx-accent;");
|
lblStatus.getStyleClass().setAll("label", "text-primary");
|
||||||
|
|
||||||
boolean validate = chkValidateAfter.isSelected();
|
boolean validate = chkValidateAfter.isSelected();
|
||||||
|
|
||||||
@@ -459,7 +461,7 @@ public final class XmlSignDialog {
|
|||||||
setFormDisabled(false);
|
setFormDisabled(false);
|
||||||
progressIndicator.setVisible(false);
|
progressIndicator.setVisible(false);
|
||||||
lblStatus.setText("✓ Documento XML assinado e validado com sucesso!");
|
lblStatus.setText("✓ Documento XML assinado e validado com sucesso!");
|
||||||
lblStatus.setStyle("-fx-text-fill: #10b981; -fx-font-weight: bold;");
|
lblStatus.getStyleClass().setAll("label", "text-success", "text-bold");
|
||||||
|
|
||||||
showSuccessDialog(targetFile);
|
showSuccessDialog(targetFile);
|
||||||
});
|
});
|
||||||
@@ -470,7 +472,7 @@ public final class XmlSignDialog {
|
|||||||
Throwable ex = signTask.getException();
|
Throwable ex = signTask.getException();
|
||||||
logger.error("Falha ao assinar XML", ex);
|
logger.error("Falha ao assinar XML", ex);
|
||||||
lblStatus.setText("✕ Falha na assinatura");
|
lblStatus.setText("✕ Falha na assinatura");
|
||||||
lblStatus.setStyle("-fx-text-fill: #ef4444; -fx-font-weight: bold;");
|
lblStatus.getStyleClass().setAll("label", "text-danger", "text-bold");
|
||||||
|
|
||||||
showAlert(Alert.AlertType.ERROR, "Erro na Assinatura do XML",
|
showAlert(Alert.AlertType.ERROR, "Erro na Assinatura do XML",
|
||||||
"Não foi possível concluir a assinatura XMLDSig:\n" + (ex != null ? ex.getMessage() : "Erro desconhecido"));
|
"Não foi possível concluir a assinatura XMLDSig:\n" + (ex != null ? ex.getMessage() : "Erro desconhecido"));
|
||||||
@@ -554,4 +556,8 @@ public final class XmlSignDialog {
|
|||||||
stage.show();
|
stage.show();
|
||||||
stage.toFront();
|
stage.toFront();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stage getStage() {
|
||||||
|
return this.stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,6 @@ assets/
|
|||||||
│ ├── tutorial.svg # Ícone Tutorial e Ajuda (monitor com vídeo/play)
|
│ ├── tutorial.svg # Ícone Tutorial e Ajuda (monitor com vídeo/play)
|
||||||
│ ├── cert.svg # Ícone Certificados Digitais (pasta com cadeado seguro)
|
│ ├── cert.svg # Ícone Certificados Digitais (pasta com cadeado seguro)
|
||||||
│ ├── about.svg # Ícone Sobre o Sistema (círculo azul com 'i')
|
│ ├── about.svg # Ícone Sobre o Sistema (círculo azul com 'i')
|
||||||
│ ├── sun.svg # Ícone Tema Claro (sol dourado com raios vetoriais)
|
|
||||||
│ ├── moon.svg # Ícone Tema Escuro (lua crescente azul/índigo)
|
|
||||||
│ ├── shield.svg # Ícone Brasão de Segurança (CyberShield com chave interna)
|
│ ├── shield.svg # Ícone Brasão de Segurança (CyberShield com chave interna)
|
||||||
│ ├── seal.svg # Ícone Designer e Gerenciador de Selos Notariais
|
│ ├── seal.svg # Ícone Designer e Gerenciador de Selos Notariais
|
||||||
│ ├── check.svg # Ícone Checkmark de sucesso e validação
|
│ ├── check.svg # Ícone Checkmark de sucesso e validação
|
||||||
|
|||||||