@thibezer/ui-components-kit 2.11.0 → 2.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/CHANGELOG.md +497 -487
  2. package/README.md +1 -1
  3. package/dist/camadas.cjs +2 -2
  4. package/dist/camadas.js +6 -9
  5. package/dist/components/ui-cores/cores-utils.d.ts +1 -1
  6. package/dist/components/ui-cores/tipos.d.ts +2 -2
  7. package/dist/components/ui-cores/ui-controle-cores.d.ts +1 -1
  8. package/dist/components/ui-cores/ui-seletor-cor.d.ts +8 -4
  9. package/dist/cores.cjs +1 -1
  10. package/dist/cores.js +2 -2
  11. package/dist/custom-elements.json +13560 -13514
  12. package/dist/form-validacao-BIOXxt-1.js +140 -0
  13. package/dist/form-validacao-ytRYSF3E.cjs +1 -0
  14. package/dist/index.cjs +1 -1
  15. package/dist/index.js +2 -2
  16. package/dist/register.cjs +1 -1
  17. package/dist/register.js +2 -2
  18. package/dist/svg-seguro-BsbNQqpd.js +50 -0
  19. package/dist/svg-seguro-C76iLlAZ.cjs +1 -0
  20. package/dist/ui-badge-BxT-sDmV.cjs +28 -0
  21. package/dist/ui-badge-C0eKBI8E.js +183 -0
  22. package/dist/ui-botao-BOOnP8ds.cjs +12 -0
  23. package/dist/ui-botao-DfqSivHl.js +165 -0
  24. package/dist/ui-botao-OGJV6Vfk.js +165 -0
  25. package/dist/ui-botao-psS1juof.cjs +12 -0
  26. package/dist/ui-campo-texto-Cnn2UAkS.cjs +16 -0
  27. package/dist/ui-campo-texto-Uu2dJE_0.js +379 -0
  28. package/dist/ui-card-B86lJXTP.cjs +20 -0
  29. package/dist/ui-card-C_o5_0cB.js +111 -0
  30. package/dist/{ui-controle-cores-DMtTUtLk.js → ui-controle-cores-B4R6ct4I.js} +9 -10
  31. package/dist/ui-controle-cores-BS0WYPdi.js +572 -0
  32. package/dist/{ui-controle-cores-CohlwXJK.cjs → ui-controle-cores-C8um-If9.cjs} +2 -2
  33. package/dist/ui-controle-cores-CHMKGemo.cjs +141 -0
  34. package/dist/ui-controle-cores-Dog7ERYv.js +697 -0
  35. package/dist/ui-controle-cores-Er6ZcQ7U.cjs +111 -0
  36. package/dist/ui-modal-CvGFGh1h.cjs +18 -0
  37. package/dist/ui-modal-DBOtSTbR.js +238 -0
  38. package/dist/ui-paleta-ferramentas-CEZYcN4J.js +624 -0
  39. package/dist/ui-paleta-ferramentas-CU4icLl0.js +1171 -0
  40. package/dist/ui-paleta-ferramentas-Ccmo-rB5.cjs +21 -0
  41. package/dist/ui-paleta-ferramentas-DLwZHIDv.cjs +21 -0
  42. package/dist/ui-paleta-ferramentas-DNN5i6nL.js +860 -0
  43. package/dist/ui-paleta-ferramentas-Dbj1kBS9.cjs +18 -0
  44. package/dist/ui-paleta-ferramentas-DbwdWVtT.js +1173 -0
  45. package/dist/ui-paleta-ferramentas-bvqxj1Yd.cjs +21 -0
  46. package/dist/ui-segmented-D6b6zxVs.cjs +19 -0
  47. package/dist/ui-segmented-DyF5YLsV.js +418 -0
  48. package/dist/ui-segmented-rn9RxRGM.cjs +19 -0
  49. package/dist/ui-segmented-wUraId2e.js +412 -0
  50. package/dist/ui-seletor-cor-C4U2eqxY.cjs +82 -0
  51. package/dist/ui-seletor-cor-D2_C6Aor.js +391 -0
  52. package/dist/ui-skeleton-DI4_9WOX.js +428 -0
  53. package/dist/ui-skeleton-SPYqku8P.cjs +52 -0
  54. package/dist/ui-skeleton-W9uwIceA.js +585 -0
  55. package/dist/ui-skeleton-elhKIh2B.cjs +33 -0
  56. package/dist/ui-switch-BcOqOkiV.js +856 -0
  57. package/dist/ui-switch-D7iPKDj-.cjs +53 -0
  58. package/dist/ui-tabela-propriedades-BBr5sqki.cjs +145 -0
  59. package/dist/ui-tabela-propriedades-BecPqC-e.cjs +141 -0
  60. package/dist/ui-tabela-propriedades-BokvDD3b.js +2648 -0
  61. package/dist/ui-tabela-propriedades-Boy7I5yW.js +2412 -0
  62. package/dist/ui-tabela-propriedades-C-PeuW1i.cjs +141 -0
  63. package/dist/ui-tabela-propriedades-Df9ajC6M.js +2648 -0
  64. package/dist/ui-toast-Bp_NlM5G.js +156 -0
  65. package/dist/ui-toast-DGTZKne5.cjs +20 -0
  66. package/dist/zero-js-triggers-C_KrvFOQ.js +244 -0
  67. package/dist/zero-js-triggers-C_gtaojZ.cjs +1 -0
  68. package/package.json +187 -186
  69. package/dist/ui-seletor-cor-D9iI7yew.js +0 -413
  70. package/dist/ui-seletor-cor-Ft06TEUH.cjs +0 -110
package/CHANGELOG.md CHANGED
@@ -1,4 +1,14 @@
1
- # Changelog
1
+ # Changelog
2
+
3
+ ## 2.11.1 — 2026-10-23
4
+
5
+ Junta à 2.11.0 o que estava fora do npm (PR #14): o redesign compacto do seletor de cor no estilo Illustrator.
6
+
7
+ ### `<ui-seletor-cor>`: painel mais compacto, campos no estilo Illustrator
8
+
9
+ - Opacidade e espessura do traçado viram **campos numéricos** compactos (rótulo à esquerda, campo com setas ▲▼ e unidade `%`/`px`), no lugar das faixas deslizantes e dos atalhos (25–100 % e 1–8 px). No campo: ↑/↓ e a roda do mouse (com o campo em foco) somam um passo (1 % ou 0,5 px), Shift soma dez; o valor digitado ("50", "50%", "2,5 px") vale no Enter, no Tab ou ao sair; texto inválido volta ao valor atual sem emitir evento. Enter no campo não fecha o seletor.
10
+ - "Sem cor" deixa de ser um botão largo com texto e vira uma amostra quadrada de 30 px (fundo neutro com diagonal vermelha, como o *None* do Illustrator) ao lado do hexadecimal e do seletor do sistema; mantém a classe `.sem-cor`, `data-cor="none"`, `aria-pressed` e o rótulo acessível.
11
+ - Saem as amostras "Cores do projeto" e "Paleta padrão". As opções `coresProjeto` e `paleta` de `abrirSeletorCor()`/`configurar()`, a propriedade `coresProjeto` do `<ui-controle-cores>` e a constante `PALETA_CORES_PADRAO` continuam aceitas, mas ficam obsoletas (`@deprecated`) e são ignoradas pelo seletor.
2
12
 
3
13
  ## 2.11.0 — 2026-10-23
4
14
 
@@ -11,489 +21,489 @@
11
21
  - Entrada hostil: valor inválido não é aplicado (a opção volta ao padrão); cor e tracejado são saneados; `null` remove a opção. Feição selecionada sempre tem traço opaco.
12
22
  - Utilitários puros em `gerencigeo-canvas`: `TIPOS_LINHA`, `normalizarTipoLinha`, `dashTipoLinha`, `escalaTipoLinhaSegura`, `terminacaoCanvas`, `juncaoCanvas`. `EstiloVetor` ganha os campos opcionais `opacidadeTraco`, `terminacao`, `juncao` e `deslocamentoDash`.
13
23
  - Guia: [`docs/linha-padrao-cad.md`](docs/linha-padrao-cad.md).
14
-
15
- ## 2.10.1 — 2026-10-23
16
-
17
- Junta à 2.10.0 tudo o que ainda estava fora do npm: a revisão da edição de formas da 2.4.0 (PR #7), a espessura do traço no controle e no seletor de cor e o estado misto do `<ui-tabela-propriedades>` (PR #12). Sai como 2.10.1 a pedido, embora traga novidades.
18
-
19
- ### `<ui-controle-cores>` e `<ui-seletor-cor>`: espessura do traço
20
-
21
- - **`<ui-controle-cores>`**: atributo `espessura-traco`, propriedade `espessuraTraco` (0,5 a 50 px, padrão 2,5) e `obterCores()`/`definirCores()` com a espessura. `ui-cores-change` ganha `espessuraTraco`, o alterado `'espessuraTraco'` e a origem `'espessura'`.
22
- - **`<ui-seletor-cor>`**: controle de espessura do traçado (faixa e atalhos de 1 a 8 px) e prévia antes × depois com a espessura real. Novas opções `espessuraTraco` e `mostrarEspessura` (padrão: só com o alvo Traço). `ui-cor-input`/`ui-cor-confirmar` trazem `espessuraTraco`.
23
- - Utilitário `normalizarEspessura()`: limita a [0,5; 50] px com uma casa decimal; valor inválido vira 2,5. Um atributo `espessura-traco` vazio volta ao padrão (antes da revisão, viraria o mínimo de 0,5 px).
24
- - Página de exemplos: card 17, com controle e seletor ligados, prévia SVG e estado em JSON.
25
-
26
- ### `<ui-tabela-propriedades>`: estado misto ("Varia") para seleção múltipla
27
-
28
-
29
-
30
- - Com várias formas selecionadas, a propriedade cujo valor difere entre elas exibe **"Varia"**; as iguais exibem o valor comum (como o *\*VARIES\** do AutoCAD).
31
-
32
- - Novo valor sentinela `VALOR_MISTO` (`Symbol.for('ui-tabela-propriedades:misto')`) e atalho `misto: true` em `ItemPropriedade`. Funções puras `combinarValores(registros)`, `valorComum(valores)` e `isValorMisto(valor)`: `painel.valores = combinarValores(formas.map(f => f.propriedades))`.
33
-
34
- - Novo atributo/propriedade `texto-misto` / `textoMisto` define a palavra exibida (padrão "Varia").
35
-
36
- - Todos os editores suportam o estado: texto e número ficam vazios com "Varia" no placeholder; seleção, tipo/espessura de linha e cor CAD mostram uma opção "Varia" não selecionável; booleano fica indeterminado (`aria-checked="mixed"`, o primeiro clique marca todos); cor e somente leitura exibem "Varia". Linha recebe a classe `ui-prop__linha--misto`; cor ajustável por `--ui-prop-misto-cor`.
37
-
38
- - Sair do campo sem digitar, apagar o texto, Esc, ↑/↓ e o arrasto do rótulo não alteram um valor misto. Ao editar, `ui-propriedade-alterada` sai com `valorAnterior: VALOR_MISTO`; **Desfazer** volta ao estado misto. Em `ui-aplicar`, as propriedades não editadas continuam com `VALOR_MISTO` — filtre com `isValorMisto()` antes de gravar.
39
-
40
-
41
- ### `<ui-canvas-cad>`: volta a revisão dos modos de edição da 2.4.0
42
-
43
- A 2.4.0 e a 2.4.1 foram publicadas no npm, mas a revisão da edição de formas que elas traziam não entrou na 2.5.0 nem nas versões seguintes. Esta versão a traz de volta, portada para os módulos extraídos depois (`cad-feicoes-edicao.ts`, `cad-feicoes-ponteiro.ts` e o novo `cad-feicoes-teclado.ts`) e combinada com a edição da 2.10.0 (alças latentes, ponto médio arrastável, dica do HUD).
44
-
45
- **Novidades**
46
-
47
- - **Desfazer/refazer/cancelar na edição**: Ctrl+Z, Ctrl+Shift+Z / Ctrl+Y e botões no HUD (100 gestos); **Cancelar** volta à geometria de quando a edição começou. Mudar a geometria por fora recomeça o histórico.
48
- - **Nó selecionado**: clique na alça destaca o nó; Delete/Backspace ou "Excluir nó" o exclui. Recusas (mínimo de nós, ponto, círculo) avisam no HUD.
49
- - **Mais formas de entrar na edição**: **Enter** com uma só feição editável selecionada e botão **Editar** na barra (`ferramentas="editar"` ou `"todas"`; reflete a edição em curso com `aria-pressed`). `cad-edicao-recusada` ganha o motivo `sem-selecao` (botão Editar sem uma feição selecionada), com aviso visível.
50
- - **Novo evento `cad-feicao-duplo-clique`** (cancelável, `{ camadaId, feicao, coordenada }`). O duplo clique sobre a feição continua abrindo o popup, como na 2.8.1 (na 2.4.0 ele abria a edição). Com `preventDefault()` o consumidor assume o gesto, por exemplo chamando `editarVertices()`.
51
- - **Retângulo**: arrastar um canto de um retângulo alinhado aos eixos mantém o retângulo (Shift solta).
52
- - **Fantasmas** não aparecem em segmentos com menos de 24 px na tela e voltam ao aproximar o zoom.
53
- - **Ferramentas de desenho avisam no HUD** quando recusam o clique (círculo abaixo de 2 m, cantos alinhados, centro repetido) ou quando Enter/duplo clique chega com pontos de menos.
54
- - O HUD de edição passa a ter vários botões (Desfazer, Refazer, Excluir nó, Cancelar, Concluir): quem buscava o botão pelo seletor `#cm-vertex-edit-hud button` deve usar `[data-acao="concluir"]`.
55
-
56
- **Correções**
57
-
58
- - **Círculo**: o popup oferece o botão **Editar**; a alça da borda fica onde foi solta (antes voltava para o leste do centro).
59
- - **Fantasma de inserção**: o duplo clique insere um vértice e não o converte mais em curva (`dblclick` até 600 ms depois da inserção é ignorado).
60
- - **Camada ou feição fechada durante a edição**: ocultar, bloquear ou remover a camada, ou bloquear a feição, encerra a edição (`cad-edicao-concluida`) e tira alças e HUD.
61
- - **Caneta**: o arraste da alça termina com o `mouseup` na janela; soltar o botão fora do mapa não deixa mais a alça seguindo o cursor.
62
- - **Snap em curvas**: feição com `curva` atrai pelos nós (vértice) e pelo meio do trecho curvo; os vértices de apoio amostrados só valem como aresta.
63
- - **Mudança externa no meio do arraste** (outro operador, API): a alça arrastada não é destruída; o gesto em curso vence ao soltar.
64
- - **Tecla S** e **Enter** com vários canvases na página só valem no que está sob o mouse ou com foco. Com um único canvas nada muda.
65
-
66
- ## 2.10.0 — 2026-10-23
67
-
68
- Junta à 2.9.0 o trabalho que tinha ficado fora do npm: o `<ui-area-texto>` e a lapidação dos textos (PR #10), a usabilidade do desenho e da edição de vértices, as curvas Bézier sempre à mão, o painel de camadas no estilo Adobe e a lapidação do `<ui-card>`.
69
-
70
- ### Novo: `<ui-area-texto>` (alias `<ui-textarea>`) para textos grandes
71
-
72
- - Área de texto de várias linhas, form-associated (`name`, `value`, `required`, `minlength`/`maxlength`, `setCustomValidity`, `form.reset()`), com rótulo, ajuda/erro e os mesmos eventos do `<ui-campo-texto>` (`ui-input`, `ui-change`, `input`, `change`).
73
- - `linhas` (1–50, padrão 4), `auto-altura` (cresce com o conteúdo, sem pular a rolagem da página) com `linhas-max` como teto, `redimensionar` (`nenhum`|`vertical`|`horizontal`|`ambos`) e `contador` (usa `maxlength` como limite).
74
- - **Enter insere quebra de linha** e não submete o formulário. **Ctrl/Cmd + Enter** emite `ui-enviar` (`detail: { value }`).
75
- - Acessibilidade: nome acessível pelo `label`, `aria-label` do host, `<label for>` ou `<label>` envolvente; `aria-required`; `aria-describedby` só com a ajuda visível; o contador avisa leitores de tela apenas quando muda de nível (perto do limite, atingido, excedido), não a cada tecla.
76
- - API programática: `focus()`, `blur()`, `select()`, `setSelectionRange()`, `limpar()`, `readOnly`, `minLength`, `maxLength`.
77
- - Parts: `rotulo`, `campo`, `input`, `rodape`, `ajuda`, `contador`. Exportado por `index`, `register` e `forms`.
78
-
79
- ### `<ui-texto>`: texto longo e lapidação
80
-
81
- - **Novo `linhas`:** limita o texto a N linhas (1–50) com reticências; `truncar` continua tendo prioridade. Valores inválidos são ignorados.
82
- - Palavras e URLs muito compridas passam a quebrar (`overflow-wrap`) em vez de estourar o contêiner; `alinhamento="justificado"` usa hifenização.
83
- - O elemento interno expõe `part="base"`. Sem DOM (SSR) o componente fica inerte, sem casts `any`.
84
-
85
- ### `<ui-campo-texto>`: lapidação
86
-
87
- - **`minlength`/`maxlength` inválidos** (`abc`, vazio, negativo) eram repassados ao `<input>` como `NaN`; agora são ignorados, e os getters `minLength`/`maxLength` devolvem `-1` em vez de `NaN`.
88
- - Acessibilidade: `aria-required` no campo obrigatório; `aria-describedby` só referencia a ajuda enquanto ela está visível; a ajuda/erro é uma região `aria-live="polite"`, então mensagens de erro passam a ser anunciadas.
89
- - Ouvintes via `ListenerBag` e temporizador de sincronização cancelado ao desconectar/reconectar (sem `any`).
90
-
91
- ### Núcleo
92
-
93
- - `core/opcoes`: `escolherInteiro` e `inteiroOuNulo` convertem atributos numéricos hostis (`NaN`, `Infinity`, texto, fora da faixa) em inteiros seguros. `sincronizarFeedbackErro` aceita `<textarea>` e um prefixo de classe opcional (padrão inalterado).
94
-
95
- ### `<ui-canvas-cad>`: usabilidade do desenho e da edição de vértices
96
-
97
-
98
-
99
- - **Esc voltava a falhar depois do primeiro popup.** O Leaflet mantém `map._popup` apontando para o último popup mesmo fechado, e o atalho de teclado do mapa engole o Esc enquanto essa referência existe: com o foco no mapa, o Esc deixava de encerrar a edição de vértices, cancelar a figura em desenho e limpar a seleção. O mapa agora solta a referência no `popupclose` (`src/gerencigeo-canvas/popup_residual.ts`); popup aberto continua fechando com Esc.
100
-
101
- - **Enter em botões durante o desenho.** Com uma ferramenta ativa, o Enter era tratado como "concluir" mesmo com o foco num botão (barra de ferramentas, "Sair" do HUD), e o clique do botão não acontecia. O Enter agora fica com botões, links e controles com `role` acionável (`focoEmControleAcionavel`, em `src/core/alvo-digitavel.ts`).
102
-
103
- - **Ferramenta ou edição recusada agora avisa.** Sem camada de desenho (ou com camada bloqueada/inválida), e ao recusar a edição de uma feição (bloqueada, com furos, multiparte...), o componente mostra um aviso visível por 5 s e o anuncia na região viva, além de emitir `cad-desenho-recusado`/`cad-edicao-recusada` como antes. **Novo atributo `sem-avisos`**: o consumidor que mostra o próprio aviso a partir dos eventos desliga o do componente.
104
-
105
- - **Ponto médio arrastável na edição de vértices.** Arrastar o ponto médio de uma aresta cria o nó já na posição solta (com snap, prévia e HUD ao vivo e um único `cad-feicao-atualizada`); o clique continua inserindo no meio da aresta. Em feição com curva, o nó nasce sobre o traçado e a curva é mantida (antes, inserir um nó numa curva a convertia em poligonal). A dica do HUD passou a mencionar inserir e excluir.
106
-
107
- - **Toque.** Um toque que começa numa alça de edição, num ponto médio ou nos HUDs não move mais o mapa junto; alças de Bézier passam a ser tratadas como as demais alças pelo mouse (não iniciam caixa de seleção).
108
-
109
- - Testes de regressão em `tests/ui-canvas-cad-edicao.regressao.test.ts`.
110
-
111
- ### Canvas CAD: curvas (Bézier) sempre à mão
112
-
113
-
114
-
115
- - **Editor de vértices**: as alças de Bézier agora aparecem **sempre**, inclusive em linhas/polígonos retos (alças latentes pontilhadas a 1/3 do vizinho, até 40 nós). Arrastar uma alça converte o nó em curva; `Shift` solta a simetria.
116
-
117
- - **Caneta**: as alças das âncoras já colocadas podem ser **ajustadas durante o desenho** (arraste a alça, ou a própria âncora para criar/ajustar a curva dela). `Shift` solta a simetria; o cursor muda ao passar sobre uma alça.
118
-
119
- - A dica do HUD de edição junta os gestos das duas mudanças acima (curvar pelas alças, ponto do meio, botão direito para excluir, duplo clique, Shift).
120
-
121
- ### `<ui-camadas>`: painel de camadas no estilo Adobe
122
-
123
- - **Mudança visual:** os selos "✓ Ativa", "N SUB" e "OFICIAL" saem da linha da camada (a informação continua em `aria-current`, na dica da contagem e no marcador da prévia). Quem estilizava `.ui-active-badge`/`.ui-group-badge` precisa revisar.
124
-
125
-
126
-
127
- - **Cor da camada editável**: clicar (ou Enter/Espaço) na barra de cor da linha abre o `<ui-seletor-cor>` ao lado, com prévia ao vivo, "cores do projeto" e Cancelar que restaura. Emite `ui-camada-cor` como antes.
128
-
129
- - **Destaque de seleção na cor da camada**: a camada ativa, as feições selecionadas e o alvo de seleção usam a cor da camada (variável `--layer-active-color`, agora definida no grupo e herdada pelas feições).
130
-
131
- - **Menos selos**: removidos "✓ Ativa", "N SUB" e "OFICIAL"; a camada ativa é indicada pelo destaque da linha (`aria-current`), a contagem vira um número discreto (a dica traz feições e subcamadas) e só a geometria prévia ganha um marcador.
132
-
133
- - Alça de arraste mais estreita (8px, antes 14px).
134
-
135
- - Colunas fixas (alça, olho, cadeado, barra de cor) alinhadas à esquerda em camadas, subcamadas e feições; só seta, miniatura e nome recuam com a profundidade, como no Illustrator. A barra de cor não se expande mais ao passar o mouse.
136
-
137
- ### `<ui-card>`: lapidação
138
-
139
-
140
-
141
- - **Teclado:** Enter/Espaço vindos de um campo ou botão dentro do card não o ativam mais nem têm o `preventDefault` aplicado (antes era impossível digitar espaço num `<input>` dentro de um card `clicavel`). Tecla mantida (`repeat`) e atalhos com Ctrl/Alt/Meta não ativam.
142
-
143
- - **Cliques internos:** o clique num ícone SVG dentro de um botão (alvo `<path>`) passou a ser reconhecido como de controle interno; `label`, `summary`, conteúdo editável, `ui-campo-texto` e `ui-segmented` também. Um `<a>`/`<label>` que *envolve* o card não o bloqueia mais.
144
-
145
- - **Acessibilidade:** `aria-label` do host dá nome ao card clicável; `focus()` no host foca o card; anel de foco visível (`:focus-visible`, contorno que sobrevive ao modo de alto contraste).
146
-
147
- - **Estilo:** o atributo `hidden` passa a esconder o card (o `display: block` do host o anulava); o realce de hover vale só com `(hover: hover)`; `prefers-reduced-motion` desliga transições e deslocamento.
148
-
149
- - **Seções:** `[slot="cabecalho|rodape|midia"]` aninhado em um descendente não revela mais a seção do card (só filhos diretos).
150
-
151
- - Lógica extraída para `card-interacao.ts` e `card-secoes.ts`; JSDoc de atributos, slots, parts e evento para o manifesto. API pública inalterada (novo observado: `aria-label`).
152
-
153
- ## 2.9.0 — 2026-10-23
154
-
155
- ### Junta a 2.8.2 com a linha 2.6.1–2.8.1
156
-
157
- - **Atenção a quem instalou a 2.8.2:** ela foi publicada a partir de um branch baseado na 2.6.0 e **não continha** o que saiu na 2.6.1, 2.7.0, 2.8.0 e 2.8.1 (entre outros: `<ui-linha>` e a altura única dos controles; popups e câmera só a pedido no `<ui-canvas-cad>`; busca por nome de camada e mapa base recolhível no `<ui-camadas>`; as correções de robustez e acessibilidade da 2.7.0 em `<ui-tooltip>`, `<ui-modal>`, `<ui-mapa>`, `<ui-tabela>`, `<ui-camadas>` e `<ui-canvas-cad>`). A 2.9.0 traz as duas linhas juntas: tudo da 2.8.1 mais tudo da 2.8.2 (medidas no elipsoide GRS80 e as pendências do ribbon e da paleta).
158
- - As mudanças da 2.8.2 no `<ui-ribbon>` e na `<ui-paleta-ferramentas>` foram portadas para os módulos extraídos na 2.7.0: grupo colapsado, botão dividido e lançador de diálogo em `ribbon-render.ts` (os cliques seguem delegados pelo painel); ativação manual das abas em `ribbon-teclado.ts` (`acaoDaTeclaAba` ganha a ação `ativar`); atalhos compostos e a paleta "dona" dos atalhos em `paleta-atalhos.ts` (`resolverAtalho` aceita o evento de teclado; `paletaDonaDosAtalhos`).
159
-
160
- ### `<ui-canvas-cad>`: desenho de muitas formas (Fases 0 e 1 do motor de formas)
161
-
162
- - **LOD por tamanho em pixels:** feição (linha, polígono, círculo) com diagonal abaixo de 3 px na tela deixa de ser um objeto Leaflet e vira uma **marca de 1–2 px** na cor do preenchimento/contorno, pintada em lote por cor num canvas por camada (`feature_lod.ts`, `renderers/marcas_lod_layer.ts`). O mapa não fica "furado". Sempre em detalhe completo: selecionada (e em edição), com rótulo e em par oficial × prévia; ponto e texto nunca viram marca. Hit-test, snap, caixa, laço e hover seguem na geometria original, então uma feição de 1 px continua clicável. Reavaliado ao mudar de zoom, não a cada pan.
163
- - **Seleção incremental:** `aplicarSelecao` deixou de reconciliar a camada inteira. Só as feições que mudaram de estado são tocadas, e só no estilo (`setStyle`/`setIcon`, sem `setLatLngs`); a camada só é reconciliada quando há ponto sob agrupamento (selecionado sai do cluster). Selecionar 1 feição em 100 mil: de ~60 ms síncronos para ~6 ms (medido no navegador).
164
- - **Simplificação em pixels:** a tolerância de 1,2 px passa a valer para feições com **mais de 8 vértices** (antes 40) em **qualquer zoom abaixo de 17** (antes 12). `simplificarLinha`/`simplificarAnel` ganham o parâmetro opcional `escalaLat` e a tolerância deixa de ser 1/cos(lat) vezes maior na vertical (2× a 60° de latitude).
165
- - **Instrumentação:** `FeicoesLayerRenderer.estatisticas()` ganha `marcasLOD` e `avaliacoesLOD`; novo `idsDesenhados()`. Página de benchmark `bench/bench-formas.html` (rAF por canal de mensagens e `getImageData`, dados sintéticos com semente fixa) para medir mudança de vista, seleção e ocultar/exibir no navegador.
166
- - **Correções (Regra #0):** `feicaoNoPonto` estourava a pilha (`Math.max(...xs)`) em polígono com ~120 mil vértices ou mais; `toleranciaPorZoom(NaN)` colapsava geometrias a 2 pontos; uma feição que o Leaflet recusa (coordenada NaN) derrubava a reconciliação da camada inteira e agora fica sem desenho.
167
-
168
- **Mudanças de comportamento**
169
-
170
- - Feições minúsculas não estão mais em `grupo.getLayers()` nem em `map._layers`: quem contava camadas Leaflet para medir desenho deve usar `estatisticas()` (o app usa `map._layers` só no benchmark).
171
- - O desenho de feições com mais de 8 vértices é mais simplificado (até 1,2 px de desvio) abaixo do zoom 17. A geometria da feição nunca é alterada.
172
- - O clique numa marca passa pelo hit-test central do mapa (`layer_hit_test`: nova `FonteHit`), não por um objeto Leaflet: uma ação `feicao-clique` por clique, com a mesma ordem das formas (linha antes de área, camada do topo, menor área) e alternância em cliques repetidos sobre marcas empilhadas (até 20).
173
- - Cada camada com marcas ganha um canvas extra (removido quando não há marcas). Não foi consolidado nesta fase.
174
-
175
- ### Motor das formas headless e renderizador Canvas opt-in (Fases 2 e 3)
176
-
177
- - **Novo atributo/propriedade `renderizador`** em `<ui-canvas-cad>`: `leaflet` (padrão, inalterado) ou `canvas`. Com `canvas`, as camadas de feições são desenhadas por um motor próprio em Canvas 2D em vez de um objeto Leaflet por feição. Trocar o valor em tempo de execução recria só as camadas de feições (`CanvasLayerManager.recriarCamadasDoTipo`), mantendo dados e seleção. Valor inválido cai em `leaflet`. Guia: [`docs/motor-formas.md`](docs/motor-formas.md).
178
- - **`estatisticasFeicoes(camadaId)`:** contadores da camada desenhada (`total`, `materializadas`, `marcasLOD`, `clusters`, `avaliacoesLOD` e, no caminho canvas, `desenhadas`, `marcadoresDom`, `canvases`; mais `renderizador`), para diagnóstico e benchmark. `null` se a camada não está desenhada.
179
- - **Motor headless** (`src/gerencigeo-canvas/motor/`, L2, sem DOM e sem Leaflet; roda em Node e serve para Web Worker): `MotorFormas` com `definirCamada`, `definirFeicoes`, `aplicarDelta`, `listaDesenho(vista, { camadas, parte })`, `listaSobreposicao`, `consultarCaixa`, seleção, `previsualizar`, `destacar` e clusters. Estilo resolvido compartilhado pelos dois renderizadores (`estilo.ts`), geometria em `Float64Array` com projeção própria e Douglas-Peucker em px (`geometria-px.ts`), loja + índice + oficial × prévia (`dados-camada.ts`).
180
- - **Novo entrypoint `@thibezer/ui-components-kit/motor`** (`src/motor.ts`): o motor sem o componente. O `check-layers` garante que nada alcançado por ele importa `leaflet` (`dist/motor.js` não importa).
181
- - **Renderizador Canvas** (`renderers/renderizador_formas.ts`, `PintorCanvas2D`): pinta forma a forma, agrupado por estilo, com recorte. **Pan incremental** (`renderers/canvas_formas_leaflet.ts`): o bitmap é deslocado em pixels de mundo e só as faixas expostas são listadas e pintadas. Seleção, hover e edição vão para uma sobreposição única por mapa, sem repintar a base. Clique e hover passam pelo hit-test central (`FonteHit`).
182
- - **Números com GPU** (máquina do dono, canvas × leaflet intercalados; tabelas em `docs/RELATORIO-motor-formas.md`): o canvas fica de 2 a 6 vezes abaixo do leaflet em todos os tamanhos. Mudança de vista: 20 mil em zoom 12, 90–222 contra 473–676 ms; 100 mil em zoom 15, 166–197 contra 811–1 652 ms. Selecionar 2 000 feições numa camada de 50 mil: 114 contra 1 316 ms. As metas absolutas do prompt (zoom 12: 20 mil ≤ 60 ms, 50 mil ≤ 100 ms, 100 mil ≤ 200 ms) não foram atingidas.
183
- - **Desempenho do caminho canvas:** o item da marca de LOD fica em cache por zoom e é reaproveitado no pan enquanto feição, estilo da camada, seleção e destaque não mudam (lista de 100 mil marcas em zoom 12: de ~390 para ~140 ms por mudança de vista, no Node). Canvas em branco (camada sem nada na vista, sobreposição sem seleção) não é mais deslocado, limpo nem redimensionado a cada pan: cada chamada de desenho fazia o navegador reenviar o canvas inteiro ao compositor (1 000 formas em zoom 18: de ~130 para ~30 ms por pan, medido sem GPU). Camada com até 500 itens na vista é repintada inteira no pan em vez de ter o bitmap deslocado (`CanvasFormas.limiteRepintar`, ajustável; também por instância): com poucas formas, deslocar o bitmap de 3072×1920 custava 3–5× mais que repintar e deixava o canvas mais lento que o leaflet (1 000 formas em zoom 12: de ~200 para ~60 ms).
184
- - **Correções** achadas pela suíte de equivalência: marca de LOD antiga ficava na tela quando a feição passava a ponto agrupado (também no caminho `leaflet`); ícone selecionado ou único restante numa célula de cluster sumia no caminho canvas.
185
-
186
- **Mudanças de comportamento do caminho `canvas`** (o caminho `leaflet` não muda)
187
-
188
- - Preenchimento translúcido é pintado forma a forma: polígonos sobrepostos da mesma camada continuam escurecendo onde se cruzam, como no Leaflet. A ordem de desenho dentro da camada é por grupo de estilo, não por ordem de inserção.
189
- - Há um canvas por camada de feições mais uma sobreposição por mapa (não 2 canvases no total). Opacidade e visibilidade seguem o pane da camada; a sobreposição ignora a opacidade da camada (a seleção aparece sempre cheia).
190
- - Curvas Bézier (`LinhaCurva`/`PoligonoCurvo`), ícone, texto, cluster e rótulo continuam como objetos/marcadores Leaflet dentro do mesmo renderizador.
191
- - As feições não aparecem em `grupo.getLayers()` nem em `map._layers`; use `estatisticasFeicoes()`.
192
-
193
- ## 2.8.2 — 2026-10-23
194
-
195
- > Publicada a partir de um branch baseado na 2.6.0: não inclui a 2.6.1, a 2.7.0, a 2.8.0 nem a 2.8.1. Use a 2.9.0, que junta as duas linhas.
196
-
197
- ### Canvas de feições: medidas no elipsoide GRS80
198
-
199
- - **Distância, comprimento e área** (`feature_metricas`) deixam a esfera de 6371 km: distância pela escala local do elipsoide (M e N) até 0,1° e Vincenty acima disso; área por Σ(λ[i+1]−λ[i−1])·sen φ·M·N/2. Antes o erro chegava a 0,5% na distância e 0,45% na área; agora fica abaixo de 0,01% na área e de 2e-7 na distância curta (linha Flinders–Buninyong confere em milímetros).
200
- - **Deslocamentos, offsets e alça de borda do círculo** (`feature_algoritmos`, `feature_edicao_ops`) usam a mesma escala (`metrosPorGrau`, exportada), para o raio lido da alça bater com o desenhado.
201
-
202
- ### `<ui-ribbon>` e `<ui-paleta-ferramentas>`: pendências de UX, acessibilidade e robustez
203
-
204
- - **Harmonização de bordas, raios e alinhamento vertical no Ribbon**: botões pequenos agora utilizam `--ui-raio-sm` (4px) com `padding: 0 8px` proporcional; colunas de ferramentas recebem `gap: 2px` evitando colisão de cantos arredondados entre itens adjacentes; botões grandes têm altura calibrada em 76px (equivalente a 3 slots de 24px + gaps) com área de itens centrada verticalmente; grupos sem rótulo não geram nó vazio, eliminando o espaçamento inferior fantasma e garantindo margens perfeitamente simétricas.
205
- - **Tooltip estilizado leve (M2)**: substituição do `title` nativo do browser por tooltip no Shadow DOM exibido em `focus-visible` e `pointerover`, com renderização do atalho formatado (`kbd`). Remoção de `aria-label` duplicado em botões que já possuem rótulo visível.
206
- - **Colapso automático de grupos no Ribbon (A4)**: suporte a `prioridade` em `RibbonGrupo` e colapso dinâmico via `ResizeObserver` quando o espaço é insuficiente, transformando o grupo em botão único com flyout (estilo Office).
207
- - **Botão dividido e Lançador de Diálogo (M4)**: suporte a `item.dividido` no ribbon (ação primária + gatilho de menu independente) e botão lançador de diálogo opcional (`grupo.lancadorDialogo`) com emissão do evento `ui-grupo-dialogo`.
208
- - **Atalhos compostos e escopo multi-paletas (M7/B9)**: suporte a atalhos combinados (`Ctrl+Z`, `Shift+V`, `Esc`, etc.) via `correspondeAtalho`. No ribbon, suporte ao atributo `atalhos` e `keytips` (Alt). Conflito entre múltiplas paletas com atalhos na mesma página resolvido via escopo de foco (`:focus-within`) e ordenação DOM.
209
- - **Navegação em grade de 2 colunas (M9)**: em paletas verticais com `colunas="2"`, navegação bidimensional intuitiva com `ArrowUp`, `ArrowDown`, `ArrowLeft` e `ArrowRight` respeitando a ordem visual e layout RTL.
210
- - **Ativação manual de abas (M10)**: atributo opt-in `ativacao-manual` e propriedade `ativacaoManual` no ribbon, onde setas apenas transferem o foco do teclado e Enter/Espaço ativam a aba.
211
- - **Fechamento de menu no window blur (M8)**: removido fechamento indevido em `window blur` para permitir inspeção no DevTools e troca de janelas; menus fecham apenas por clique fora, `Escape` ou rolagem.
212
- - **Exclusividade configurável de ferramentas no Ribbon (B8)**: suporte a `tipo: 'ferramenta'` com seleção única por grupo e propriedade `grupo.exclusivo` (padrão `true`, `false` para múltipla ativação).
213
- - **Submenus aninhados**: suporte a múltiplos níveis de `filhos` dentro de `filhos` na paleta e no ribbon, com abertura/fechamento via `ArrowRight` e `ArrowLeft` (respeitando RTL).
214
- - **Modo controlado sem mutação de dados (A1)**: propriedade `modoControlado` e atributos `modo-controlado` / `copiar-dados` para clonagem profunda na entrada, garantindo que objetos do consumidor não sofram mutações.
215
- - **IDs em SVG inline e allowlist (B1)**: prefixagem de IDs em `<defs>` de ícones SVG (`url(#...)` e `href`) no `svg-seguro.ts` para evitar colisões no DOM, e aviso em console para elementos descartados pela allowlist.
216
- - **SSR e prevenção de layout shift (B9)**: reserva de altura mínima no `:host` do ribbon para evitar Cumulative Layout Shift (CLS) antes da hidratação.
217
-
218
- ## 2.8.1 — 2026-10-21
219
-
220
- ### `<ui-canvas-cad>`: popups e câmera só a pedido (como CAD/GIS)
221
-
222
- - **Popups sob demanda (mudança de comportamento):** clicar numa feição, vértice, linha ou polígono **não abre mais o popup nem move o mapa** (o Leaflet abria no clique e fazia auto-pan). Abrem com **duplo clique** sobre a camada, por `abrirPopupFeicao(camadaId, id)` ou, no código, por `marcador.openPopup()` fora de um clique. Popups abertos a pedido não fazem auto-pan. Novo atributo/propriedade `popups`: `sob-demanda` (padrão), `automatico` (comportamento antigo: abre ao selecionar uma feição, com auto-pan e `definirMargensPopup`) e `desligado` (clique e duplo clique não abrem). A consulta SIGEF segue abrindo ao clicar (é um pedido explícito). No modo sob demanda, terminar a edição de vértices não reabre o popup.
223
- - **Implementação:** `popup_gate.ts` (`PortaoPopups`, `liberarPopups`, `lerModoPopups`) intercepta `map.addLayer` só para popups e separa clique do mouse de abertura pelo código; `FeicaoPopup` aceita `liberar`; `PonteiroFeicoes.duploClique` e `GerenciadorFeicoes.abrirPopupFeicao`/`aoDuploClique`.
224
- - **`selectPonto(id)` / `selecionarPonto(id)`:** só seleciona. Antes recentralizava o mapa sempre e abria o popup; agora isso acontece apenas com `zoomLevel` informado (pedido de "ir até o ponto").
225
- - **Zoom em passos finos:** `zoomSnap: 0.25`, `zoomDelta: 0.5`, `wheelPxPerZoomLevel: 90` (antes 1, 1 e 60: cada giro da roda saltava um nível inteiro). Zoom fracionário é aceito por simplificação, clusters, densidade e pré-carga de tiles.
226
- - **Botão do meio:** o duplo clique que enquadra (`zoomExtents`) agora exige dois cliques limpos; dois arrastes rápidos em sequência (pan) não enquadram mais.
227
- - **Toque:** removido o zoom próprio da pinça (passos de 1 nível, em paralelo ao `touchZoom` do Leaflet, que também reagia ao mesmo gesto). Um dedo continua arrastando.
228
- - **Não mudou:** `zoomParaFeicao(s)`, `ajustarTudo`, `ajustarCamada`, `fitBounds`, clique em cluster, ação "zoom" do popup e o botão "Enquadrar" seguem como pedidos explícitos. `zoomExtents` continua considerando só os vértices (não as feições).
229
-
230
- ### `<ui-camadas>`: busca, espaço da lista e documentação de eventos
231
-
232
- - **Busca por nome de camada (correção):** o campo "Buscar camada ou feição" só filtrava feições; buscar `eixos` deixava todas as camadas na tela com "Nenhum item correspondente", inclusive a que casava. Agora a árvore mostra só camadas que casam (pelo nome) ou que levam a um resultado, mantendo os grupos ancestrais; um grupo que casa traz suas subcamadas; sem resultado há uma mensagem única (termo escapado). Nova função pura `filtrarCamadasPorBusca()` em `camadas-selecao.ts`; a seleção em intervalo (`Shift`) segue as mesmas linhas.
233
- - **Mapa base recolhível:** novo atributo/propriedade `mapas-base-recolhido` / `mapasBaseRecolhido`, e o cabeçalho "Mapa Base" vira botão (`aria-expanded`) que mostra o mapa ativo. Novo evento `ui-mapas-base-recolhimento` (`{ recolhido }`), só para ação do usuário. Padrão inalterado (aberta).
234
- - **Espaço da lista:** `.ui-panel-box` passa de `min-height: 80px` para `140px`, para a árvore não ficar espremida por rodapé e mapas base em painéis `docked` baixos (o corpo rola abaixo disso).
235
- - **Documentação:** `ui-camada-grupo-adicionar` é emitido com `{ isGroup: true }` (o README dizia `{ grupoId, nome }`); `ui-camada-adicionar` (`{}` ou `{ parentId }`) passa a constar na tabela. Ambos são pedidos: sem handler no aplicativo, os botões não criam nada.
236
-
237
- ## 2.8.0 — 2026-10-17
238
-
239
- ### Controles na mesma linha: altura única e `<ui-linha>`
240
-
241
- - **Novo `<ui-linha>`** (L3): contêiner que alinha botão, campo, lista, segmented e badge na mesma linha. `tamanho`/`densidade` (`sm|md|lg`), `altura`, `alinhar`, `justificar`, `espaco`, `sem-quebra`, `inline`; filhos ganham largura natural (botão e campo deixam de ocupar 100%), campos e listas ocupam o espaço restante e qualquer filho aceita `expandir`.
242
- - **`tamanho`, `densidade` e `altura` agora funcionam** em `ui-botao`, `ui-campo-texto`, `ui-lista-flutuante` e `ui-segmented` (o README já os documentava, mas só `altura` tinha efeito). Lógica única em `src/core/altura-controle.ts` (exportada em `core`): entrada hostil é descartada, `altura` é limitada a 15–200px.
243
- - **`ui-segmented` segue `--ui-campo-altura`** (antes tinha alturas fixas de 28/36/42px, o que o desalinhava do botão e do campo). O host passou a `inline-flex` com `vertical-align: middle`. `tamanho` inválido não vira mais classe CSS arbitrária (cai em `md`).
244
- - **Escala de tokens** `--ui-altura-sm|md|lg` (26/34/42px). **Mudança de comportamento:** a altura padrão dos controles passou de 15px para `--ui-altura-md` (34px), o valor que o README já descrevia como padrão. Quem quiser o antigo define `--ui-campo-altura: 15px`.
245
- - **`ui-badge[controle]`** assume a altura dos controles (e o alvo de toque mínimo no mobile).
246
- - Hosts `inline` de botão, campo e lista ganharam `vertical-align: middle`.
247
- - Teste de contrato `tests/contrato-linha.test.ts` + regressões; guia em `docs/layout-linha.md`.
248
-
249
- ## 2.7.0 — 2026-10-17
250
-
251
- ### `UIBus`: independente do componente de toast
252
-
253
- - `src/core/ui-bus.ts` (L1) deixou de importar `ui-alerta` (L3). Novo `UIBus.registrarProvedorToast(fn | null)`; o `ui-toast` se registra sozinho ao ser carregado (`registrarToastNoBus()`).
254
- - **Mudança de comportamento:** quem importar apenas `core/ui-bus` sem carregar `ui-alerta` (ou o pacote principal) passa a ter `UIBus.notificar()` como no-op, sem erro. Exceções do provedor são capturadas (`console.error`).
255
- - Contratos `UIToastOpcoes`/`UIToastAcao` movidos para `src/core/tipos-toast.ts` e reexportados por `ui-alerta` (nomes públicos inalterados).
256
-
257
- ### `<ui-ribbon>`, `<ui-paleta-ferramentas>` e `<ui-tabela>`: organização interna
258
-
259
- - **Sem mudança de comportamento nem de API.** Teclado, renderização, clique longo, atalhos, rolagem de linhas e estados (erro, anúncio do filtro, aria) passam a viver em módulos próprios com testes unitários.
260
-
261
- ### Componentes simples e de formulário: robustez e acessibilidade
262
-
263
- - **`<ui-skeleton>`:** `linhas` é limitado a 100 (um valor enorme derrubava a página). Decorativo por padrão (`aria-hidden`); com `aria-label` vira `role="status"`.
264
- - **Classes por atributo:** `variante`, `cor`, `peso`, `alinhamento` (`ui-texto`), `elevacao` (`ui-stat`, `ui-card`), `variante`/`estilo` (`ui-badge`) só aceitam valores conhecidos; qualquer outro usa o padrão (antes, o texto virava classes CSS arbitrárias).
265
- - **`<ui-avatar>`:** host com `role="img"` e `aria-label` (nome, e o status quando houver) ou `aria-hidden` sem nome; **a imagem interna agora tem `alt=""`** (antes `alt` = nome ou "Avatar"). `aria-label`/`aria-hidden` do consumidor são respeitados. Tamanho numérico aceito entre 8 e 512 px.
266
- - **`<ui-stat>`:** grupo rotulado e indicador de variação como imagem nomeada. **`<ui-card clicavel>`:** `role="button"`, `aria-disabled`. **`<ui-badge>`:** `disabled` desativa o ✕; nome acessível "Remover …".
267
- - **`<ui-radio>`:** o grupo agora acompanha `setAttribute('name')` depois de conectado, mudança de nome e remoção do formulário; o primeiro radio habilitado entra na ordem de Tab.
268
- - **`<ui-checkbox>`, `<ui-switch>`, `<ui-radio>`:** `focus()` no host foca o controle; checkbox e radio passam a respeitar `<label for>`, `<label>` envolvente e `aria-label` (como o switch).
269
-
270
- ### `<ui-tooltip>` e `<ui-modal>`: correções de comportamento
271
-
272
- - **`<ui-tooltip>`:** o clique no gatilho deixa de ser interrompido (`stopPropagation`) e volta a chegar aos ouvintes de ancestrais; `disabled` fecha o balão aberto (e `aberto` + `disabled` não o mostra); ambientes sem `matchMedia` não lançam; o quadro de animação do posicionamento é cancelado ao fechar/desconectar.
273
- - **`<ui-modal>`:** `Shift+Tab` no botão de fechar agora leva ao último controle do conteúdo (antes o foco escapava do modal); `Tab` no último controle volta ao primeiro. O foco real é lido atravessando Shadow DOMs.
274
- - Ouvintes globais de ambos passam a ser gerenciados por `ListenerBag`. Sem mudança de API.
275
-
276
- ### `<ui-camadas>`: arrastos interrompidos e organização interna
277
-
278
- - Desconectar o painel durante a pintura de controles (olho, cadeado, seleção) deixava `user-select: none` no `body` da página e ouvintes em `window`; o gesto agora é cancelado. O mesmo vale para o arraste por toque de camadas.
279
- - Dados com ciclo de `parentId` não travam mais a verificação de descendência ao aninhar camadas por arraste.
280
- - **Sem mudança de API.** Código dividido em módulos de responsabilidade única (renderização, seleção, lembrança, coleções, regras de soltar, renomear, miniaturas e hierarquia).
281
-
282
- ### `<ui-mapa>`, `<ui-mapa-marcador>` e `<ui-mapa-linha>`: robustez e acessibilidade
283
-
284
- - **Segurança:** `cor` do `<ui-mapa-marcador>` era interpolada em HTML/estilo do ícone (injeção de atributo). O ícone agora é montado com DOM e `cor` só aceita hex, `rgb()`/`hsl()` ou nome de cor; outro valor usa a cor padrão (`#00f5a0`; `#3388ff` na linha).
285
- - `lat`, `lng` e `zoom` inválidos (ou fora de -90..90, -180..180, 0..22) caem no padrão em vez de lançar; `espessura` inválida usa 3.
286
- - Desconectar o `<ui-mapa>` antes de o Leaflet carregar não cria mais um mapa órfão. `<ui-mapa-linha>` reaparece quando o mapa é recriado e ignora pares inválidos em `pontos`.
287
- - **Acessibilidade:** o mapa é uma região com nome (`aria-label` do host, padrão "Mapa"; o atributo passa a ser observado); marcadores são focáveis por teclado (`role=button`) e usam `titulo` como nome acessível.
288
-
289
- ### `<ui-canvas-cad>`: organização interna e acessibilidade
290
-
291
- - **Sem mudança de API.** `GerenciadorFeicoes` virou fachada sobre módulos de responsabilidade única (`cad-feicoes-dados`, `-operacoes`, `-versoes`, `-camera`, `-desenho`, `-edicao`, `-consulta`, `-ponteiro`, `-selecao-regiao`, `-tipos`); tipos do Leaflet em `tipos.ts`, `cad-leaflet-adaptador.ts` e `cad-selecao-pontos.ts` substituem casts `as any`.
292
- - **Acessibilidade:** o canvas é uma região nomeada ("Mapa CAD"), a barra rápida é uma `toolbar` com botões nomeados, o botão de snap expõe `aria-pressed` e uma região viva anuncia a seleção ("2 feições selecionadas") e a ferramenta ativa.
293
-
294
- ### Ferramentas de arquitetura
295
-
296
- - `scripts/check-layers.mjs` verifica o DAG L0→L5 resolvendo imports (estáticos, de efeito colateral, re-exports e dinâmicos); `npm run gate` / `gate:full` agrupam as verificações.
297
-
298
- ## 2.6.1 — 2026-10-17
299
-
300
- ### `<ui-controle-cores>` e `<ui-seletor-cor>`: revisão de erros e visual
301
-
302
- **Correções**
303
-
304
- - O botão "sem cor" ficava por cima da amostra do traço e roubava os cliques da metade inferior esquerda dela. As ações (inverter, sem cor, restaurar padrão) agora ficam numa coluna ao lado das amostras, cada uma com a própria área de clique, sem sobreposição.
305
- - `desabilitado` agora desativa todos os botões (antes só bloqueava o mouse: Tab + Enter ainda inverter/limpar/restaurar) e fecha um seletor aberto; remover o atributo reativa o controle.
306
- - Trocar o alvo pelo controle (`alternarAlvo()`, `definirAlvo()`, clique na outra amostra) agora acompanha a aba do seletor aberto, em vez de deixá-las divergentes.
307
- - `fecharSeletor(false)` agora cancela de verdade: as cores iniciais voltam (com um `ui-cores-change` não confirmado) em vez de ficar a pré-visualização aplicada sem nenhuma confirmação.
308
- - `ui-alvo-change` não é mais emitido em duplicidade no documento (o painel do seletor deixou de repassá-lo ao abrir pelo popover; use `aoTrocarAlvo`).
309
- - Espaço/Enter na amostra ativa com o seletor aberto fecham como o clique, e o Espaço não fecha mais o painel recém-aberto em navegadores que disparam o clique ao soltar a tecla.
310
-
311
- **Acessibilidade**
312
-
313
- - `abrirSeletorCor({ focar: true })` leva o foco ao painel (o controle usa ao abrir por teclado); ao fechar, o foco volta para a âncora. O painel ganha `aria-label` e as amostras da paleta `aria-pressed`.
314
- - O grupo de amostras é anunciado como "Cores de desenho"; a dica/`aria-label` do preenchimento informa a opacidade.
315
-
316
- **Visual**
317
-
318
- - Amostras maiores (`--ui-controle-cores-amostra`, padrão 26px), coluna de ações alinhada, ícone "padrão" refeito e anel de foco visível.
319
- - Ambos os componentes passam a usar os tokens do kit (`--ui-cor-texto`, `--ui-cor-borda`, `--ui-cor-primaria`…) e acompanham o tema claro; antes eram fixos no escuro.
320
- - Seletor: trilha da opacidade preenchida até o valor, rótulos ORIGINAL/NOVA sempre legíveis, diagonal de "sem cor" igual à do controle, aba de preenchimento "sem cor" com a diagonal vermelha, `prefers-reduced-motion` respeitado e painel reposicionado ao redimensionar a janela.
321
-
322
- ## 2.6.0 — 2026-10-17
323
-
324
- ### `<ui-ribbon>` e `<ui-paleta-ferramentas>`: contrato com o consumidor
325
-
326
- - **Valor inicial pendente**: `aba-ativa` / `valor` definidos antes de `abas` / `ferramentas` são aplicados quando os dados chegam; `aba-ativa` reflete a aba realmente ativa.
327
- - **`valor = ''` / `limpar()`** removem a seleção da paleta (`ui-change` com `item: null`). Novas propriedades `orientacao`, `colunas`, `tamanho` (paleta) e `compacto` (ribbon); `valor` reflete no atributo usado (`valor` ou `value`).
328
- - **Eventos**: `ui-ferramenta` agora é cancelável (`preventDefault()` reverte toggle/seleção); `definirAbaVisivel` emite `ui-aba-change` quando a aba ativa muda; novo `ui-recolhido-change`; tipos dos eventos no `HTMLElementEventMap` (`FerramentaDetail`, `AbaChangeDetail`, `PaletaChangeDetail`, `RecolhidoChangeDetail`).
329
- - **Foco e roving tabindex**: re-renderizações preservam o foco; o tabindex acompanha o botão focado; `atualizarItem` não recria o DOM para mudanças de estado (paleta) nem para itens de outra aba (ribbon).
330
- - **Menu/flyout**: `menuitemcheckbox`/`menuitemradio` com `aria-checked`, `aria-controls`, foco devolvido ao botão, fecha ao rolar, `ArrowDown` com menu aberto foca o primeiro item.
331
- - **ARIA**: abas/painel ligados (`aria-controls`/`aria-labelledby`), `aria-label` e `rotulo-abas` configuráveis, `FerramentaItem.rotuloAcessivel`, `aria-keyshortcuts`; o nome acessível não cai mais no `id` quando há `rotulo` ou ícone nomeado.
332
- - **Paleta**: grupo pula filho desabilitado/separador; clique longo cancelado ao arrastar; `suprimirClique` não prende mais o clique seguinte; atalhos ignoram `role=textbox` e `aria-modal`.
333
- - `console.warn` para ids duplicados, `ativar()`/`atualizarItem()`/`selecionarAba()` com id inválido.
334
-
335
- ## 2.5.0
336
-
337
- ### `<ui-tabela>`: robustez, modo remoto e ordenação
338
-
339
- - **Render protegido**: exceção em `coluna.render` deixa só a célula vazia (com `console.error`); o scroll virtual não trava mais após um erro de render.
340
- - **Modo remoto**: `fetchOpcoes` (headers, credentials...), `fetcher` (substitui o `fetch`), `carregarDoEndpoint()`/`recarregar()` resolvem `true`/`false` (antes `void`; nunca rejeitam) e `carregarDoEndpoint(url)` passa a guardar a URL como `src`. `ui-fetch-erro` ganha `origem` (`rede`, `http`, `json`, `render`) e `status`. Novo evento `ui-fetch-cancelado`. Abortar (desconexão ou nova requisição) libera `carregando`; mesma URL em andamento é reaproveitada (sem requisição duplicada ao registrar o elemento depois do HTML).
341
- - **Ordenação**: `ui-sort` agora é cancelável (`preventDefault()` desfaz); atributo/propriedade `ordenacao-remota` para ordenar no servidor. `TabelaColuna.tipo` (`numero`, `data`, `booleano`, `texto`) e `TabelaColuna.comparador`. `Date`, `dd/mm/aaaa` (com `tipo: 'data'`) e booleanos ordenam pelo valor; `Intl.Collator` reutilizado; **nulos ficam sempre no fim** (antes iam para o início em `desc`).
342
- - **Filtro**: ignora acentos; datas entram na busca; `TabelaColuna.textoFiltro` torna pesquisável o texto formatado por `render`; filtra antes de ordenar (mais rápido).
343
- - **Dados do consumidor**: `colunas` é copiado (redimensionar não altera nem falha com objetos congelados/reativos/compartilhados; `tabela.colunas` devolve a cópia interna); entradas `null`/`undefined` em `dados` são descartadas.
344
- - **Seleção**: `id` nulo não marca várias linhas; a identidade do objeto tem prioridade sobre a chave; novo `ui-selecao-atualizada` quando os dados recarregados trazem outro objeto do mesmo registro.
345
- - **Cliques**: botões dentro do shadow DOM de custom elements na célula não selecionam a linha (`composedPath`).
346
- - **Redimensionamento**: clique na alça sem arrastar não emite `ui-column-resize` (antes `"undefined"`); desconectar durante o arraste não bloqueia mais a ordenação; o popover só emite se a largura mudou.
347
- - Eventos do `<ui-tabela>` documentados no README.
348
-
349
- ### `<ui-tabela>`: estética, usabilidade e acessibilidade
350
-
351
- - **Cabeçalho**: sem as larguras fixas de 90px/70px; o vão texto-seta vem de `--ui-tabela-th-gap` (6px) e a seta de `--ui-tabela-th-icone` (16px). Só colunas ordenáveis têm ícone (dois chevrons, o ativo em destaque); `aria-sort="none"` nas ordenáveis inativas. O `sticky` ficou só nos `<th>`.
352
- - **Tokens**: `--ui-tabela-zebra`, `--ui-tabela-hover`, `--ui-tabela-selecionada(-hover)`, `--ui-tabela-cor-foco`. Zebrado visível nos dois temas; texto do botão "Aplicar" usa `--ui-cor-texto-sobre-primaria`. `tabular-nums` e `--ui-fonte-base`.
353
- - **Estados**: erro de carregamento visível (`part="erro"`, `role="alert"`, "Tentar novamente"; atributo `texto-erro`); overlay de carregamento com a cor do tema, `role="status"` e `texto-carregando`; vazio mantém o cabeçalho e o filtro sem resultado usa `texto-sem-resultados` (também anunciado a leitores de tela).
354
- - **Linhas**: `cursor: pointer`, hover só com `(hover: hover)`, foco de teclado (cor do texto) distinto da seleção (primária), texto das células não é selecionado ao clicar (opt-in: `selecionar-texto`).
355
- - **ARIA**: `role="grid"`, `aria-rowcount/colcount`, `aria-rowindex`, `aria-selected` (mantém `aria-current`), espaçadores `aria-hidden`, contêiner `role="region"` com o `aria-label`.
356
- - **Redimensionamento**: toque/caneta (Pointer Events), traço visível em repouso, alça focável (`role="separator"`; ←/→ ajustam, Shift = passo maior, Enter abre o diálogo) e clique na alça não ordena. Diálogo de largura com rótulo, campo numérico, validação inline, posição dentro da janela e retorno do foco.
357
- - `prefers-reduced-motion` (sem transições; spinner lento) e alvos de toque maiores com `pointer: coarse`. Removido o CSS morto do menu de contexto.
358
-
359
- ## 2.3.0 — 2026-10-14
360
-
361
- ### Novos: `<ui-controle-cores>` e `<ui-seletor-cor>` (entrada `@thibezer/ui-components-kit/cores`)
362
-
363
- - **`<ui-controle-cores>`**: par de cores de desenho no estilo Illustrator. Preenchimento na frente, traço atrás; clique na amostra de trás a ativa, clique no alvo ativo (ou duplo clique, Enter, Espaço) abre o seletor. Botões de canto para inverter, "sem cor" no alvo ativo e restaurar o padrão (`padrao-preenchimento`/`padrao-traco`). Estado misto (`definirMisto()`, mostra `?`) e `so-traco` (esmaece o preenchimento).
364
- - Ações do usuário emitem `ui-cores-change` com `{ preenchimento, traco, opacidade, alvo, confirmado, alterados, origem }` (`origem`: `seletor`, `inverter`, `sem-cor` ou `padrao`): `confirmado: false` é pré-visualização com o seletor aberto; na confirmação, `alterados` lista o que mudou desde a abertura. `definirCores()`, `definirAlvo()` e `definirMisto()` não emitem eventos.
365
- - O kit não registra atalhos de teclado: a aplicação chama `alternarAlvo()`, `inverter()`, `semCor()` e `restaurarPadrao()` e pode mostrar as teclas nas dicas com `atalhos`.
366
- - **`<ui-seletor-cor>`** + **`abrirSeletorCor({ ancora, ... })`**: painel flutuante com abas Preenchimento/Traço (modo `duplo`) ou cor única (`simples`), prévia antes × depois, hexadecimal, seletor do sistema, opacidade do preenchimento, cores do projeto, paleta padrão e "sem cor". Um aberto por vez; Concluir, ✕, Enter no hexadecimal, Esc e clique fora confirmam; **Cancelar restaura todas as cores iniciais** (preenchimento, traço e opacidade); `X` alterna o alvo.
367
- - Utilitários: `normalizarCor()`, `normalizarOpacidade()`, `coresUnicas()`, `PALETA_CORES_PADRAO`.
368
-
369
- ## 2.2.2 — 2026-10-14
370
-
371
- ### `<ui-canvas-cad>`: edição de círculo
372
-
373
- - `editarVertices` passa a aceitar `Circle`: alça no centro (move o círculo sem mudar o raio) e alça na borda (define o raio; mínimo 0,5 m). O HUD mostra raio e área ao vivo. `pontosDe`/`comPontos` e `pontoDaBordaDoCirculo` tratam o círculo.
374
-
375
- ### `<ui-camadas>`: arraste sobre controles
376
-
377
- - Clicar e arrastar sobre a coluna de olho, cadeado ou seleção aplica a todos os controles percorridos o estado resultante do primeiro, em vez de reordenar a camada.
378
-
379
- ## 2.2.1 — 2026-10-14
380
-
381
- ### `<ui-camadas>`: miniatura da feição fiel à geometria
382
-
383
- - A miniatura ao lado do nome da feição desenha a geometria real (polígonos com furos, linhas, multi-geometrias), enquadrada com proporção preservada; sem coordenadas, usa o ícone genérico.
384
- - Miniatura maior (20px; 17px no modo compacto).
385
-
386
- ## 2.2.0 — 2026-10-14
387
-
388
- ### `<ui-canvas-cad>`: curvas de Bézier de verdade
389
-
390
- - A **caneta** deixou de quebrar a curva em trechos retos. Linha e polígono ganham `curva` (lista de nós `{ ponto, entrada?, saida? }`, alças em coordenadas absolutas); o desenho é calculado a cada zoom (`bezierCurveTo` no canvas, comandos `C` no SVG), então a curva continua lisa em qualquer escala.
391
- - `coordenadas`/`aneis` seguem existindo como vértices de apoio amostrados sobre a curva (24 por segmento): métricas, snap, seleção e exportação continuam funcionando sem mudança.
392
- - Entrada: `adicionarFeicao`/`definirFeicoes` aceitam `curva` em linha de uma parte e polígono de um anel; nós inválidos são ignorados e a feição vira poligonal.
393
- - Edição de vértices numa feição curva trabalha com os nós: mover leva as alças junto, inserir divide o segmento sem mudar o traçado (de Casteljau) e excluir remove o nó. A caneta mostra as alças enquanto se arrasta.
394
- - **Alças editáveis**: em feição curva, o editor mostra uma alça arrastável por alça (com haste até o nó). A oposta espelha; segurar `Shift` solta a simetria. Duplo clique num nó alterna canto ↔ suave; numa linha/polígono comum isso converte em curva.
395
- - Curvas não são simplificadas por zoom; `deslocarGeometria` move nós e alças.
396
-
397
- ### `<ui-camadas>`: aprimoramento do rodapé e hierarquia de camadas (subcamadas e grupos)
398
-
399
- - **Rodapé Redesenhado e Dual-Mode**:
400
- - Resolução dos conflitos e sobreposição de controles visuais (o contador de camadas não colide mais com o botão de visibilidade nem com o seletor de movimentação).
401
- - **Modo Resumo (sem seleção)**: Exibe contagem limpa e elegante (`X camadas`), tipografia tabular e botões rápidos `+ Grupo` e `+ Camada`.
402
- - **Modo Ações em Massa (com seleção ativa)**: Habilita barra contextual com botões de visibilidade, trava, exclusão, preview circular moderno de cor em lote com aro brilhante (substituindo o input retangular nativo) e seletor compacto de destino com recuo hierárquico e truncamento seguro.
403
- - **Hierarquia de Camadas e Subcamadas**:
404
- - Suporte completo a camadas dentro de outras camadas através de `parentId?: string | null`, `children?: CamadaItem[]` e flag `isGroup?: boolean`. Suporte tanto a arrays planos quanto a árvores aninhadas (com achatamento inteligente e reversão automática).
405
- - **Arrastar e Soltar Hierárquico**: Arraste de camadas para dentro de outras camadas (`drop-into`) na área central da linha para transformar em subcamada, com prevenção automática de ciclos (não permite arrastar um pai para dentro de seus próprios filhos).
406
- - **Drawer e Ferramentas**: Seletor de camada pai e botão `+ Subcamada` integrados no drawer de configurações; botão `Novo Grupo` na toolbar superior.
407
- - **Proteção e Cascata**: Colapsar uma pasta/camada pai recolhe todas as suas subcamadas e feições; excluir um grupo promove automaticamente suas subcamadas para o nível raiz para evitar perda de dados.
408
- - Novos eventos públicos disparados por ação do usuário: `ui-camada-grupo-adicionar` e `ui-camada-mover-hierarquia`.
409
-
410
- ## 2.1.0 — 2026-10-11
411
-
412
- ### `<ui-canvas-cad>`: feições genéricas (migração do MapEngine)
413
-
414
- Tudo é opt-in: o componente **não cria camadas** de feições sozinho. Pontos, segmentos, vizinhos e SIGEF continuam como antes.
415
-
416
- - **Feições**: `definirFeicoes`, `adicionarFeicao`, `atualizarFeicao`, `removerFeicao`, `obterFeicoes`. Point, LineString, Polygon (com furos), Circle (metros) e Text (arrastável), com estilo por feição, ícones SVG, rótulos permanentes (`area`, `extensao`, propriedades) e atualização incremental.
417
- - **Seleção e popup**: clique, Ctrl/Cmd/Shift, caixa Window (E→D) / Crossing (D→E) com toque geométrico real, popup único (`cad-selecao`, `cad-feicao-selecionada`, `cad-feicao-acao`), hit-test unificado entre camadas empilhadas (tolerância em px, alternância de cliques), `cad-feicao-hover` e `cad-feicao-menu`.
418
- - **Desempenho**: índice espacial próprio (sem dependências), culling por viewport acima de 300 feições, clusters de pontos (grade de 55 px), simplificação Douglas-Peucker por zoom. Meta: 100 mil feições.
419
- - **Snap e edição**: snap vértice > ponto médio > aresta (tecla `S`, `Alt` desliga, botão na barra, `definirSnap`); edição de vértices com alças, fantasmas nos pontos médios, exclusão pelo botão direito e HUD ao vivo (`editarVertices`).
420
- - **Ferramentas**: `ponto`, `linha`, `poligono`, `retangulo`, `elipse`, `poligono-regular` (hexágono), `estrela`, `circulo`, `texto`, `caneta` (Bézier), `medir-distancia`, `medir-area`, `dividir`, `selecao-laco`, `conta-gotas`. Atributo `ferramentas` mostra uma barra opcional; `definirFerramenta(nome, { fechar, iniciarEm })`, `definirCamadaDesenho`, `definirEstiloDesenho`. Clicar no primeiro vértice fecha polígono, laço, medir área e caneta (3+ vértices); o botão direito com ferramenta ativa conclui, desfaz o último vértice ou sai; `selecao-laco` seleciona feições dentro do laço e encerra. Utilitários: `gerarBuffer`, `duplicarFeicao`, `juntarFeicoes`.
421
- - **Versões e colaboração**: oficial × prévia (`entidadeId`, `definirPreviasGlobais`, `alternarPrevia`, `cad-previas`); cursores remotos (`atualizarCursorRemoto`, nome escapado e cor só `#hex`).
422
- - **Câmera**: `zoomParaFeicao(s)`, `ajustarTudo`, `ajustarCamada` (ligam camadas e prévias escondidas).
423
- - Exportados pelo entrypoint `./canvas`: modelo `Feicao`, algoritmos (`dividirPoligonoComLinha`, `gerarBuffer`, `juntarLinhas`, `juntarPoligonos`, `ddParaDms`, `dmsParaDd`, `converterComprimento`, `converterArea`...), índice, snap e editores.
424
- - Guia de migração: `docs/guia-migracao-mapengine.md`.
425
-
426
- ### `<ui-canvas-cad>`: opções para integração agnóstica com aplicações externas
427
-
428
- Tudo opt-in: sem os atributos e opções abaixo, o componente se comporta como antes. Detalhes e exemplos em
429
- `docs/guia-migracao-mapengine.md`; eventos em `docs/canvas-cad-eventos.md`; números em `docs/desempenho-feicoes.md`.
430
-
431
- - **Montagem só com API pública**: `inicializarAgora(): L.Map | null` (síncrono, idempotente, cancela o agendamento do `connectedCallback`, nunca cria um 2º mapa); atributos `sem-camadas-padrao` (não cria satélite Google, SIGEF/INCRA, homologados, perímetro, vizinhos, vértices nem grade, e o clique nunca consulta o INCRA), `sem-painel-camadas`, `sem-barra-rapida` e `sem-atalho-snap` (a tecla S não alterna o snap desde o primeiro instante); `definirEstilosExternos(css | CSSStyleSheet[] | null)` injeta CSS do app no shadow root (`adoptedStyleSheets`, com `<style>` de reserva; `destroy()` limpa e a reconexão recoloca). Sem referência a `initCAD`/`initTimeout`.
432
- - **Posição dos HUDs**: `definirMargensHud({ topo, base, esquerda, direita })` (px) e as propriedades CSS `--cad-hud-topo/-base/-esquerda/-direita`. Os HUDs de desenho e de edição de vértices usam a mesma regra: centralizados na área visível, com largura/altura máximas e quebra de linha. Padrões = posição antiga (`base: 16`).
433
- - **Linha multiparte (MultiLineString)**: `LineString` aceita `partes: LatLonPar[][]` (ou `coordenadas`). *Decisão:* estender `LineString` em vez de criar um tipo `MultiLineString`, porque quebra menos: `coordenadas` continua existindo (é a 1ª parte) e todo código/consumidor que só lê `coordenadas` segue enxergando uma linha válida; uma parte só normaliza para a linha simples de sempre; nenhum `switch (geometria.tipo)` do consumidor ganha um caso novo. Coberto: normalização, desenho, hit-test (tolerância por parte), caixa Window/Crossing, laço, snap (vértices e arestas de todas as partes, sem aresta entre partes), extensão somada (métricas e rótulo), popup, simplificação por zoom, `obterFeicoes`, `duplicarFeicao`. Recusas com motivo: `editarVertices` → `cad-edicao-recusada` `multiparte`; `juntarFeicoes` → `cad-uniao-falhou` `multiparte`; `dividir` com só multilinhas → `cad-divisao-falhou` `multiparte`; `gerarBuffer` de multilinha devolve `null`. Helpers exportados: `partesDaLinha`, `ehMultilinha`, `criarLinha`.
434
- - **Lote e ordem**: `aplicarLote(camadaId, { adicionar, atualizar, remover }, opcoes)` faz uma regravação e um `aposDados` e devolve `{ adicionadas, atualizadas, removidas, recusadas }`; `adicionarFeicao`/`atualizarFeicao`/`removerFeicao` passam pelo mesmo caminho. O renderizador recebe só a diferença (índice, loja, oficiais e clusters por célula atualizados item a item) e a busca por id é O(1): com 100 mil feições, uma edição caiu de 60–250 ms para ~0,5 ms (ver `docs/desempenho-feicoes.md`). `atualizarFeicao(camadaId, id, parcial, { substituirPropriedades: true })` (e o mesmo em `aplicarLote`) troca as `propriedades` por inteiro em vez de mesclar.
435
- - **`Feicao.oculta`**: não desenha, não seleciona (clique, caixa, laço, hit-test, hover, `selecionarFeicoes`), não atrai snap e fica fora de `ajustarTudo`/`ajustarCamada`; segue nos dados, não encerra a edição de vértices em andamento, não conta como excluída e participa da regra oficial × prévia como as demais. `excluida` mantém o significado.
436
- - **Estilo**: texto com selo (`corFundo`, `corBorda`, `opacidadeFundo`, `raioBorda`; o selo substitui o `halo`) e ponto sem ícone com `corContorno`/`espessuraContorno`, separados do preenchimento. Cores só passam por `corSegura`; números só se finitos.
437
- - **Ferramentas**: `medir-distancia` com 3+ pontos mostra também área e perímetro no HUD e traz `areaM2`/`perimetroM` em `cad-medicao` (sem criar feição); círculo ignora raio < 2 m; `definirOpcoesPopup({ copiarParaAreaDeTransferencia })` torna opcional a escrita da ação "copiar"; evento cancelável `cad-texto-solicitado` (`preventDefault()` + `detalhe.concluir(texto)`) para usar modal próprio. Teste parametrizado cobre as 15 ferramentas: o ouvinte do evento de conclusão pode desativá-las sem efeito colateral e `cad-selecao` nunca sai com a ferramenta ainda ativa quando a seleção é consequência dela.
438
- - **Coordenada (0, 0)**: atributo/propriedade `aceitar-origem` / `aceitarOrigem` (padrão `false`) e `{ aceitarOrigem }` nas funções puras de normalização.
439
-
440
- ### Correções da revisão dos componentes
441
-
442
- - **`<ui-tabela src>`**: deixou de refazer o `fetch` a cada mudança de atributo (chegava a dezenas de requisições); busca só ao conectar e quando o `src` muda. Filtro e ordenação passam a valer juntos, e o filtro olha as colunas declaradas (ignora campos fora delas e objetos).
443
- - **`<ui-camadas>`**: foco do teclado preservado após redesenhar; novo `emLote(fn)` agrupa mutações numa só renderização; `definirCamadas` troca a camada ativa que sumiu; `selecionarFeicao` alcança feições de camadas recolhidas; a ordem enviada pela aplicação vence a ordem salva do usuário quando ela mudou; excluir uma camada apaga o override guardado; aviso no console quando painéis dividem a chave de persistência padrão.
444
- - **`<ui-lista-flutuante>`**: `formReset` volta ao valor inicial mesmo após mover o elemento; `input` antes de `change`; Esc no gatilho, Tab, Home/End e foco devolvido ao gatilho; abre para cima quando falta espaço embaixo.
445
- - **`<ui-seletor-mapa-base>`**: `desabilitado` agora funciona; `selecionar` ignora ids fora da lista; setas/Home/End e foco no mapa ativo ao abrir pelo teclado; miniaturas sem `referrer`.
446
- - **`<ui-canvas-cad>`**: atalhos (`S`, Backspace, Esc) não disparam mais enquanto se digita em campos com shadow DOM; ferramentas funcionam após reconectar o componente; dividir/juntar não perdem a feição original quando o resultado é recusado; `juntarFeicoes` respeita bloqueios; `tamanhoIcone` não numérico não injeta HTML; texto bloqueado e depois liberado persiste o arraste.
447
- - **`<ui-tabela-propriedades>`**: `-2^2` agora vale -4 (o sinal unário pesa menos que a potência).
448
-
449
- ### ⚠️ Mudanças de comportamento
450
-
451
- - Ctrl/Cmd/Shift + clique no vazio **não limpa mais** a seleção de pontos (modo aditivo).
452
- - Clique em marcador DOM não é mais repassado às feições do canvas por baixo.
453
- - `MapaConfiguracoes.magnetSnap` está **obsoleto** e sem efeito (o snap agora é do componente).
454
- - `adicionarFeicao`/`aplicarLote` com um `id` que já existe **substituem a feição no mesmo lugar**; antes ela ia para o fim da lista (mudava a ordem de empilhamento dentro da camada).
455
- - **`cad-medicao`** de `medir-distancia` passa a trazer `areaM2` e `perimetroM` quando a régua tem 3 ou mais pontos (antes só `medir-area` trazia). Quem usava `areaM2 !== undefined` para distinguir o tipo deve olhar `tipo`.
456
- - **Círculo**: o clique que daria raio menor que 2 m é ignorado (antes aceitava qualquer raio > 0). Feições com `Circle` de raio menor continuam válidas na entrada de dados.
457
- - **`dividir`**: se a feição cortada estava selecionada, a ferramenta encerra antes de a seleção mudar (para `cad-selecao` sair sem ferramenta ativa). Sem seleção envolvida, continua ativa para o próximo corte.
458
- - **Ordem do aviso nas medições**: `cad-medicao` agora sai depois de a ferramenta estar pronta para a próxima figura; desativar a ferramenta dentro desse evento deixava a sessão "ressuscitar" (corrigido).
459
- - **HUDs**: ganham `width: max-content`, largura/altura máximas e quebra de linha (o de edição de vértices não quebrava linha). Na posição padrão ficam onde estavam.
460
- - Valores novos nos `motivo` de `cad-edicao-recusada`, `cad-uniao-falhou` e `cad-divisao-falhou`: `multiparte`. Quem faz `switch` exaustivo precisa tratá-lo.
461
- - `selecionarFeicoes` e `alternarSelecaoFeicao` ignoram feições `oculta`.
462
- - `GerenciadorFeicoes.emitir` (uso interno/testes) ganhou um 3º parâmetro opcional `cancelavel` e pode devolver `true` para "cancelado"; o `normalizarFeicaoEntrada`, `normalizarCoordenada` e `parseCoordenada` ganharam um último parâmetro opcional `{ aceitarOrigem }` (compatível).
463
-
464
- ## 2.0.0
465
-
466
- ### ⚠️ Mudanças que podem exigir ajuste
467
-
468
- - **Tema segue o sistema operacional por padrão.** Antes o kit era sempre escuro. Para manter o escuro fixo, use `<html data-tema="escuro">`. `UIBus.definirTema('auto')` volta a seguir o sistema.
469
- - **Enter em `<ui-campo-texto>` envia o formulário**, como um campo nativo. Formulários com botão `type="submit"` e sem listener de `submit` com `preventDefault()` passam a enviar (e recarregar a página) ao pressionar Enter.
470
- - **`ui-abrir` / `ui-fechar` do modal disparam também ao mudar o atributo `aberto`**, não só pelos métodos `abrir()`/`fechar()`.
471
- - **A seleção de `<ui-tabela>` é mantida ao ordenar, filtrar ou trocar `dados`** (se o item ainda existir). Use `limparSelecao()` para zerar.
472
- - **Variáveis CSS renomeadas:** `--ui-fonte-mono`, `--ui-fonte-familia` e `--ui-fonte-principal` → `--ui-fonte-codigo` / `--ui-fonte-base`; `--ui-radius-*` → `--ui-raio-*`.
473
- - **Cores do tema claro mais escuras** para atingir contraste WCAG AA (primária `#006b40`). No escuro, o botão destrutivo passou para `#d32f2f`.
474
- - Removidos `UIModal._openCount` e o atributo interno `data-scroll-locked`.
475
-
476
- ### Usuário final
477
-
478
- - Textos legíveis nos dois temas (contraste WCAG AA verificado em testes).
479
- - Tabela por teclado: cabeçalhos ordenáveis com Tab + Enter, linhas com setas, Home/End, PageUp/PageDown, Enter para selecionar.
480
- - Modal: foco inicial no conteúdo (ou `[autofocus]`), foco devolvido ao fechar, Esc fecha o modal do topo, segundo modal aberto continua interativo, "✕" oculto com `bloquear-fechamento`.
481
- - Tooltip lido por leitores de tela, fecha com Esc e não some ao levar o ponteiro até o balão.
482
- - Botões só com ícone recebem nome acessível (`aria-label` ou `title`).
483
- - Gatilhos Zero-JS ignoram botões desabilitados ou carregando.
484
- - Correção de segurança: o `label` de campos nunca é interpretado como HTML.
485
-
486
- ### Desenvolvedor
487
-
488
- - **Tipos para React e Vue** gerados a partir do Custom Elements Manifest: `@thibezer/ui-components-kit/tipos-react`, `/tipos-vue` e `/tipos-elementos`.
489
- - **`custom-elements.json`** publicado (campo `customElements`), para IDEs, Storybook e documentação.
490
- - **`::part`** nos 14 componentes principais, com estados (`opcao-selecionada`, `linha-selecionada`).
491
- - **Tokens unificados:** fontes `--ui-fonte-base` / `--ui-fonte-codigo`; escala de raio derivada de `--ui-raio-borda` (`--ui-raio-sm/lg/xl/pill`); novos `--ui-cor-texto-info`, `--ui-cor-texto-sobre-status`, `--ui-cor-fundo-recuado`, `--ui-sombra-sm/md/lg`.
492
- - `<ui-botao>` form-associated: `form="id"`, `<fieldset disabled>`, `name`/`value` no `e.submitter`, `disabled` como propriedade, `click()` no host.
493
- - `UIBus.obterTema()`; `tema:alterado` também quando o sistema troca de tema.
494
- - `<ui-tabela>`: `aria-label` repassado à tabela, `aria-busy` durante o carregamento.
495
- - README corrigido (exemplo React, atributos do catálogo, import map para mapa/CAD via CDN).
496
-
497
- ## 1.0.0
498
-
499
- - Primeira publicação no npm como `@thibezer/ui-components-kit`.
24
+
25
+ ## 2.10.1 — 2026-10-23
26
+
27
+ Junta à 2.10.0 tudo o que ainda estava fora do npm: a revisão da edição de formas da 2.4.0 (PR #7), a espessura do traço no controle e no seletor de cor e o estado misto do `<ui-tabela-propriedades>` (PR #12). Sai como 2.10.1 a pedido, embora traga novidades.
28
+
29
+ ### `<ui-controle-cores>` e `<ui-seletor-cor>`: espessura do traço
30
+
31
+ - **`<ui-controle-cores>`**: atributo `espessura-traco`, propriedade `espessuraTraco` (0,5 a 50 px, padrão 2,5) e `obterCores()`/`definirCores()` com a espessura. `ui-cores-change` ganha `espessuraTraco`, o alterado `'espessuraTraco'` e a origem `'espessura'`.
32
+ - **`<ui-seletor-cor>`**: controle de espessura do traçado (faixa e atalhos de 1 a 8 px) e prévia antes × depois com a espessura real. Novas opções `espessuraTraco` e `mostrarEspessura` (padrão: só com o alvo Traço). `ui-cor-input`/`ui-cor-confirmar` trazem `espessuraTraco`.
33
+ - Utilitário `normalizarEspessura()`: limita a [0,5; 50] px com uma casa decimal; valor inválido vira 2,5. Um atributo `espessura-traco` vazio volta ao padrão (antes da revisão, viraria o mínimo de 0,5 px).
34
+ - Página de exemplos: card 17, com controle e seletor ligados, prévia SVG e estado em JSON.
35
+
36
+ ### `<ui-tabela-propriedades>`: estado misto ("Varia") para seleção múltipla
37
+
38
+
39
+
40
+ - Com várias formas selecionadas, a propriedade cujo valor difere entre elas exibe **"Varia"**; as iguais exibem o valor comum (como o *\*VARIES\** do AutoCAD).
41
+
42
+ - Novo valor sentinela `VALOR_MISTO` (`Symbol.for('ui-tabela-propriedades:misto')`) e atalho `misto: true` em `ItemPropriedade`. Funções puras `combinarValores(registros)`, `valorComum(valores)` e `isValorMisto(valor)`: `painel.valores = combinarValores(formas.map(f => f.propriedades))`.
43
+
44
+ - Novo atributo/propriedade `texto-misto` / `textoMisto` define a palavra exibida (padrão "Varia").
45
+
46
+ - Todos os editores suportam o estado: texto e número ficam vazios com "Varia" no placeholder; seleção, tipo/espessura de linha e cor CAD mostram uma opção "Varia" não selecionável; booleano fica indeterminado (`aria-checked="mixed"`, o primeiro clique marca todos); cor e somente leitura exibem "Varia". Linha recebe a classe `ui-prop__linha--misto`; cor ajustável por `--ui-prop-misto-cor`.
47
+
48
+ - Sair do campo sem digitar, apagar o texto, Esc, ↑/↓ e o arrasto do rótulo não alteram um valor misto. Ao editar, `ui-propriedade-alterada` sai com `valorAnterior: VALOR_MISTO`; **Desfazer** volta ao estado misto. Em `ui-aplicar`, as propriedades não editadas continuam com `VALOR_MISTO` — filtre com `isValorMisto()` antes de gravar.
49
+
50
+
51
+ ### `<ui-canvas-cad>`: volta a revisão dos modos de edição da 2.4.0
52
+
53
+ A 2.4.0 e a 2.4.1 foram publicadas no npm, mas a revisão da edição de formas que elas traziam não entrou na 2.5.0 nem nas versões seguintes. Esta versão a traz de volta, portada para os módulos extraídos depois (`cad-feicoes-edicao.ts`, `cad-feicoes-ponteiro.ts` e o novo `cad-feicoes-teclado.ts`) e combinada com a edição da 2.10.0 (alças latentes, ponto médio arrastável, dica do HUD).
54
+
55
+ **Novidades**
56
+
57
+ - **Desfazer/refazer/cancelar na edição**: Ctrl+Z, Ctrl+Shift+Z / Ctrl+Y e botões no HUD (100 gestos); **Cancelar** volta à geometria de quando a edição começou. Mudar a geometria por fora recomeça o histórico.
58
+ - **Nó selecionado**: clique na alça destaca o nó; Delete/Backspace ou "Excluir nó" o exclui. Recusas (mínimo de nós, ponto, círculo) avisam no HUD.
59
+ - **Mais formas de entrar na edição**: **Enter** com uma só feição editável selecionada e botão **Editar** na barra (`ferramentas="editar"` ou `"todas"`; reflete a edição em curso com `aria-pressed`). `cad-edicao-recusada` ganha o motivo `sem-selecao` (botão Editar sem uma feição selecionada), com aviso visível.
60
+ - **Novo evento `cad-feicao-duplo-clique`** (cancelável, `{ camadaId, feicao, coordenada }`). O duplo clique sobre a feição continua abrindo o popup, como na 2.8.1 (na 2.4.0 ele abria a edição). Com `preventDefault()` o consumidor assume o gesto, por exemplo chamando `editarVertices()`.
61
+ - **Retângulo**: arrastar um canto de um retângulo alinhado aos eixos mantém o retângulo (Shift solta).
62
+ - **Fantasmas** não aparecem em segmentos com menos de 24 px na tela e voltam ao aproximar o zoom.
63
+ - **Ferramentas de desenho avisam no HUD** quando recusam o clique (círculo abaixo de 2 m, cantos alinhados, centro repetido) ou quando Enter/duplo clique chega com pontos de menos.
64
+ - O HUD de edição passa a ter vários botões (Desfazer, Refazer, Excluir nó, Cancelar, Concluir): quem buscava o botão pelo seletor `#cm-vertex-edit-hud button` deve usar `[data-acao="concluir"]`.
65
+
66
+ **Correções**
67
+
68
+ - **Círculo**: o popup oferece o botão **Editar**; a alça da borda fica onde foi solta (antes voltava para o leste do centro).
69
+ - **Fantasma de inserção**: o duplo clique insere um vértice e não o converte mais em curva (`dblclick` até 600 ms depois da inserção é ignorado).
70
+ - **Camada ou feição fechada durante a edição**: ocultar, bloquear ou remover a camada, ou bloquear a feição, encerra a edição (`cad-edicao-concluida`) e tira alças e HUD.
71
+ - **Caneta**: o arraste da alça termina com o `mouseup` na janela; soltar o botão fora do mapa não deixa mais a alça seguindo o cursor.
72
+ - **Snap em curvas**: feição com `curva` atrai pelos nós (vértice) e pelo meio do trecho curvo; os vértices de apoio amostrados só valem como aresta.
73
+ - **Mudança externa no meio do arraste** (outro operador, API): a alça arrastada não é destruída; o gesto em curso vence ao soltar.
74
+ - **Tecla S** e **Enter** com vários canvases na página só valem no que está sob o mouse ou com foco. Com um único canvas nada muda.
75
+
76
+ ## 2.10.0 — 2026-10-23
77
+
78
+ Junta à 2.9.0 o trabalho que tinha ficado fora do npm: o `<ui-area-texto>` e a lapidação dos textos (PR #10), a usabilidade do desenho e da edição de vértices, as curvas Bézier sempre à mão, o painel de camadas no estilo Adobe e a lapidação do `<ui-card>`.
79
+
80
+ ### Novo: `<ui-area-texto>` (alias `<ui-textarea>`) para textos grandes
81
+
82
+ - Área de texto de várias linhas, form-associated (`name`, `value`, `required`, `minlength`/`maxlength`, `setCustomValidity`, `form.reset()`), com rótulo, ajuda/erro e os mesmos eventos do `<ui-campo-texto>` (`ui-input`, `ui-change`, `input`, `change`).
83
+ - `linhas` (1–50, padrão 4), `auto-altura` (cresce com o conteúdo, sem pular a rolagem da página) com `linhas-max` como teto, `redimensionar` (`nenhum`|`vertical`|`horizontal`|`ambos`) e `contador` (usa `maxlength` como limite).
84
+ - **Enter insere quebra de linha** e não submete o formulário. **Ctrl/Cmd + Enter** emite `ui-enviar` (`detail: { value }`).
85
+ - Acessibilidade: nome acessível pelo `label`, `aria-label` do host, `<label for>` ou `<label>` envolvente; `aria-required`; `aria-describedby` só com a ajuda visível; o contador avisa leitores de tela apenas quando muda de nível (perto do limite, atingido, excedido), não a cada tecla.
86
+ - API programática: `focus()`, `blur()`, `select()`, `setSelectionRange()`, `limpar()`, `readOnly`, `minLength`, `maxLength`.
87
+ - Parts: `rotulo`, `campo`, `input`, `rodape`, `ajuda`, `contador`. Exportado por `index`, `register` e `forms`.
88
+
89
+ ### `<ui-texto>`: texto longo e lapidação
90
+
91
+ - **Novo `linhas`:** limita o texto a N linhas (1–50) com reticências; `truncar` continua tendo prioridade. Valores inválidos são ignorados.
92
+ - Palavras e URLs muito compridas passam a quebrar (`overflow-wrap`) em vez de estourar o contêiner; `alinhamento="justificado"` usa hifenização.
93
+ - O elemento interno expõe `part="base"`. Sem DOM (SSR) o componente fica inerte, sem casts `any`.
94
+
95
+ ### `<ui-campo-texto>`: lapidação
96
+
97
+ - **`minlength`/`maxlength` inválidos** (`abc`, vazio, negativo) eram repassados ao `<input>` como `NaN`; agora são ignorados, e os getters `minLength`/`maxLength` devolvem `-1` em vez de `NaN`.
98
+ - Acessibilidade: `aria-required` no campo obrigatório; `aria-describedby` só referencia a ajuda enquanto ela está visível; a ajuda/erro é uma região `aria-live="polite"`, então mensagens de erro passam a ser anunciadas.
99
+ - Ouvintes via `ListenerBag` e temporizador de sincronização cancelado ao desconectar/reconectar (sem `any`).
100
+
101
+ ### Núcleo
102
+
103
+ - `core/opcoes`: `escolherInteiro` e `inteiroOuNulo` convertem atributos numéricos hostis (`NaN`, `Infinity`, texto, fora da faixa) em inteiros seguros. `sincronizarFeedbackErro` aceita `<textarea>` e um prefixo de classe opcional (padrão inalterado).
104
+
105
+ ### `<ui-canvas-cad>`: usabilidade do desenho e da edição de vértices
106
+
107
+
108
+
109
+ - **Esc voltava a falhar depois do primeiro popup.** O Leaflet mantém `map._popup` apontando para o último popup mesmo fechado, e o atalho de teclado do mapa engole o Esc enquanto essa referência existe: com o foco no mapa, o Esc deixava de encerrar a edição de vértices, cancelar a figura em desenho e limpar a seleção. O mapa agora solta a referência no `popupclose` (`src/gerencigeo-canvas/popup_residual.ts`); popup aberto continua fechando com Esc.
110
+
111
+ - **Enter em botões durante o desenho.** Com uma ferramenta ativa, o Enter era tratado como "concluir" mesmo com o foco num botão (barra de ferramentas, "Sair" do HUD), e o clique do botão não acontecia. O Enter agora fica com botões, links e controles com `role` acionável (`focoEmControleAcionavel`, em `src/core/alvo-digitavel.ts`).
112
+
113
+ - **Ferramenta ou edição recusada agora avisa.** Sem camada de desenho (ou com camada bloqueada/inválida), e ao recusar a edição de uma feição (bloqueada, com furos, multiparte...), o componente mostra um aviso visível por 5 s e o anuncia na região viva, além de emitir `cad-desenho-recusado`/`cad-edicao-recusada` como antes. **Novo atributo `sem-avisos`**: o consumidor que mostra o próprio aviso a partir dos eventos desliga o do componente.
114
+
115
+ - **Ponto médio arrastável na edição de vértices.** Arrastar o ponto médio de uma aresta cria o nó já na posição solta (com snap, prévia e HUD ao vivo e um único `cad-feicao-atualizada`); o clique continua inserindo no meio da aresta. Em feição com curva, o nó nasce sobre o traçado e a curva é mantida (antes, inserir um nó numa curva a convertia em poligonal). A dica do HUD passou a mencionar inserir e excluir.
116
+
117
+ - **Toque.** Um toque que começa numa alça de edição, num ponto médio ou nos HUDs não move mais o mapa junto; alças de Bézier passam a ser tratadas como as demais alças pelo mouse (não iniciam caixa de seleção).
118
+
119
+ - Testes de regressão em `tests/ui-canvas-cad-edicao.regressao.test.ts`.
120
+
121
+ ### Canvas CAD: curvas (Bézier) sempre à mão
122
+
123
+
124
+
125
+ - **Editor de vértices**: as alças de Bézier agora aparecem **sempre**, inclusive em linhas/polígonos retos (alças latentes pontilhadas a 1/3 do vizinho, até 40 nós). Arrastar uma alça converte o nó em curva; `Shift` solta a simetria.
126
+
127
+ - **Caneta**: as alças das âncoras já colocadas podem ser **ajustadas durante o desenho** (arraste a alça, ou a própria âncora para criar/ajustar a curva dela). `Shift` solta a simetria; o cursor muda ao passar sobre uma alça.
128
+
129
+ - A dica do HUD de edição junta os gestos das duas mudanças acima (curvar pelas alças, ponto do meio, botão direito para excluir, duplo clique, Shift).
130
+
131
+ ### `<ui-camadas>`: painel de camadas no estilo Adobe
132
+
133
+ - **Mudança visual:** os selos "✓ Ativa", "N SUB" e "OFICIAL" saem da linha da camada (a informação continua em `aria-current`, na dica da contagem e no marcador da prévia). Quem estilizava `.ui-active-badge`/`.ui-group-badge` precisa revisar.
134
+
135
+
136
+
137
+ - **Cor da camada editável**: clicar (ou Enter/Espaço) na barra de cor da linha abre o `<ui-seletor-cor>` ao lado, com prévia ao vivo, "cores do projeto" e Cancelar que restaura. Emite `ui-camada-cor` como antes.
138
+
139
+ - **Destaque de seleção na cor da camada**: a camada ativa, as feições selecionadas e o alvo de seleção usam a cor da camada (variável `--layer-active-color`, agora definida no grupo e herdada pelas feições).
140
+
141
+ - **Menos selos**: removidos "✓ Ativa", "N SUB" e "OFICIAL"; a camada ativa é indicada pelo destaque da linha (`aria-current`), a contagem vira um número discreto (a dica traz feições e subcamadas) e só a geometria prévia ganha um marcador.
142
+
143
+ - Alça de arraste mais estreita (8px, antes 14px).
144
+
145
+ - Colunas fixas (alça, olho, cadeado, barra de cor) alinhadas à esquerda em camadas, subcamadas e feições; só seta, miniatura e nome recuam com a profundidade, como no Illustrator. A barra de cor não se expande mais ao passar o mouse.
146
+
147
+ ### `<ui-card>`: lapidação
148
+
149
+
150
+
151
+ - **Teclado:** Enter/Espaço vindos de um campo ou botão dentro do card não o ativam mais nem têm o `preventDefault` aplicado (antes era impossível digitar espaço num `<input>` dentro de um card `clicavel`). Tecla mantida (`repeat`) e atalhos com Ctrl/Alt/Meta não ativam.
152
+
153
+ - **Cliques internos:** o clique num ícone SVG dentro de um botão (alvo `<path>`) passou a ser reconhecido como de controle interno; `label`, `summary`, conteúdo editável, `ui-campo-texto` e `ui-segmented` também. Um `<a>`/`<label>` que *envolve* o card não o bloqueia mais.
154
+
155
+ - **Acessibilidade:** `aria-label` do host dá nome ao card clicável; `focus()` no host foca o card; anel de foco visível (`:focus-visible`, contorno que sobrevive ao modo de alto contraste).
156
+
157
+ - **Estilo:** o atributo `hidden` passa a esconder o card (o `display: block` do host o anulava); o realce de hover vale só com `(hover: hover)`; `prefers-reduced-motion` desliga transições e deslocamento.
158
+
159
+ - **Seções:** `[slot="cabecalho|rodape|midia"]` aninhado em um descendente não revela mais a seção do card (só filhos diretos).
160
+
161
+ - Lógica extraída para `card-interacao.ts` e `card-secoes.ts`; JSDoc de atributos, slots, parts e evento para o manifesto. API pública inalterada (novo observado: `aria-label`).
162
+
163
+ ## 2.9.0 — 2026-10-23
164
+
165
+ ### Junta a 2.8.2 com a linha 2.6.1–2.8.1
166
+
167
+ - **Atenção a quem instalou a 2.8.2:** ela foi publicada a partir de um branch baseado na 2.6.0 e **não continha** o que saiu na 2.6.1, 2.7.0, 2.8.0 e 2.8.1 (entre outros: `<ui-linha>` e a altura única dos controles; popups e câmera só a pedido no `<ui-canvas-cad>`; busca por nome de camada e mapa base recolhível no `<ui-camadas>`; as correções de robustez e acessibilidade da 2.7.0 em `<ui-tooltip>`, `<ui-modal>`, `<ui-mapa>`, `<ui-tabela>`, `<ui-camadas>` e `<ui-canvas-cad>`). A 2.9.0 traz as duas linhas juntas: tudo da 2.8.1 mais tudo da 2.8.2 (medidas no elipsoide GRS80 e as pendências do ribbon e da paleta).
168
+ - As mudanças da 2.8.2 no `<ui-ribbon>` e na `<ui-paleta-ferramentas>` foram portadas para os módulos extraídos na 2.7.0: grupo colapsado, botão dividido e lançador de diálogo em `ribbon-render.ts` (os cliques seguem delegados pelo painel); ativação manual das abas em `ribbon-teclado.ts` (`acaoDaTeclaAba` ganha a ação `ativar`); atalhos compostos e a paleta "dona" dos atalhos em `paleta-atalhos.ts` (`resolverAtalho` aceita o evento de teclado; `paletaDonaDosAtalhos`).
169
+
170
+ ### `<ui-canvas-cad>`: desenho de muitas formas (Fases 0 e 1 do motor de formas)
171
+
172
+ - **LOD por tamanho em pixels:** feição (linha, polígono, círculo) com diagonal abaixo de 3 px na tela deixa de ser um objeto Leaflet e vira uma **marca de 1–2 px** na cor do preenchimento/contorno, pintada em lote por cor num canvas por camada (`feature_lod.ts`, `renderers/marcas_lod_layer.ts`). O mapa não fica "furado". Sempre em detalhe completo: selecionada (e em edição), com rótulo e em par oficial × prévia; ponto e texto nunca viram marca. Hit-test, snap, caixa, laço e hover seguem na geometria original, então uma feição de 1 px continua clicável. Reavaliado ao mudar de zoom, não a cada pan.
173
+ - **Seleção incremental:** `aplicarSelecao` deixou de reconciliar a camada inteira. Só as feições que mudaram de estado são tocadas, e só no estilo (`setStyle`/`setIcon`, sem `setLatLngs`); a camada só é reconciliada quando há ponto sob agrupamento (selecionado sai do cluster). Selecionar 1 feição em 100 mil: de ~60 ms síncronos para ~6 ms (medido no navegador).
174
+ - **Simplificação em pixels:** a tolerância de 1,2 px passa a valer para feições com **mais de 8 vértices** (antes 40) em **qualquer zoom abaixo de 17** (antes 12). `simplificarLinha`/`simplificarAnel` ganham o parâmetro opcional `escalaLat` e a tolerância deixa de ser 1/cos(lat) vezes maior na vertical (2× a 60° de latitude).
175
+ - **Instrumentação:** `FeicoesLayerRenderer.estatisticas()` ganha `marcasLOD` e `avaliacoesLOD`; novo `idsDesenhados()`. Página de benchmark `bench/bench-formas.html` (rAF por canal de mensagens e `getImageData`, dados sintéticos com semente fixa) para medir mudança de vista, seleção e ocultar/exibir no navegador.
176
+ - **Correções (Regra #0):** `feicaoNoPonto` estourava a pilha (`Math.max(...xs)`) em polígono com ~120 mil vértices ou mais; `toleranciaPorZoom(NaN)` colapsava geometrias a 2 pontos; uma feição que o Leaflet recusa (coordenada NaN) derrubava a reconciliação da camada inteira e agora fica sem desenho.
177
+
178
+ **Mudanças de comportamento**
179
+
180
+ - Feições minúsculas não estão mais em `grupo.getLayers()` nem em `map._layers`: quem contava camadas Leaflet para medir desenho deve usar `estatisticas()` (o app usa `map._layers` só no benchmark).
181
+ - O desenho de feições com mais de 8 vértices é mais simplificado (até 1,2 px de desvio) abaixo do zoom 17. A geometria da feição nunca é alterada.
182
+ - O clique numa marca passa pelo hit-test central do mapa (`layer_hit_test`: nova `FonteHit`), não por um objeto Leaflet: uma ação `feicao-clique` por clique, com a mesma ordem das formas (linha antes de área, camada do topo, menor área) e alternância em cliques repetidos sobre marcas empilhadas (até 20).
183
+ - Cada camada com marcas ganha um canvas extra (removido quando não há marcas). Não foi consolidado nesta fase.
184
+
185
+ ### Motor das formas headless e renderizador Canvas opt-in (Fases 2 e 3)
186
+
187
+ - **Novo atributo/propriedade `renderizador`** em `<ui-canvas-cad>`: `leaflet` (padrão, inalterado) ou `canvas`. Com `canvas`, as camadas de feições são desenhadas por um motor próprio em Canvas 2D em vez de um objeto Leaflet por feição. Trocar o valor em tempo de execução recria só as camadas de feições (`CanvasLayerManager.recriarCamadasDoTipo`), mantendo dados e seleção. Valor inválido cai em `leaflet`. Guia: [`docs/motor-formas.md`](docs/motor-formas.md).
188
+ - **`estatisticasFeicoes(camadaId)`:** contadores da camada desenhada (`total`, `materializadas`, `marcasLOD`, `clusters`, `avaliacoesLOD` e, no caminho canvas, `desenhadas`, `marcadoresDom`, `canvases`; mais `renderizador`), para diagnóstico e benchmark. `null` se a camada não está desenhada.
189
+ - **Motor headless** (`src/gerencigeo-canvas/motor/`, L2, sem DOM e sem Leaflet; roda em Node e serve para Web Worker): `MotorFormas` com `definirCamada`, `definirFeicoes`, `aplicarDelta`, `listaDesenho(vista, { camadas, parte })`, `listaSobreposicao`, `consultarCaixa`, seleção, `previsualizar`, `destacar` e clusters. Estilo resolvido compartilhado pelos dois renderizadores (`estilo.ts`), geometria em `Float64Array` com projeção própria e Douglas-Peucker em px (`geometria-px.ts`), loja + índice + oficial × prévia (`dados-camada.ts`).
190
+ - **Novo entrypoint `@thibezer/ui-components-kit/motor`** (`src/motor.ts`): o motor sem o componente. O `check-layers` garante que nada alcançado por ele importa `leaflet` (`dist/motor.js` não importa).
191
+ - **Renderizador Canvas** (`renderers/renderizador_formas.ts`, `PintorCanvas2D`): pinta forma a forma, agrupado por estilo, com recorte. **Pan incremental** (`renderers/canvas_formas_leaflet.ts`): o bitmap é deslocado em pixels de mundo e só as faixas expostas são listadas e pintadas. Seleção, hover e edição vão para uma sobreposição única por mapa, sem repintar a base. Clique e hover passam pelo hit-test central (`FonteHit`).
192
+ - **Números com GPU** (máquina do dono, canvas × leaflet intercalados; tabelas em `docs/RELATORIO-motor-formas.md`): o canvas fica de 2 a 6 vezes abaixo do leaflet em todos os tamanhos. Mudança de vista: 20 mil em zoom 12, 90–222 contra 473–676 ms; 100 mil em zoom 15, 166–197 contra 811–1 652 ms. Selecionar 2 000 feições numa camada de 50 mil: 114 contra 1 316 ms. As metas absolutas do prompt (zoom 12: 20 mil ≤ 60 ms, 50 mil ≤ 100 ms, 100 mil ≤ 200 ms) não foram atingidas.
193
+ - **Desempenho do caminho canvas:** o item da marca de LOD fica em cache por zoom e é reaproveitado no pan enquanto feição, estilo da camada, seleção e destaque não mudam (lista de 100 mil marcas em zoom 12: de ~390 para ~140 ms por mudança de vista, no Node). Canvas em branco (camada sem nada na vista, sobreposição sem seleção) não é mais deslocado, limpo nem redimensionado a cada pan: cada chamada de desenho fazia o navegador reenviar o canvas inteiro ao compositor (1 000 formas em zoom 18: de ~130 para ~30 ms por pan, medido sem GPU). Camada com até 500 itens na vista é repintada inteira no pan em vez de ter o bitmap deslocado (`CanvasFormas.limiteRepintar`, ajustável; também por instância): com poucas formas, deslocar o bitmap de 3072×1920 custava 3–5× mais que repintar e deixava o canvas mais lento que o leaflet (1 000 formas em zoom 12: de ~200 para ~60 ms).
194
+ - **Correções** achadas pela suíte de equivalência: marca de LOD antiga ficava na tela quando a feição passava a ponto agrupado (também no caminho `leaflet`); ícone selecionado ou único restante numa célula de cluster sumia no caminho canvas.
195
+
196
+ **Mudanças de comportamento do caminho `canvas`** (o caminho `leaflet` não muda)
197
+
198
+ - Preenchimento translúcido é pintado forma a forma: polígonos sobrepostos da mesma camada continuam escurecendo onde se cruzam, como no Leaflet. A ordem de desenho dentro da camada é por grupo de estilo, não por ordem de inserção.
199
+ - Há um canvas por camada de feições mais uma sobreposição por mapa (não 2 canvases no total). Opacidade e visibilidade seguem o pane da camada; a sobreposição ignora a opacidade da camada (a seleção aparece sempre cheia).
200
+ - Curvas Bézier (`LinhaCurva`/`PoligonoCurvo`), ícone, texto, cluster e rótulo continuam como objetos/marcadores Leaflet dentro do mesmo renderizador.
201
+ - As feições não aparecem em `grupo.getLayers()` nem em `map._layers`; use `estatisticasFeicoes()`.
202
+
203
+ ## 2.8.2 — 2026-10-23
204
+
205
+ > Publicada a partir de um branch baseado na 2.6.0: não inclui a 2.6.1, a 2.7.0, a 2.8.0 nem a 2.8.1. Use a 2.9.0, que junta as duas linhas.
206
+
207
+ ### Canvas de feições: medidas no elipsoide GRS80
208
+
209
+ - **Distância, comprimento e área** (`feature_metricas`) deixam a esfera de 6371 km: distância pela escala local do elipsoide (M e N) até 0,1° e Vincenty acima disso; área por Σ(λ[i+1]−λ[i−1])·sen φ·M·N/2. Antes o erro chegava a 0,5% na distância e 0,45% na área; agora fica abaixo de 0,01% na área e de 2e-7 na distância curta (linha Flinders–Buninyong confere em milímetros).
210
+ - **Deslocamentos, offsets e alça de borda do círculo** (`feature_algoritmos`, `feature_edicao_ops`) usam a mesma escala (`metrosPorGrau`, exportada), para o raio lido da alça bater com o desenhado.
211
+
212
+ ### `<ui-ribbon>` e `<ui-paleta-ferramentas>`: pendências de UX, acessibilidade e robustez
213
+
214
+ - **Harmonização de bordas, raios e alinhamento vertical no Ribbon**: botões pequenos agora utilizam `--ui-raio-sm` (4px) com `padding: 0 8px` proporcional; colunas de ferramentas recebem `gap: 2px` evitando colisão de cantos arredondados entre itens adjacentes; botões grandes têm altura calibrada em 76px (equivalente a 3 slots de 24px + gaps) com área de itens centrada verticalmente; grupos sem rótulo não geram nó vazio, eliminando o espaçamento inferior fantasma e garantindo margens perfeitamente simétricas.
215
+ - **Tooltip estilizado leve (M2)**: substituição do `title` nativo do browser por tooltip no Shadow DOM exibido em `focus-visible` e `pointerover`, com renderização do atalho formatado (`kbd`). Remoção de `aria-label` duplicado em botões que já possuem rótulo visível.
216
+ - **Colapso automático de grupos no Ribbon (A4)**: suporte a `prioridade` em `RibbonGrupo` e colapso dinâmico via `ResizeObserver` quando o espaço é insuficiente, transformando o grupo em botão único com flyout (estilo Office).
217
+ - **Botão dividido e Lançador de Diálogo (M4)**: suporte a `item.dividido` no ribbon (ação primária + gatilho de menu independente) e botão lançador de diálogo opcional (`grupo.lancadorDialogo`) com emissão do evento `ui-grupo-dialogo`.
218
+ - **Atalhos compostos e escopo multi-paletas (M7/B9)**: suporte a atalhos combinados (`Ctrl+Z`, `Shift+V`, `Esc`, etc.) via `correspondeAtalho`. No ribbon, suporte ao atributo `atalhos` e `keytips` (Alt). Conflito entre múltiplas paletas com atalhos na mesma página resolvido via escopo de foco (`:focus-within`) e ordenação DOM.
219
+ - **Navegação em grade de 2 colunas (M9)**: em paletas verticais com `colunas="2"`, navegação bidimensional intuitiva com `ArrowUp`, `ArrowDown`, `ArrowLeft` e `ArrowRight` respeitando a ordem visual e layout RTL.
220
+ - **Ativação manual de abas (M10)**: atributo opt-in `ativacao-manual` e propriedade `ativacaoManual` no ribbon, onde setas apenas transferem o foco do teclado e Enter/Espaço ativam a aba.
221
+ - **Fechamento de menu no window blur (M8)**: removido fechamento indevido em `window blur` para permitir inspeção no DevTools e troca de janelas; menus fecham apenas por clique fora, `Escape` ou rolagem.
222
+ - **Exclusividade configurável de ferramentas no Ribbon (B8)**: suporte a `tipo: 'ferramenta'` com seleção única por grupo e propriedade `grupo.exclusivo` (padrão `true`, `false` para múltipla ativação).
223
+ - **Submenus aninhados**: suporte a múltiplos níveis de `filhos` dentro de `filhos` na paleta e no ribbon, com abertura/fechamento via `ArrowRight` e `ArrowLeft` (respeitando RTL).
224
+ - **Modo controlado sem mutação de dados (A1)**: propriedade `modoControlado` e atributos `modo-controlado` / `copiar-dados` para clonagem profunda na entrada, garantindo que objetos do consumidor não sofram mutações.
225
+ - **IDs em SVG inline e allowlist (B1)**: prefixagem de IDs em `<defs>` de ícones SVG (`url(#...)` e `href`) no `svg-seguro.ts` para evitar colisões no DOM, e aviso em console para elementos descartados pela allowlist.
226
+ - **SSR e prevenção de layout shift (B9)**: reserva de altura mínima no `:host` do ribbon para evitar Cumulative Layout Shift (CLS) antes da hidratação.
227
+
228
+ ## 2.8.1 — 2026-10-21
229
+
230
+ ### `<ui-canvas-cad>`: popups e câmera só a pedido (como CAD/GIS)
231
+
232
+ - **Popups sob demanda (mudança de comportamento):** clicar numa feição, vértice, linha ou polígono **não abre mais o popup nem move o mapa** (o Leaflet abria no clique e fazia auto-pan). Abrem com **duplo clique** sobre a camada, por `abrirPopupFeicao(camadaId, id)` ou, no código, por `marcador.openPopup()` fora de um clique. Popups abertos a pedido não fazem auto-pan. Novo atributo/propriedade `popups`: `sob-demanda` (padrão), `automatico` (comportamento antigo: abre ao selecionar uma feição, com auto-pan e `definirMargensPopup`) e `desligado` (clique e duplo clique não abrem). A consulta SIGEF segue abrindo ao clicar (é um pedido explícito). No modo sob demanda, terminar a edição de vértices não reabre o popup.
233
+ - **Implementação:** `popup_gate.ts` (`PortaoPopups`, `liberarPopups`, `lerModoPopups`) intercepta `map.addLayer` só para popups e separa clique do mouse de abertura pelo código; `FeicaoPopup` aceita `liberar`; `PonteiroFeicoes.duploClique` e `GerenciadorFeicoes.abrirPopupFeicao`/`aoDuploClique`.
234
+ - **`selectPonto(id)` / `selecionarPonto(id)`:** só seleciona. Antes recentralizava o mapa sempre e abria o popup; agora isso acontece apenas com `zoomLevel` informado (pedido de "ir até o ponto").
235
+ - **Zoom em passos finos:** `zoomSnap: 0.25`, `zoomDelta: 0.5`, `wheelPxPerZoomLevel: 90` (antes 1, 1 e 60: cada giro da roda saltava um nível inteiro). Zoom fracionário é aceito por simplificação, clusters, densidade e pré-carga de tiles.
236
+ - **Botão do meio:** o duplo clique que enquadra (`zoomExtents`) agora exige dois cliques limpos; dois arrastes rápidos em sequência (pan) não enquadram mais.
237
+ - **Toque:** removido o zoom próprio da pinça (passos de 1 nível, em paralelo ao `touchZoom` do Leaflet, que também reagia ao mesmo gesto). Um dedo continua arrastando.
238
+ - **Não mudou:** `zoomParaFeicao(s)`, `ajustarTudo`, `ajustarCamada`, `fitBounds`, clique em cluster, ação "zoom" do popup e o botão "Enquadrar" seguem como pedidos explícitos. `zoomExtents` continua considerando só os vértices (não as feições).
239
+
240
+ ### `<ui-camadas>`: busca, espaço da lista e documentação de eventos
241
+
242
+ - **Busca por nome de camada (correção):** o campo "Buscar camada ou feição" só filtrava feições; buscar `eixos` deixava todas as camadas na tela com "Nenhum item correspondente", inclusive a que casava. Agora a árvore mostra só camadas que casam (pelo nome) ou que levam a um resultado, mantendo os grupos ancestrais; um grupo que casa traz suas subcamadas; sem resultado há uma mensagem única (termo escapado). Nova função pura `filtrarCamadasPorBusca()` em `camadas-selecao.ts`; a seleção em intervalo (`Shift`) segue as mesmas linhas.
243
+ - **Mapa base recolhível:** novo atributo/propriedade `mapas-base-recolhido` / `mapasBaseRecolhido`, e o cabeçalho "Mapa Base" vira botão (`aria-expanded`) que mostra o mapa ativo. Novo evento `ui-mapas-base-recolhimento` (`{ recolhido }`), só para ação do usuário. Padrão inalterado (aberta).
244
+ - **Espaço da lista:** `.ui-panel-box` passa de `min-height: 80px` para `140px`, para a árvore não ficar espremida por rodapé e mapas base em painéis `docked` baixos (o corpo rola abaixo disso).
245
+ - **Documentação:** `ui-camada-grupo-adicionar` é emitido com `{ isGroup: true }` (o README dizia `{ grupoId, nome }`); `ui-camada-adicionar` (`{}` ou `{ parentId }`) passa a constar na tabela. Ambos são pedidos: sem handler no aplicativo, os botões não criam nada.
246
+
247
+ ## 2.8.0 — 2026-10-17
248
+
249
+ ### Controles na mesma linha: altura única e `<ui-linha>`
250
+
251
+ - **Novo `<ui-linha>`** (L3): contêiner que alinha botão, campo, lista, segmented e badge na mesma linha. `tamanho`/`densidade` (`sm|md|lg`), `altura`, `alinhar`, `justificar`, `espaco`, `sem-quebra`, `inline`; filhos ganham largura natural (botão e campo deixam de ocupar 100%), campos e listas ocupam o espaço restante e qualquer filho aceita `expandir`.
252
+ - **`tamanho`, `densidade` e `altura` agora funcionam** em `ui-botao`, `ui-campo-texto`, `ui-lista-flutuante` e `ui-segmented` (o README já os documentava, mas só `altura` tinha efeito). Lógica única em `src/core/altura-controle.ts` (exportada em `core`): entrada hostil é descartada, `altura` é limitada a 15–200px.
253
+ - **`ui-segmented` segue `--ui-campo-altura`** (antes tinha alturas fixas de 28/36/42px, o que o desalinhava do botão e do campo). O host passou a `inline-flex` com `vertical-align: middle`. `tamanho` inválido não vira mais classe CSS arbitrária (cai em `md`).
254
+ - **Escala de tokens** `--ui-altura-sm|md|lg` (26/34/42px). **Mudança de comportamento:** a altura padrão dos controles passou de 15px para `--ui-altura-md` (34px), o valor que o README já descrevia como padrão. Quem quiser o antigo define `--ui-campo-altura: 15px`.
255
+ - **`ui-badge[controle]`** assume a altura dos controles (e o alvo de toque mínimo no mobile).
256
+ - Hosts `inline` de botão, campo e lista ganharam `vertical-align: middle`.
257
+ - Teste de contrato `tests/contrato-linha.test.ts` + regressões; guia em `docs/layout-linha.md`.
258
+
259
+ ## 2.7.0 — 2026-10-17
260
+
261
+ ### `UIBus`: independente do componente de toast
262
+
263
+ - `src/core/ui-bus.ts` (L1) deixou de importar `ui-alerta` (L3). Novo `UIBus.registrarProvedorToast(fn | null)`; o `ui-toast` se registra sozinho ao ser carregado (`registrarToastNoBus()`).
264
+ - **Mudança de comportamento:** quem importar apenas `core/ui-bus` sem carregar `ui-alerta` (ou o pacote principal) passa a ter `UIBus.notificar()` como no-op, sem erro. Exceções do provedor são capturadas (`console.error`).
265
+ - Contratos `UIToastOpcoes`/`UIToastAcao` movidos para `src/core/tipos-toast.ts` e reexportados por `ui-alerta` (nomes públicos inalterados).
266
+
267
+ ### `<ui-ribbon>`, `<ui-paleta-ferramentas>` e `<ui-tabela>`: organização interna
268
+
269
+ - **Sem mudança de comportamento nem de API.** Teclado, renderização, clique longo, atalhos, rolagem de linhas e estados (erro, anúncio do filtro, aria) passam a viver em módulos próprios com testes unitários.
270
+
271
+ ### Componentes simples e de formulário: robustez e acessibilidade
272
+
273
+ - **`<ui-skeleton>`:** `linhas` é limitado a 100 (um valor enorme derrubava a página). Decorativo por padrão (`aria-hidden`); com `aria-label` vira `role="status"`.
274
+ - **Classes por atributo:** `variante`, `cor`, `peso`, `alinhamento` (`ui-texto`), `elevacao` (`ui-stat`, `ui-card`), `variante`/`estilo` (`ui-badge`) só aceitam valores conhecidos; qualquer outro usa o padrão (antes, o texto virava classes CSS arbitrárias).
275
+ - **`<ui-avatar>`:** host com `role="img"` e `aria-label` (nome, e o status quando houver) ou `aria-hidden` sem nome; **a imagem interna agora tem `alt=""`** (antes `alt` = nome ou "Avatar"). `aria-label`/`aria-hidden` do consumidor são respeitados. Tamanho numérico aceito entre 8 e 512 px.
276
+ - **`<ui-stat>`:** grupo rotulado e indicador de variação como imagem nomeada. **`<ui-card clicavel>`:** `role="button"`, `aria-disabled`. **`<ui-badge>`:** `disabled` desativa o ✕; nome acessível "Remover …".
277
+ - **`<ui-radio>`:** o grupo agora acompanha `setAttribute('name')` depois de conectado, mudança de nome e remoção do formulário; o primeiro radio habilitado entra na ordem de Tab.
278
+ - **`<ui-checkbox>`, `<ui-switch>`, `<ui-radio>`:** `focus()` no host foca o controle; checkbox e radio passam a respeitar `<label for>`, `<label>` envolvente e `aria-label` (como o switch).
279
+
280
+ ### `<ui-tooltip>` e `<ui-modal>`: correções de comportamento
281
+
282
+ - **`<ui-tooltip>`:** o clique no gatilho deixa de ser interrompido (`stopPropagation`) e volta a chegar aos ouvintes de ancestrais; `disabled` fecha o balão aberto (e `aberto` + `disabled` não o mostra); ambientes sem `matchMedia` não lançam; o quadro de animação do posicionamento é cancelado ao fechar/desconectar.
283
+ - **`<ui-modal>`:** `Shift+Tab` no botão de fechar agora leva ao último controle do conteúdo (antes o foco escapava do modal); `Tab` no último controle volta ao primeiro. O foco real é lido atravessando Shadow DOMs.
284
+ - Ouvintes globais de ambos passam a ser gerenciados por `ListenerBag`. Sem mudança de API.
285
+
286
+ ### `<ui-camadas>`: arrastos interrompidos e organização interna
287
+
288
+ - Desconectar o painel durante a pintura de controles (olho, cadeado, seleção) deixava `user-select: none` no `body` da página e ouvintes em `window`; o gesto agora é cancelado. O mesmo vale para o arraste por toque de camadas.
289
+ - Dados com ciclo de `parentId` não travam mais a verificação de descendência ao aninhar camadas por arraste.
290
+ - **Sem mudança de API.** Código dividido em módulos de responsabilidade única (renderização, seleção, lembrança, coleções, regras de soltar, renomear, miniaturas e hierarquia).
291
+
292
+ ### `<ui-mapa>`, `<ui-mapa-marcador>` e `<ui-mapa-linha>`: robustez e acessibilidade
293
+
294
+ - **Segurança:** `cor` do `<ui-mapa-marcador>` era interpolada em HTML/estilo do ícone (injeção de atributo). O ícone agora é montado com DOM e `cor` só aceita hex, `rgb()`/`hsl()` ou nome de cor; outro valor usa a cor padrão (`#00f5a0`; `#3388ff` na linha).
295
+ - `lat`, `lng` e `zoom` inválidos (ou fora de -90..90, -180..180, 0..22) caem no padrão em vez de lançar; `espessura` inválida usa 3.
296
+ - Desconectar o `<ui-mapa>` antes de o Leaflet carregar não cria mais um mapa órfão. `<ui-mapa-linha>` reaparece quando o mapa é recriado e ignora pares inválidos em `pontos`.
297
+ - **Acessibilidade:** o mapa é uma região com nome (`aria-label` do host, padrão "Mapa"; o atributo passa a ser observado); marcadores são focáveis por teclado (`role=button`) e usam `titulo` como nome acessível.
298
+
299
+ ### `<ui-canvas-cad>`: organização interna e acessibilidade
300
+
301
+ - **Sem mudança de API.** `GerenciadorFeicoes` virou fachada sobre módulos de responsabilidade única (`cad-feicoes-dados`, `-operacoes`, `-versoes`, `-camera`, `-desenho`, `-edicao`, `-consulta`, `-ponteiro`, `-selecao-regiao`, `-tipos`); tipos do Leaflet em `tipos.ts`, `cad-leaflet-adaptador.ts` e `cad-selecao-pontos.ts` substituem casts `as any`.
302
+ - **Acessibilidade:** o canvas é uma região nomeada ("Mapa CAD"), a barra rápida é uma `toolbar` com botões nomeados, o botão de snap expõe `aria-pressed` e uma região viva anuncia a seleção ("2 feições selecionadas") e a ferramenta ativa.
303
+
304
+ ### Ferramentas de arquitetura
305
+
306
+ - `scripts/check-layers.mjs` verifica o DAG L0→L5 resolvendo imports (estáticos, de efeito colateral, re-exports e dinâmicos); `npm run gate` / `gate:full` agrupam as verificações.
307
+
308
+ ## 2.6.1 — 2026-10-17
309
+
310
+ ### `<ui-controle-cores>` e `<ui-seletor-cor>`: revisão de erros e visual
311
+
312
+ **Correções**
313
+
314
+ - O botão "sem cor" ficava por cima da amostra do traço e roubava os cliques da metade inferior esquerda dela. As ações (inverter, sem cor, restaurar padrão) agora ficam numa coluna ao lado das amostras, cada uma com a própria área de clique, sem sobreposição.
315
+ - `desabilitado` agora desativa todos os botões (antes só bloqueava o mouse: Tab + Enter ainda inverter/limpar/restaurar) e fecha um seletor aberto; remover o atributo reativa o controle.
316
+ - Trocar o alvo pelo controle (`alternarAlvo()`, `definirAlvo()`, clique na outra amostra) agora acompanha a aba do seletor aberto, em vez de deixá-las divergentes.
317
+ - `fecharSeletor(false)` agora cancela de verdade: as cores iniciais voltam (com um `ui-cores-change` não confirmado) em vez de ficar a pré-visualização aplicada sem nenhuma confirmação.
318
+ - `ui-alvo-change` não é mais emitido em duplicidade no documento (o painel do seletor deixou de repassá-lo ao abrir pelo popover; use `aoTrocarAlvo`).
319
+ - Espaço/Enter na amostra ativa com o seletor aberto fecham como o clique, e o Espaço não fecha mais o painel recém-aberto em navegadores que disparam o clique ao soltar a tecla.
320
+
321
+ **Acessibilidade**
322
+
323
+ - `abrirSeletorCor({ focar: true })` leva o foco ao painel (o controle usa ao abrir por teclado); ao fechar, o foco volta para a âncora. O painel ganha `aria-label` e as amostras da paleta `aria-pressed`.
324
+ - O grupo de amostras é anunciado como "Cores de desenho"; a dica/`aria-label` do preenchimento informa a opacidade.
325
+
326
+ **Visual**
327
+
328
+ - Amostras maiores (`--ui-controle-cores-amostra`, padrão 26px), coluna de ações alinhada, ícone "padrão" refeito e anel de foco visível.
329
+ - Ambos os componentes passam a usar os tokens do kit (`--ui-cor-texto`, `--ui-cor-borda`, `--ui-cor-primaria`…) e acompanham o tema claro; antes eram fixos no escuro.
330
+ - Seletor: trilha da opacidade preenchida até o valor, rótulos ORIGINAL/NOVA sempre legíveis, diagonal de "sem cor" igual à do controle, aba de preenchimento "sem cor" com a diagonal vermelha, `prefers-reduced-motion` respeitado e painel reposicionado ao redimensionar a janela.
331
+
332
+ ## 2.6.0 — 2026-10-17
333
+
334
+ ### `<ui-ribbon>` e `<ui-paleta-ferramentas>`: contrato com o consumidor
335
+
336
+ - **Valor inicial pendente**: `aba-ativa` / `valor` definidos antes de `abas` / `ferramentas` são aplicados quando os dados chegam; `aba-ativa` reflete a aba realmente ativa.
337
+ - **`valor = ''` / `limpar()`** removem a seleção da paleta (`ui-change` com `item: null`). Novas propriedades `orientacao`, `colunas`, `tamanho` (paleta) e `compacto` (ribbon); `valor` reflete no atributo usado (`valor` ou `value`).
338
+ - **Eventos**: `ui-ferramenta` agora é cancelável (`preventDefault()` reverte toggle/seleção); `definirAbaVisivel` emite `ui-aba-change` quando a aba ativa muda; novo `ui-recolhido-change`; tipos dos eventos no `HTMLElementEventMap` (`FerramentaDetail`, `AbaChangeDetail`, `PaletaChangeDetail`, `RecolhidoChangeDetail`).
339
+ - **Foco e roving tabindex**: re-renderizações preservam o foco; o tabindex acompanha o botão focado; `atualizarItem` não recria o DOM para mudanças de estado (paleta) nem para itens de outra aba (ribbon).
340
+ - **Menu/flyout**: `menuitemcheckbox`/`menuitemradio` com `aria-checked`, `aria-controls`, foco devolvido ao botão, fecha ao rolar, `ArrowDown` com menu aberto foca o primeiro item.
341
+ - **ARIA**: abas/painel ligados (`aria-controls`/`aria-labelledby`), `aria-label` e `rotulo-abas` configuráveis, `FerramentaItem.rotuloAcessivel`, `aria-keyshortcuts`; o nome acessível não cai mais no `id` quando há `rotulo` ou ícone nomeado.
342
+ - **Paleta**: grupo pula filho desabilitado/separador; clique longo cancelado ao arrastar; `suprimirClique` não prende mais o clique seguinte; atalhos ignoram `role=textbox` e `aria-modal`.
343
+ - `console.warn` para ids duplicados, `ativar()`/`atualizarItem()`/`selecionarAba()` com id inválido.
344
+
345
+ ## 2.5.0
346
+
347
+ ### `<ui-tabela>`: robustez, modo remoto e ordenação
348
+
349
+ - **Render protegido**: exceção em `coluna.render` deixa só a célula vazia (com `console.error`); o scroll virtual não trava mais após um erro de render.
350
+ - **Modo remoto**: `fetchOpcoes` (headers, credentials...), `fetcher` (substitui o `fetch`), `carregarDoEndpoint()`/`recarregar()` resolvem `true`/`false` (antes `void`; nunca rejeitam) e `carregarDoEndpoint(url)` passa a guardar a URL como `src`. `ui-fetch-erro` ganha `origem` (`rede`, `http`, `json`, `render`) e `status`. Novo evento `ui-fetch-cancelado`. Abortar (desconexão ou nova requisição) libera `carregando`; mesma URL em andamento é reaproveitada (sem requisição duplicada ao registrar o elemento depois do HTML).
351
+ - **Ordenação**: `ui-sort` agora é cancelável (`preventDefault()` desfaz); atributo/propriedade `ordenacao-remota` para ordenar no servidor. `TabelaColuna.tipo` (`numero`, `data`, `booleano`, `texto`) e `TabelaColuna.comparador`. `Date`, `dd/mm/aaaa` (com `tipo: 'data'`) e booleanos ordenam pelo valor; `Intl.Collator` reutilizado; **nulos ficam sempre no fim** (antes iam para o início em `desc`).
352
+ - **Filtro**: ignora acentos; datas entram na busca; `TabelaColuna.textoFiltro` torna pesquisável o texto formatado por `render`; filtra antes de ordenar (mais rápido).
353
+ - **Dados do consumidor**: `colunas` é copiado (redimensionar não altera nem falha com objetos congelados/reativos/compartilhados; `tabela.colunas` devolve a cópia interna); entradas `null`/`undefined` em `dados` são descartadas.
354
+ - **Seleção**: `id` nulo não marca várias linhas; a identidade do objeto tem prioridade sobre a chave; novo `ui-selecao-atualizada` quando os dados recarregados trazem outro objeto do mesmo registro.
355
+ - **Cliques**: botões dentro do shadow DOM de custom elements na célula não selecionam a linha (`composedPath`).
356
+ - **Redimensionamento**: clique na alça sem arrastar não emite `ui-column-resize` (antes `"undefined"`); desconectar durante o arraste não bloqueia mais a ordenação; o popover só emite se a largura mudou.
357
+ - Eventos do `<ui-tabela>` documentados no README.
358
+
359
+ ### `<ui-tabela>`: estética, usabilidade e acessibilidade
360
+
361
+ - **Cabeçalho**: sem as larguras fixas de 90px/70px; o vão texto-seta vem de `--ui-tabela-th-gap` (6px) e a seta de `--ui-tabela-th-icone` (16px). Só colunas ordenáveis têm ícone (dois chevrons, o ativo em destaque); `aria-sort="none"` nas ordenáveis inativas. O `sticky` ficou só nos `<th>`.
362
+ - **Tokens**: `--ui-tabela-zebra`, `--ui-tabela-hover`, `--ui-tabela-selecionada(-hover)`, `--ui-tabela-cor-foco`. Zebrado visível nos dois temas; texto do botão "Aplicar" usa `--ui-cor-texto-sobre-primaria`. `tabular-nums` e `--ui-fonte-base`.
363
+ - **Estados**: erro de carregamento visível (`part="erro"`, `role="alert"`, "Tentar novamente"; atributo `texto-erro`); overlay de carregamento com a cor do tema, `role="status"` e `texto-carregando`; vazio mantém o cabeçalho e o filtro sem resultado usa `texto-sem-resultados` (também anunciado a leitores de tela).
364
+ - **Linhas**: `cursor: pointer`, hover só com `(hover: hover)`, foco de teclado (cor do texto) distinto da seleção (primária), texto das células não é selecionado ao clicar (opt-in: `selecionar-texto`).
365
+ - **ARIA**: `role="grid"`, `aria-rowcount/colcount`, `aria-rowindex`, `aria-selected` (mantém `aria-current`), espaçadores `aria-hidden`, contêiner `role="region"` com o `aria-label`.
366
+ - **Redimensionamento**: toque/caneta (Pointer Events), traço visível em repouso, alça focável (`role="separator"`; ←/→ ajustam, Shift = passo maior, Enter abre o diálogo) e clique na alça não ordena. Diálogo de largura com rótulo, campo numérico, validação inline, posição dentro da janela e retorno do foco.
367
+ - `prefers-reduced-motion` (sem transições; spinner lento) e alvos de toque maiores com `pointer: coarse`. Removido o CSS morto do menu de contexto.
368
+
369
+ ## 2.3.0 — 2026-10-14
370
+
371
+ ### Novos: `<ui-controle-cores>` e `<ui-seletor-cor>` (entrada `@thibezer/ui-components-kit/cores`)
372
+
373
+ - **`<ui-controle-cores>`**: par de cores de desenho no estilo Illustrator. Preenchimento na frente, traço atrás; clique na amostra de trás a ativa, clique no alvo ativo (ou duplo clique, Enter, Espaço) abre o seletor. Botões de canto para inverter, "sem cor" no alvo ativo e restaurar o padrão (`padrao-preenchimento`/`padrao-traco`). Estado misto (`definirMisto()`, mostra `?`) e `so-traco` (esmaece o preenchimento).
374
+ - Ações do usuário emitem `ui-cores-change` com `{ preenchimento, traco, opacidade, alvo, confirmado, alterados, origem }` (`origem`: `seletor`, `inverter`, `sem-cor` ou `padrao`): `confirmado: false` é pré-visualização com o seletor aberto; na confirmação, `alterados` lista o que mudou desde a abertura. `definirCores()`, `definirAlvo()` e `definirMisto()` não emitem eventos.
375
+ - O kit não registra atalhos de teclado: a aplicação chama `alternarAlvo()`, `inverter()`, `semCor()` e `restaurarPadrao()` e pode mostrar as teclas nas dicas com `atalhos`.
376
+ - **`<ui-seletor-cor>`** + **`abrirSeletorCor({ ancora, ... })`**: painel flutuante com abas Preenchimento/Traço (modo `duplo`) ou cor única (`simples`), prévia antes × depois, hexadecimal, seletor do sistema, opacidade do preenchimento, cores do projeto, paleta padrão e "sem cor". Um aberto por vez; Concluir, ✕, Enter no hexadecimal, Esc e clique fora confirmam; **Cancelar restaura todas as cores iniciais** (preenchimento, traço e opacidade); `X` alterna o alvo.
377
+ - Utilitários: `normalizarCor()`, `normalizarOpacidade()`, `coresUnicas()`, `PALETA_CORES_PADRAO`.
378
+
379
+ ## 2.2.2 — 2026-10-14
380
+
381
+ ### `<ui-canvas-cad>`: edição de círculo
382
+
383
+ - `editarVertices` passa a aceitar `Circle`: alça no centro (move o círculo sem mudar o raio) e alça na borda (define o raio; mínimo 0,5 m). O HUD mostra raio e área ao vivo. `pontosDe`/`comPontos` e `pontoDaBordaDoCirculo` tratam o círculo.
384
+
385
+ ### `<ui-camadas>`: arraste sobre controles
386
+
387
+ - Clicar e arrastar sobre a coluna de olho, cadeado ou seleção aplica a todos os controles percorridos o estado resultante do primeiro, em vez de reordenar a camada.
388
+
389
+ ## 2.2.1 — 2026-10-14
390
+
391
+ ### `<ui-camadas>`: miniatura da feição fiel à geometria
392
+
393
+ - A miniatura ao lado do nome da feição desenha a geometria real (polígonos com furos, linhas, multi-geometrias), enquadrada com proporção preservada; sem coordenadas, usa o ícone genérico.
394
+ - Miniatura maior (20px; 17px no modo compacto).
395
+
396
+ ## 2.2.0 — 2026-10-14
397
+
398
+ ### `<ui-canvas-cad>`: curvas de Bézier de verdade
399
+
400
+ - A **caneta** deixou de quebrar a curva em trechos retos. Linha e polígono ganham `curva` (lista de nós `{ ponto, entrada?, saida? }`, alças em coordenadas absolutas); o desenho é calculado a cada zoom (`bezierCurveTo` no canvas, comandos `C` no SVG), então a curva continua lisa em qualquer escala.
401
+ - `coordenadas`/`aneis` seguem existindo como vértices de apoio amostrados sobre a curva (24 por segmento): métricas, snap, seleção e exportação continuam funcionando sem mudança.
402
+ - Entrada: `adicionarFeicao`/`definirFeicoes` aceitam `curva` em linha de uma parte e polígono de um anel; nós inválidos são ignorados e a feição vira poligonal.
403
+ - Edição de vértices numa feição curva trabalha com os nós: mover leva as alças junto, inserir divide o segmento sem mudar o traçado (de Casteljau) e excluir remove o nó. A caneta mostra as alças enquanto se arrasta.
404
+ - **Alças editáveis**: em feição curva, o editor mostra uma alça arrastável por alça (com haste até o nó). A oposta espelha; segurar `Shift` solta a simetria. Duplo clique num nó alterna canto ↔ suave; numa linha/polígono comum isso converte em curva.
405
+ - Curvas não são simplificadas por zoom; `deslocarGeometria` move nós e alças.
406
+
407
+ ### `<ui-camadas>`: aprimoramento do rodapé e hierarquia de camadas (subcamadas e grupos)
408
+
409
+ - **Rodapé Redesenhado e Dual-Mode**:
410
+ - Resolução dos conflitos e sobreposição de controles visuais (o contador de camadas não colide mais com o botão de visibilidade nem com o seletor de movimentação).
411
+ - **Modo Resumo (sem seleção)**: Exibe contagem limpa e elegante (`X camadas`), tipografia tabular e botões rápidos `+ Grupo` e `+ Camada`.
412
+ - **Modo Ações em Massa (com seleção ativa)**: Habilita barra contextual com botões de visibilidade, trava, exclusão, preview circular moderno de cor em lote com aro brilhante (substituindo o input retangular nativo) e seletor compacto de destino com recuo hierárquico e truncamento seguro.
413
+ - **Hierarquia de Camadas e Subcamadas**:
414
+ - Suporte completo a camadas dentro de outras camadas através de `parentId?: string | null`, `children?: CamadaItem[]` e flag `isGroup?: boolean`. Suporte tanto a arrays planos quanto a árvores aninhadas (com achatamento inteligente e reversão automática).
415
+ - **Arrastar e Soltar Hierárquico**: Arraste de camadas para dentro de outras camadas (`drop-into`) na área central da linha para transformar em subcamada, com prevenção automática de ciclos (não permite arrastar um pai para dentro de seus próprios filhos).
416
+ - **Drawer e Ferramentas**: Seletor de camada pai e botão `+ Subcamada` integrados no drawer de configurações; botão `Novo Grupo` na toolbar superior.
417
+ - **Proteção e Cascata**: Colapsar uma pasta/camada pai recolhe todas as suas subcamadas e feições; excluir um grupo promove automaticamente suas subcamadas para o nível raiz para evitar perda de dados.
418
+ - Novos eventos públicos disparados por ação do usuário: `ui-camada-grupo-adicionar` e `ui-camada-mover-hierarquia`.
419
+
420
+ ## 2.1.0 — 2026-10-11
421
+
422
+ ### `<ui-canvas-cad>`: feições genéricas (migração do MapEngine)
423
+
424
+ Tudo é opt-in: o componente **não cria camadas** de feições sozinho. Pontos, segmentos, vizinhos e SIGEF continuam como antes.
425
+
426
+ - **Feições**: `definirFeicoes`, `adicionarFeicao`, `atualizarFeicao`, `removerFeicao`, `obterFeicoes`. Point, LineString, Polygon (com furos), Circle (metros) e Text (arrastável), com estilo por feição, ícones SVG, rótulos permanentes (`area`, `extensao`, propriedades) e atualização incremental.
427
+ - **Seleção e popup**: clique, Ctrl/Cmd/Shift, caixa Window (E→D) / Crossing (D→E) com toque geométrico real, popup único (`cad-selecao`, `cad-feicao-selecionada`, `cad-feicao-acao`), hit-test unificado entre camadas empilhadas (tolerância em px, alternância de cliques), `cad-feicao-hover` e `cad-feicao-menu`.
428
+ - **Desempenho**: índice espacial próprio (sem dependências), culling por viewport acima de 300 feições, clusters de pontos (grade de 55 px), simplificação Douglas-Peucker por zoom. Meta: 100 mil feições.
429
+ - **Snap e edição**: snap vértice > ponto médio > aresta (tecla `S`, `Alt` desliga, botão na barra, `definirSnap`); edição de vértices com alças, fantasmas nos pontos médios, exclusão pelo botão direito e HUD ao vivo (`editarVertices`).
430
+ - **Ferramentas**: `ponto`, `linha`, `poligono`, `retangulo`, `elipse`, `poligono-regular` (hexágono), `estrela`, `circulo`, `texto`, `caneta` (Bézier), `medir-distancia`, `medir-area`, `dividir`, `selecao-laco`, `conta-gotas`. Atributo `ferramentas` mostra uma barra opcional; `definirFerramenta(nome, { fechar, iniciarEm })`, `definirCamadaDesenho`, `definirEstiloDesenho`. Clicar no primeiro vértice fecha polígono, laço, medir área e caneta (3+ vértices); o botão direito com ferramenta ativa conclui, desfaz o último vértice ou sai; `selecao-laco` seleciona feições dentro do laço e encerra. Utilitários: `gerarBuffer`, `duplicarFeicao`, `juntarFeicoes`.
431
+ - **Versões e colaboração**: oficial × prévia (`entidadeId`, `definirPreviasGlobais`, `alternarPrevia`, `cad-previas`); cursores remotos (`atualizarCursorRemoto`, nome escapado e cor só `#hex`).
432
+ - **Câmera**: `zoomParaFeicao(s)`, `ajustarTudo`, `ajustarCamada` (ligam camadas e prévias escondidas).
433
+ - Exportados pelo entrypoint `./canvas`: modelo `Feicao`, algoritmos (`dividirPoligonoComLinha`, `gerarBuffer`, `juntarLinhas`, `juntarPoligonos`, `ddParaDms`, `dmsParaDd`, `converterComprimento`, `converterArea`...), índice, snap e editores.
434
+ - Guia de migração: `docs/guia-migracao-mapengine.md`.
435
+
436
+ ### `<ui-canvas-cad>`: opções para integração agnóstica com aplicações externas
437
+
438
+ Tudo opt-in: sem os atributos e opções abaixo, o componente se comporta como antes. Detalhes e exemplos em
439
+ `docs/guia-migracao-mapengine.md`; eventos em `docs/canvas-cad-eventos.md`; números em `docs/desempenho-feicoes.md`.
440
+
441
+ - **Montagem só com API pública**: `inicializarAgora(): L.Map | null` (síncrono, idempotente, cancela o agendamento do `connectedCallback`, nunca cria um 2º mapa); atributos `sem-camadas-padrao` (não cria satélite Google, SIGEF/INCRA, homologados, perímetro, vizinhos, vértices nem grade, e o clique nunca consulta o INCRA), `sem-painel-camadas`, `sem-barra-rapida` e `sem-atalho-snap` (a tecla S não alterna o snap desde o primeiro instante); `definirEstilosExternos(css | CSSStyleSheet[] | null)` injeta CSS do app no shadow root (`adoptedStyleSheets`, com `<style>` de reserva; `destroy()` limpa e a reconexão recoloca). Sem referência a `initCAD`/`initTimeout`.
442
+ - **Posição dos HUDs**: `definirMargensHud({ topo, base, esquerda, direita })` (px) e as propriedades CSS `--cad-hud-topo/-base/-esquerda/-direita`. Os HUDs de desenho e de edição de vértices usam a mesma regra: centralizados na área visível, com largura/altura máximas e quebra de linha. Padrões = posição antiga (`base: 16`).
443
+ - **Linha multiparte (MultiLineString)**: `LineString` aceita `partes: LatLonPar[][]` (ou `coordenadas`). *Decisão:* estender `LineString` em vez de criar um tipo `MultiLineString`, porque quebra menos: `coordenadas` continua existindo (é a 1ª parte) e todo código/consumidor que só lê `coordenadas` segue enxergando uma linha válida; uma parte só normaliza para a linha simples de sempre; nenhum `switch (geometria.tipo)` do consumidor ganha um caso novo. Coberto: normalização, desenho, hit-test (tolerância por parte), caixa Window/Crossing, laço, snap (vértices e arestas de todas as partes, sem aresta entre partes), extensão somada (métricas e rótulo), popup, simplificação por zoom, `obterFeicoes`, `duplicarFeicao`. Recusas com motivo: `editarVertices` → `cad-edicao-recusada` `multiparte`; `juntarFeicoes` → `cad-uniao-falhou` `multiparte`; `dividir` com só multilinhas → `cad-divisao-falhou` `multiparte`; `gerarBuffer` de multilinha devolve `null`. Helpers exportados: `partesDaLinha`, `ehMultilinha`, `criarLinha`.
444
+ - **Lote e ordem**: `aplicarLote(camadaId, { adicionar, atualizar, remover }, opcoes)` faz uma regravação e um `aposDados` e devolve `{ adicionadas, atualizadas, removidas, recusadas }`; `adicionarFeicao`/`atualizarFeicao`/`removerFeicao` passam pelo mesmo caminho. O renderizador recebe só a diferença (índice, loja, oficiais e clusters por célula atualizados item a item) e a busca por id é O(1): com 100 mil feições, uma edição caiu de 60–250 ms para ~0,5 ms (ver `docs/desempenho-feicoes.md`). `atualizarFeicao(camadaId, id, parcial, { substituirPropriedades: true })` (e o mesmo em `aplicarLote`) troca as `propriedades` por inteiro em vez de mesclar.
445
+ - **`Feicao.oculta`**: não desenha, não seleciona (clique, caixa, laço, hit-test, hover, `selecionarFeicoes`), não atrai snap e fica fora de `ajustarTudo`/`ajustarCamada`; segue nos dados, não encerra a edição de vértices em andamento, não conta como excluída e participa da regra oficial × prévia como as demais. `excluida` mantém o significado.
446
+ - **Estilo**: texto com selo (`corFundo`, `corBorda`, `opacidadeFundo`, `raioBorda`; o selo substitui o `halo`) e ponto sem ícone com `corContorno`/`espessuraContorno`, separados do preenchimento. Cores só passam por `corSegura`; números só se finitos.
447
+ - **Ferramentas**: `medir-distancia` com 3+ pontos mostra também área e perímetro no HUD e traz `areaM2`/`perimetroM` em `cad-medicao` (sem criar feição); círculo ignora raio < 2 m; `definirOpcoesPopup({ copiarParaAreaDeTransferencia })` torna opcional a escrita da ação "copiar"; evento cancelável `cad-texto-solicitado` (`preventDefault()` + `detalhe.concluir(texto)`) para usar modal próprio. Teste parametrizado cobre as 15 ferramentas: o ouvinte do evento de conclusão pode desativá-las sem efeito colateral e `cad-selecao` nunca sai com a ferramenta ainda ativa quando a seleção é consequência dela.
448
+ - **Coordenada (0, 0)**: atributo/propriedade `aceitar-origem` / `aceitarOrigem` (padrão `false`) e `{ aceitarOrigem }` nas funções puras de normalização.
449
+
450
+ ### Correções da revisão dos componentes
451
+
452
+ - **`<ui-tabela src>`**: deixou de refazer o `fetch` a cada mudança de atributo (chegava a dezenas de requisições); busca só ao conectar e quando o `src` muda. Filtro e ordenação passam a valer juntos, e o filtro olha as colunas declaradas (ignora campos fora delas e objetos).
453
+ - **`<ui-camadas>`**: foco do teclado preservado após redesenhar; novo `emLote(fn)` agrupa mutações numa só renderização; `definirCamadas` troca a camada ativa que sumiu; `selecionarFeicao` alcança feições de camadas recolhidas; a ordem enviada pela aplicação vence a ordem salva do usuário quando ela mudou; excluir uma camada apaga o override guardado; aviso no console quando painéis dividem a chave de persistência padrão.
454
+ - **`<ui-lista-flutuante>`**: `formReset` volta ao valor inicial mesmo após mover o elemento; `input` antes de `change`; Esc no gatilho, Tab, Home/End e foco devolvido ao gatilho; abre para cima quando falta espaço embaixo.
455
+ - **`<ui-seletor-mapa-base>`**: `desabilitado` agora funciona; `selecionar` ignora ids fora da lista; setas/Home/End e foco no mapa ativo ao abrir pelo teclado; miniaturas sem `referrer`.
456
+ - **`<ui-canvas-cad>`**: atalhos (`S`, Backspace, Esc) não disparam mais enquanto se digita em campos com shadow DOM; ferramentas funcionam após reconectar o componente; dividir/juntar não perdem a feição original quando o resultado é recusado; `juntarFeicoes` respeita bloqueios; `tamanhoIcone` não numérico não injeta HTML; texto bloqueado e depois liberado persiste o arraste.
457
+ - **`<ui-tabela-propriedades>`**: `-2^2` agora vale -4 (o sinal unário pesa menos que a potência).
458
+
459
+ ### ⚠️ Mudanças de comportamento
460
+
461
+ - Ctrl/Cmd/Shift + clique no vazio **não limpa mais** a seleção de pontos (modo aditivo).
462
+ - Clique em marcador DOM não é mais repassado às feições do canvas por baixo.
463
+ - `MapaConfiguracoes.magnetSnap` está **obsoleto** e sem efeito (o snap agora é do componente).
464
+ - `adicionarFeicao`/`aplicarLote` com um `id` que já existe **substituem a feição no mesmo lugar**; antes ela ia para o fim da lista (mudava a ordem de empilhamento dentro da camada).
465
+ - **`cad-medicao`** de `medir-distancia` passa a trazer `areaM2` e `perimetroM` quando a régua tem 3 ou mais pontos (antes só `medir-area` trazia). Quem usava `areaM2 !== undefined` para distinguir o tipo deve olhar `tipo`.
466
+ - **Círculo**: o clique que daria raio menor que 2 m é ignorado (antes aceitava qualquer raio > 0). Feições com `Circle` de raio menor continuam válidas na entrada de dados.
467
+ - **`dividir`**: se a feição cortada estava selecionada, a ferramenta encerra antes de a seleção mudar (para `cad-selecao` sair sem ferramenta ativa). Sem seleção envolvida, continua ativa para o próximo corte.
468
+ - **Ordem do aviso nas medições**: `cad-medicao` agora sai depois de a ferramenta estar pronta para a próxima figura; desativar a ferramenta dentro desse evento deixava a sessão "ressuscitar" (corrigido).
469
+ - **HUDs**: ganham `width: max-content`, largura/altura máximas e quebra de linha (o de edição de vértices não quebrava linha). Na posição padrão ficam onde estavam.
470
+ - Valores novos nos `motivo` de `cad-edicao-recusada`, `cad-uniao-falhou` e `cad-divisao-falhou`: `multiparte`. Quem faz `switch` exaustivo precisa tratá-lo.
471
+ - `selecionarFeicoes` e `alternarSelecaoFeicao` ignoram feições `oculta`.
472
+ - `GerenciadorFeicoes.emitir` (uso interno/testes) ganhou um 3º parâmetro opcional `cancelavel` e pode devolver `true` para "cancelado"; o `normalizarFeicaoEntrada`, `normalizarCoordenada` e `parseCoordenada` ganharam um último parâmetro opcional `{ aceitarOrigem }` (compatível).
473
+
474
+ ## 2.0.0
475
+
476
+ ### ⚠️ Mudanças que podem exigir ajuste
477
+
478
+ - **Tema segue o sistema operacional por padrão.** Antes o kit era sempre escuro. Para manter o escuro fixo, use `<html data-tema="escuro">`. `UIBus.definirTema('auto')` volta a seguir o sistema.
479
+ - **Enter em `<ui-campo-texto>` envia o formulário**, como um campo nativo. Formulários com botão `type="submit"` e sem listener de `submit` com `preventDefault()` passam a enviar (e recarregar a página) ao pressionar Enter.
480
+ - **`ui-abrir` / `ui-fechar` do modal disparam também ao mudar o atributo `aberto`**, não só pelos métodos `abrir()`/`fechar()`.
481
+ - **A seleção de `<ui-tabela>` é mantida ao ordenar, filtrar ou trocar `dados`** (se o item ainda existir). Use `limparSelecao()` para zerar.
482
+ - **Variáveis CSS renomeadas:** `--ui-fonte-mono`, `--ui-fonte-familia` e `--ui-fonte-principal` → `--ui-fonte-codigo` / `--ui-fonte-base`; `--ui-radius-*` → `--ui-raio-*`.
483
+ - **Cores do tema claro mais escuras** para atingir contraste WCAG AA (primária `#006b40`). No escuro, o botão destrutivo passou para `#d32f2f`.
484
+ - Removidos `UIModal._openCount` e o atributo interno `data-scroll-locked`.
485
+
486
+ ### Usuário final
487
+
488
+ - Textos legíveis nos dois temas (contraste WCAG AA verificado em testes).
489
+ - Tabela por teclado: cabeçalhos ordenáveis com Tab + Enter, linhas com setas, Home/End, PageUp/PageDown, Enter para selecionar.
490
+ - Modal: foco inicial no conteúdo (ou `[autofocus]`), foco devolvido ao fechar, Esc fecha o modal do topo, segundo modal aberto continua interativo, "✕" oculto com `bloquear-fechamento`.
491
+ - Tooltip lido por leitores de tela, fecha com Esc e não some ao levar o ponteiro até o balão.
492
+ - Botões só com ícone recebem nome acessível (`aria-label` ou `title`).
493
+ - Gatilhos Zero-JS ignoram botões desabilitados ou carregando.
494
+ - Correção de segurança: o `label` de campos nunca é interpretado como HTML.
495
+
496
+ ### Desenvolvedor
497
+
498
+ - **Tipos para React e Vue** gerados a partir do Custom Elements Manifest: `@thibezer/ui-components-kit/tipos-react`, `/tipos-vue` e `/tipos-elementos`.
499
+ - **`custom-elements.json`** publicado (campo `customElements`), para IDEs, Storybook e documentação.
500
+ - **`::part`** nos 14 componentes principais, com estados (`opcao-selecionada`, `linha-selecionada`).
501
+ - **Tokens unificados:** fontes `--ui-fonte-base` / `--ui-fonte-codigo`; escala de raio derivada de `--ui-raio-borda` (`--ui-raio-sm/lg/xl/pill`); novos `--ui-cor-texto-info`, `--ui-cor-texto-sobre-status`, `--ui-cor-fundo-recuado`, `--ui-sombra-sm/md/lg`.
502
+ - `<ui-botao>` form-associated: `form="id"`, `<fieldset disabled>`, `name`/`value` no `e.submitter`, `disabled` como propriedade, `click()` no host.
503
+ - `UIBus.obterTema()`; `tema:alterado` também quando o sistema troca de tema.
504
+ - `<ui-tabela>`: `aria-label` repassado à tabela, `aria-busy` durante o carregamento.
505
+ - README corrigido (exemplo React, atributos do catálogo, import map para mapa/CAD via CDN).
506
+
507
+ ## 1.0.0
508
+
509
+ - Primeira publicação no npm como `@thibezer/ui-components-kit`.