@praxisui/settings-panel 3.0.0-beta.0 → 3.0.0-beta.10

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.
@@ -3,21 +3,21 @@ import { inject, DestroyRef, ViewContainerRef, HostListener, ViewChild, ChangeDe
3
3
  import { ComponentPortal } from '@angular/cdk/portal';
4
4
  import * as i3 from '@angular/common';
5
5
  import { CommonModule } from '@angular/common';
6
+ import { CdkTrapFocus } from '@angular/cdk/a11y';
6
7
  import * as i3$1 from '@angular/material/button';
7
8
  import { MatButtonModule } from '@angular/material/button';
9
+ import * as i1 from '@angular/material/dialog';
10
+ import { MatDialogModule } from '@angular/material/dialog';
8
11
  import * as i4 from '@angular/material/icon';
9
12
  import { MatIconModule } from '@angular/material/icon';
10
- import { PraxisIconDirective, GlobalConfigService, FieldControlType, IconPickerService, LoggerService } from '@praxisui/core';
11
- import * as i5 from '@angular/material/tooltip';
12
- import { MatTooltipModule } from '@angular/material/tooltip';
13
- import { CdkTrapFocus } from '@angular/cdk/a11y';
14
13
  import * as i6 from '@angular/material/progress-spinner';
15
14
  import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
16
- import * as i1 from '@angular/material/dialog';
17
- import { MatDialogModule } from '@angular/material/dialog';
15
+ import * as i5 from '@angular/material/tooltip';
16
+ import { MatTooltipModule } from '@angular/material/tooltip';
18
17
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
19
- import { isObservable, firstValueFrom, of, Subject, BehaviorSubject } from 'rxjs';
20
18
  import { filter, switchMap, debounceTime, distinctUntilChanged } from 'rxjs/operators';
19
+ import { isObservable, firstValueFrom, of, Subject, BehaviorSubject } from 'rxjs';
20
+ import { providePraxisI18n, PraxisI18nService, PraxisIconDirective, SETTINGS_PANEL_DATA as SETTINGS_PANEL_DATA$1, SETTINGS_PANEL_BRIDGE, GlobalConfigService, FieldControlType, IconPickerService, LoggerService } from '@praxisui/core';
21
21
  import { ConfirmDialogComponent } from '@praxisui/dynamic-fields';
22
22
  import * as i1$1 from '@angular/cdk/overlay';
23
23
  import * as i2 from '@angular/material/snack-bar';
@@ -28,6 +28,457 @@ import * as i9 from '@angular/material/chips';
28
28
  import { MatChipsModule } from '@angular/material/chips';
29
29
  import { AiBackendApiService } from '@praxisui/ai';
30
30
 
31
+ const PRAXIS_SETTINGS_PANEL_GLOBAL_CONFIG_EDITOR_PT_BR = {
32
+ CRUD: 'CRUD',
33
+ 'Dynamic Fields': 'Campos Dinâmicos',
34
+ 'Cache & Persistence': 'Cache & Persistência',
35
+ Table: 'Tabela',
36
+ Dialog: 'Dialog',
37
+ 'Settings saved': 'Configurações salvas',
38
+ 'Save failed.': 'Falha ao salvar configurações.',
39
+ 'Global settings': 'Configurações globais',
40
+ 'Global policies for opening, back and header': 'Políticas globais de abertura, back e header',
41
+ 'Server defaults (env vars)': 'Padrões do servidor (env vars)',
42
+ 'Saved configuration (UI) - overrides server defaults': 'Configuração salva (UI) - sobrescreve os defaults do servidor',
43
+ 'Failed to test connection.': 'Falha ao testar conexão.',
44
+ 'Connection established successfully!': 'Conexão estabelecida com sucesso!',
45
+ 'Unknown error': 'Erro desconhecido',
46
+ 'Key provided': 'Chave informada',
47
+ 'Saved key (****{{last4}})': 'Chave salva (****{{last4}})',
48
+ 'Saved key': 'Chave salva',
49
+ 'Test connection with the provided key': 'Testar conexão com a chave informada',
50
+ 'Test connection': 'Testar conexão',
51
+ 'Refresh model list': 'Atualizar lista de modelos',
52
+ Close: 'Fechar',
53
+ 'This provider has no model catalog.': 'Este provedor não oferece catálogo de modelos.',
54
+ 'Enter an API key to fetch models.': 'Insira uma chave de API para buscar modelos.',
55
+ 'Error fetching models.': 'Erro ao buscar modelos.',
56
+ 'Error fetching models. Check your API key.': 'Erro ao buscar modelos. Verifique a chave de API.',
57
+ 'Model list updated ({{count}} models)': 'Lista de modelos atualizada ({{count}} modelos)',
58
+ 'Enter an API key to test.': 'Insira uma chave de API para testar.',
59
+ 'No model details available.': 'Detalhes do modelo não disponíveis.',
60
+ 'Model: {{model}} | Tokens (in/out): {{in}}/{{out}} | Methods: {{methods}}': 'Modelo: {{model}} | Tokens (in/out): {{in}}/{{out}} | Métodos: {{methods}}',
61
+ 'Global config cleared. Using server defaults.': 'Configuração global limpa. Usando defaults do servidor.',
62
+ 'Clear saved config and return to server defaults': 'Apaga o config salvo e volta aos defaults do servidor',
63
+ 'Failed to clear global config.': 'Falha ao limpar a configuração global.',
64
+ 'No key required': 'Sem chave necessária',
65
+ 'Model & Behavior': 'Modelo & Comportamento',
66
+ 'Choose the model after validating the key.': 'Escolha o modelo após validar a chave.',
67
+ 'API key validated required': 'Requer chave de API validada',
68
+ 'Configure and test your API key to unlock model selection.': 'Configure e teste sua chave de API para desbloquear a seleção de modelos.',
69
+ 'Embeddings (RAG)': 'Embeddings (RAG)',
70
+ 'Use the LLM provider and key for embeddings': 'Aplicar provedor e chave do LLM aos embeddings',
71
+ 'Provider has no embeddings': 'Provedor sem embeddings',
72
+ 'Configure the embeddings provider for vector search (templates and schemas).': 'Configure o provedor de embeddings para buscas vetoriais (templates e schemas).',
73
+ 'Embedding dimension mismatch with database (768). Adjust to 768 or redo the migration.': 'Dimensão de embeddings diferente do banco (768). Ajuste para 768 ou refaça a migração.',
74
+ 'Toolbar, appearance and advanced filter': 'Toolbar, aparência e filtro avançado',
75
+ 'Defaults and variants (danger, info, success, question, error)': 'Defaults e variants (danger, info, success, question, error)',
76
+ 'Icons by profile (shortcuts)': 'Ícones por perfil (atalhos)',
77
+ 'Pick icon': 'Escolher ícone',
78
+ 'Pick icon for {{key}}': 'Escolher ícone para {{key}}',
79
+ 'Clear icon': 'Limpar ícone',
80
+ 'Clear icon for {{key}}': 'Limpar ícone de {{key}}',
81
+ 'The text field above remains editable. These buttons only help with selection/clear preview.': 'O campo de texto acima permanece editável. Estes botões apenas ajudam na seleção/limpeza com preview.',
82
+ 'Fast multimodal models from Google.': 'Modelos rápidos e multimodais do Google.',
83
+ 'Grok models focused on reasoning.': 'Modelos Grok focados em raciocínio.',
84
+ 'Local mode for tests without a key.': 'Modo local para testes sem chave.',
85
+ 'No saved key': 'Sem chave salva',
86
+ 'Aparência: densidade': 'Aparência: densidade',
87
+ compact: 'compact',
88
+ comfortable: 'comfortable',
89
+ spacious: 'spacious',
90
+ 'Aparência: padding das células': 'Aparência: padding das células',
91
+ 'Aceita 1 a 4 medidas CSS com unidade, var(...) ou calc(...). Ex.: 6px 12px': 'Aceita 1 a 4 medidas CSS com unidade, var(...) ou calc(...). Ex.: 6px 12px',
92
+ 'Use 1 a 4 medidas CSS válidas, como 6px 12px.': 'Use 1 a 4 medidas CSS válidas, como 6px 12px.',
93
+ 'Aparência: padding do cabeçalho': 'Aparência: padding do cabeçalho',
94
+ 'Aceita 1 a 4 medidas CSS com unidade, var(...) ou calc(...). Ex.: 8px 12px': 'Aceita 1 a 4 medidas CSS com unidade, var(...) ou calc(...). Ex.: 8px 12px',
95
+ 'Use 1 a 4 medidas CSS válidas, como 8px 12px.': 'Use 1 a 4 medidas CSS válidas, como 8px 12px.',
96
+ 'Aparência: fonte das células': 'Aparência: fonte das células',
97
+ 'Aceita uma medida CSS com unidade, var(...) ou calc(...). Ex.: 13px': 'Aceita uma medida CSS com unidade, var(...) ou calc(...). Ex.: 13px',
98
+ 'Use uma medida CSS válida, como 13px.': 'Use uma medida CSS válida, como 13px.',
99
+ 'Aparência: fonte do cabeçalho': 'Aparência: fonte do cabeçalho',
100
+ 'Filtro: modo de abertura': 'Filtro: modo de abertura',
101
+ overlay: 'overlay',
102
+ 'Filtro: overlay (visual)': 'Filtro: overlay (visual)',
103
+ card: 'card',
104
+ frosted: 'frosted',
105
+ 'Filtro: backdrop no overlay': 'Filtro: backdrop no overlay',
106
+ 'Excluir — confirmação': 'Excluir — confirmação',
107
+ 'Excluir (múltiplos) — confirmação': 'Excluir (múltiplos) — confirmação',
108
+ 'Salvar — confirmação': 'Salvar — confirmação',
109
+ 'Cancelar — confirmação': 'Cancelar — confirmação',
110
+ 'Exportar — confirmação': 'Exportar — confirmação',
111
+ 'Excluir — progresso': 'Excluir — progresso',
112
+ 'Excluir (múltiplos) — progresso': 'Excluir (múltiplos) — progresso',
113
+ 'Excluir — sucesso': 'Excluir — sucesso',
114
+ 'Salvar — sucesso': 'Salvar — sucesso',
115
+ 'Exportar — sucesso': 'Exportar — sucesso',
116
+ 'Importar — sucesso': 'Importar — sucesso',
117
+ 'Excluir — erro': 'Excluir — erro',
118
+ 'Salvar — erro': 'Salvar — erro',
119
+ 'Exportar — erro': 'Exportar — erro',
120
+ 'Rede — erro': 'Rede — erro',
121
+ 'Permissão — erro': 'Permissão — erro',
122
+ 'Modo de abertura padrão': 'Modo de abertura padrão',
123
+ Rota: 'Rota',
124
+ 'Aplicado quando ação/metadado não definem modo.': 'Aplicado quando ação/metadado não definem modo.',
125
+ 'Confirmar ao sair com alterações': 'Confirmar ao sair com alterações',
126
+ 'Back: estratégia': 'Back: estratégia',
127
+ 'Modal: largura (ex.: 900px)': 'Modal: largura (ex.: 900px)',
128
+ 'Modal: largura máxima (ex.: 96vw)': 'Modal: largura máxima (ex.: 96vw)',
129
+ 'Modal: backdrop': 'Modal: backdrop',
130
+ blur: 'blur',
131
+ dim: 'dim',
132
+ transparent: 'transparent',
133
+ 'Estratégia de primeira carga quando metadado não definir.': 'Estratégia de primeira carga quando metadado não definir.',
134
+ 'Async Select, cascade and pagination': 'Async Select, cascata e paginação',
135
+ 'Cascata nativa habilitada': 'Cascata nativa habilitada',
136
+ 'Cascata: loadOnChange': 'Cascata: loadOnChange',
137
+ respectLoadOn: 'respectLoadOn',
138
+ immediate: 'immediate',
139
+ manual: 'manual',
140
+ 'Cascata: debounce (ms)': 'Cascata: debounce (ms)',
141
+ 'Async Select: loadOn': 'Async Select: loadOn',
142
+ open: 'open',
143
+ init: 'init',
144
+ none: 'none',
145
+ 'Async Select: pageSize': 'Async Select: pageSize',
146
+ 'Async Select: usar cursor': 'Async Select: usar cursor',
147
+ 'LLM provider': 'Provedor LLM',
148
+ 'Select the provider to list models and test the key.': 'Selecione o provedor para listar modelos e testar a chave.',
149
+ 'API key': 'Chave de API',
150
+ 'Enter the selected provider key to test.': 'Informe a chave do provedor selecionado para testar.',
151
+ 'AI model': 'Modelo de IA',
152
+ 'Temperature (Creativity)': 'Temperatura (Criatividade)',
153
+ '0.0 (deterministic) to 1.0 (creative)': '0.0 (determinístico) a 1.0 (criativo)',
154
+ 'Assistant: risk validation': 'Assistente: validação de risco',
155
+ 'Standard': 'Padrão',
156
+ 'Strict requires confirmation for medium/high risk. Standard follows the indication returned by the backend.': 'Estrito exige confirmação em risco médio/alto. Padrão segue a indicação retornada pelo backend.',
157
+ 'Use same LLM for embeddings': 'Embeddings: usar mesmo LLM',
158
+ 'Replicates the LLM provider and key for embeddings.': 'Replica provedor e chave do LLM para embeddings.',
159
+ 'Embeddings provider': 'Embeddings: provedor',
160
+ 'Provider used to generate embeddings (RAG).': 'Provedor usado para gerar embeddings (RAG).',
161
+ 'Embeddings API key': 'Embeddings: chave de API',
162
+ 'Embeddings provider key.': 'Chave do provedor de embeddings.',
163
+ 'Embeddings model': 'Embeddings: modelo',
164
+ 'Provider default': 'Padrão do provedor',
165
+ 'OpenAI models for embeddings.': 'Modelos OpenAI para embeddings.',
166
+ 'Embeddings: dimensions': 'Embeddings: dimensões',
167
+ 'Embedding vector dimensions (default 768).': 'Dimensões do vetor de embedding (default 768).',
168
+ 'Artificial Intelligence': 'Inteligência Artificial',
169
+ 'LLM integration': 'Integração com LLM',
170
+ 'Appearance: density': 'Aparência: densidade',
171
+ 'Appearance: cell padding': 'Aparência: padding das células',
172
+ 'Accepts 1 to 4 CSS measures with units, var(...) or calc(...). Example: 6px 12px': 'Aceita 1 a 4 medidas CSS com unidade, var(...) ou calc(...). Ex.: 6px 12px',
173
+ 'Use 1 to 4 valid CSS measures, such as 6px 12px.': 'Use 1 a 4 medidas CSS válidas, como 6px 12px.',
174
+ 'Appearance: header padding': 'Aparência: padding do cabeçalho',
175
+ 'Accepts 1 to 4 CSS measures with units, var(...) or calc(...). Example: 8px 12px': 'Aceita 1 a 4 medidas CSS com unidade, var(...) ou calc(...). Ex.: 8px 12px',
176
+ 'Use 1 to 4 valid CSS measures, such as 8px 12px.': 'Use 1 a 4 medidas CSS válidas, como 8px 12px.',
177
+ 'Appearance: cell font size': 'Aparência: fonte das células',
178
+ 'Accepts a CSS measure with units, var(...) or calc(...). Example: 13px': 'Aceita uma medida CSS com unidade, var(...) ou calc(...). Ex.: 13px',
179
+ 'Use a valid CSS measure, such as 13px.': 'Use uma medida CSS válida, como 13px.',
180
+ 'Appearance: header font size': 'Aparência: fonte do cabeçalho',
181
+ 'Provedor LLM': 'Provedor LLM',
182
+ 'Selecione o provedor para listar modelos e testar a chave.': 'Selecione o provedor para listar modelos e testar a chave.',
183
+ 'Chave de API': 'Chave de API',
184
+ 'Informe a chave do provedor selecionado para testar.': 'Informe a chave do provedor selecionado para testar.',
185
+ 'Modelo de IA': 'Modelo de IA',
186
+ 'Temperatura (Criatividade)': 'Temperatura (Criatividade)',
187
+ '0.0 (determinístico) a 1.0 (criativo)': '0.0 (determinístico) a 1.0 (criativo)',
188
+ 'Max tokens': 'Max tokens',
189
+ 'Limite maximo de tokens na resposta.': 'Limite maximo de tokens na resposta.',
190
+ 'Assistente: validação de risco': 'Assistente: validação de risco',
191
+ 'Estrito (recomendado)': 'Estrito (recomendado)',
192
+ 'Padrão': 'Padrão',
193
+ 'Estrito exige confirmação em risco médio/alto. Padrão segue a indicação retornada pelo backend.': 'Estrito exige confirmação em risco médio/alto. Padrão segue a indicação retornada pelo backend.',
194
+ 'Embeddings: usar mesmo LLM': 'Embeddings: usar mesmo LLM',
195
+ 'Replica provedor e chave do LLM para embeddings.': 'Replica provedor e chave do LLM para embeddings.',
196
+ 'Embeddings: provedor': 'Embeddings: provedor',
197
+ 'Provedor usado para gerar embeddings (RAG).': 'Provedor usado para gerar embeddings (RAG).',
198
+ 'Embeddings: chave de API': 'Embeddings: chave de API',
199
+ 'Chave do provedor de embeddings.': 'Chave do provedor de embeddings.',
200
+ 'Embeddings: modelo': 'Embeddings: modelo',
201
+ 'Padrão do provedor': 'Padrão do provedor',
202
+ 'Modelos OpenAI para embeddings.': 'Modelos OpenAI para embeddings.',
203
+ 'Embeddings: dimensoes': 'Embeddings: dimensões',
204
+ 'Dimensoes do vetor de embedding (default 768).': 'Dimensões do vetor de embedding (default 768).',
205
+ 'GPT models for text and chat.': 'Modelos GPT para texto e chat.',
206
+ 'Synchronized with the LLM. The fields below follow the primary credential.': 'Sincronizado com o LLM. Os campos abaixo acompanham a credencial principal.',
207
+ 'Example JSON: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)"}': 'Ex.: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)"}',
208
+ 'Example JSON array: [{"id":"cancel","text":"Cancel","role":"secondary","close":true,"cssClass":"btn"},{"id":"confirm","text":"Delete","role":"primary","close":true,"cssClass":"btn btn-danger","icon":"delete"}]': 'Ex.: [{"id":"cancel","text":"Cancelar","role":"secondary","close":true,"cssClass":"btn"},{"id":"confirm","text":"Excluir","role":"primary","close":true,"cssClass":"btn btn-danger","icon":"delete"}]',
209
+ 'Example JSON: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)","actionButtonRadius":"8px"}': 'Ex.: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)","actionButtonRadius":"8px"}',
210
+ 'Desabilitar cache de schema (LocalStorage)': 'Desabilitar cache de schema (LocalStorage)',
211
+ 'Se ativo, sempre baixa o schema do servidor (ignora cache local). Útil para desenvolvimento.': 'Se ativo, sempre baixa o schema do servidor (ignora cache local). Útil para desenvolvimento.',
212
+ 'Paginação habilitada': 'Paginação habilitada',
213
+ 'Tamanho da página': 'Tamanho da página',
214
+ 'Ordenação habilitada': 'Ordenação habilitada',
215
+ 'Seleção habilitada': 'Seleção habilitada',
216
+ 'Paginação: estratégia': 'Paginação: estratégia',
217
+ 'Ordenação: estratégia': 'Ordenação: estratégia',
218
+ 'Paginação: botões Primeiro/Último': 'Paginação: botões Primeiro/Último',
219
+ 'Toolbar visível': 'Toolbar visível',
220
+ 'Cache & Persistência': 'Cache & Persistence',
221
+ Tabela: 'Table',
222
+ Salvar: 'Save',
223
+ Cancelar: 'Cancel',
224
+ Redefinir: 'Reset',
225
+ };
226
+ const PRAXIS_SETTINGS_PANEL_GLOBAL_CONFIG_EDITOR_EN_US = {
227
+ CRUD: 'CRUD',
228
+ 'Dynamic Fields': 'Dynamic Fields',
229
+ 'Cache & Persistence': 'Cache & Persistence',
230
+ Table: 'Table',
231
+ Dialog: 'Dialog',
232
+ 'Settings saved': 'Settings saved',
233
+ 'Save failed.': 'Save failed.',
234
+ 'Global settings': 'Global settings',
235
+ 'Global policies for opening, back and header': 'Global policies for opening, back and header',
236
+ 'Server defaults (env vars)': 'Server defaults (env vars)',
237
+ 'Saved configuration (UI) - overrides server defaults': 'Saved configuration (UI) - overrides server defaults',
238
+ 'Failed to test connection.': 'Failed to test connection.',
239
+ 'Connection established successfully!': 'Connection established successfully!',
240
+ 'Unknown error': 'Unknown error',
241
+ 'Key provided': 'Key provided',
242
+ 'Saved key (****{{last4}})': 'Saved key (****{{last4}})',
243
+ 'Saved key': 'Saved key',
244
+ 'Test connection with the provided key': 'Test connection with the provided key',
245
+ 'Test connection': 'Test connection',
246
+ 'Refresh model list': 'Refresh model list',
247
+ Close: 'Close',
248
+ 'This provider has no model catalog.': 'This provider has no model catalog.',
249
+ 'Enter an API key to fetch models.': 'Enter an API key to fetch models.',
250
+ 'Error fetching models.': 'Error fetching models.',
251
+ 'Error fetching models. Check your API key.': 'Error fetching models. Check your API key.',
252
+ 'Model list updated ({{count}} models)': 'Model list updated ({{count}} models)',
253
+ 'Enter an API key to test.': 'Enter an API key to test.',
254
+ 'No model details available.': 'No model details available.',
255
+ 'Model: {{model}} | Tokens (in/out): {{in}}/{{out}} | Methods: {{methods}}': 'Model: {{model}} | Tokens (in/out): {{in}}/{{out}} | Methods: {{methods}}',
256
+ 'Global config cleared. Using server defaults.': 'Global config cleared. Using server defaults.',
257
+ 'Clear saved config and return to server defaults': 'Clear saved config and return to server defaults',
258
+ 'Failed to clear global config.': 'Failed to clear global config.',
259
+ 'No key required': 'No key required',
260
+ 'Model & Behavior': 'Model & Behavior',
261
+ 'Choose the model after validating the key.': 'Choose the model after validating the key.',
262
+ 'API key validated required': 'Validated API key required',
263
+ 'Configure and test your API key to unlock model selection.': 'Configure and test your API key to unlock model selection.',
264
+ 'Embeddings (RAG)': 'Embeddings (RAG)',
265
+ 'Use the LLM provider and key for embeddings': 'Apply the LLM provider and key to embeddings',
266
+ 'Provider has no embeddings': 'Provider has no embeddings',
267
+ 'Configure the embeddings provider for vector search (templates and schemas).': 'Configure the embeddings provider for vector search (templates and schemas).',
268
+ 'Embedding dimension mismatch with database (768). Adjust to 768 or redo the migration.': 'Embedding dimension mismatch with the database (768). Adjust to 768 or redo the migration.',
269
+ 'Toolbar, appearance and advanced filter': 'Toolbar, appearance and advanced filter',
270
+ 'Defaults and variants (danger, info, success, question, error)': 'Defaults and variants (danger, info, success, question, error)',
271
+ 'Icons by profile (shortcuts)': 'Icons by profile (shortcuts)',
272
+ 'Pick icon': 'Pick icon',
273
+ 'Pick icon for {{key}}': 'Pick icon for {{key}}',
274
+ 'Clear icon': 'Clear icon',
275
+ 'Clear icon for {{key}}': 'Clear icon for {{key}}',
276
+ 'The text field above remains editable. These buttons only help with selection/clear preview.': 'The text field above remains editable. These buttons only help with selection/clear preview.',
277
+ 'No saved key': 'No saved key',
278
+ 'Aparência: densidade': 'Appearance: density',
279
+ compact: 'compact',
280
+ comfortable: 'comfortable',
281
+ spacious: 'spacious',
282
+ 'Aparência: padding das células': 'Appearance: cell padding',
283
+ 'Aceita 1 a 4 medidas CSS com unidade, var(...) ou calc(...). Ex.: 6px 12px': 'Accepts 1 to 4 CSS measures with units, var(...) or calc(...). Example: 6px 12px',
284
+ 'Use 1 a 4 medidas CSS válidas, como 6px 12px.': 'Use 1 to 4 valid CSS measures, such as 6px 12px.',
285
+ 'Aparência: padding do cabeçalho': 'Appearance: header padding',
286
+ 'Aceita 1 a 4 medidas CSS com unidade, var(...) ou calc(...). Ex.: 8px 12px': 'Accepts 1 to 4 CSS measures with units, var(...) or calc(...). Example: 8px 12px',
287
+ 'Use 1 a 4 medidas CSS válidas, como 8px 12px.': 'Use 1 to 4 valid CSS measures, such as 8px 12px.',
288
+ 'Aparência: fonte das células': 'Appearance: cell font size',
289
+ 'Aceita uma medida CSS com unidade, var(...) ou calc(...). Ex.: 13px': 'Accepts a CSS measure with units, var(...) or calc(...). Example: 13px',
290
+ 'Use uma medida CSS válida, como 13px.': 'Use a valid CSS measure, such as 13px.',
291
+ 'Aparência: fonte do cabeçalho': 'Appearance: header font size',
292
+ 'Filtro: modo de abertura': 'Filter: open mode',
293
+ overlay: 'overlay',
294
+ 'Filtro: overlay (visual)': 'Filter: overlay (visual)',
295
+ card: 'card',
296
+ frosted: 'frosted',
297
+ 'Filtro: backdrop no overlay': 'Filter: overlay backdrop',
298
+ 'Excluir — confirmação': 'Delete - confirmation',
299
+ 'Excluir (múltiplos) — confirmação': 'Delete (multiple) - confirmation',
300
+ 'Salvar — confirmação': 'Save - confirmation',
301
+ 'Cancelar — confirmação': 'Cancel - confirmation',
302
+ 'Exportar — confirmação': 'Export - confirmation',
303
+ 'Excluir — progresso': 'Delete - progress',
304
+ 'Excluir (múltiplos) — progresso': 'Delete (multiple) - progress',
305
+ 'Excluir — sucesso': 'Delete - success',
306
+ 'Salvar — sucesso': 'Save - success',
307
+ 'Exportar — sucesso': 'Export - success',
308
+ 'Importar — sucesso': 'Import - success',
309
+ 'Excluir — erro': 'Delete - error',
310
+ 'Salvar — erro': 'Save - error',
311
+ 'Exportar — erro': 'Export - error',
312
+ 'Rede — erro': 'Network - error',
313
+ 'Permissão — erro': 'Permission - error',
314
+ 'Modo de abertura padrão': 'Default opening mode',
315
+ Rota: 'Route',
316
+ 'Aplicado quando ação/metadado não definem modo.': 'Applied when the action/metadata does not define a mode.',
317
+ 'Confirmar ao sair com alterações': 'Confirm when leaving with unsaved changes',
318
+ 'Back: estratégia': 'Back: strategy',
319
+ 'Modal: largura (ex.: 900px)': 'Modal: width (e.g. 900px)',
320
+ 'Modal: largura máxima (ex.: 96vw)': 'Modal: max width (e.g. 96vw)',
321
+ 'Modal: backdrop': 'Modal: backdrop',
322
+ blur: 'blur',
323
+ dim: 'dim',
324
+ transparent: 'transparent',
325
+ 'Estratégia de primeira carga quando metadado não definir.': 'First-load strategy when metadata does not define one.',
326
+ 'Async Select, cascade and pagination': 'Async Select, cascade and pagination',
327
+ 'Cascata nativa habilitada': 'Native cascade enabled',
328
+ 'Cascata: loadOnChange': 'Cascade: loadOnChange',
329
+ respectLoadOn: 'respectLoadOn',
330
+ immediate: 'immediate',
331
+ manual: 'manual',
332
+ 'Cascata: debounce (ms)': 'Cascade: debounce (ms)',
333
+ 'Async Select: loadOn': 'Async Select: loadOn',
334
+ open: 'open',
335
+ init: 'init',
336
+ none: 'none',
337
+ 'Async Select: pageSize': 'Async Select: pageSize',
338
+ 'Async Select: usar cursor': 'Async Select: use cursor',
339
+ 'LLM provider': 'LLM provider',
340
+ 'Select the provider to list models and test the key.': 'Select the provider to list models and test the key.',
341
+ 'API key': 'API key',
342
+ 'Enter the selected provider key to test.': 'Enter the selected provider key to test.',
343
+ 'AI model': 'AI model',
344
+ 'Temperature (Creativity)': 'Temperature (Creativity)',
345
+ '0.0 (deterministic) to 1.0 (creative)': '0.0 (deterministic) to 1.0 (creative)',
346
+ 'Assistant: risk validation': 'Assistant: risk validation',
347
+ 'Standard': 'Standard',
348
+ 'Strict requires confirmation for medium/high risk. Standard follows the indication returned by the backend.': 'Strict requires confirmation for medium/high risk. Standard follows the indication returned by the backend.',
349
+ 'Use same LLM for embeddings': 'Use same LLM for embeddings',
350
+ 'Replicates the LLM provider and key for embeddings.': 'Replicates the LLM provider and key for embeddings.',
351
+ 'Embeddings provider': 'Embeddings provider',
352
+ 'Provider used to generate embeddings (RAG).': 'Provider used to generate embeddings (RAG).',
353
+ 'Embeddings API key': 'Embeddings API key',
354
+ 'Embeddings provider key.': 'Embeddings provider key.',
355
+ 'Embeddings model': 'Embeddings model',
356
+ 'Provider default': 'Provider default',
357
+ 'OpenAI models for embeddings.': 'OpenAI models for embeddings.',
358
+ 'Embeddings: dimensions': 'Embeddings: dimensions',
359
+ 'Embedding vector dimensions (default 768).': 'Embedding vector dimensions (default 768).',
360
+ 'Artificial Intelligence': 'Artificial Intelligence',
361
+ 'LLM integration': 'LLM integration',
362
+ 'Appearance: density': 'Appearance: density',
363
+ 'Appearance: cell padding': 'Appearance: cell padding',
364
+ 'Accepts 1 to 4 CSS measures with units, var(...) or calc(...). Example: 6px 12px': 'Accepts 1 to 4 CSS measures with units, var(...) or calc(...). Example: 6px 12px',
365
+ 'Use 1 to 4 valid CSS measures, such as 6px 12px.': 'Use 1 to 4 valid CSS measures, such as 6px 12px.',
366
+ 'Appearance: header padding': 'Appearance: header padding',
367
+ 'Accepts 1 to 4 CSS measures with units, var(...) or calc(...). Example: 8px 12px': 'Accepts 1 to 4 CSS measures with units, var(...) or calc(...). Example: 8px 12px',
368
+ 'Use 1 to 4 valid CSS measures, such as 8px 12px.': 'Use 1 to 4 valid CSS measures, such as 8px 12px.',
369
+ 'Appearance: cell font size': 'Appearance: cell font size',
370
+ 'Accepts a CSS measure with units, var(...) or calc(...). Example: 13px': 'Accepts a CSS measure with units, var(...) or calc(...). Example: 13px',
371
+ 'Use a valid CSS measure, such as 13px.': 'Use a valid CSS measure, such as 13px.',
372
+ 'Appearance: header font size': 'Appearance: header font size',
373
+ 'Provedor LLM': 'LLM provider',
374
+ 'Selecione o provedor para listar modelos e testar a chave.': 'Select the provider to list models and test the key.',
375
+ 'Chave de API': 'API key',
376
+ 'Informe a chave do provedor selecionado para testar.': 'Enter the selected provider key to test.',
377
+ 'Modelo de IA': 'AI model',
378
+ 'Temperatura (Criatividade)': 'Temperature (Creativity)',
379
+ '0.0 (determinístico) a 1.0 (criativo)': '0.0 (deterministic) to 1.0 (creative)',
380
+ 'Max tokens': 'Max tokens',
381
+ 'Limite maximo de tokens na resposta.': 'Maximum token limit for the response.',
382
+ 'Assistente: validação de risco': 'Assistant: risk validation',
383
+ 'Estrito (recomendado)': 'Strict (recommended)',
384
+ 'Padrão': 'Standard',
385
+ 'Estrito exige confirmação em risco médio/alto. Padrão segue a indicação retornada pelo backend.': 'Strict requires confirmation for medium/high risk. Standard follows the indication returned by the backend.',
386
+ 'Embeddings: usar mesmo LLM': 'Embeddings: use same LLM',
387
+ 'Replica provedor e chave do LLM para embeddings.': 'Replicates the LLM provider and key for embeddings.',
388
+ 'Embeddings: provedor': 'Embeddings: provider',
389
+ 'Provedor usado para gerar embeddings (RAG).': 'Provider used to generate embeddings (RAG).',
390
+ 'Embeddings: chave de API': 'Embeddings: API key',
391
+ 'Chave do provedor de embeddings.': 'Embeddings provider key.',
392
+ 'Embeddings: modelo': 'Embeddings: model',
393
+ 'Padrão do provedor': 'Provider default',
394
+ 'Modelos OpenAI para embeddings.': 'OpenAI models for embeddings.',
395
+ 'Embeddings: dimensoes': 'Embeddings: dimensions',
396
+ 'Dimensoes do vetor de embedding (default 768).': 'Embedding vector dimensions (default 768).',
397
+ 'GPT models for text and chat.': 'GPT models for text and chat.',
398
+ 'Synchronized with the LLM. The fields below follow the primary credential.': 'Synchronized with the LLM. The fields below follow the primary credential.',
399
+ 'Example JSON: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)"}': 'Example: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)"}',
400
+ 'Example JSON array: [{"id":"cancel","text":"Cancel","role":"secondary","close":true,"cssClass":"btn"},{"id":"confirm","text":"Delete","role":"primary","close":true,"cssClass":"btn btn-danger","icon":"delete"}]': 'Example: [{"id":"cancel","text":"Cancel","role":"secondary","close":true,"cssClass":"btn"},{"id":"confirm","text":"Delete","role":"primary","close":true,"cssClass":"btn btn-danger","icon":"delete"}]',
401
+ 'Example JSON: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)","actionButtonRadius":"8px"}': 'Example: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)","actionButtonRadius":"8px"}',
402
+ 'Desabilitar cache de schema (LocalStorage)': 'Disable schema cache (LocalStorage)',
403
+ 'Se ativo, sempre baixa o schema do servidor (ignora cache local). Útil para desenvolvimento.': 'If enabled, always downloads the schema from the server (ignores local cache). Useful for development.',
404
+ 'Paginação habilitada': 'Pagination enabled',
405
+ 'Tamanho da página': 'Page size',
406
+ 'Ordenação habilitada': 'Sorting enabled',
407
+ 'Seleção habilitada': 'Selection enabled',
408
+ 'Paginação: estratégia': 'Pagination: strategy',
409
+ 'Ordenação: estratégia': 'Sorting: strategy',
410
+ 'Paginação: botões Primeiro/Último': 'Pagination: First/Last buttons',
411
+ 'Toolbar visível': 'Toolbar visible',
412
+ 'Cache & Persistência': 'Cache & Persistence',
413
+ Tabela: 'Table',
414
+ Salvar: 'Save',
415
+ Cancelar: 'Cancel',
416
+ Redefinir: 'Reset',
417
+ };
418
+
419
+ const PRAXIS_SETTINGS_PANEL_EN_US = {
420
+ 'Operation in progress...': 'Operation in progress...',
421
+ 'The form contains errors that need to be fixed.': 'The form contains errors that need to be fixed.',
422
+ 'No changes to apply or save.': 'No changes to apply or save.',
423
+ 'Unsaved changes': 'Unsaved changes',
424
+ 'Saved at {{time}}': 'Saved at {{time}}',
425
+ 'No changes': 'No changes',
426
+ 'Reset Changes': 'Reset Changes',
427
+ 'Are you sure you want to reset all changes?': 'Are you sure you want to reset all changes?',
428
+ Reset: 'Reset',
429
+ Cancel: 'Cancel',
430
+ 'Discard Changes': 'Discard Changes',
431
+ 'You have unsaved changes. Are you sure you want to discard them?': 'You have unsaved changes. Are you sure you want to discard them?',
432
+ Discard: 'Discard',
433
+ 'Continue Editing': 'Continue Editing',
434
+ 'Collapse panel': 'Collapse panel',
435
+ 'Expand panel': 'Expand panel',
436
+ Close: 'Close',
437
+ Apply: 'Apply',
438
+ 'Save & Close': 'Save & Close',
439
+ };
440
+
441
+ const PRAXIS_SETTINGS_PANEL_PT_BR = {
442
+ 'Operation in progress...': 'Operação em andamento...',
443
+ 'The form contains errors that need to be fixed.': 'O formulário contém erros que precisam ser corrigidos.',
444
+ 'No changes to apply or save.': 'Nenhuma alteração para aplicar ou salvar.',
445
+ 'Unsaved changes': 'Alterações não salvas',
446
+ 'Saved at {{time}}': 'Salvo às {{time}}',
447
+ 'No changes': 'Sem alterações',
448
+ 'Reset Changes': 'Redefinir Alterações',
449
+ 'Are you sure you want to reset all changes?': 'Tem certeza de que deseja redefinir todas as alterações?',
450
+ Reset: 'Redefinir',
451
+ Cancel: 'Cancelar',
452
+ 'Discard Changes': 'Descartar Alterações',
453
+ 'You have unsaved changes. Are you sure you want to discard them?': 'Você tem alterações não salvas. Tem certeza de que deseja descartá-las?',
454
+ Discard: 'Descartar',
455
+ 'Continue Editing': 'Continuar Editando',
456
+ 'Collapse panel': 'Reduzir painel',
457
+ 'Expand panel': 'Expandir painel',
458
+ Close: 'Fechar',
459
+ Apply: 'Aplicar',
460
+ 'Save & Close': 'Salvar & Fechar',
461
+ };
462
+
463
+ const PRAXIS_SETTINGS_PANEL_I18N_NAMESPACE = 'praxis-settings-panel';
464
+ const PRAXIS_SETTINGS_PANEL_DEFAULT_TITLE = 'Global settings';
465
+ function providePraxisSettingsPanelI18n() {
466
+ return providePraxisI18n({
467
+ namespaces: {
468
+ [PRAXIS_SETTINGS_PANEL_I18N_NAMESPACE]: {
469
+ 'pt-BR': {
470
+ ...PRAXIS_SETTINGS_PANEL_PT_BR,
471
+ ...PRAXIS_SETTINGS_PANEL_GLOBAL_CONFIG_EDITOR_PT_BR,
472
+ },
473
+ 'en-US': {
474
+ ...PRAXIS_SETTINGS_PANEL_EN_US,
475
+ ...PRAXIS_SETTINGS_PANEL_GLOBAL_CONFIG_EDITOR_EN_US,
476
+ },
477
+ },
478
+ },
479
+ });
480
+ }
481
+
31
482
  class SettingsPanelComponent {
32
483
  cdr;
33
484
  dialog;
@@ -42,6 +493,13 @@ class SettingsPanelComponent {
42
493
  isValid = true;
43
494
  isBusy = false;
44
495
  lastSavedAt = null;
496
+ destroyRef = inject(DestroyRef);
497
+ i18n = inject(PraxisI18nService);
498
+ contentHost;
499
+ constructor(cdr, dialog) {
500
+ this.cdr = cdr;
501
+ this.dialog = dialog;
502
+ }
45
503
  get canApply() {
46
504
  return this.isDirty && this.isValid && !this.isBusy;
47
505
  }
@@ -50,13 +508,13 @@ class SettingsPanelComponent {
50
508
  }
51
509
  get disabledReason() {
52
510
  if (this.isBusy) {
53
- return 'Operação em andamento...';
511
+ return this.tx('Operation in progress...');
54
512
  }
55
513
  if (!this.isValid) {
56
- return 'O formulário contém erros que precisam ser corrigidos.';
514
+ return this.tx('The form contains errors that need to be fixed.');
57
515
  }
58
516
  if (!this.isDirty) {
59
- return 'Nenhuma alteração para aplicar ou salvar.';
517
+ return this.tx('No changes to apply or save.');
60
518
  }
61
519
  return '';
62
520
  }
@@ -71,27 +529,27 @@ class SettingsPanelComponent {
71
529
  }
72
530
  get statusMessage() {
73
531
  if (this.statusTone === 'busy') {
74
- return 'Operação em andamento...';
532
+ return this.tx('Operation in progress...');
75
533
  }
76
534
  if (this.statusTone === 'dirty') {
77
- return 'Alterações não salvas';
535
+ return this.tx('Unsaved changes');
78
536
  }
79
537
  if (this.statusTone === 'saved' && this.lastSavedAt) {
80
- return `Salvo às ${this.formatClock(this.lastSavedAt)}`;
538
+ return this.tx('Saved at {{time}}', {
539
+ time: this.formatClock(this.lastSavedAt),
540
+ });
81
541
  }
82
- return 'Sem alterações';
542
+ return this.tx('No changes');
83
543
  }
84
- destroyRef = inject(DestroyRef);
85
- contentHost;
86
- constructor(cdr, dialog) {
87
- this.cdr = cdr;
88
- this.dialog = dialog;
544
+ tx(text, params) {
545
+ return this.i18n.t(text, params, text, PRAXIS_SETTINGS_PANEL_I18N_NAMESPACE);
89
546
  }
90
- attachContent(component, injector, ref) {
547
+ attachContent(component, injector, ref, inputs) {
91
548
  this.ref = ref;
92
549
  this.contentRef = this.contentHost.createComponent(component, {
93
550
  injector,
94
551
  });
552
+ this.applyInputs(this.contentRef, inputs);
95
553
  const instance = this.contentRef.instance;
96
554
  if (instance.isDirty$) {
97
555
  instance.isDirty$
@@ -117,7 +575,6 @@ class SettingsPanelComponent {
117
575
  this.cdr.markForCheck();
118
576
  });
119
577
  }
120
- // Generic output bridging: if the embedded component emits `selected`, save and close.
121
578
  try {
122
579
  const selected$ = instance?.selected;
123
580
  if (selected$ && typeof selected$.subscribe === 'function') {
@@ -125,19 +582,47 @@ class SettingsPanelComponent {
125
582
  ? selected$.pipe(takeUntilDestroyed(this.destroyRef))
126
583
  : selected$;
127
584
  obs.subscribe((value) => {
128
- // Apply emits value to the opener without closing the panel.
129
585
  this.ref.apply(value);
130
586
  });
131
587
  }
132
588
  }
133
589
  catch { }
134
590
  }
591
+ applyInputs(contentRef, inputs) {
592
+ if (!inputs) {
593
+ return;
594
+ }
595
+ const supportedInputs = this.resolveSupportedInputs(contentRef);
596
+ for (const [key, value] of Object.entries(inputs)) {
597
+ if (supportedInputs && !supportedInputs.has(key)) {
598
+ continue;
599
+ }
600
+ try {
601
+ if (typeof contentRef.setInput === 'function') {
602
+ contentRef.setInput(key, value);
603
+ }
604
+ else {
605
+ contentRef.instance[key] = value;
606
+ }
607
+ }
608
+ catch { }
609
+ }
610
+ contentRef.changeDetectorRef.detectChanges();
611
+ }
612
+ resolveSupportedInputs(contentRef) {
613
+ const componentDef = contentRef.componentType?.ɵcmp;
614
+ const declaredInputs = componentDef?.inputs;
615
+ if (!declaredInputs || typeof declaredInputs !== 'object') {
616
+ return null;
617
+ }
618
+ return new Set(Object.keys(declaredInputs));
619
+ }
135
620
  onReset() {
136
621
  const dialogData = {
137
- title: 'Redefinir Alterações',
138
- message: 'Tem certeza de que deseja redefinir todas as alterações?',
139
- confirmText: 'Redefinir',
140
- cancelText: 'Cancelar',
622
+ title: this.tx('Reset Changes'),
623
+ message: this.tx('Are you sure you want to reset all changes?'),
624
+ confirmText: this.tx('Reset'),
625
+ cancelText: this.tx('Cancel'),
141
626
  type: 'warning',
142
627
  icon: 'restart_alt',
143
628
  };
@@ -189,15 +674,21 @@ class SettingsPanelComponent {
189
674
  this.cdr.markForCheck();
190
675
  }
191
676
  formatClock(timestamp) {
677
+ const options = {
678
+ hour: '2-digit',
679
+ minute: '2-digit',
680
+ second: '2-digit',
681
+ };
192
682
  try {
193
- return new Intl.DateTimeFormat('pt-BR', {
194
- hour: '2-digit',
195
- minute: '2-digit',
196
- second: '2-digit',
197
- }).format(timestamp);
683
+ return new Intl.DateTimeFormat(this.i18n.getLocale(), options).format(timestamp);
198
684
  }
199
685
  catch {
200
- return '';
686
+ try {
687
+ return new Intl.DateTimeFormat(this.i18n.getFallbackLocale(), options).format(timestamp);
688
+ }
689
+ catch {
690
+ return '';
691
+ }
201
692
  }
202
693
  }
203
694
  toggleExpand() {
@@ -211,10 +702,10 @@ class SettingsPanelComponent {
211
702
  return of(true);
212
703
  }
213
704
  const dialogData = {
214
- title: 'Descartar Alterações',
215
- message: 'Você tem alterações não salvas. Tem certeza de que deseja descartá-las?',
216
- confirmText: 'Descartar',
217
- cancelText: 'Continuar Editando',
705
+ title: this.tx('Discard Changes'),
706
+ message: this.tx('You have unsaved changes. Are you sure you want to discard them?'),
707
+ confirmText: this.tx('Discard'),
708
+ cancelText: this.tx('Continue Editing'),
218
709
  type: 'warning',
219
710
  icon: 'warning',
220
711
  };
@@ -235,13 +726,14 @@ class SettingsPanelComponent {
235
726
  event.preventDefault();
236
727
  this.onSave();
237
728
  }
238
- else if ((event.key === 's' || event.key === 'S') && (event.ctrlKey || event.metaKey)) {
729
+ else if ((event.key === 's' || event.key === 'S') &&
730
+ (event.ctrlKey || event.metaKey)) {
239
731
  event.preventDefault();
240
732
  this.onSave();
241
733
  }
242
734
  }
243
735
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SettingsPanelComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1.MatDialog }], target: i0.ɵɵFactoryTarget.Component });
244
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: SettingsPanelComponent, isStandalone: true, selector: "praxis-settings-panel", host: { listeners: { "document:keydown": "handleKeydown($event)" } }, viewQueries: [{ propertyName: "contentHost", first: true, predicate: ["contentHost"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: "<div\n class=\"settings-panel\"\n data-testid=\"settings-panel-root\"\n [class.expanded]=\"expanded\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"titleId\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n>\n <header class=\"settings-panel-header\">\n <h2 class=\"settings-panel-title\" [id]=\"titleId\">\n <mat-icon *ngIf=\"titleIcon\" class=\"title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n <span>{{ title }}</span>\n </h2>\n <span class=\"spacer\"></span>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-toggle-expand\"\n [attr.aria-label]=\"expanded ? 'Reduzir painel' : 'Expandir painel'\"\n [attr.aria-expanded]=\"expanded\"\n [matTooltip]=\"expanded ? 'Reduzir painel' : 'Expandir painel'\"\n (click)=\"toggleExpand()\"\n >\n <mat-icon [praxisIcon]=\"expanded ? 'close_fullscreen' : 'open_in_full'\"></mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-close-icon\"\n aria-label=\"Fechar\"\n matTooltip=\"Fechar\"\n (click)=\"onCancel()\"\n >\n <mat-icon [praxisIcon]=\"'close'\"></mat-icon>\n </button>\n </header>\n <div\n class=\"settings-panel-status\"\n [attr.data-status]=\"statusTone\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <mat-icon\n aria-hidden=\"true\"\n [praxisIcon]=\"\n statusTone === 'dirty'\n ? 'warning'\n : statusTone === 'saved'\n ? 'check_circle'\n : statusTone === 'busy'\n ? 'autorenew'\n : 'info'\n \"\n ></mat-icon>\n <span>{{ statusMessage }}</span>\n </div>\n <div class=\"settings-panel-body\">\n <ng-template #contentHost></ng-template>\n </div>\n <footer class=\"settings-panel-footer\">\n <button mat-button type=\"button\" data-testid=\"settings-panel-reset\" (click)=\"onReset()\" [disabled]=\"isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'restart_alt'\"></mat-icon>\n <span>Redefinir</span>\n </button>\n <span class=\"spacer\"></span>\n <button\n mat-button\n type=\"button\"\n data-testid=\"settings-panel-cancel\"\n (click)=\"onCancel()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'close'\"></mat-icon>\n <span>Cancelar</span>\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n data-testid=\"settings-panel-apply\"\n (click)=\"onApply()\"\n [disabled]=\"!canApply\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canApply\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'done'\"></mat-icon>\n <span>Aplicar</span>\n </ng-container>\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"settings-panel-save\"\n (click)=\"onSave()\"\n [disabled]=\"!canSave\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canSave\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'save'\"></mat-icon>\n <span>Salvar &amp; Fechar</span>\n </ng-container>\n </button>\n </footer>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100%}.settings-panel{display:grid;grid-template-rows:auto auto 1fr auto;grid-template-areas:\"header\" \"status\" \"body\" \"footer\";height:100%;background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface);border-left:1px solid var(--md-sys-color-outline-variant);width:var(--pfx-settings-panel-width, 720px);transition:width .3s ease;overflow:hidden}.settings-panel.expanded{width:min(var(--pfx-settings-panel-width-expanded, 95vw),var(--pfx-settings-panel-max-width, 2400px))}.settings-panel-header{grid-area:header;display:flex;align-items:center;gap:var(--pfx-settings-panel-header-gap, 8px);padding:0 var(--pfx-settings-panel-header-padding-x, 16px);height:var(--pfx-settings-panel-header-height, 64px);border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-header-shadow, var(--md-sys-elevation-level1, 0 2px 6px rgba(0, 0, 0, .08)));flex-shrink:0}.settings-panel-header .spacer{flex:1}.settings-panel-status{grid-area:status;display:flex;align-items:center;gap:8px;min-height:36px;padding:6px 16px;border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface);font-size:.85rem;font-weight:500}.settings-panel-status mat-icon{width:18px;height:18px;font-size:18px}.settings-panel-status[data-status=dirty]{color:var(--md-sys-color-error);background:color-mix(in srgb,var(--md-sys-color-error-container) 30%,var(--md-sys-color-surface-container-high))}.settings-panel-status[data-status=saved]{color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 28%,var(--md-sys-color-surface-container-high))}.settings-panel-body{grid-area:body;overflow-y:auto;min-height:0;padding:var(--pfx-settings-panel-body-padding, 8px 8px 24px 8px);background:var(--md-sys-color-surface);display:flex;flex-direction:column}.settings-panel-content{display:block}.settings-panel-footer{grid-area:footer;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-footer-shadow, var(--md-sys-elevation-level1, 0 -2px 6px rgba(0, 0, 0, .08)));display:flex;align-items:center;padding:var(--pfx-settings-panel-footer-padding, 12px 16px);column-gap:var(--pfx-settings-panel-footer-gap, 12px);flex-shrink:0}.spacer{flex:1}.settings-panel-title{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-title-gap, 8px);font-weight:700;letter-spacing:.2px;margin:0}.settings-panel-title mat-icon{color:var(--md-sys-color-primary)}.settings-panel-title .title-icon{width:20px;height:20px;font-size:20px}.settings-panel-footer button+button{margin-left:var(--pfx-settings-panel-footer-gap, 12px)}.settings-panel-footer button{display:inline-flex;align-items:center}.settings-panel-footer button .mat-icon{font-size:20px;width:20px;height:20px;line-height:1;display:inline-flex;align-items:center;justify-content:center}.settings-panel-footer .mat-button-wrapper{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px)}.settings-panel-footer .mat-progress-spinner{margin-right:8px}.settings-panel-footer .mat-flat-button[color=primary]{font-weight:600}:host ::ng-deep .settings-panel .mdc-button__label{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px);line-height:1}:host ::ng-deep .settings-panel .mdc-button__label>span{display:inline-flex;align-items:center;line-height:1}:host ::ng-deep .settings-panel .mdc-button__label .mat-icon{display:inline-flex;align-items:center;justify-content:center;line-height:1}:host ::ng-deep .praxis-settings-panel-backdrop{background:var(--pfx-backdrop, var(--md-sys-color-scrim, rgba(0, 0, 0, .45)));backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%)}.settings-panel .mat-divider{background-color:var(--md-sys-color-outline-variant)!important}.settings-panel .mat-expansion-panel{background:var(--md-sys-color-surface-container);border:1px solid var(--md-sys-color-outline-variant);border-radius:var(--md-sys-shape-corner-medium, 12px);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08))}.settings-panel .mat-expansion-panel-header{background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface)}.settings-panel .mat-expansion-panel-header mat-icon,.settings-panel .mat-expansion-panel-header .mat-icon{color:var(--md-sys-color-on-surface)!important}.settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title,.settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--md-sys-color-on-surface)}.settings-panel .mat-expansion-panel-header .mat-expansion-indicator,.settings-panel .mat-expansion-panel-header .mat-expansion-indicator:after{color:var(--md-sys-color-on-surface-variant);border-color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-expansion-panel-body{padding:8px}.settings-panel .mat-mdc-tab-group .mat-mdc-tab-header{border-bottom:1px solid var(--md-sys-color-outline-variant)}.settings-panel .mat-mdc-tab-group .mdc-tab .mdc-tab__text-label{color:var(--md-sys-color-on-surface-variant)}.settings-panel .mat-mdc-tab-group .mdc-tab.mdc-tab--active .mdc-tab__text-label{color:var(--md-sys-color-on-surface)}.settings-panel .mat-mdc-tab-group .mdc-tab-indicator__content--underline{border-color:var(--md-sys-color-primary)}.settings-panel .mat-mdc-form-field{--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-secondary, var(--md-sys-color-primary));--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary)}:host ::ng-deep .praxis-settings-panel-pane{position:fixed!important;top:0!important;right:0!important;height:100vh!important;z-index:1000}:host ::ng-deep .praxis-settings-panel-pane .settings-panel{pointer-events:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i6.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatDialogModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
736
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: SettingsPanelComponent, isStandalone: true, selector: "praxis-settings-panel", host: { listeners: { "document:keydown": "handleKeydown($event)" } }, providers: [providePraxisSettingsPanelI18n()], viewQueries: [{ propertyName: "contentHost", first: true, predicate: ["contentHost"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: "<div\n class=\"settings-panel\"\n data-testid=\"settings-panel-root\"\n [class.expanded]=\"expanded\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"titleId\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n>\n <header class=\"settings-panel-header\">\n <h2 class=\"settings-panel-title\" [id]=\"titleId\">\n <mat-icon *ngIf=\"titleIcon\" class=\"title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n <span>{{ title }}</span>\n </h2>\n <span class=\"spacer\"></span>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-toggle-expand\"\n [attr.aria-label]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n [attr.aria-expanded]=\"expanded\"\n [matTooltip]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n (click)=\"toggleExpand()\"\n >\n <mat-icon [praxisIcon]=\"expanded ? 'close_fullscreen' : 'open_in_full'\"></mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-close-icon\"\n [attr.aria-label]=\"tx('Close')\"\n [matTooltip]=\"tx('Close')\"\n (click)=\"onCancel()\"\n >\n <mat-icon [praxisIcon]=\"'close'\"></mat-icon>\n </button>\n </header>\n <div\n class=\"settings-panel-status\"\n [attr.data-status]=\"statusTone\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <mat-icon\n aria-hidden=\"true\"\n [praxisIcon]=\"\n statusTone === 'dirty'\n ? 'warning'\n : statusTone === 'saved'\n ? 'check_circle'\n : statusTone === 'busy'\n ? 'autorenew'\n : 'info'\n \"\n ></mat-icon>\n <span>{{ statusMessage }}</span>\n </div>\n <div class=\"settings-panel-body\">\n <ng-template #contentHost></ng-template>\n </div>\n <footer class=\"settings-panel-footer\">\n <button mat-button type=\"button\" data-testid=\"settings-panel-reset\" (click)=\"onReset()\" [disabled]=\"isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'restart_alt'\"></mat-icon>\n <span>{{ tx('Reset') }}</span>\n </button>\n <span class=\"spacer\"></span>\n <button\n mat-button\n type=\"button\"\n data-testid=\"settings-panel-cancel\"\n (click)=\"onCancel()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'close'\"></mat-icon>\n <span>{{ tx('Cancel') }}</span>\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n data-testid=\"settings-panel-apply\"\n (click)=\"onApply()\"\n [disabled]=\"!canApply\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canApply\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'done'\"></mat-icon>\n <span>{{ tx('Apply') }}</span>\n </ng-container>\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"settings-panel-save\"\n (click)=\"onSave()\"\n [disabled]=\"!canSave\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canSave\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'save'\"></mat-icon>\n <span>{{ tx('Save & Close') }}</span>\n </ng-container>\n </button>\n </footer>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100%}.settings-panel{display:grid;grid-template-rows:auto auto 1fr auto;grid-template-areas:\"header\" \"status\" \"body\" \"footer\";height:100%;background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface);border-left:1px solid var(--md-sys-color-outline-variant);width:var(--pfx-settings-panel-width, 720px);transition:width .3s ease;overflow:hidden}.settings-panel.expanded{width:min(var(--pfx-settings-panel-width-expanded, 95vw),var(--pfx-settings-panel-max-width, 2400px))}.settings-panel-header{grid-area:header;display:flex;align-items:center;gap:var(--pfx-settings-panel-header-gap, 8px);padding:0 var(--pfx-settings-panel-header-padding-x, 16px);height:var(--pfx-settings-panel-header-height, 64px);border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-header-shadow, var(--md-sys-elevation-level1, 0 2px 6px rgba(0, 0, 0, .08)));flex-shrink:0}.settings-panel-header .spacer{flex:1}.settings-panel-status{grid-area:status;display:flex;align-items:center;gap:8px;min-height:36px;padding:6px 16px;border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface);font-size:.85rem;font-weight:500}.settings-panel-status mat-icon{width:18px;height:18px;font-size:18px}.settings-panel-status[data-status=dirty]{color:var(--md-sys-color-error);background:color-mix(in srgb,var(--md-sys-color-error-container) 30%,var(--md-sys-color-surface-container-high))}.settings-panel-status[data-status=saved]{color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 28%,var(--md-sys-color-surface-container-high))}.settings-panel-body{grid-area:body;overflow-y:auto;min-height:0;padding:var(--pfx-settings-panel-body-padding, 8px 8px 24px 8px);background:var(--md-sys-color-surface);display:flex;flex-direction:column}.settings-panel-content{display:block}.settings-panel-footer{grid-area:footer;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-footer-shadow, var(--md-sys-elevation-level1, 0 -2px 6px rgba(0, 0, 0, .08)));display:flex;align-items:center;padding:var(--pfx-settings-panel-footer-padding, 12px 16px);column-gap:var(--pfx-settings-panel-footer-gap, 12px);flex-shrink:0}.spacer{flex:1}.settings-panel-title{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-title-gap, 8px);font-weight:700;letter-spacing:.2px;margin:0}.settings-panel-title mat-icon{color:var(--md-sys-color-primary)}.settings-panel-title .title-icon{width:20px;height:20px;font-size:20px}.settings-panel-footer button+button{margin-left:var(--pfx-settings-panel-footer-gap, 12px)}.settings-panel-footer button{display:inline-flex;align-items:center}.settings-panel-footer button .mat-icon{font-size:20px;width:20px;height:20px;line-height:1;display:inline-flex;align-items:center;justify-content:center}.settings-panel-footer .mat-button-wrapper{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px)}.settings-panel-footer .mat-progress-spinner{margin-right:8px}.settings-panel-footer .mat-flat-button[color=primary]{font-weight:600}:host ::ng-deep .settings-panel .mdc-button__label{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px);line-height:1}:host ::ng-deep .settings-panel .mdc-button__label>span{display:inline-flex;align-items:center;line-height:1}:host ::ng-deep .settings-panel .mdc-button__label .mat-icon{display:inline-flex;align-items:center;justify-content:center;line-height:1}:host ::ng-deep .praxis-settings-panel-backdrop{background:var(--pfx-backdrop, var(--md-sys-color-scrim, rgba(0, 0, 0, .45)));backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%)}:host ::ng-deep .settings-panel .mat-divider{background-color:var(--md-sys-color-outline-variant)!important}:host ::ng-deep .settings-panel .mat-expansion-panel{background:var(--md-sys-color-surface-container);border:1px solid var(--md-sys-color-outline-variant);border-radius:var(--md-sys-shape-corner-medium, 12px);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08));color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel-header{background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel:not(.mat-expanded) .mat-expansion-panel-header:hover{background:color-mix(in srgb,var(--md-sys-color-surface-container-high) 84%,var(--md-sys-color-primary) 16%)}:host ::ng-deep .settings-panel .mat-expansion-panel.mat-expanded>.mat-expansion-panel-header{background:var(--md-sys-color-surface-container-high)}:host ::ng-deep .settings-panel .mat-expansion-panel-header mat-icon,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-icon{color:var(--md-sys-color-on-surface)!important}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-indicator,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-indicator:after{color:var(--md-sys-color-on-surface-variant);border-color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-action-row{border-top-color:var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-expansion-panel-body{padding:8px}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header{border-bottom:1px solid var(--md-sys-color-outline-variant);background:transparent}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab .mdc-tab__text-label{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab.mdc-tab--active .mdc-tab__text-label{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab-indicator__content--underline{border-color:var(--md-sys-color-primary)}:host ::ng-deep .settings-panel .mat-mdc-card{background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08))}:host ::ng-deep .settings-panel .mat-mdc-card-subtitle,:host ::ng-deep .settings-panel .mat-mdc-card-content{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-card-title{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-form-field{width:100%;--mdc-filled-text-field-container-color: var(--md-sys-color-surface-container);--mdc-filled-text-field-hover-container-color: var( --md-sys-color-surface-container-high );--mdc-filled-text-field-focus-container-color: var( --md-sys-color-surface-container-high );--mdc-filled-text-field-active-indicator-color: var( --md-sys-color-outline-variant );--mdc-filled-text-field-hover-active-indicator-color: var( --md-sys-color-secondary, var(--md-sys-color-primary) );--mdc-filled-text-field-focus-active-indicator-color: var( --md-sys-color-primary );--mdc-filled-text-field-label-text-color: var( --md-sys-color-on-surface-variant );--mdc-filled-text-field-focus-label-text-color: var(--md-sys-color-primary);--mdc-filled-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-filled-text-field-caret-color: var(--md-sys-color-primary);--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var( --md-sys-color-secondary, var(--md-sys-color-primary) );--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-label-text-color: var( --md-sys-color-on-surface-variant );--mdc-outlined-text-field-focus-label-text-color: var(--md-sys-color-primary);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-caret-color: var(--md-sys-color-primary);--mat-form-field-focus-select-arrow-color: var(--md-sys-color-primary);--mat-form-field-enabled-select-arrow-color: var( --md-sys-color-on-surface-variant )}:host ::ng-deep .settings-panel .mdc-text-field--filled{border-radius:var(--md-sys-shape-corner-small, 8px) var(--md-sys-shape-corner-small, 8px) 0 0}:host ::ng-deep .settings-panel .mat-mdc-form-field-subscript-wrapper,:host ::ng-deep .settings-panel .mat-mdc-form-field-hint-wrapper,:host ::ng-deep .settings-panel .mat-mdc-form-field-error-wrapper,:host ::ng-deep .settings-panel .mat-mdc-hint{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-button-toggle-group{border-color:var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .settings-panel .mat-button-toggle{background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-button-toggle+.mat-button-toggle{border-left-color:var(--md-sys-color-outline-variant)}:host ::ng-deep .settings-panel .mat-button-toggle-checked{background:var(--md-sys-color-primary-container)!important;color:var(--md-sys-color-on-primary-container)!important}:host ::ng-deep .settings-panel .mat-button-toggle .mat-icon,:host ::ng-deep .settings-panel .mat-button-toggle-checked .mat-icon{color:inherit}:host ::ng-deep .praxis-settings-panel-pane{position:fixed!important;top:0!important;right:0!important;height:100vh!important;z-index:1000}:host ::ng-deep .praxis-settings-panel-pane .settings-panel{pointer-events:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i6.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatDialogModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
245
737
  }
246
738
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SettingsPanelComponent, decorators: [{
247
739
  type: Component,
@@ -254,7 +746,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
254
746
  CdkTrapFocus,
255
747
  MatProgressSpinnerModule,
256
748
  MatDialogModule,
257
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n class=\"settings-panel\"\n data-testid=\"settings-panel-root\"\n [class.expanded]=\"expanded\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"titleId\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n>\n <header class=\"settings-panel-header\">\n <h2 class=\"settings-panel-title\" [id]=\"titleId\">\n <mat-icon *ngIf=\"titleIcon\" class=\"title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n <span>{{ title }}</span>\n </h2>\n <span class=\"spacer\"></span>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-toggle-expand\"\n [attr.aria-label]=\"expanded ? 'Reduzir painel' : 'Expandir painel'\"\n [attr.aria-expanded]=\"expanded\"\n [matTooltip]=\"expanded ? 'Reduzir painel' : 'Expandir painel'\"\n (click)=\"toggleExpand()\"\n >\n <mat-icon [praxisIcon]=\"expanded ? 'close_fullscreen' : 'open_in_full'\"></mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-close-icon\"\n aria-label=\"Fechar\"\n matTooltip=\"Fechar\"\n (click)=\"onCancel()\"\n >\n <mat-icon [praxisIcon]=\"'close'\"></mat-icon>\n </button>\n </header>\n <div\n class=\"settings-panel-status\"\n [attr.data-status]=\"statusTone\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <mat-icon\n aria-hidden=\"true\"\n [praxisIcon]=\"\n statusTone === 'dirty'\n ? 'warning'\n : statusTone === 'saved'\n ? 'check_circle'\n : statusTone === 'busy'\n ? 'autorenew'\n : 'info'\n \"\n ></mat-icon>\n <span>{{ statusMessage }}</span>\n </div>\n <div class=\"settings-panel-body\">\n <ng-template #contentHost></ng-template>\n </div>\n <footer class=\"settings-panel-footer\">\n <button mat-button type=\"button\" data-testid=\"settings-panel-reset\" (click)=\"onReset()\" [disabled]=\"isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'restart_alt'\"></mat-icon>\n <span>Redefinir</span>\n </button>\n <span class=\"spacer\"></span>\n <button\n mat-button\n type=\"button\"\n data-testid=\"settings-panel-cancel\"\n (click)=\"onCancel()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'close'\"></mat-icon>\n <span>Cancelar</span>\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n data-testid=\"settings-panel-apply\"\n (click)=\"onApply()\"\n [disabled]=\"!canApply\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canApply\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'done'\"></mat-icon>\n <span>Aplicar</span>\n </ng-container>\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"settings-panel-save\"\n (click)=\"onSave()\"\n [disabled]=\"!canSave\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canSave\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'save'\"></mat-icon>\n <span>Salvar &amp; Fechar</span>\n </ng-container>\n </button>\n </footer>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100%}.settings-panel{display:grid;grid-template-rows:auto auto 1fr auto;grid-template-areas:\"header\" \"status\" \"body\" \"footer\";height:100%;background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface);border-left:1px solid var(--md-sys-color-outline-variant);width:var(--pfx-settings-panel-width, 720px);transition:width .3s ease;overflow:hidden}.settings-panel.expanded{width:min(var(--pfx-settings-panel-width-expanded, 95vw),var(--pfx-settings-panel-max-width, 2400px))}.settings-panel-header{grid-area:header;display:flex;align-items:center;gap:var(--pfx-settings-panel-header-gap, 8px);padding:0 var(--pfx-settings-panel-header-padding-x, 16px);height:var(--pfx-settings-panel-header-height, 64px);border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-header-shadow, var(--md-sys-elevation-level1, 0 2px 6px rgba(0, 0, 0, .08)));flex-shrink:0}.settings-panel-header .spacer{flex:1}.settings-panel-status{grid-area:status;display:flex;align-items:center;gap:8px;min-height:36px;padding:6px 16px;border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface);font-size:.85rem;font-weight:500}.settings-panel-status mat-icon{width:18px;height:18px;font-size:18px}.settings-panel-status[data-status=dirty]{color:var(--md-sys-color-error);background:color-mix(in srgb,var(--md-sys-color-error-container) 30%,var(--md-sys-color-surface-container-high))}.settings-panel-status[data-status=saved]{color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 28%,var(--md-sys-color-surface-container-high))}.settings-panel-body{grid-area:body;overflow-y:auto;min-height:0;padding:var(--pfx-settings-panel-body-padding, 8px 8px 24px 8px);background:var(--md-sys-color-surface);display:flex;flex-direction:column}.settings-panel-content{display:block}.settings-panel-footer{grid-area:footer;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-footer-shadow, var(--md-sys-elevation-level1, 0 -2px 6px rgba(0, 0, 0, .08)));display:flex;align-items:center;padding:var(--pfx-settings-panel-footer-padding, 12px 16px);column-gap:var(--pfx-settings-panel-footer-gap, 12px);flex-shrink:0}.spacer{flex:1}.settings-panel-title{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-title-gap, 8px);font-weight:700;letter-spacing:.2px;margin:0}.settings-panel-title mat-icon{color:var(--md-sys-color-primary)}.settings-panel-title .title-icon{width:20px;height:20px;font-size:20px}.settings-panel-footer button+button{margin-left:var(--pfx-settings-panel-footer-gap, 12px)}.settings-panel-footer button{display:inline-flex;align-items:center}.settings-panel-footer button .mat-icon{font-size:20px;width:20px;height:20px;line-height:1;display:inline-flex;align-items:center;justify-content:center}.settings-panel-footer .mat-button-wrapper{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px)}.settings-panel-footer .mat-progress-spinner{margin-right:8px}.settings-panel-footer .mat-flat-button[color=primary]{font-weight:600}:host ::ng-deep .settings-panel .mdc-button__label{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px);line-height:1}:host ::ng-deep .settings-panel .mdc-button__label>span{display:inline-flex;align-items:center;line-height:1}:host ::ng-deep .settings-panel .mdc-button__label .mat-icon{display:inline-flex;align-items:center;justify-content:center;line-height:1}:host ::ng-deep .praxis-settings-panel-backdrop{background:var(--pfx-backdrop, var(--md-sys-color-scrim, rgba(0, 0, 0, .45)));backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%)}.settings-panel .mat-divider{background-color:var(--md-sys-color-outline-variant)!important}.settings-panel .mat-expansion-panel{background:var(--md-sys-color-surface-container);border:1px solid var(--md-sys-color-outline-variant);border-radius:var(--md-sys-shape-corner-medium, 12px);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08))}.settings-panel .mat-expansion-panel-header{background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface)}.settings-panel .mat-expansion-panel-header mat-icon,.settings-panel .mat-expansion-panel-header .mat-icon{color:var(--md-sys-color-on-surface)!important}.settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title,.settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--md-sys-color-on-surface)}.settings-panel .mat-expansion-panel-header .mat-expansion-indicator,.settings-panel .mat-expansion-panel-header .mat-expansion-indicator:after{color:var(--md-sys-color-on-surface-variant);border-color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-expansion-panel-body{padding:8px}.settings-panel .mat-mdc-tab-group .mat-mdc-tab-header{border-bottom:1px solid var(--md-sys-color-outline-variant)}.settings-panel .mat-mdc-tab-group .mdc-tab .mdc-tab__text-label{color:var(--md-sys-color-on-surface-variant)}.settings-panel .mat-mdc-tab-group .mdc-tab.mdc-tab--active .mdc-tab__text-label{color:var(--md-sys-color-on-surface)}.settings-panel .mat-mdc-tab-group .mdc-tab-indicator__content--underline{border-color:var(--md-sys-color-primary)}.settings-panel .mat-mdc-form-field{--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-secondary, var(--md-sys-color-primary));--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary)}:host ::ng-deep .praxis-settings-panel-pane{position:fixed!important;top:0!important;right:0!important;height:100vh!important;z-index:1000}:host ::ng-deep .praxis-settings-panel-pane .settings-panel{pointer-events:auto}\n"] }]
749
+ ], changeDetection: ChangeDetectionStrategy.OnPush, providers: [providePraxisSettingsPanelI18n()], template: "<div\n class=\"settings-panel\"\n data-testid=\"settings-panel-root\"\n [class.expanded]=\"expanded\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"titleId\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n>\n <header class=\"settings-panel-header\">\n <h2 class=\"settings-panel-title\" [id]=\"titleId\">\n <mat-icon *ngIf=\"titleIcon\" class=\"title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n <span>{{ title }}</span>\n </h2>\n <span class=\"spacer\"></span>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-toggle-expand\"\n [attr.aria-label]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n [attr.aria-expanded]=\"expanded\"\n [matTooltip]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n (click)=\"toggleExpand()\"\n >\n <mat-icon [praxisIcon]=\"expanded ? 'close_fullscreen' : 'open_in_full'\"></mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-close-icon\"\n [attr.aria-label]=\"tx('Close')\"\n [matTooltip]=\"tx('Close')\"\n (click)=\"onCancel()\"\n >\n <mat-icon [praxisIcon]=\"'close'\"></mat-icon>\n </button>\n </header>\n <div\n class=\"settings-panel-status\"\n [attr.data-status]=\"statusTone\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <mat-icon\n aria-hidden=\"true\"\n [praxisIcon]=\"\n statusTone === 'dirty'\n ? 'warning'\n : statusTone === 'saved'\n ? 'check_circle'\n : statusTone === 'busy'\n ? 'autorenew'\n : 'info'\n \"\n ></mat-icon>\n <span>{{ statusMessage }}</span>\n </div>\n <div class=\"settings-panel-body\">\n <ng-template #contentHost></ng-template>\n </div>\n <footer class=\"settings-panel-footer\">\n <button mat-button type=\"button\" data-testid=\"settings-panel-reset\" (click)=\"onReset()\" [disabled]=\"isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'restart_alt'\"></mat-icon>\n <span>{{ tx('Reset') }}</span>\n </button>\n <span class=\"spacer\"></span>\n <button\n mat-button\n type=\"button\"\n data-testid=\"settings-panel-cancel\"\n (click)=\"onCancel()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'close'\"></mat-icon>\n <span>{{ tx('Cancel') }}</span>\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n data-testid=\"settings-panel-apply\"\n (click)=\"onApply()\"\n [disabled]=\"!canApply\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canApply\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'done'\"></mat-icon>\n <span>{{ tx('Apply') }}</span>\n </ng-container>\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"settings-panel-save\"\n (click)=\"onSave()\"\n [disabled]=\"!canSave\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canSave\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'save'\"></mat-icon>\n <span>{{ tx('Save & Close') }}</span>\n </ng-container>\n </button>\n </footer>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100%}.settings-panel{display:grid;grid-template-rows:auto auto 1fr auto;grid-template-areas:\"header\" \"status\" \"body\" \"footer\";height:100%;background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface);border-left:1px solid var(--md-sys-color-outline-variant);width:var(--pfx-settings-panel-width, 720px);transition:width .3s ease;overflow:hidden}.settings-panel.expanded{width:min(var(--pfx-settings-panel-width-expanded, 95vw),var(--pfx-settings-panel-max-width, 2400px))}.settings-panel-header{grid-area:header;display:flex;align-items:center;gap:var(--pfx-settings-panel-header-gap, 8px);padding:0 var(--pfx-settings-panel-header-padding-x, 16px);height:var(--pfx-settings-panel-header-height, 64px);border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-header-shadow, var(--md-sys-elevation-level1, 0 2px 6px rgba(0, 0, 0, .08)));flex-shrink:0}.settings-panel-header .spacer{flex:1}.settings-panel-status{grid-area:status;display:flex;align-items:center;gap:8px;min-height:36px;padding:6px 16px;border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface);font-size:.85rem;font-weight:500}.settings-panel-status mat-icon{width:18px;height:18px;font-size:18px}.settings-panel-status[data-status=dirty]{color:var(--md-sys-color-error);background:color-mix(in srgb,var(--md-sys-color-error-container) 30%,var(--md-sys-color-surface-container-high))}.settings-panel-status[data-status=saved]{color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 28%,var(--md-sys-color-surface-container-high))}.settings-panel-body{grid-area:body;overflow-y:auto;min-height:0;padding:var(--pfx-settings-panel-body-padding, 8px 8px 24px 8px);background:var(--md-sys-color-surface);display:flex;flex-direction:column}.settings-panel-content{display:block}.settings-panel-footer{grid-area:footer;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-footer-shadow, var(--md-sys-elevation-level1, 0 -2px 6px rgba(0, 0, 0, .08)));display:flex;align-items:center;padding:var(--pfx-settings-panel-footer-padding, 12px 16px);column-gap:var(--pfx-settings-panel-footer-gap, 12px);flex-shrink:0}.spacer{flex:1}.settings-panel-title{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-title-gap, 8px);font-weight:700;letter-spacing:.2px;margin:0}.settings-panel-title mat-icon{color:var(--md-sys-color-primary)}.settings-panel-title .title-icon{width:20px;height:20px;font-size:20px}.settings-panel-footer button+button{margin-left:var(--pfx-settings-panel-footer-gap, 12px)}.settings-panel-footer button{display:inline-flex;align-items:center}.settings-panel-footer button .mat-icon{font-size:20px;width:20px;height:20px;line-height:1;display:inline-flex;align-items:center;justify-content:center}.settings-panel-footer .mat-button-wrapper{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px)}.settings-panel-footer .mat-progress-spinner{margin-right:8px}.settings-panel-footer .mat-flat-button[color=primary]{font-weight:600}:host ::ng-deep .settings-panel .mdc-button__label{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px);line-height:1}:host ::ng-deep .settings-panel .mdc-button__label>span{display:inline-flex;align-items:center;line-height:1}:host ::ng-deep .settings-panel .mdc-button__label .mat-icon{display:inline-flex;align-items:center;justify-content:center;line-height:1}:host ::ng-deep .praxis-settings-panel-backdrop{background:var(--pfx-backdrop, var(--md-sys-color-scrim, rgba(0, 0, 0, .45)));backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%)}:host ::ng-deep .settings-panel .mat-divider{background-color:var(--md-sys-color-outline-variant)!important}:host ::ng-deep .settings-panel .mat-expansion-panel{background:var(--md-sys-color-surface-container);border:1px solid var(--md-sys-color-outline-variant);border-radius:var(--md-sys-shape-corner-medium, 12px);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08));color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel-header{background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel:not(.mat-expanded) .mat-expansion-panel-header:hover{background:color-mix(in srgb,var(--md-sys-color-surface-container-high) 84%,var(--md-sys-color-primary) 16%)}:host ::ng-deep .settings-panel .mat-expansion-panel.mat-expanded>.mat-expansion-panel-header{background:var(--md-sys-color-surface-container-high)}:host ::ng-deep .settings-panel .mat-expansion-panel-header mat-icon,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-icon{color:var(--md-sys-color-on-surface)!important}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-indicator,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-indicator:after{color:var(--md-sys-color-on-surface-variant);border-color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-action-row{border-top-color:var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-expansion-panel-body{padding:8px}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header{border-bottom:1px solid var(--md-sys-color-outline-variant);background:transparent}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab .mdc-tab__text-label{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab.mdc-tab--active .mdc-tab__text-label{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab-indicator__content--underline{border-color:var(--md-sys-color-primary)}:host ::ng-deep .settings-panel .mat-mdc-card{background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08))}:host ::ng-deep .settings-panel .mat-mdc-card-subtitle,:host ::ng-deep .settings-panel .mat-mdc-card-content{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-card-title{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-form-field{width:100%;--mdc-filled-text-field-container-color: var(--md-sys-color-surface-container);--mdc-filled-text-field-hover-container-color: var( --md-sys-color-surface-container-high );--mdc-filled-text-field-focus-container-color: var( --md-sys-color-surface-container-high );--mdc-filled-text-field-active-indicator-color: var( --md-sys-color-outline-variant );--mdc-filled-text-field-hover-active-indicator-color: var( --md-sys-color-secondary, var(--md-sys-color-primary) );--mdc-filled-text-field-focus-active-indicator-color: var( --md-sys-color-primary );--mdc-filled-text-field-label-text-color: var( --md-sys-color-on-surface-variant );--mdc-filled-text-field-focus-label-text-color: var(--md-sys-color-primary);--mdc-filled-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-filled-text-field-caret-color: var(--md-sys-color-primary);--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var( --md-sys-color-secondary, var(--md-sys-color-primary) );--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-label-text-color: var( --md-sys-color-on-surface-variant );--mdc-outlined-text-field-focus-label-text-color: var(--md-sys-color-primary);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-caret-color: var(--md-sys-color-primary);--mat-form-field-focus-select-arrow-color: var(--md-sys-color-primary);--mat-form-field-enabled-select-arrow-color: var( --md-sys-color-on-surface-variant )}:host ::ng-deep .settings-panel .mdc-text-field--filled{border-radius:var(--md-sys-shape-corner-small, 8px) var(--md-sys-shape-corner-small, 8px) 0 0}:host ::ng-deep .settings-panel .mat-mdc-form-field-subscript-wrapper,:host ::ng-deep .settings-panel .mat-mdc-form-field-hint-wrapper,:host ::ng-deep .settings-panel .mat-mdc-form-field-error-wrapper,:host ::ng-deep .settings-panel .mat-mdc-hint{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-button-toggle-group{border-color:var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .settings-panel .mat-button-toggle{background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-button-toggle+.mat-button-toggle{border-left-color:var(--md-sys-color-outline-variant)}:host ::ng-deep .settings-panel .mat-button-toggle-checked{background:var(--md-sys-color-primary-container)!important;color:var(--md-sys-color-on-primary-container)!important}:host ::ng-deep .settings-panel .mat-button-toggle .mat-icon,:host ::ng-deep .settings-panel .mat-button-toggle-checked .mat-icon{color:inherit}:host ::ng-deep .praxis-settings-panel-pane{position:fixed!important;top:0!important;right:0!important;height:100vh!important;z-index:1000}:host ::ng-deep .praxis-settings-panel-pane .settings-panel{pointer-events:auto}\n"] }]
258
750
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1.MatDialog }], propDecorators: { contentHost: [{
259
751
  type: ViewChild,
260
752
  args: ['contentHost', { read: ViewContainerRef, static: true }]
@@ -311,13 +803,14 @@ class SettingsPanelRef {
311
803
  }
312
804
  }
313
805
 
314
- const SETTINGS_PANEL_DATA = new InjectionToken('SETTINGS_PANEL_DATA');
806
+ const SETTINGS_PANEL_DATA = SETTINGS_PANEL_DATA$1;
315
807
  const SETTINGS_PANEL_REF = new InjectionToken('SETTINGS_PANEL_REF');
316
808
 
317
809
  class SettingsPanelService {
318
810
  overlay;
319
811
  injector;
320
812
  currentRef;
813
+ i18n = inject(PraxisI18nService);
321
814
  constructor(overlay, injector) {
322
815
  this.overlay = overlay;
323
816
  this.injector = injector;
@@ -343,7 +836,9 @@ class SettingsPanelService {
343
836
  const ref = new SettingsPanelRef(overlayRef);
344
837
  const panelPortal = new ComponentPortal(SettingsPanelComponent);
345
838
  const panelRef = overlayRef.attach(panelPortal);
346
- panelRef.instance.title = config.title;
839
+ panelRef.instance.title = config.title?.trim()
840
+ ? config.title
841
+ : this.i18n.t(PRAXIS_SETTINGS_PANEL_DEFAULT_TITLE, undefined, PRAXIS_SETTINGS_PANEL_DEFAULT_TITLE, PRAXIS_SETTINGS_PANEL_I18N_NAMESPACE);
347
842
  panelRef.instance.titleIcon = config.titleIcon;
348
843
  panelRef.instance.expanded = config.expanded || false;
349
844
  const inputs = config.content.inputs;
@@ -359,7 +854,7 @@ class SettingsPanelService {
359
854
  ],
360
855
  parent: this.injector,
361
856
  });
362
- panelRef.instance.attachContent(config.content.component, injector, ref);
857
+ panelRef.instance.attachContent(config.content.component, injector, ref, inputs);
363
858
  overlayRef.backdropClick().subscribe(() => ref.close('backdrop'));
364
859
  overlayRef.keydownEvents().subscribe((event) => {
365
860
  if (event.key === 'Escape') {
@@ -386,6 +881,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
386
881
  args: [{ providedIn: 'root' }]
387
882
  }], ctorParameters: () => [{ type: i1$1.Overlay }, { type: i0.Injector }] });
388
883
 
884
+ function providePraxisSettingsPanelBridge() {
885
+ return {
886
+ provide: SETTINGS_PANEL_BRIDGE,
887
+ useFactory: (service) => ({
888
+ open: (opts) => service.open({
889
+ id: opts.id,
890
+ title: opts.title,
891
+ titleIcon: opts.titleIcon,
892
+ content: {
893
+ component: opts.content.component,
894
+ inputs: (opts.content.inputs || undefined),
895
+ },
896
+ }),
897
+ }),
898
+ deps: [SettingsPanelService],
899
+ };
900
+ }
901
+
389
902
  /**
390
903
  * Admin helper to load, patch and persist GlobalConfig.
391
904
  * Intended to be used by the Global Config Editor UI.
@@ -425,6 +938,38 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
425
938
  args: [{ providedIn: 'root' }]
426
939
  }] });
427
940
 
941
+ function normalizeMojibakeText(text) {
942
+ if (!text.includes('\u00C3') &&
943
+ !text.includes('\u00C2') &&
944
+ !text.includes('\u00E2') &&
945
+ !text.includes('\u00AA') &&
946
+ !text.includes('\u00AB')) {
947
+ return text;
948
+ }
949
+ let current = text;
950
+ for (let i = 0; i < 3; i++) {
951
+ let next = current;
952
+ try {
953
+ next = decodeURIComponent(escape(current));
954
+ }
955
+ catch {
956
+ break;
957
+ }
958
+ if (next === current) {
959
+ break;
960
+ }
961
+ current = next;
962
+ if (!current.includes('\u00C3') &&
963
+ !current.includes('\u00C2') &&
964
+ !current.includes('\u00E2') &&
965
+ !current.includes('\u00AA') &&
966
+ !current.includes('\u00AB')) {
967
+ break;
968
+ }
969
+ }
970
+ return current;
971
+ }
972
+
428
973
  const TABLE_COMPACT_LENGTH_TOKEN$1 = '(?:0|(?:\\d+|\\d*\\.\\d+)(?:px|rem|em|%|vh|vw|svh|svw|lvh|lvw|dvh|dvw|ch|ex))';
429
974
  const TABLE_COMPACT_SPACING_PATTERN = `^(?:var\\(.+\\)|calc\\(.+\\)|${TABLE_COMPACT_LENGTH_TOKEN$1}(?:\\s+${TABLE_COMPACT_LENGTH_TOKEN$1}){0,3})$`;
430
975
  const TABLE_COMPACT_FONT_SIZE_PATTERN = `^(?:var\\(.+\\)|calc\\(.+\\)|${TABLE_COMPACT_LENGTH_TOKEN$1})$`;
@@ -435,30 +980,31 @@ function safeName(path) {
435
980
  * Minimal FormConfig schema for the Global Config editor UI.
436
981
  * This can be extended incrementally as new domains/settings are added.
437
982
  */
438
- function buildGlobalConfigFormConfig() {
983
+ function buildGlobalConfigFormConfig(translate = (text) => text) {
984
+ const tx = (text, params) => translate(normalizeMojibakeText(text), params);
439
985
  const fields = [
440
986
  // CRUD
441
987
  {
442
988
  name: safeName('crud.defaults.openMode'),
443
- label: 'Modo de abertura padrão',
989
+ label: tx('Modo de abertura padrão'),
444
990
  controlType: FieldControlType.SELECT,
445
991
  selectOptions: [
446
- { text: 'Rota', value: 'route' },
992
+ { text: tx('Rota'), value: 'route' },
447
993
  { text: 'Modal', value: 'modal' },
448
994
  { text: 'Drawer', value: 'drawer' },
449
995
  ],
450
- hint: 'Aplicado quando ação/metadado não definem modo.',
996
+ hint: tx('Aplicado quando ação/metadado não definem modo.'),
451
997
  dataAttributes: { globalPath: 'crud.defaults.openMode' },
452
998
  },
453
999
  {
454
1000
  name: safeName('crud.defaults.back.confirmOnDirty'),
455
- label: 'Confirmar ao sair com alterações',
1001
+ label: tx('Confirmar ao sair com alterações'),
456
1002
  controlType: FieldControlType.TOGGLE,
457
1003
  dataAttributes: { globalPath: 'crud.defaults.back.confirmOnDirty' },
458
1004
  },
459
1005
  {
460
1006
  name: safeName('crud.defaults.back.strategy'),
461
- label: 'Back: estratégia',
1007
+ label: tx('Back: estratégia'),
462
1008
  controlType: FieldControlType.SELECT,
463
1009
  selectOptions: [
464
1010
  { text: 'auto', value: 'auto' },
@@ -469,13 +1015,13 @@ function buildGlobalConfigFormConfig() {
469
1015
  },
470
1016
  {
471
1017
  name: safeName('crud.defaults.header.showBack'),
472
- label: 'Header: mostrar voltar',
1018
+ label: tx('Header: show back'),
473
1019
  controlType: FieldControlType.TOGGLE,
474
1020
  dataAttributes: { globalPath: 'crud.defaults.header.showBack' },
475
1021
  },
476
1022
  {
477
1023
  name: safeName('crud.defaults.header.variant'),
478
- label: 'Header: variante',
1024
+ label: tx('Header: variant'),
479
1025
  controlType: FieldControlType.SELECT,
480
1026
  selectOptions: [
481
1027
  { text: 'ghost', value: 'ghost' },
@@ -486,31 +1032,31 @@ function buildGlobalConfigFormConfig() {
486
1032
  },
487
1033
  {
488
1034
  name: safeName('crud.defaults.header.sticky'),
489
- label: 'Header: fixo (sticky)',
1035
+ label: tx('Header: sticky'),
490
1036
  controlType: FieldControlType.TOGGLE,
491
1037
  dataAttributes: { globalPath: 'crud.defaults.header.sticky' },
492
1038
  },
493
1039
  {
494
1040
  name: safeName('crud.defaults.header.divider'),
495
- label: 'Header: mostrar divisor',
1041
+ label: tx('Header: show divider'),
496
1042
  controlType: FieldControlType.TOGGLE,
497
1043
  dataAttributes: { globalPath: 'crud.defaults.header.divider' },
498
1044
  },
499
1045
  {
500
1046
  name: safeName('crud.defaults.modal.width'),
501
- label: 'Modal: largura (ex.: 900px)',
1047
+ label: tx('Modal: width (e.g. 900px)'),
502
1048
  controlType: FieldControlType.INPUT,
503
1049
  dataAttributes: { globalPath: 'crud.defaults.modal.width' },
504
1050
  },
505
1051
  {
506
1052
  name: safeName('crud.defaults.modal.maxWidth'),
507
- label: 'Modal: largura máxima (ex.: 96vw)',
1053
+ label: tx('Modal: largura máxima (ex.: 96vw)'),
508
1054
  controlType: FieldControlType.INPUT,
509
1055
  dataAttributes: { globalPath: 'crud.defaults.modal.maxWidth' },
510
1056
  },
511
1057
  {
512
1058
  name: safeName('crud.defaults.modal.backdropStyle'),
513
- label: 'Modal: backdrop',
1059
+ label: tx('Modal: backdrop'),
514
1060
  controlType: FieldControlType.SELECT,
515
1061
  selectOptions: [
516
1062
  { text: 'blur', value: 'blur' },
@@ -522,25 +1068,25 @@ function buildGlobalConfigFormConfig() {
522
1068
  // Dynamic Fields
523
1069
  {
524
1070
  name: safeName('dynamicFields.asyncSelect.loadOn'),
525
- label: 'Async Select: loadOn',
1071
+ label: tx('Async Select: loadOn'),
526
1072
  controlType: FieldControlType.SELECT,
527
1073
  selectOptions: [
528
1074
  { text: 'open', value: 'open' },
529
1075
  { text: 'init', value: 'init' },
530
1076
  { text: 'none', value: 'none' },
531
1077
  ],
532
- hint: 'Estratégia de primeira carga quando metadado não definir.',
1078
+ hint: tx('Estratégia de primeira carga quando metadado não definir.'),
533
1079
  dataAttributes: { globalPath: 'dynamicFields.asyncSelect.loadOn' },
534
1080
  },
535
1081
  {
536
1082
  name: safeName('dynamicFields.cascade.enable'),
537
- label: 'Cascata nativa habilitada',
1083
+ label: tx('Native cascade enabled'),
538
1084
  controlType: FieldControlType.TOGGLE,
539
1085
  dataAttributes: { globalPath: 'dynamicFields.cascade.enable' },
540
1086
  },
541
1087
  {
542
1088
  name: safeName('dynamicFields.cascade.loadOnChange'),
543
- label: 'Cascata: loadOnChange',
1089
+ label: tx('Cascade: loadOnChange'),
544
1090
  controlType: FieldControlType.SELECT,
545
1091
  selectOptions: [
546
1092
  { text: 'respectLoadOn', value: 'respectLoadOn' },
@@ -551,42 +1097,42 @@ function buildGlobalConfigFormConfig() {
551
1097
  },
552
1098
  {
553
1099
  name: safeName('dynamicFields.cascade.debounceMs'),
554
- label: 'Cascata: debounce (ms)',
1100
+ label: tx('Cascade: debounce (ms)'),
555
1101
  controlType: FieldControlType.NUMERIC_TEXT_BOX,
556
1102
  dataAttributes: { globalPath: 'dynamicFields.cascade.debounceMs' },
557
1103
  },
558
1104
  // Dynamic Fields async-select extra
559
1105
  {
560
1106
  name: safeName('dynamicFields.asyncSelect.pageSize'),
561
- label: 'Async Select: pageSize',
1107
+ label: tx('Async Select: pageSize'),
562
1108
  controlType: FieldControlType.NUMERIC_TEXT_BOX,
563
1109
  dataAttributes: { globalPath: 'dynamicFields.asyncSelect.pageSize' },
564
1110
  },
565
1111
  {
566
1112
  name: safeName('dynamicFields.asyncSelect.useCursor'),
567
- label: 'Async Select: usar cursor',
1113
+ label: tx('Async Select: use cursor'),
568
1114
  controlType: FieldControlType.TOGGLE,
569
1115
  dataAttributes: { globalPath: 'dynamicFields.asyncSelect.useCursor' },
570
1116
  },
571
1117
  // AI
572
1118
  {
573
1119
  name: safeName('ai.provider'),
574
- label: 'Provedor LLM',
1120
+ label: tx('LLM provider'),
575
1121
  controlType: FieldControlType.SELECT,
576
1122
  selectOptions: [],
577
- hint: 'Selecione o provedor para listar modelos e testar a chave.',
1123
+ hint: tx('Select the provider to list models and test the key.'),
578
1124
  dataAttributes: { globalPath: 'ai.provider' },
579
1125
  },
580
1126
  {
581
1127
  name: safeName('ai.apiKey'),
582
- label: 'Chave de API',
1128
+ label: tx('API key'),
583
1129
  controlType: FieldControlType.INPUT,
584
- hint: 'Informe a chave do provedor selecionado para testar.',
1130
+ hint: tx('Enter the selected provider key to test.'),
585
1131
  dataAttributes: { globalPath: 'ai.apiKey' },
586
1132
  },
587
1133
  {
588
1134
  name: safeName('ai.model'),
589
- label: 'Modelo de IA',
1135
+ label: tx('AI model'),
590
1136
  controlType: FieldControlType.SEARCHABLE_SELECT,
591
1137
  selectOptions: [],
592
1138
  resourcePath: '',
@@ -594,113 +1140,113 @@ function buildGlobalConfigFormConfig() {
594
1140
  },
595
1141
  {
596
1142
  name: safeName('ai.temperature'),
597
- label: 'Temperatura (Criatividade)',
1143
+ label: tx('Temperature (Creativity)'),
598
1144
  controlType: FieldControlType.NUMERIC_TEXT_BOX,
599
- hint: '0.0 (determinístico) a 1.0 (criativo)',
1145
+ hint: tx('0.0 (deterministic) to 1.0 (creative)'),
600
1146
  dataAttributes: { globalPath: 'ai.temperature' },
601
1147
  },
602
1148
  {
603
1149
  name: safeName('ai.maxTokens'),
604
- label: 'Max tokens',
1150
+ label: tx('Max tokens'),
605
1151
  controlType: FieldControlType.NUMERIC_TEXT_BOX,
606
- hint: 'Limite maximo de tokens na resposta.',
1152
+ hint: tx('Maximum token limit for the response.'),
607
1153
  dataAttributes: { globalPath: 'ai.maxTokens' },
608
1154
  },
609
1155
  {
610
1156
  name: safeName('ai.riskPolicy'),
611
- label: 'Assistente: validação de risco',
1157
+ label: tx('Assistant: risk validation'),
612
1158
  controlType: FieldControlType.SELECT,
613
1159
  selectOptions: [
614
- { text: 'Estrito (recomendado)', value: 'strict' },
615
- { text: 'Padrão', value: 'standard' },
1160
+ { text: 'Strict (recommended)', value: 'strict' },
1161
+ { text: tx('Standard'), value: 'standard' },
616
1162
  ],
617
- hint: 'Estrito exige confirmação em risco médio/alto. Padrão segue a indicação retornada pelo backend.',
1163
+ hint: tx('Strict requires confirmation for medium/high risk. Standard follows the indication returned by the backend.'),
618
1164
  dataAttributes: { globalPath: 'ai.riskPolicy' },
619
1165
  },
620
1166
  {
621
1167
  name: safeName('ai.embedding.useSameAsLlm'),
622
- label: 'Embeddings: usar mesmo LLM',
1168
+ label: tx('Use same LLM for embeddings'),
623
1169
  controlType: FieldControlType.TOGGLE,
624
- hint: 'Replica provedor e chave do LLM para embeddings.',
1170
+ hint: tx('Replicates the LLM provider and key for embeddings.'),
625
1171
  dataAttributes: { globalPath: 'ai.embedding.useSameAsLlm' },
626
1172
  },
627
1173
  {
628
1174
  name: safeName('ai.embedding.provider'),
629
- label: 'Embeddings: provedor',
1175
+ label: tx('Embeddings provider'),
630
1176
  controlType: FieldControlType.SELECT,
631
1177
  selectOptions: [],
632
- hint: 'Provedor usado para gerar embeddings (RAG).',
1178
+ hint: tx('Provider used to generate embeddings (RAG).'),
633
1179
  dataAttributes: { globalPath: 'ai.embedding.provider' },
634
1180
  },
635
1181
  {
636
1182
  name: safeName('ai.embedding.apiKey'),
637
- label: 'Embeddings: chave de API',
1183
+ label: tx('Embeddings API key'),
638
1184
  controlType: FieldControlType.INPUT,
639
- hint: 'Chave do provedor de embeddings.',
1185
+ hint: tx('Embeddings provider key.'),
640
1186
  dataAttributes: { globalPath: 'ai.embedding.apiKey' },
641
1187
  },
642
1188
  {
643
1189
  name: safeName('ai.embedding.model'),
644
- label: 'Embeddings: modelo',
1190
+ label: tx('Embeddings model'),
645
1191
  controlType: FieldControlType.SEARCHABLE_SELECT,
646
1192
  selectOptions: [
647
1193
  { text: 'text-embedding-3-large', value: 'text-embedding-3-large' },
648
1194
  { text: 'text-embedding-3-small', value: 'text-embedding-3-small' },
649
1195
  { text: 'text-embedding-ada-002 (legacy)', value: 'text-embedding-ada-002' },
650
1196
  ],
651
- emptyOptionText: 'Padrão do provedor',
652
- hint: 'Modelos OpenAI para embeddings.',
1197
+ emptyOptionText: tx('Provider default'),
1198
+ hint: tx('OpenAI models for embeddings.'),
653
1199
  dataAttributes: { globalPath: 'ai.embedding.model' },
654
1200
  },
655
1201
  {
656
1202
  name: safeName('ai.embedding.dimensions'),
657
- label: 'Embeddings: dimensoes',
1203
+ label: tx('Embeddings: dimensions'),
658
1204
  controlType: FieldControlType.NUMERIC_TEXT_BOX,
659
- hint: 'Dimensoes do vetor de embedding (default 768).',
1205
+ hint: tx('Embedding vector dimensions (default 768).'),
660
1206
  dataAttributes: { globalPath: 'ai.embedding.dimensions' },
661
1207
  },
662
- // Cache
1208
+ // Cache // Cache
663
1209
  {
664
1210
  name: safeName('cache.disableSchemaCache'),
665
- label: 'Desabilitar cache de schema (LocalStorage)',
1211
+ label: tx('Desabilitar cache de schema (LocalStorage)'),
666
1212
  controlType: FieldControlType.TOGGLE,
667
- hint: 'Se ativo, sempre baixa o schema do servidor (ignora cache local). Útil para desenvolvimento.',
1213
+ hint: tx('Se ativo, sempre baixa o schema do servidor (ignora cache local). Útil para desenvolvimento.'),
668
1214
  dataAttributes: { globalPath: 'cache.disableSchemaCache' },
669
1215
  },
670
1216
  // Table
671
1217
  {
672
1218
  name: safeName('table.behavior.pagination.enabled'),
673
- label: 'Paginação habilitada',
1219
+ label: tx('Paginação habilitada'),
674
1220
  controlType: FieldControlType.TOGGLE,
675
1221
  dataAttributes: { globalPath: 'table.behavior.pagination.enabled' },
676
1222
  },
677
1223
  {
678
1224
  name: safeName('table.behavior.pagination.pageSize'),
679
- label: 'Tamanho da página',
1225
+ label: tx('Tamanho da página'),
680
1226
  controlType: FieldControlType.NUMERIC_TEXT_BOX,
681
1227
  dataAttributes: { globalPath: 'table.behavior.pagination.pageSize' },
682
1228
  },
683
1229
  {
684
1230
  name: safeName('table.behavior.filtering.debounceTime'),
685
- label: 'Debounce do filtro (ms)',
1231
+ label: tx('Filter debounce (ms)'),
686
1232
  controlType: FieldControlType.NUMERIC_TEXT_BOX,
687
1233
  dataAttributes: { globalPath: 'table.behavior.filtering.debounceTime' },
688
1234
  },
689
1235
  {
690
1236
  name: safeName('table.behavior.sorting.enabled'),
691
- label: 'Ordenação habilitada',
1237
+ label: tx('Ordenação habilitada'),
692
1238
  controlType: FieldControlType.TOGGLE,
693
1239
  dataAttributes: { globalPath: 'table.behavior.sorting.enabled' },
694
1240
  },
695
1241
  {
696
1242
  name: safeName('table.behavior.selection.enabled'),
697
- label: 'Seleção habilitada',
1243
+ label: tx('Seleção habilitada'),
698
1244
  controlType: FieldControlType.TOGGLE,
699
1245
  dataAttributes: { globalPath: 'table.behavior.selection.enabled' },
700
1246
  },
701
1247
  {
702
1248
  name: safeName('table.behavior.pagination.strategy'),
703
- label: 'Paginação: estratégia',
1249
+ label: tx('Paginação: estratégia'),
704
1250
  controlType: FieldControlType.SELECT,
705
1251
  selectOptions: [
706
1252
  { text: 'client', value: 'client' },
@@ -710,7 +1256,7 @@ function buildGlobalConfigFormConfig() {
710
1256
  },
711
1257
  {
712
1258
  name: safeName('table.behavior.sorting.strategy'),
713
- label: 'Ordenação: estratégia',
1259
+ label: tx('Ordenação: estratégia'),
714
1260
  controlType: FieldControlType.SELECT,
715
1261
  selectOptions: [
716
1262
  { text: 'client', value: 'client' },
@@ -720,114 +1266,113 @@ function buildGlobalConfigFormConfig() {
720
1266
  },
721
1267
  {
722
1268
  name: safeName('table.behavior.pagination.showFirstLastButtons'),
723
- label: 'Paginação: botões Primeiro/Último',
1269
+ label: tx('Paginação: botões Primeiro/Último'),
724
1270
  controlType: FieldControlType.CHECKBOX,
725
1271
  dataAttributes: { globalPath: 'table.behavior.pagination.showFirstLastButtons' },
726
1272
  },
727
1273
  {
728
1274
  name: safeName('table.toolbar.visible'),
729
- label: 'Toolbar visível',
1275
+ label: tx('Toolbar visível'),
730
1276
  controlType: FieldControlType.CHECKBOX,
731
1277
  dataAttributes: { globalPath: 'table.toolbar.visible' },
732
1278
  },
733
1279
  {
734
1280
  name: safeName('table.appearance.density'),
735
- label: 'Aparência: densidade',
1281
+ label: tx('Appearance: density'),
736
1282
  controlType: FieldControlType.SELECT,
737
1283
  selectOptions: [
738
- { text: 'compact', value: 'compact' },
739
- { text: 'comfortable', value: 'comfortable' },
740
- { text: 'spacious', value: 'spacious' },
1284
+ { text: tx('compact'), value: 'compact' },
1285
+ { text: tx('comfortable'), value: 'comfortable' },
1286
+ { text: tx('spacious'), value: 'spacious' },
741
1287
  ],
742
1288
  dataAttributes: { globalPath: 'table.appearance.density' },
743
1289
  },
744
1290
  {
745
1291
  name: safeName('table.appearance.spacing.cellPadding'),
746
- label: 'Aparência: padding das células',
1292
+ label: tx('Appearance: cell padding'),
747
1293
  controlType: FieldControlType.INPUT,
748
- hint: 'Aceita 1 a 4 medidas CSS com unidade, var(...) ou calc(...). Ex.: 6px 12px',
1294
+ hint: tx('Accepts 1 to 4 CSS measures with units, var(...) or calc(...). Example: 6px 12px'),
749
1295
  pattern: TABLE_COMPACT_SPACING_PATTERN,
750
- patternMessage: 'Use 1 a 4 medidas CSS válidas, como 6px 12px.',
1296
+ patternMessage: tx('Use 1 to 4 valid CSS measures, such as 6px 12px.'),
751
1297
  dataAttributes: { globalPath: 'table.appearance.spacing.cellPadding' },
752
1298
  },
753
1299
  {
754
1300
  name: safeName('table.appearance.spacing.headerPadding'),
755
- label: 'Aparência: padding do cabeçalho',
1301
+ label: tx('Appearance: header padding'),
756
1302
  controlType: FieldControlType.INPUT,
757
- hint: 'Aceita 1 a 4 medidas CSS com unidade, var(...) ou calc(...). Ex.: 8px 12px',
1303
+ hint: tx('Accepts 1 to 4 CSS measures with units, var(...) or calc(...). Example: 8px 12px'),
758
1304
  pattern: TABLE_COMPACT_SPACING_PATTERN,
759
- patternMessage: 'Use 1 a 4 medidas CSS válidas, como 8px 12px.',
1305
+ patternMessage: tx('Use 1 to 4 valid CSS measures, such as 8px 12px.'),
760
1306
  dataAttributes: { globalPath: 'table.appearance.spacing.headerPadding' },
761
1307
  },
762
1308
  {
763
1309
  name: safeName('table.appearance.typography.fontSize'),
764
- label: 'Aparência: fonte das células',
1310
+ label: tx('Appearance: cell font size'),
765
1311
  controlType: FieldControlType.INPUT,
766
- hint: 'Aceita uma medida CSS com unidade, var(...) ou calc(...). Ex.: 13px',
1312
+ hint: tx('Accepts a CSS measure with units, var(...) or calc(...). Example: 13px'),
767
1313
  pattern: TABLE_COMPACT_FONT_SIZE_PATTERN,
768
- patternMessage: 'Use uma medida CSS válida, como 13px.',
1314
+ patternMessage: tx('Use a valid CSS measure, such as 13px.'),
769
1315
  dataAttributes: { globalPath: 'table.appearance.typography.fontSize' },
770
1316
  },
771
1317
  {
772
1318
  name: safeName('table.appearance.typography.headerFontSize'),
773
- label: 'Aparência: fonte do cabeçalho',
1319
+ label: tx('Appearance: header font size'),
774
1320
  controlType: FieldControlType.INPUT,
775
- hint: 'Aceita uma medida CSS com unidade, var(...) ou calc(...). Ex.: 13px',
1321
+ hint: tx('Accepts a CSS measure with units, var(...) or calc(...). Example: 13px'),
776
1322
  pattern: TABLE_COMPACT_FONT_SIZE_PATTERN,
777
- patternMessage: 'Use uma medida CSS válida, como 13px.',
1323
+ patternMessage: tx('Use a valid CSS measure, such as 13px.'),
778
1324
  dataAttributes: { globalPath: 'table.appearance.typography.headerFontSize' },
779
- },
780
- {
1325
+ }, {
781
1326
  name: safeName('table.filteringUi.advancedOpenMode'),
782
- label: 'Filtro: modo de abertura',
1327
+ label: tx('Filtro: modo de abertura'),
783
1328
  controlType: FieldControlType.SELECT,
784
1329
  selectOptions: [
785
- { text: 'overlay', value: 'overlay' },
786
- { text: 'modal', value: 'modal' },
787
- { text: 'drawer', value: 'drawer' },
1330
+ { text: tx('overlay'), value: 'overlay' },
1331
+ { text: tx('modal'), value: 'modal' },
1332
+ { text: tx('drawer'), value: 'drawer' },
788
1333
  ],
789
1334
  dataAttributes: { globalPath: 'table.filteringUi.advancedOpenMode' },
790
1335
  },
791
1336
  {
792
1337
  name: safeName('table.filteringUi.overlayVariant'),
793
- label: 'Filtro: overlay (visual)',
1338
+ label: tx('Filtro: overlay (visual)'),
794
1339
  controlType: FieldControlType.SELECT,
795
1340
  selectOptions: [
796
- { text: 'card', value: 'card' },
797
- { text: 'frosted', value: 'frosted' },
1341
+ { text: tx('card'), value: 'card' },
1342
+ { text: tx('frosted'), value: 'frosted' },
798
1343
  ],
799
1344
  dataAttributes: { globalPath: 'table.filteringUi.overlayVariant' },
800
1345
  },
801
1346
  {
802
1347
  name: safeName('table.filteringUi.overlayBackdrop'),
803
- label: 'Filtro: backdrop no overlay',
1348
+ label: tx('Filtro: backdrop no overlay'),
804
1349
  controlType: FieldControlType.CHECKBOX,
805
1350
  dataAttributes: { globalPath: 'table.filteringUi.overlayBackdrop' },
806
1351
  },
807
1352
  // Table messages - confirmations
808
- { name: safeName('table.messages.actions.confirmations.delete'), label: 'Excluir — confirmação', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.confirmations.delete' } },
809
- { name: safeName('table.messages.actions.confirmations.deleteMultiple'), label: 'Excluir (múltiplos) — confirmação', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.confirmations.deleteMultiple' } },
810
- { name: safeName('table.messages.actions.confirmations.save'), label: 'Salvar — confirmação', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.confirmations.save' } },
811
- { name: safeName('table.messages.actions.confirmations.cancel'), label: 'Cancelar — confirmação', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.confirmations.cancel' } },
812
- { name: safeName('table.messages.actions.confirmations.export'), label: 'Exportar — confirmação', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.confirmations.export' } },
1353
+ { name: safeName('table.messages.actions.confirmations.delete'), label: tx('Excluir — confirmação'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.confirmations.delete' } },
1354
+ { name: safeName('table.messages.actions.confirmations.deleteMultiple'), label: tx('Excluir (múltiplos) — confirmação'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.confirmations.deleteMultiple' } },
1355
+ { name: safeName('table.messages.actions.confirmations.save'), label: tx('Salvar — confirmação'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.confirmations.save' } },
1356
+ { name: safeName('table.messages.actions.confirmations.cancel'), label: tx('Cancelar — confirmação'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.confirmations.cancel' } },
1357
+ { name: safeName('table.messages.actions.confirmations.export'), label: tx('Exportar — confirmação'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.confirmations.export' } },
813
1358
  // Table messages - progress
814
- { name: safeName('table.messages.actions.progress.delete'), label: 'Excluir — progresso', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.progress.delete' } },
815
- { name: safeName('table.messages.actions.progress.deleteMultiple'), label: 'Excluir (múltiplos) — progresso', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.progress.deleteMultiple' } },
1359
+ { name: safeName('table.messages.actions.progress.delete'), label: tx('Excluir — progresso'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.progress.delete' } },
1360
+ { name: safeName('table.messages.actions.progress.deleteMultiple'), label: tx('Excluir (múltiplos) — progresso'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.progress.deleteMultiple' } },
816
1361
  // Table messages - success
817
- { name: safeName('table.messages.actions.success.delete'), label: 'Excluir — sucesso', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.success.delete' } },
818
- { name: safeName('table.messages.actions.success.save'), label: 'Salvar — sucesso', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.success.save' } },
819
- { name: safeName('table.messages.actions.success.export'), label: 'Exportar — sucesso', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.success.export' } },
820
- { name: safeName('table.messages.actions.success.import'), label: 'Importar — sucesso', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.success.import' } },
1362
+ { name: safeName('table.messages.actions.success.delete'), label: tx('Excluir — sucesso'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.success.delete' } },
1363
+ { name: safeName('table.messages.actions.success.save'), label: tx('Salvar — sucesso'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.success.save' } },
1364
+ { name: safeName('table.messages.actions.success.export'), label: tx('Exportar — sucesso'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.success.export' } },
1365
+ { name: safeName('table.messages.actions.success.import'), label: tx('Importar — sucesso'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.success.import' } },
821
1366
  // Table messages - errors
822
- { name: safeName('table.messages.actions.errors.delete'), label: 'Excluir — erro', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.errors.delete' } },
823
- { name: safeName('table.messages.actions.errors.save'), label: 'Salvar — erro', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.errors.save' } },
824
- { name: safeName('table.messages.actions.errors.export'), label: 'Exportar — erro', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.errors.export' } },
825
- { name: safeName('table.messages.actions.errors.network'), label: 'Rede — erro', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.errors.network' } },
826
- { name: safeName('table.messages.actions.errors.permission'), label: 'Permissão — erro', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.errors.permission' } },
827
- // Dialog Defaults by type
1367
+ { name: safeName('table.messages.actions.errors.delete'), label: tx('Excluir — erro'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.errors.delete' } },
1368
+ { name: safeName('table.messages.actions.errors.save'), label: tx('Salvar — erro'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.errors.save' } },
1369
+ { name: safeName('table.messages.actions.errors.export'), label: tx('Exportar — erro'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.errors.export' } },
1370
+ { name: safeName('table.messages.actions.errors.network'), label: tx('Rede — erro'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.errors.network' } },
1371
+ { name: safeName('table.messages.actions.errors.permission'), label: tx('Permissão — erro'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'table.messages.actions.errors.permission' } },
1372
+ // Dialog - Defaults by type
828
1373
  {
829
1374
  name: safeName('dialog.defaults.confirm.ariaRole'),
830
- label: 'Dialog Defaults: confirm ariaRole',
1375
+ label: tx('Dialog defaults: confirm - ariaRole'),
831
1376
  controlType: FieldControlType.SELECT,
832
1377
  selectOptions: [
833
1378
  { text: 'dialog', value: 'dialog' },
@@ -835,24 +1380,24 @@ function buildGlobalConfigFormConfig() {
835
1380
  ],
836
1381
  dataAttributes: { globalPath: 'dialog.defaults.confirm.ariaRole' },
837
1382
  },
838
- { name: safeName('dialog.defaults.confirm.title'), label: 'Dialog Defaults: confirm título', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.defaults.confirm.title' } },
839
- { name: safeName('dialog.defaults.confirm.icon'), label: 'Dialog Defaults: confirm ícone', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.defaults.confirm.icon' } },
840
- { name: safeName('dialog.defaults.confirm.message'), label: 'Dialog Defaults: confirm mensagem', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.defaults.confirm.message' } },
841
- { name: safeName('dialog.defaults.confirm.themeColor'), label: 'Dialog Defaults: confirm themeColor', controlType: FieldControlType.SELECT, selectOptions: [{ text: 'light', value: 'light' }, { text: 'primary', value: 'primary' }, { text: 'dark', value: 'dark' }], dataAttributes: { globalPath: 'dialog.defaults.confirm.themeColor' } },
1383
+ { name: safeName('dialog.defaults.confirm.title'), label: tx('Dialog defaults: confirm - title'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.defaults.confirm.title' } },
1384
+ { name: safeName('dialog.defaults.confirm.icon'), label: tx('Dialog defaults: confirm - icon'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.defaults.confirm.icon' } },
1385
+ { name: safeName('dialog.defaults.confirm.message'), label: tx('Dialog defaults: confirm - message'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.defaults.confirm.message' } },
1386
+ { name: safeName('dialog.defaults.confirm.themeColor'), label: tx('Dialog defaults: confirm - themeColor'), controlType: FieldControlType.SELECT, selectOptions: [{ text: 'light', value: 'light' }, { text: 'primary', value: 'primary' }, { text: 'dark', value: 'dark' }], dataAttributes: { globalPath: 'dialog.defaults.confirm.themeColor' } },
842
1387
  {
843
1388
  name: safeName('dialog.defaults.confirm.closeOnBackdropClick'),
844
- label: 'Dialog Defaults: confirm fechar ao clicar no backdrop',
1389
+ label: tx('Dialog defaults: confirm - close on backdrop click'),
845
1390
  controlType: FieldControlType.TOGGLE,
846
1391
  dataAttributes: { globalPath: 'dialog.defaults.confirm.closeOnBackdropClick' },
847
1392
  },
848
- { name: safeName('dialog.defaults.confirm.disableClose'), label: 'Dialog Defaults: confirm disableClose', controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.defaults.confirm.disableClose' } },
849
- { name: safeName('dialog.defaults.confirm.hasBackdrop'), label: 'Dialog Defaults: confirm hasBackdrop', controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.defaults.confirm.hasBackdrop' } },
850
- { name: safeName('dialog.defaults.confirm.panelClass'), label: 'Dialog Defaults: confirm panelClass', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.defaults.confirm.panelClass' } },
851
- { name: safeName('dialog.defaults.confirm.styles'), label: 'Dialog Defaults: confirm styles (JSON)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.defaults.confirm.styles', monospace: true }, hint: 'Ex.: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)"}' },
852
- { name: safeName('dialog.defaults.confirm.animation'), label: 'Dialog Defaults: confirm animation (JSON/boolean)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.defaults.confirm.animation', monospace: true } },
1393
+ { name: safeName('dialog.defaults.confirm.disableClose'), label: tx('Dialog defaults: confirm - disableClose'), controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.defaults.confirm.disableClose' } },
1394
+ { name: safeName('dialog.defaults.confirm.hasBackdrop'), label: tx('Dialog defaults: confirm - hasBackdrop'), controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.defaults.confirm.hasBackdrop' } },
1395
+ { name: safeName('dialog.defaults.confirm.panelClass'), label: tx('Dialog defaults: confirm - panelClass'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.defaults.confirm.panelClass' } },
1396
+ { name: safeName('dialog.defaults.confirm.styles'), label: tx('Dialog defaults: confirm - styles (JSON)'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.defaults.confirm.styles', monospace: true }, hint: tx('Example JSON: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)"}') },
1397
+ { name: safeName('dialog.defaults.confirm.animation'), label: tx('Dialog defaults: confirm - animation (JSON/boolean)'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.defaults.confirm.animation', monospace: true } },
853
1398
  {
854
1399
  name: safeName('dialog.defaults.alert.ariaRole'),
855
- label: 'Dialog Defaults: alert ariaRole',
1400
+ label: tx('Dialog defaults: alert - ariaRole'),
856
1401
  controlType: FieldControlType.SELECT,
857
1402
  selectOptions: [
858
1403
  { text: 'dialog', value: 'dialog' },
@@ -860,24 +1405,24 @@ function buildGlobalConfigFormConfig() {
860
1405
  ],
861
1406
  dataAttributes: { globalPath: 'dialog.defaults.alert.ariaRole' },
862
1407
  },
863
- { name: safeName('dialog.defaults.alert.title'), label: 'Dialog Defaults: alert título', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.defaults.alert.title' } },
864
- { name: safeName('dialog.defaults.alert.icon'), label: 'Dialog Defaults: alert ícone', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.defaults.alert.icon' } },
865
- { name: safeName('dialog.defaults.alert.message'), label: 'Dialog Defaults: alert mensagem', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.defaults.alert.message' } },
866
- { name: safeName('dialog.defaults.alert.themeColor'), label: 'Dialog Defaults: alert themeColor', controlType: FieldControlType.SELECT, selectOptions: [{ text: 'light', value: 'light' }, { text: 'primary', value: 'primary' }, { text: 'dark', value: 'dark' }], dataAttributes: { globalPath: 'dialog.defaults.alert.themeColor' } },
1408
+ { name: safeName('dialog.defaults.alert.title'), label: tx('Dialog defaults: alert - title'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.defaults.alert.title' } },
1409
+ { name: safeName('dialog.defaults.alert.icon'), label: tx('Dialog defaults: alert - icon'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.defaults.alert.icon' } },
1410
+ { name: safeName('dialog.defaults.alert.message'), label: tx('Dialog defaults: alert - message'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.defaults.alert.message' } },
1411
+ { name: safeName('dialog.defaults.alert.themeColor'), label: tx('Dialog defaults: alert - themeColor'), controlType: FieldControlType.SELECT, selectOptions: [{ text: 'light', value: 'light' }, { text: 'primary', value: 'primary' }, { text: 'dark', value: 'dark' }], dataAttributes: { globalPath: 'dialog.defaults.alert.themeColor' } },
867
1412
  {
868
1413
  name: safeName('dialog.defaults.alert.closeOnBackdropClick'),
869
- label: 'Dialog Defaults: alert fechar ao clicar no backdrop',
1414
+ label: tx('Dialog defaults: alert - close on backdrop click'),
870
1415
  controlType: FieldControlType.TOGGLE,
871
1416
  dataAttributes: { globalPath: 'dialog.defaults.alert.closeOnBackdropClick' },
872
1417
  },
873
- { name: safeName('dialog.defaults.alert.disableClose'), label: 'Dialog Defaults: alert disableClose', controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.defaults.alert.disableClose' } },
874
- { name: safeName('dialog.defaults.alert.hasBackdrop'), label: 'Dialog Defaults: alert hasBackdrop', controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.defaults.alert.hasBackdrop' } },
875
- { name: safeName('dialog.defaults.alert.panelClass'), label: 'Dialog Defaults: alert panelClass', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.defaults.alert.panelClass' } },
876
- { name: safeName('dialog.defaults.alert.styles'), label: 'Dialog Defaults: alert styles (JSON)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.defaults.alert.styles', monospace: true }, hint: 'Ex.: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)"}' },
877
- { name: safeName('dialog.defaults.alert.animation'), label: 'Dialog Defaults: alert animation (JSON/boolean)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.defaults.alert.animation', monospace: true } },
1418
+ { name: safeName('dialog.defaults.alert.disableClose'), label: tx('Dialog defaults: alert - disableClose'), controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.defaults.alert.disableClose' } },
1419
+ { name: safeName('dialog.defaults.alert.hasBackdrop'), label: tx('Dialog defaults: alert - hasBackdrop'), controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.defaults.alert.hasBackdrop' } },
1420
+ { name: safeName('dialog.defaults.alert.panelClass'), label: tx('Dialog defaults: alert - panelClass'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.defaults.alert.panelClass' } },
1421
+ { name: safeName('dialog.defaults.alert.styles'), label: tx('Dialog defaults: alert - styles (JSON)'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.defaults.alert.styles', monospace: true }, hint: tx('Example JSON: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)"}') },
1422
+ { name: safeName('dialog.defaults.alert.animation'), label: tx('Dialog defaults: alert - animation (JSON/boolean)'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.defaults.alert.animation', monospace: true } },
878
1423
  {
879
1424
  name: safeName('dialog.defaults.prompt.ariaRole'),
880
- label: 'Dialog Defaults: prompt ariaRole',
1425
+ label: tx('Dialog defaults: prompt - ariaRole'),
881
1426
  controlType: FieldControlType.SELECT,
882
1427
  selectOptions: [
883
1428
  { text: 'dialog', value: 'dialog' },
@@ -885,79 +1430,79 @@ function buildGlobalConfigFormConfig() {
885
1430
  ],
886
1431
  dataAttributes: { globalPath: 'dialog.defaults.prompt.ariaRole' },
887
1432
  },
888
- { name: safeName('dialog.defaults.prompt.title'), label: 'Dialog Defaults: prompt título', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.defaults.prompt.title' } },
889
- { name: safeName('dialog.defaults.prompt.icon'), label: 'Dialog Defaults: prompt ícone', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.defaults.prompt.icon' } },
890
- { name: safeName('dialog.defaults.prompt.message'), label: 'Dialog Defaults: prompt mensagem', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.defaults.prompt.message' } },
891
- { name: safeName('dialog.defaults.prompt.themeColor'), label: 'Dialog Defaults: prompt themeColor', controlType: FieldControlType.SELECT, selectOptions: [{ text: 'light', value: 'light' }, { text: 'primary', value: 'primary' }, { text: 'dark', value: 'dark' }], dataAttributes: { globalPath: 'dialog.defaults.prompt.themeColor' } },
1433
+ { name: safeName('dialog.defaults.prompt.title'), label: tx('Dialog defaults: prompt - title'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.defaults.prompt.title' } },
1434
+ { name: safeName('dialog.defaults.prompt.icon'), label: tx('Dialog defaults: prompt - icon'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.defaults.prompt.icon' } },
1435
+ { name: safeName('dialog.defaults.prompt.message'), label: tx('Dialog defaults: prompt - message'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.defaults.prompt.message' } },
1436
+ { name: safeName('dialog.defaults.prompt.themeColor'), label: tx('Dialog defaults: prompt - themeColor'), controlType: FieldControlType.SELECT, selectOptions: [{ text: 'light', value: 'light' }, { text: 'primary', value: 'primary' }, { text: 'dark', value: 'dark' }], dataAttributes: { globalPath: 'dialog.defaults.prompt.themeColor' } },
892
1437
  {
893
1438
  name: safeName('dialog.defaults.prompt.closeOnBackdropClick'),
894
- label: 'Dialog Defaults: prompt fechar ao clicar no backdrop',
1439
+ label: tx('Dialog defaults: prompt - close on backdrop click'),
895
1440
  controlType: FieldControlType.TOGGLE,
896
1441
  dataAttributes: { globalPath: 'dialog.defaults.prompt.closeOnBackdropClick' },
897
1442
  },
898
- { name: safeName('dialog.defaults.prompt.disableClose'), label: 'Dialog Defaults: prompt disableClose', controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.defaults.prompt.disableClose' } },
899
- { name: safeName('dialog.defaults.prompt.hasBackdrop'), label: 'Dialog Defaults: prompt hasBackdrop', controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.defaults.prompt.hasBackdrop' } },
900
- { name: safeName('dialog.defaults.prompt.panelClass'), label: 'Dialog Defaults: prompt panelClass', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.defaults.prompt.panelClass' } },
901
- { name: safeName('dialog.defaults.prompt.styles'), label: 'Dialog Defaults: prompt styles (JSON)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.defaults.prompt.styles', monospace: true }, hint: 'Ex.: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)"}' },
902
- { name: safeName('dialog.defaults.prompt.animation'), label: 'Dialog Defaults: prompt animation (JSON/boolean)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.defaults.prompt.animation', monospace: true } },
903
- // Dialog Variants per profile (danger, info, success, question, error)
1443
+ { name: safeName('dialog.defaults.prompt.disableClose'), label: tx('Dialog defaults: prompt - disableClose'), controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.defaults.prompt.disableClose' } },
1444
+ { name: safeName('dialog.defaults.prompt.hasBackdrop'), label: tx('Dialog defaults: prompt - hasBackdrop'), controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.defaults.prompt.hasBackdrop' } },
1445
+ { name: safeName('dialog.defaults.prompt.panelClass'), label: tx('Dialog defaults: prompt - panelClass'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.defaults.prompt.panelClass' } },
1446
+ { name: safeName('dialog.defaults.prompt.styles'), label: tx('Dialog defaults: prompt - styles (JSON)'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.defaults.prompt.styles', monospace: true }, hint: tx('Example JSON: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)"}') },
1447
+ { name: safeName('dialog.defaults.prompt.animation'), label: tx('Dialog defaults: prompt - animation (JSON/boolean)'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.defaults.prompt.animation', monospace: true } },
1448
+ // Dialog - Variants per profile (danger, info, success, question, error)
904
1449
  // Each variant exposes commonly customized fields + JSON editors for actions/styles
905
1450
  // danger
906
- { name: safeName('dialog.variants.danger.title'), label: 'Variant danger título', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.danger.title' } },
907
- { name: safeName('dialog.variants.danger.icon'), label: 'Variant danger ícone', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.danger.icon' } },
908
- { name: safeName('dialog.variants.danger.message'), label: 'Variant danger mensagem', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.danger.message' } },
909
- { name: safeName('dialog.variants.danger.ariaRole'), label: 'Variant danger ariaRole', controlType: FieldControlType.SELECT, selectOptions: [{ text: 'dialog', value: 'dialog' }, { text: 'alertdialog', value: 'alertdialog' }], dataAttributes: { globalPath: 'dialog.variants.danger.ariaRole' } },
910
- { name: safeName('dialog.variants.danger.closeOnBackdropClick'), label: 'Variant danger fechar ao clicar no backdrop', controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.variants.danger.closeOnBackdropClick' } },
911
- { name: safeName('dialog.variants.danger.panelClass'), label: 'Variant danger panelClass', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.danger.panelClass' } },
912
- { name: safeName('dialog.variants.danger.actions'), label: 'Variant danger actions (JSON array)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.danger.actions', monospace: true }, hint: 'Ex.: [{"id":"cancel","text":"Cancelar","role":"secondary","close":true,"cssClass":"btn"},{"id":"confirm","text":"Excluir","role":"primary","close":true,"cssClass":"btn btn-danger","icon":"delete"}]' },
913
- { name: safeName('dialog.variants.danger.styles'), label: 'Variant danger styles (JSON)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.danger.styles', monospace: true }, hint: 'Ex.: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)","actionButtonRadius":"8px"}' },
914
- { name: safeName('dialog.variants.danger.animation'), label: 'Variant danger animation (JSON/boolean)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.danger.animation', monospace: true } },
1451
+ { name: safeName('dialog.variants.danger.title'), label: tx('Variant danger - title'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.danger.title' } },
1452
+ { name: safeName('dialog.variants.danger.icon'), label: tx('Variant danger - icon'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.danger.icon' } },
1453
+ { name: safeName('dialog.variants.danger.message'), label: tx('Variant danger - message'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.danger.message' } },
1454
+ { name: safeName('dialog.variants.danger.ariaRole'), label: tx('Variant danger - ariaRole'), controlType: FieldControlType.SELECT, selectOptions: [{ text: 'dialog', value: 'dialog' }, { text: 'alertdialog', value: 'alertdialog' }], dataAttributes: { globalPath: 'dialog.variants.danger.ariaRole' } },
1455
+ { name: safeName('dialog.variants.danger.closeOnBackdropClick'), label: tx('Variant danger - close on backdrop click'), controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.variants.danger.closeOnBackdropClick' } },
1456
+ { name: safeName('dialog.variants.danger.panelClass'), label: tx('Variant danger - panelClass'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.danger.panelClass' } },
1457
+ { name: safeName('dialog.variants.danger.actions'), label: tx('Variant danger - actions (JSON array)'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.danger.actions', monospace: true }, hint: tx('Example JSON array: [{"id":"cancel","text":"Cancel","role":"secondary","close":true,"cssClass":"btn"},{"id":"confirm","text":"Delete","role":"primary","close":true,"cssClass":"btn btn-danger","icon":"delete"}]') },
1458
+ { name: safeName('dialog.variants.danger.styles'), label: tx('Variant danger - styles (JSON)'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.danger.styles', monospace: true }, hint: tx('Example JSON: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)","actionButtonRadius":"8px"}') },
1459
+ { name: safeName('dialog.variants.danger.animation'), label: tx('Variant danger - animation (JSON/boolean)'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.danger.animation', monospace: true } },
915
1460
  // info
916
- { name: safeName('dialog.variants.info.title'), label: 'Variant info título', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.info.title' } },
917
- { name: safeName('dialog.variants.info.icon'), label: 'Variant info ícone', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.info.icon' } },
918
- { name: safeName('dialog.variants.info.message'), label: 'Variant info mensagem', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.info.message' } },
919
- { name: safeName('dialog.variants.info.ariaRole'), label: 'Variant info ariaRole', controlType: FieldControlType.SELECT, selectOptions: [{ text: 'dialog', value: 'dialog' }, { text: 'alertdialog', value: 'alertdialog' }], dataAttributes: { globalPath: 'dialog.variants.info.ariaRole' } },
920
- { name: safeName('dialog.variants.info.closeOnBackdropClick'), label: 'Variant info fechar ao clicar no backdrop', controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.variants.info.closeOnBackdropClick' } },
921
- { name: safeName('dialog.variants.info.panelClass'), label: 'Variant info panelClass', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.info.panelClass' } },
922
- { name: safeName('dialog.variants.info.actions'), label: 'Variant info actions (JSON array)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.info.actions', monospace: true } },
923
- { name: safeName('dialog.variants.info.styles'), label: 'Variant info styles (JSON)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.info.styles', monospace: true }, hint: 'Ex.: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)","actionButtonRadius":"8px"}' },
924
- { name: safeName('dialog.variants.info.animation'), label: 'Variant info animation (JSON/boolean)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.info.animation', monospace: true } },
1461
+ { name: safeName('dialog.variants.info.title'), label: tx('Variant info - title'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.info.title' } },
1462
+ { name: safeName('dialog.variants.info.icon'), label: tx('Variant info - icon'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.info.icon' } },
1463
+ { name: safeName('dialog.variants.info.message'), label: tx('Variant info - message'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.info.message' } },
1464
+ { name: safeName('dialog.variants.info.ariaRole'), label: tx('Variant info - ariaRole'), controlType: FieldControlType.SELECT, selectOptions: [{ text: 'dialog', value: 'dialog' }, { text: 'alertdialog', value: 'alertdialog' }], dataAttributes: { globalPath: 'dialog.variants.info.ariaRole' } },
1465
+ { name: safeName('dialog.variants.info.closeOnBackdropClick'), label: tx('Variant info - close on backdrop click'), controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.variants.info.closeOnBackdropClick' } },
1466
+ { name: safeName('dialog.variants.info.panelClass'), label: tx('Variant info - panelClass'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.info.panelClass' } },
1467
+ { name: safeName('dialog.variants.info.actions'), label: tx('Variant info - actions (JSON array)'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.info.actions', monospace: true } },
1468
+ { name: safeName('dialog.variants.info.styles'), label: tx('Variant info - styles (JSON)'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.info.styles', monospace: true }, hint: tx('Example JSON: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)","actionButtonRadius":"8px"}') },
1469
+ { name: safeName('dialog.variants.info.animation'), label: tx('Variant info - animation (JSON/boolean)'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.info.animation', monospace: true } },
925
1470
  // success
926
- { name: safeName('dialog.variants.success.title'), label: 'Variant success título', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.success.title' } },
927
- { name: safeName('dialog.variants.success.icon'), label: 'Variant success ícone', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.success.icon' } },
928
- { name: safeName('dialog.variants.success.message'), label: 'Variant success mensagem', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.success.message' } },
929
- { name: safeName('dialog.variants.success.ariaRole'), label: 'Variant success ariaRole', controlType: FieldControlType.SELECT, selectOptions: [{ text: 'dialog', value: 'dialog' }, { text: 'alertdialog', value: 'alertdialog' }], dataAttributes: { globalPath: 'dialog.variants.success.ariaRole' } },
930
- { name: safeName('dialog.variants.success.closeOnBackdropClick'), label: 'Variant success fechar ao clicar no backdrop', controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.variants.success.closeOnBackdropClick' } },
931
- { name: safeName('dialog.variants.success.panelClass'), label: 'Variant success panelClass', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.success.panelClass' } },
932
- { name: safeName('dialog.variants.success.actions'), label: 'Variant success actions (JSON array)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.success.actions', monospace: true } },
933
- { name: safeName('dialog.variants.success.styles'), label: 'Variant success styles (JSON)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.success.styles', monospace: true }, hint: 'Ex.: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)","actionButtonRadius":"8px"}' },
934
- { name: safeName('dialog.variants.success.animation'), label: 'Variant success animation (JSON/boolean)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.success.animation', monospace: true } },
1471
+ { name: safeName('dialog.variants.success.title'), label: tx('Variant success - title'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.success.title' } },
1472
+ { name: safeName('dialog.variants.success.icon'), label: tx('Variant success - icon'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.success.icon' } },
1473
+ { name: safeName('dialog.variants.success.message'), label: tx('Variant success - message'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.success.message' } },
1474
+ { name: safeName('dialog.variants.success.ariaRole'), label: tx('Variant success - ariaRole'), controlType: FieldControlType.SELECT, selectOptions: [{ text: 'dialog', value: 'dialog' }, { text: 'alertdialog', value: 'alertdialog' }], dataAttributes: { globalPath: 'dialog.variants.success.ariaRole' } },
1475
+ { name: safeName('dialog.variants.success.closeOnBackdropClick'), label: tx('Variant success - close on backdrop click'), controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.variants.success.closeOnBackdropClick' } },
1476
+ { name: safeName('dialog.variants.success.panelClass'), label: tx('Variant success - panelClass'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.success.panelClass' } },
1477
+ { name: safeName('dialog.variants.success.actions'), label: tx('Variant success - actions (JSON array)'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.success.actions', monospace: true } },
1478
+ { name: safeName('dialog.variants.success.styles'), label: tx('Variant success - styles (JSON)'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.success.styles', monospace: true }, hint: tx('Example JSON: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)","actionButtonRadius":"8px"}') },
1479
+ { name: safeName('dialog.variants.success.animation'), label: tx('Variant success - animation (JSON/boolean)'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.success.animation', monospace: true } },
935
1480
  // question
936
- { name: safeName('dialog.variants.question.title'), label: 'Variant question título', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.question.title' } },
937
- { name: safeName('dialog.variants.question.icon'), label: 'Variant question ícone', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.question.icon' } },
938
- { name: safeName('dialog.variants.question.message'), label: 'Variant question mensagem', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.question.message' } },
939
- { name: safeName('dialog.variants.question.ariaRole'), label: 'Variant question ariaRole', controlType: FieldControlType.SELECT, selectOptions: [{ text: 'dialog', value: 'dialog' }, { text: 'alertdialog', value: 'alertdialog' }], dataAttributes: { globalPath: 'dialog.variants.question.ariaRole' } },
940
- { name: safeName('dialog.variants.question.closeOnBackdropClick'), label: 'Variant question fechar ao clicar no backdrop', controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.variants.question.closeOnBackdropClick' } },
941
- { name: safeName('dialog.variants.question.panelClass'), label: 'Variant question panelClass', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.question.panelClass' } },
942
- { name: safeName('dialog.variants.question.actions'), label: 'Variant question actions (JSON array)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.question.actions', monospace: true } },
943
- { name: safeName('dialog.variants.question.styles'), label: 'Variant question styles (JSON)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.question.styles', monospace: true }, hint: 'Ex.: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)","actionButtonRadius":"8px"}' },
944
- { name: safeName('dialog.variants.question.animation'), label: 'Variant question animation (JSON/boolean)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.question.animation', monospace: true } },
1481
+ { name: safeName('dialog.variants.question.title'), label: tx('Variant question - title'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.question.title' } },
1482
+ { name: safeName('dialog.variants.question.icon'), label: tx('Variant question - icon'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.question.icon' } },
1483
+ { name: safeName('dialog.variants.question.message'), label: tx('Variant question - message'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.question.message' } },
1484
+ { name: safeName('dialog.variants.question.ariaRole'), label: tx('Variant question - ariaRole'), controlType: FieldControlType.SELECT, selectOptions: [{ text: 'dialog', value: 'dialog' }, { text: 'alertdialog', value: 'alertdialog' }], dataAttributes: { globalPath: 'dialog.variants.question.ariaRole' } },
1485
+ { name: safeName('dialog.variants.question.closeOnBackdropClick'), label: tx('Variant question - close on backdrop click'), controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.variants.question.closeOnBackdropClick' } },
1486
+ { name: safeName('dialog.variants.question.panelClass'), label: tx('Variant question - panelClass'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.question.panelClass' } },
1487
+ { name: safeName('dialog.variants.question.actions'), label: tx('Variant question - actions (JSON array)'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.question.actions', monospace: true } },
1488
+ { name: safeName('dialog.variants.question.styles'), label: tx('Variant question - styles (JSON)'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.question.styles', monospace: true }, hint: tx('Example JSON: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)","actionButtonRadius":"8px"}') },
1489
+ { name: safeName('dialog.variants.question.animation'), label: tx('Variant question - animation (JSON/boolean)'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.question.animation', monospace: true } },
945
1490
  // error
946
- { name: safeName('dialog.variants.error.title'), label: 'Variant error título', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.error.title' } },
947
- { name: safeName('dialog.variants.error.icon'), label: 'Variant error ícone', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.error.icon' } },
948
- { name: safeName('dialog.variants.error.message'), label: 'Variant error mensagem', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.error.message' } },
949
- { name: safeName('dialog.variants.error.ariaRole'), label: 'Variant error ariaRole', controlType: FieldControlType.SELECT, selectOptions: [{ text: 'dialog', value: 'dialog' }, { text: 'alertdialog', value: 'alertdialog' }], dataAttributes: { globalPath: 'dialog.variants.error.ariaRole' } },
950
- { name: safeName('dialog.variants.error.closeOnBackdropClick'), label: 'Variant error fechar ao clicar no backdrop', controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.variants.error.closeOnBackdropClick' } },
951
- { name: safeName('dialog.variants.error.panelClass'), label: 'Variant error panelClass', controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.error.panelClass' } },
952
- { name: safeName('dialog.variants.error.actions'), label: 'Variant error actions (JSON array)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.error.actions', monospace: true } },
953
- { name: safeName('dialog.variants.error.styles'), label: 'Variant error styles (JSON)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.error.styles', monospace: true }, hint: 'Ex.: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)","actionButtonRadius":"8px"}' },
954
- { name: safeName('dialog.variants.error.animation'), label: 'Variant error animation (JSON/boolean)', controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.error.animation', monospace: true } },
1491
+ { name: safeName('dialog.variants.error.title'), label: tx('Variant error - title'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.error.title' } },
1492
+ { name: safeName('dialog.variants.error.icon'), label: tx('Variant error - icon'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.error.icon' } },
1493
+ { name: safeName('dialog.variants.error.message'), label: tx('Variant error - message'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.error.message' } },
1494
+ { name: safeName('dialog.variants.error.ariaRole'), label: tx('Variant error - ariaRole'), controlType: FieldControlType.SELECT, selectOptions: [{ text: 'dialog', value: 'dialog' }, { text: 'alertdialog', value: 'alertdialog' }], dataAttributes: { globalPath: 'dialog.variants.error.ariaRole' } },
1495
+ { name: safeName('dialog.variants.error.closeOnBackdropClick'), label: tx('Variant error - close on backdrop click'), controlType: FieldControlType.TOGGLE, dataAttributes: { globalPath: 'dialog.variants.error.closeOnBackdropClick' } },
1496
+ { name: safeName('dialog.variants.error.panelClass'), label: tx('Variant error - panelClass'), controlType: FieldControlType.INPUT, dataAttributes: { globalPath: 'dialog.variants.error.panelClass' } },
1497
+ { name: safeName('dialog.variants.error.actions'), label: tx('Variant error - actions (JSON array)'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.error.actions', monospace: true } },
1498
+ { name: safeName('dialog.variants.error.styles'), label: tx('Variant error - styles (JSON)'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.error.styles', monospace: true }, hint: tx('Example JSON: {"containerColor":"var(--md-sys-color-surface)","containerBorderColor":"var(--md-sys-color-outline-variant)","backdropColor":"var(--md-sys-color-scrim)","actionButtonRadius":"8px"}') },
1499
+ { name: safeName('dialog.variants.error.animation'), label: tx('Variant error - animation (JSON/boolean)'), controlType: FieldControlType.TEXTAREA, dataAttributes: { globalPath: 'dialog.variants.error.animation', monospace: true } },
955
1500
  ];
956
1501
  const cfg = {
957
1502
  sections: [
958
1503
  {
959
1504
  id: 'crud',
960
- title: 'CRUD',
1505
+ title: tx('CRUD'),
961
1506
  rows: [
962
1507
  {
963
1508
  id: 'crud-row-1',
@@ -984,7 +1529,7 @@ function buildGlobalConfigFormConfig() {
984
1529
  },
985
1530
  {
986
1531
  id: 'dynamic-fields',
987
- title: 'Dynamic Fields',
1532
+ title: tx('Dynamic Fields'),
988
1533
  rows: [
989
1534
  {
990
1535
  id: 'df-row-1',
@@ -1005,7 +1550,7 @@ function buildGlobalConfigFormConfig() {
1005
1550
  },
1006
1551
  {
1007
1552
  id: 'ai-credentials',
1008
- title: 'Credenciais', // Internal title, UI might override
1553
+ title: tx('Credentials'), // Internal title, UI might override
1009
1554
  rows: [
1010
1555
  {
1011
1556
  id: 'ai-cred-row-1',
@@ -1018,7 +1563,7 @@ function buildGlobalConfigFormConfig() {
1018
1563
  },
1019
1564
  {
1020
1565
  id: 'ai-model',
1021
- title: 'Modelo & Comportamento',
1566
+ title: tx('Model & Behavior'),
1022
1567
  rows: [
1023
1568
  {
1024
1569
  id: 'ai-mod-row-1',
@@ -1031,7 +1576,7 @@ function buildGlobalConfigFormConfig() {
1031
1576
  },
1032
1577
  {
1033
1578
  id: 'ai-embedding',
1034
- title: 'Embeddings',
1579
+ title: tx('Embeddings (RAG)'),
1035
1580
  rows: [
1036
1581
  {
1037
1582
  id: 'ai-embed-row-1',
@@ -1045,7 +1590,7 @@ function buildGlobalConfigFormConfig() {
1045
1590
  },
1046
1591
  {
1047
1592
  id: 'cache',
1048
- title: 'Cache & Persistência',
1593
+ title: tx('Cache & Persistência'),
1049
1594
  rows: [
1050
1595
  {
1051
1596
  id: 'cache-row-1',
@@ -1057,7 +1602,7 @@ function buildGlobalConfigFormConfig() {
1057
1602
  },
1058
1603
  {
1059
1604
  id: 'table',
1060
- title: 'Tabela',
1605
+ title: tx('Tabela'),
1061
1606
  rows: [
1062
1607
  {
1063
1608
  id: 'tbl-row-1',
@@ -1118,7 +1663,7 @@ function buildGlobalConfigFormConfig() {
1118
1663
  },
1119
1664
  {
1120
1665
  id: 'dialog',
1121
- title: 'Dialog',
1666
+ title: tx('Dialog'),
1122
1667
  rows: [
1123
1668
  {
1124
1669
  id: 'dlg-row-defaults',
@@ -1243,9 +1788,9 @@ function buildGlobalConfigFormConfig() {
1243
1788
  actions: {
1244
1789
  placement: 'afterSections',
1245
1790
  // Hide internal form actions; Settings Panel footer handles Apply/Save
1246
- submit: { visible: false, label: 'Salvar' },
1247
- cancel: { visible: false, label: 'Cancelar' },
1248
- reset: { visible: false, label: 'Redefinir', type: 'reset' },
1791
+ submit: { visible: false, label: tx('Salvar') },
1792
+ cancel: { visible: false, label: tx('Cancelar') },
1793
+ reset: { visible: false, label: tx('Redefinir'), type: 'reset' },
1249
1794
  },
1250
1795
  fieldMetadata: fields,
1251
1796
  };
@@ -1271,10 +1816,10 @@ const TABLE_COMPACT_FONT_SIZE_PATHS = new Set([
1271
1816
  'table.appearance.typography.headerFontSize',
1272
1817
  ]);
1273
1818
  const TABLE_COMPACT_FIELD_LABELS = {
1274
- 'table.appearance.spacing.cellPadding': 'padding das células',
1275
- 'table.appearance.spacing.headerPadding': 'padding do cabeçalho',
1276
- 'table.appearance.typography.fontSize': 'fonte das células',
1277
- 'table.appearance.typography.headerFontSize': 'fonte do cabeçalho',
1819
+ 'table.appearance.spacing.cellPadding': 'cell padding',
1820
+ 'table.appearance.spacing.headerPadding': 'header padding',
1821
+ 'table.appearance.typography.fontSize': 'cell font size',
1822
+ 'table.appearance.typography.headerFontSize': 'header font size',
1278
1823
  };
1279
1824
  const TABLE_COMPACT_LENGTH_TOKEN = '(?:0|(?:\\d+|\\d*\\.\\d+)(?:px|rem|em|%|vh|vw|svh|svw|lvh|lvw|dvh|dvw|ch|ex))';
1280
1825
  const TABLE_COMPACT_SPACING_REGEX = new RegExp(`^(?:var\\(.+\\)|calc\\(.+\\)|${TABLE_COMPACT_LENGTH_TOKEN}(?:\\s+${TABLE_COMPACT_LENGTH_TOKEN}){0,3})$`, 'i');
@@ -1301,6 +1846,7 @@ class GlobalConfigEditorComponent {
1301
1846
  hostAiModel;
1302
1847
  hostAiEmbedding;
1303
1848
  destroyRef = inject(DestroyRef);
1849
+ i18n = inject(PraxisI18nService);
1304
1850
  iconPicker = inject(IconPickerService);
1305
1851
  aiApi = inject(AiBackendApiService);
1306
1852
  cdr = inject(ChangeDetectorRef);
@@ -1334,7 +1880,7 @@ class GlobalConfigEditorComponent {
1334
1880
  apiKeyLast4 = null;
1335
1881
  hasStoredApiKey = false;
1336
1882
  hasStoredGlobalConfig = false;
1337
- configSourceLabel = 'Padrões do servidor (env vars)';
1883
+ configSourceLabel = this.tx('Server defaults (env vars)');
1338
1884
  // UX: API Key monitoring
1339
1885
  apiKeyChanged$ = new Subject();
1340
1886
  hasApiKey = false;
@@ -1346,12 +1892,12 @@ class GlobalConfigEditorComponent {
1346
1892
  get apiKeyStatusLabel() {
1347
1893
  const apiKey = this.currentValues?.[this.safeName('ai.apiKey')] || '';
1348
1894
  if (apiKey)
1349
- return 'Chave informada';
1895
+ return this.tx('Key provided');
1350
1896
  if (this.apiKeyLast4)
1351
- return `Chave salva (****${this.apiKeyLast4})`;
1897
+ return this.tx('Saved key (****{{last4}})', { last4: this.apiKeyLast4 });
1352
1898
  if (this.hasStoredApiKey)
1353
- return 'Chave salva';
1354
- return 'Sem chave salva';
1899
+ return this.tx('Saved key');
1900
+ return this.tx('No saved key');
1355
1901
  }
1356
1902
  constructor(admin, snack) {
1357
1903
  this.admin = admin;
@@ -1360,7 +1906,7 @@ class GlobalConfigEditorComponent {
1360
1906
  async ngOnInit() {
1361
1907
  const cfg = this.admin.getEffectiveConfig();
1362
1908
  const flat = this.flatten(cfg);
1363
- this.formConfig = buildGlobalConfigFormConfig();
1909
+ this.formConfig = buildGlobalConfigFormConfig(this.tx.bind(this));
1364
1910
  this.applyConfigSnapshot(flat, { resetForms: false });
1365
1911
  await this.loadProviderCatalog();
1366
1912
  this.applyProviderOptions();
@@ -1505,7 +2051,7 @@ class GlobalConfigEditorComponent {
1505
2051
  const ref = g.host.createComponent(this.dynamicFormCtor);
1506
2052
  ref.setInput('config', g.cfg);
1507
2053
  ref.setInput('mode', 'edit');
1508
- // Não habilitar o modo de edição de layout (canvas) queremos apenas edição de valores
2054
+ // Não habilitar o modo de edição de layout (canvas) - queremos apenas edição de valores
1509
2055
  ref.setInput('enableCustomization', false);
1510
2056
  const inst = ref.instance;
1511
2057
  // FORÇAR estado de sucesso para renderizar o formulário sem resourcePath
@@ -1578,7 +2124,7 @@ class GlobalConfigEditorComponent {
1578
2124
  {
1579
2125
  id: 'gemini',
1580
2126
  label: 'Google Gemini',
1581
- description: 'Modelos rápidos e multimodais do Google.',
2127
+ description: this.tx('Fast multimodal models from Google.'),
1582
2128
  defaultModel: 'gemini-2.0-flash',
1583
2129
  requiresApiKey: true,
1584
2130
  supportsModels: true,
@@ -1588,7 +2134,7 @@ class GlobalConfigEditorComponent {
1588
2134
  {
1589
2135
  id: 'openai',
1590
2136
  label: 'OpenAI',
1591
- description: 'Modelos GPT para texto e chat.',
2137
+ description: this.tx('GPT models for text and chat.'),
1592
2138
  defaultModel: 'gpt-4o-mini',
1593
2139
  requiresApiKey: true,
1594
2140
  supportsModels: true,
@@ -1598,7 +2144,7 @@ class GlobalConfigEditorComponent {
1598
2144
  {
1599
2145
  id: 'xai',
1600
2146
  label: 'xAI (Grok)',
1601
- description: 'Modelos Grok focados em raciocínio.',
2147
+ description: this.tx('Grok models focused on reasoning.'),
1602
2148
  defaultModel: 'grok-2-latest',
1603
2149
  requiresApiKey: true,
1604
2150
  supportsModels: true,
@@ -1608,7 +2154,7 @@ class GlobalConfigEditorComponent {
1608
2154
  {
1609
2155
  id: 'mock',
1610
2156
  label: 'Mock (dev)',
1611
- description: 'Modo local para testes sem chave.',
2157
+ description: this.tx('Local mode for tests without a key.'),
1612
2158
  defaultModel: 'mock-default',
1613
2159
  requiresApiKey: false,
1614
2160
  supportsModels: true,
@@ -1797,8 +2343,8 @@ class GlobalConfigEditorComponent {
1797
2343
  this.hasStoredGlobalConfig = false;
1798
2344
  }
1799
2345
  this.configSourceLabel = this.hasStoredGlobalConfig
1800
- ? 'Configuração salva (UI) sobrescreve os defaults do servidor'
1801
- : 'Padrões do servidor (env vars)';
2346
+ ? this.tx('Saved configuration (UI) - overrides server defaults')
2347
+ : this.tx('Server defaults (env vars)');
1802
2348
  }
1803
2349
  async clearStoredConfig() {
1804
2350
  if (this.isClearingGlobalConfig)
@@ -1813,13 +2359,13 @@ class GlobalConfigEditorComponent {
1813
2359
  this.refreshAiStateAfterConfig();
1814
2360
  await this.refreshStoredConfigState();
1815
2361
  try {
1816
- this.snack.open('Configuração global limpa. Usando defaults do servidor.', undefined, { duration: 2500 });
2362
+ this.snack.open(this.tx('Global config cleared. Using server defaults.'), undefined, { duration: 2500 });
1817
2363
  }
1818
2364
  catch { }
1819
2365
  }
1820
2366
  catch (err) {
1821
2367
  try {
1822
- this.snack.open('Falha ao limpar a configuração global.', undefined, { duration: 4000 });
2368
+ this.snack.open(this.tx('Failed to clear global config.'), undefined, { duration: 4000 });
1823
2369
  }
1824
2370
  catch { }
1825
2371
  }
@@ -1946,10 +2492,10 @@ class GlobalConfigEditorComponent {
1946
2492
  const model = this.availableModels.find(m => m.name.endsWith(modelName));
1947
2493
  if (model) {
1948
2494
  const formatLimit = (n) => n ? (n >= 1000 ? (n / 1000).toFixed(0) + 'k' : n) : 'N/A';
1949
- this.selectedModelDetails = `Modelo: ${model.displayName || modelName} | Tokens (in/out): ${formatLimit(model.inputTokenLimit)}/${formatLimit(model.outputTokenLimit)} | Métodos: ${model.supportedGenerationMethods?.join(', ') || 'N/A'}`;
2495
+ this.selectedModelDetails = this.tx('Model: {{model}} | Tokens (in/out): {{in}}/{{out}} | Methods: {{methods}}', { model: model.displayName || modelName, in: formatLimit(model.inputTokenLimit), out: formatLimit(model.outputTokenLimit), methods: model.supportedGenerationMethods?.join(', ') || 'N/A' });
1950
2496
  }
1951
2497
  else {
1952
- this.selectedModelDetails = 'Detalhes do modelo não disponíveis.';
2498
+ this.selectedModelDetails = this.tx('No model details available.');
1953
2499
  }
1954
2500
  }
1955
2501
  // SettingsPanel expects these methods
@@ -1985,7 +2531,7 @@ class GlobalConfigEditorComponent {
1985
2531
  this.initialValues = { ...this.currentValues };
1986
2532
  this.isDirty$.next(false);
1987
2533
  try {
1988
- this.snack.open('Configurações salvas', undefined, { duration: 2000 });
2534
+ this.snack.open(this.tx('Settings saved'), undefined, { duration: 2000 });
1989
2535
  }
1990
2536
  catch { }
1991
2537
  return partial;
@@ -2008,7 +2554,7 @@ class GlobalConfigEditorComponent {
2008
2554
  const meta = this.findProvider(provider);
2009
2555
  if (meta?.supportsModels === false) {
2010
2556
  if (!silent) {
2011
- this.aiTestResult = { success: false, message: 'Este provedor não oferece catálogo de modelos.' };
2557
+ this.aiTestResult = { success: false, message: this.tx('This provider has no model catalog.') };
2012
2558
  this.cdr.detectChanges();
2013
2559
  }
2014
2560
  return;
@@ -2016,7 +2562,7 @@ class GlobalConfigEditorComponent {
2016
2562
  const requiresKey = meta?.requiresApiKey !== false;
2017
2563
  const hasKey = !!apiKey || this.hasStoredApiKey || !requiresKey;
2018
2564
  if (requiresKey && !hasKey) {
2019
- this.aiTestResult = { success: false, message: 'Insira uma chave de API para buscar modelos.' };
2565
+ this.aiTestResult = { success: false, message: this.tx('Enter an API key to fetch models.') };
2020
2566
  this.cdr.detectChanges();
2021
2567
  return;
2022
2568
  }
@@ -2031,8 +2577,7 @@ class GlobalConfigEditorComponent {
2031
2577
  request.apiKey = apiKey;
2032
2578
  const response = await firstValueFrom(this.aiApi.listModels(request));
2033
2579
  if (!response?.success) {
2034
- const msg = response?.message || 'Erro ao buscar modelos.';
2035
- throw new Error(msg);
2580
+ throw new Error(this.tx('Error fetching models.'));
2036
2581
  }
2037
2582
  const models = response.models || [];
2038
2583
  this.availableModels = models; // Store available models
@@ -2053,9 +2598,9 @@ class GlobalConfigEditorComponent {
2053
2598
  }
2054
2599
  catch (error) {
2055
2600
  this.logger.error('[GlobalConfigEditor] Error fetching models', this.buildLogOptions({ error, provider }, { context: { actionId: 'models.fetch' } }));
2056
- this.aiTestResult = { success: false, message: error.message || 'Erro ao buscar modelos.' };
2601
+ this.aiTestResult = { success: false, message: this.tx('Error fetching models.') };
2057
2602
  if (!silent) {
2058
- this.snack.open('Erro ao buscar modelos. Verifique a chave de API.', 'Fechar', { duration: 5000 });
2603
+ this.snack.open(this.tx('Error fetching models. Check your API key.'), this.tx('Close'), { duration: 5000 });
2059
2604
  }
2060
2605
  }
2061
2606
  finally {
@@ -2085,7 +2630,7 @@ class GlobalConfigEditorComponent {
2085
2630
  this.recreateAiModelForm();
2086
2631
  if (!silent) {
2087
2632
  try {
2088
- this.snack.open(`Lista de modelos atualizada (${modelOptions.length} modelos)`, undefined, { duration: 3000 });
2633
+ this.snack.open(this.tx('Model list updated ({{count}} models)', { count: modelOptions.length }), undefined, { duration: 3000 });
2089
2634
  }
2090
2635
  catch { }
2091
2636
  }
@@ -2103,7 +2648,7 @@ class GlobalConfigEditorComponent {
2103
2648
  const requiresKey = meta?.requiresApiKey !== false;
2104
2649
  const hasKey = !!apiKey || this.hasStoredApiKey || !requiresKey;
2105
2650
  if (requiresKey && !hasKey) {
2106
- this.aiTestResult = { success: false, message: 'Insira uma chave de API para testar.' };
2651
+ this.aiTestResult = { success: false, message: this.tx('Enter an API key to test.') };
2107
2652
  this.isTestingAi = false;
2108
2653
  this.cdr.detectChanges();
2109
2654
  return;
@@ -2114,17 +2659,16 @@ class GlobalConfigEditorComponent {
2114
2659
  request.apiKey = apiKey;
2115
2660
  const response = await firstValueFrom(this.aiApi.testProvider(request));
2116
2661
  if (!response?.success) {
2117
- throw new Error(response?.message || 'Falha ao testar conexao.');
2662
+ throw new Error(this.tx('Failed to test connection.'));
2118
2663
  }
2119
- this.aiTestResult = { success: true, message: response.message || 'Conexao estabelecida com sucesso!' };
2664
+ this.aiTestResult = { success: true, message: this.tx('Connection established successfully!') };
2120
2665
  this.hasApiKey = true; // Garantir desbloqueio da seção de modelo após sucesso
2121
2666
  // Após validar a chave, já buscar a lista de modelos para preencher o select
2122
2667
  await this.refreshModels(true);
2123
2668
  }
2124
2669
  catch (err) {
2125
2670
  this.logger.error('[GlobalConfigEditor] AI Connection Test Error', this.buildLogOptions({ error: err, provider, model: selectedModel }, { context: { actionId: 'provider.test-connection' } }));
2126
- const msg = err?.message || 'Erro desconhecido';
2127
- this.aiTestResult = { success: false, message: msg };
2671
+ this.aiTestResult = { success: false, message: this.tx('Failed to test connection.') };
2128
2672
  }
2129
2673
  finally {
2130
2674
  this.isTestingAi = false;
@@ -2145,6 +2689,10 @@ class GlobalConfigEditorComponent {
2145
2689
  safeName(path) {
2146
2690
  return path.replace(/[^a-zA-Z0-9_]/g, '_');
2147
2691
  }
2692
+ tx(text, params) {
2693
+ const normalized = normalizeMojibakeText(text);
2694
+ return this.i18n.t(normalized, params, normalized, PRAXIS_SETTINGS_PANEL_I18N_NAMESPACE);
2695
+ }
2148
2696
  buildChangedValues() {
2149
2697
  const out = {};
2150
2698
  for (const [safe, value] of Object.entries(this.currentValues)) {
@@ -2169,21 +2717,7 @@ class GlobalConfigEditorComponent {
2169
2717
  return initial !== current;
2170
2718
  }
2171
2719
  resolveSaveErrorMessage(err) {
2172
- const apiMessage = typeof err?.error?.message === 'string' ? err.error.message : null;
2173
- const details = err?.error?.details;
2174
- if (apiMessage && apiMessage.trim()) {
2175
- return apiMessage;
2176
- }
2177
- if (details && typeof details === 'object') {
2178
- const first = Object.values(details).find((v) => typeof v === 'string' && v.trim());
2179
- if (typeof first === 'string') {
2180
- return first;
2181
- }
2182
- }
2183
- if (typeof err?.message === 'string' && err.message.trim()) {
2184
- return err.message;
2185
- }
2186
- return 'Falha ao salvar configurações.';
2720
+ return this.tx('Save failed.');
2187
2721
  }
2188
2722
  normalizeFieldValue(path, value) {
2189
2723
  if (typeof value !== 'string')
@@ -2230,7 +2764,7 @@ class GlobalConfigEditorComponent {
2230
2764
  if (!normalized)
2231
2765
  continue;
2232
2766
  if (!check.regex.test(normalized)) {
2233
- return `Valor inválido em ${TABLE_COMPACT_FIELD_LABELS[check.path]}. Use uma string CSS simples, como ${check.example}.`;
2767
+ return `Invalid value in ${TABLE_COMPACT_FIELD_LABELS[check.path]}. Use a simple CSS string, such as ${check.example}.`;
2234
2768
  }
2235
2769
  }
2236
2770
  return null;
@@ -2340,9 +2874,9 @@ class GlobalConfigEditorComponent {
2340
2874
  <mat-expansion-panel-header>
2341
2875
  <mat-panel-title>
2342
2876
  <mat-icon class="panel-icon">construction</mat-icon>
2343
- CRUD
2877
+ {{ tx('CRUD') }}
2344
2878
  </mat-panel-title>
2345
- <mat-panel-description>Políticas globais de abertura, back e header</mat-panel-description>
2879
+ <mat-panel-description>{{ tx('Global policies for opening, back and header') }}</mat-panel-description>
2346
2880
  </mat-expansion-panel-header>
2347
2881
  <ng-template #hostCrud></ng-template>
2348
2882
  </mat-expansion-panel>
@@ -2350,9 +2884,9 @@ class GlobalConfigEditorComponent {
2350
2884
  <mat-expansion-panel-header>
2351
2885
  <mat-panel-title>
2352
2886
  <mat-icon class="panel-icon">dynamic_form</mat-icon>
2353
- Dynamic Fields
2887
+ {{ tx('Dynamic Fields') }}
2354
2888
  </mat-panel-title>
2355
- <mat-panel-description>Async Select, cascata e paginação</mat-panel-description>
2889
+ <mat-panel-description>{{ tx('Async Select, cascade and pagination') }}</mat-panel-description>
2356
2890
  </mat-expansion-panel-header>
2357
2891
  <ng-template #hostFields></ng-template>
2358
2892
  </mat-expansion-panel>
@@ -2360,9 +2894,9 @@ class GlobalConfigEditorComponent {
2360
2894
  <mat-expansion-panel-header>
2361
2895
  <mat-panel-title>
2362
2896
  <mat-icon class="panel-icon">cached</mat-icon>
2363
- Cache & Persistência
2897
+ {{ tx('Cache & Persistence') }}
2364
2898
  </mat-panel-title>
2365
- <mat-panel-description>Estratégia de cache de schema (local vs server)</mat-panel-description>
2899
+ <mat-panel-description>{{ tx('Schema cache strategy (local vs server)') }}</mat-panel-description>
2366
2900
  </mat-expansion-panel-header>
2367
2901
  <ng-template #hostCache></ng-template>
2368
2902
  </mat-expansion-panel>
@@ -2370,9 +2904,9 @@ class GlobalConfigEditorComponent {
2370
2904
  <mat-expansion-panel-header>
2371
2905
  <mat-panel-title>
2372
2906
  <mat-icon class="panel-icon">psychology</mat-icon>
2373
- Inteligência Artificial
2907
+ {{ tx('Artificial Intelligence') }}
2374
2908
  </mat-panel-title>
2375
- <mat-panel-description>Integração com LLM</mat-panel-description>
2909
+ <mat-panel-description>{{ tx('LLM integration') }}</mat-panel-description>
2376
2910
  </mat-expansion-panel-header>
2377
2911
 
2378
2912
  <div class="ai-config-container">
@@ -2381,23 +2915,23 @@ class GlobalConfigEditorComponent {
2381
2915
  <mat-icon>settings_suggest</mat-icon>
2382
2916
  <span>{{ configSourceLabel }}</span>
2383
2917
  </div>
2384
- <button
2385
- mat-stroked-button
2386
- type="button"
2387
- class="ai-action-btn ai-action-btn--clear"
2388
- *ngIf="hasStoredGlobalConfig"
2389
- [attr.aria-busy]="isClearingGlobalConfig ? 'true' : null"
2390
- [disabled]="isClearingGlobalConfig"
2391
- (click)="clearStoredConfig()"
2392
- matTooltip="Apaga o config salvo e volta aos defaults do servidor"
2918
+ <button
2919
+ mat-stroked-button
2920
+ type="button"
2921
+ class="ai-action-btn ai-action-btn--clear"
2922
+ *ngIf="hasStoredGlobalConfig"
2923
+ [attr.aria-busy]="isClearingGlobalConfig ? 'true' : null"
2924
+ [disabled]="isClearingGlobalConfig"
2925
+ (click)="clearStoredConfig()"
2926
+ [matTooltip]="tx('Clear saved config and return to server defaults')"
2393
2927
  >
2394
2928
  <ng-container *ngIf="isClearingGlobalConfig; else clearContent">
2395
2929
  <mat-spinner diameter="16" class="btn-spinner"></mat-spinner>
2396
- <span class="ai-action-label">Limpando...</span>
2930
+ <span class="ai-action-label">{{ tx('Clearing...') }}</span>
2397
2931
  </ng-container>
2398
2932
  <ng-template #clearContent>
2399
2933
  <mat-icon>delete_sweep</mat-icon>
2400
- <span class="ai-action-label">Limpar config salvo</span>
2934
+ <span class="ai-action-label">{{ tx('Clear saved config') }}</span>
2401
2935
  </ng-template>
2402
2936
  </button>
2403
2937
  </div>
@@ -2405,7 +2939,7 @@ class GlobalConfigEditorComponent {
2405
2939
  <div class="ai-group">
2406
2940
  <div class="ai-group-header">
2407
2941
  <div class="ai-group-title">
2408
- <mat-icon>vpn_key</mat-icon> Credenciais
2942
+ <mat-icon>vpn_key</mat-icon> {{ tx('Credentials') }}
2409
2943
  </div>
2410
2944
  <button mat-stroked-button
2411
2945
  type="button"
@@ -2414,15 +2948,15 @@ class GlobalConfigEditorComponent {
2414
2948
  [attr.aria-busy]="isTestingAi ? 'true' : null"
2415
2949
  (click)="testAiConnection()"
2416
2950
  [disabled]="isTestingAi || !hasApiKey"
2417
- matTooltip="Testar conexão com a chave informada">
2951
+ [matTooltip]="tx('Test connection with the provided key')">
2418
2952
  <ng-container *ngIf="isTestingAi; else btnContent">
2419
2953
  <mat-spinner diameter="16" class="btn-spinner"></mat-spinner>
2420
- <span class="ai-action-label">Conectando...</span>
2954
+ <span class="ai-action-label">{{ tx('Connecting...') }}</span>
2421
2955
  </ng-container>
2422
2956
  <ng-template #btnContent>
2423
2957
  <mat-icon>{{ aiTestResult?.success ? 'check' : 'bolt' }}</mat-icon>
2424
2958
  <span class="ai-action-label">
2425
- {{ aiTestResult?.success ? 'Conectado' : 'Testar conexão' }}
2959
+ {{ aiTestResult?.success ? tx('Connected') : tx('Test connection') }}
2426
2960
  </span>
2427
2961
  </ng-template>
2428
2962
  </button>
@@ -2464,7 +2998,7 @@ class GlobalConfigEditorComponent {
2464
2998
  </div>
2465
2999
  <div class="ai-provider-key is-unlocked" *ngIf="selectedProvider?.requiresApiKey === false">
2466
3000
  <mat-icon>check_circle</mat-icon>
2467
- <span>Sem chave necessária</span>
3001
+ <span>{{ tx('No key required') }}</span>
2468
3002
  </div>
2469
3003
  </div>
2470
3004
  <div class="ai-credentials-row">
@@ -2484,12 +3018,12 @@ class GlobalConfigEditorComponent {
2484
3018
  <div class="ai-group" [class.disabled-group]="!hasApiKey">
2485
3019
  <div class="ai-group-header">
2486
3020
  <div class="ai-group-title">
2487
- <mat-icon>smart_toy</mat-icon> Modelo & Comportamento
3021
+ <mat-icon>smart_toy</mat-icon> {{ tx('Model & Behavior') }}
2488
3022
  </div>
2489
3023
  <div class="ai-header-actions">
2490
- <span class="ai-subtext" *ngIf="hasApiKey">Escolha o modelo após validar a chave.</span>
2491
- <mat-chip-option *ngIf="!hasApiKey" disabled>Requer chave API validada</mat-chip-option>
2492
- <button mat-icon-button (click)="refreshModels(true)" [disabled]="isTestingAi || !hasApiKey" matTooltip="Atualizar lista de modelos" aria-label="Atualizar lista de modelos">
3024
+ <span class="ai-subtext" *ngIf="hasApiKey">{{ tx('Choose the model after validating the key.') }}</span>
3025
+ <mat-chip-option *ngIf="!hasApiKey" disabled>{{ tx('API key validated required') }}</mat-chip-option>
3026
+ <button mat-icon-button (click)="refreshModels(true)" [disabled]="isTestingAi || !hasApiKey" [matTooltip]="tx('Refresh model list')" [attr.aria-label]="tx('Refresh model list')">
2493
3027
  <mat-icon [class.spin]="isRefetchingModels">sync</mat-icon>
2494
3028
  </button>
2495
3029
  </div>
@@ -2508,7 +3042,7 @@ class GlobalConfigEditorComponent {
2508
3042
 
2509
3043
  <div class="ai-placeholder" *ngIf="!hasApiKey">
2510
3044
  <mat-icon>lock</mat-icon>
2511
- <span>Configure e teste sua chave de API para desbloquear a seleção de modelos.</span>
3045
+ <span>{{ tx('Configure and test your API key to unlock model selection.') }}</span>
2512
3046
  </div>
2513
3047
  </div>
2514
3048
 
@@ -2516,7 +3050,7 @@ class GlobalConfigEditorComponent {
2516
3050
  <div class="ai-group">
2517
3051
  <div class="ai-group-header">
2518
3052
  <div class="ai-group-title">
2519
- <mat-icon>scatter_plot</mat-icon> Embeddings (RAG)
3053
+ <mat-icon>scatter_plot</mat-icon> {{ tx('Embeddings (RAG)') }}
2520
3054
  </div>
2521
3055
  <div class="ai-header-actions">
2522
3056
  <button
@@ -2525,19 +3059,19 @@ class GlobalConfigEditorComponent {
2525
3059
  class="ai-action-btn"
2526
3060
  (click)="useLlmForEmbeddings()"
2527
3061
  [disabled]="!canUseLlmForEmbeddings"
2528
- matTooltip="Aplicar provedor e chave do LLM aos embeddings"
3062
+ [matTooltip]="tx('Use the LLM provider and key for embeddings')"
2529
3063
  >
2530
3064
  <mat-icon>merge_type</mat-icon>
2531
- <span class="ai-action-label">Usar mesmo LLM</span>
3065
+ <span class="ai-action-label">{{ tx('Use the LLM provider and key for embeddings') }}</span>
2532
3066
  </button>
2533
- <mat-chip-option *ngIf="!canUseLlmForEmbeddings" disabled>Provedor sem embeddings</mat-chip-option>
3067
+ <mat-chip-option *ngIf="!canUseLlmForEmbeddings" disabled>{{ tx('Provider has no embeddings') }}</mat-chip-option>
2534
3068
  </div>
2535
3069
  </div>
2536
3070
  <div class="ai-subtext">
2537
- Configure o provedor de embeddings para buscas vetoriais (templates e schemas).
3071
+ {{ tx('Configure the embeddings provider for vector search (templates and schemas).') }}
2538
3072
  </div>
2539
3073
  <div class="ai-subtext" *ngIf="embeddingUseSameAsLlm">
2540
- Sincronizado com o LLM. Os campos abaixo acompanham a credencial principal.
3074
+ {{ tx('Synchronized with the LLM. The fields below follow the primary credential.') }}
2541
3075
  </div>
2542
3076
  <div class="ai-embedding-row">
2543
3077
  <ng-template #hostAiEmbedding></ng-template>
@@ -2545,7 +3079,7 @@ class GlobalConfigEditorComponent {
2545
3079
  <div class="ai-feedback ai-feedback--warn" *ngIf="embeddingDimensionMismatch">
2546
3080
  <mat-icon>warning</mat-icon>
2547
3081
  <span class="error-text">
2548
- Dimensão de embeddings diferente do banco (768). Ajuste para 768 ou refaça a migração.
3082
+ {{ tx('Embedding dimension mismatch with database (768). Adjust to 768 or redo the migration.') }}
2549
3083
  </span>
2550
3084
  </div>
2551
3085
  </div>
@@ -2556,9 +3090,9 @@ class GlobalConfigEditorComponent {
2556
3090
  <mat-expansion-panel-header>
2557
3091
  <mat-panel-title>
2558
3092
  <mat-icon class="panel-icon">table_chart</mat-icon>
2559
- Tabela
3093
+ {{ tx('Table') }}
2560
3094
  </mat-panel-title>
2561
- <mat-panel-description>Toolbar, aparência e filtro avançado</mat-panel-description>
3095
+ <mat-panel-description>{{ tx('Toolbar, appearance and advanced filter') }}</mat-panel-description>
2562
3096
  </mat-expansion-panel-header>
2563
3097
  <ng-template #hostTable></ng-template>
2564
3098
  </mat-expansion-panel>
@@ -2566,27 +3100,27 @@ class GlobalConfigEditorComponent {
2566
3100
  <mat-expansion-panel-header>
2567
3101
  <mat-panel-title>
2568
3102
  <mat-icon class="panel-icon">forum</mat-icon>
2569
- Dialog
3103
+ {{ tx('Dialog') }}
2570
3104
  </mat-panel-title>
2571
- <mat-panel-description>Defaults e variants (danger, info, success, question, error)</mat-panel-description>
3105
+ <mat-panel-description>{{ tx('Defaults and variants (danger, info, success, question, error)') }}</mat-panel-description>
2572
3106
  </mat-expansion-panel-header>
2573
3107
  <ng-template #hostDialog></ng-template>
2574
3108
  <!-- Icon UX helpers for Dialog variants -->
2575
3109
  <div class="dlg-icon-helpers">
2576
- <div class="dlg-icon-helpers__head">Ícones por perfil (atalhos)</div>
3110
+ <div class="dlg-icon-helpers__head">{{ tx('Icons by profile (shortcuts)') }}</div>
2577
3111
  <div class="dlg-icon-helpers__row" *ngFor="let k of dialogVariantKeys">
2578
3112
  <div class="dlg-icon-helpers__label">{{ k }}</div>
2579
3113
  <mat-icon aria-hidden="true" [praxisIcon]="getVariantIcon(k) || 'mi:help_outline'" class="dlg-icon-helpers__preview"></mat-icon>
2580
- <button mat-stroked-button color="primary" type="button" (click)="pickVariantIcon(k)" matTooltip="Escolher ícone para {{k}}">
3114
+ <button mat-stroked-button color="primary" type="button" (click)="pickVariantIcon(k)" [matTooltip]="tx('Pick icon for {{key}}', { key: k })">
2581
3115
  <mat-icon>search</mat-icon>
2582
- Escolher ícone
3116
+ {{ tx('Pick icon') }}
2583
3117
  </button>
2584
- <button mat-icon-button type="button" *ngIf="getVariantIcon(k)" (click)="clearVariantIcon(k)" matTooltip="Limpar ícone de {{k}}" aria-label="Limpar ícone">
3118
+ <button mat-icon-button type="button" *ngIf="getVariantIcon(k)" (click)="clearVariantIcon(k)" [matTooltip]="tx('Clear icon for {{key}}', { key: k })" [attr.aria-label]="tx('Clear icon')">
2585
3119
  <mat-icon>backspace</mat-icon>
2586
3120
  </button>
2587
3121
  <span class="dlg-icon-helpers__value" *ngIf="getVariantIcon(k)">{{ getVariantIcon(k) }}</span>
2588
3122
  </div>
2589
- <div class="dlg-icon-helpers__hint">Dica: O campo de texto acima permanece editável. Estes botões apenas facilitam a escolha/limpeza com preview.</div>
3123
+ <div class="dlg-icon-helpers__hint">{{ tx('The text field above remains editable. These buttons only help with selection/clear preview.') }}</div>
2590
3124
  </div>
2591
3125
  </mat-expansion-panel>
2592
3126
  </mat-accordion>
@@ -2600,9 +3134,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2600
3134
  <mat-expansion-panel-header>
2601
3135
  <mat-panel-title>
2602
3136
  <mat-icon class="panel-icon">construction</mat-icon>
2603
- CRUD
3137
+ {{ tx('CRUD') }}
2604
3138
  </mat-panel-title>
2605
- <mat-panel-description>Políticas globais de abertura, back e header</mat-panel-description>
3139
+ <mat-panel-description>{{ tx('Global policies for opening, back and header') }}</mat-panel-description>
2606
3140
  </mat-expansion-panel-header>
2607
3141
  <ng-template #hostCrud></ng-template>
2608
3142
  </mat-expansion-panel>
@@ -2610,9 +3144,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2610
3144
  <mat-expansion-panel-header>
2611
3145
  <mat-panel-title>
2612
3146
  <mat-icon class="panel-icon">dynamic_form</mat-icon>
2613
- Dynamic Fields
3147
+ {{ tx('Dynamic Fields') }}
2614
3148
  </mat-panel-title>
2615
- <mat-panel-description>Async Select, cascata e paginação</mat-panel-description>
3149
+ <mat-panel-description>{{ tx('Async Select, cascade and pagination') }}</mat-panel-description>
2616
3150
  </mat-expansion-panel-header>
2617
3151
  <ng-template #hostFields></ng-template>
2618
3152
  </mat-expansion-panel>
@@ -2620,9 +3154,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2620
3154
  <mat-expansion-panel-header>
2621
3155
  <mat-panel-title>
2622
3156
  <mat-icon class="panel-icon">cached</mat-icon>
2623
- Cache & Persistência
3157
+ {{ tx('Cache & Persistence') }}
2624
3158
  </mat-panel-title>
2625
- <mat-panel-description>Estratégia de cache de schema (local vs server)</mat-panel-description>
3159
+ <mat-panel-description>{{ tx('Schema cache strategy (local vs server)') }}</mat-panel-description>
2626
3160
  </mat-expansion-panel-header>
2627
3161
  <ng-template #hostCache></ng-template>
2628
3162
  </mat-expansion-panel>
@@ -2630,9 +3164,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2630
3164
  <mat-expansion-panel-header>
2631
3165
  <mat-panel-title>
2632
3166
  <mat-icon class="panel-icon">psychology</mat-icon>
2633
- Inteligência Artificial
3167
+ {{ tx('Artificial Intelligence') }}
2634
3168
  </mat-panel-title>
2635
- <mat-panel-description>Integração com LLM</mat-panel-description>
3169
+ <mat-panel-description>{{ tx('LLM integration') }}</mat-panel-description>
2636
3170
  </mat-expansion-panel-header>
2637
3171
 
2638
3172
  <div class="ai-config-container">
@@ -2641,23 +3175,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2641
3175
  <mat-icon>settings_suggest</mat-icon>
2642
3176
  <span>{{ configSourceLabel }}</span>
2643
3177
  </div>
2644
- <button
2645
- mat-stroked-button
2646
- type="button"
2647
- class="ai-action-btn ai-action-btn--clear"
2648
- *ngIf="hasStoredGlobalConfig"
2649
- [attr.aria-busy]="isClearingGlobalConfig ? 'true' : null"
2650
- [disabled]="isClearingGlobalConfig"
2651
- (click)="clearStoredConfig()"
2652
- matTooltip="Apaga o config salvo e volta aos defaults do servidor"
3178
+ <button
3179
+ mat-stroked-button
3180
+ type="button"
3181
+ class="ai-action-btn ai-action-btn--clear"
3182
+ *ngIf="hasStoredGlobalConfig"
3183
+ [attr.aria-busy]="isClearingGlobalConfig ? 'true' : null"
3184
+ [disabled]="isClearingGlobalConfig"
3185
+ (click)="clearStoredConfig()"
3186
+ [matTooltip]="tx('Clear saved config and return to server defaults')"
2653
3187
  >
2654
3188
  <ng-container *ngIf="isClearingGlobalConfig; else clearContent">
2655
3189
  <mat-spinner diameter="16" class="btn-spinner"></mat-spinner>
2656
- <span class="ai-action-label">Limpando...</span>
3190
+ <span class="ai-action-label">{{ tx('Clearing...') }}</span>
2657
3191
  </ng-container>
2658
3192
  <ng-template #clearContent>
2659
3193
  <mat-icon>delete_sweep</mat-icon>
2660
- <span class="ai-action-label">Limpar config salvo</span>
3194
+ <span class="ai-action-label">{{ tx('Clear saved config') }}</span>
2661
3195
  </ng-template>
2662
3196
  </button>
2663
3197
  </div>
@@ -2665,7 +3199,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2665
3199
  <div class="ai-group">
2666
3200
  <div class="ai-group-header">
2667
3201
  <div class="ai-group-title">
2668
- <mat-icon>vpn_key</mat-icon> Credenciais
3202
+ <mat-icon>vpn_key</mat-icon> {{ tx('Credentials') }}
2669
3203
  </div>
2670
3204
  <button mat-stroked-button
2671
3205
  type="button"
@@ -2674,15 +3208,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2674
3208
  [attr.aria-busy]="isTestingAi ? 'true' : null"
2675
3209
  (click)="testAiConnection()"
2676
3210
  [disabled]="isTestingAi || !hasApiKey"
2677
- matTooltip="Testar conexão com a chave informada">
3211
+ [matTooltip]="tx('Test connection with the provided key')">
2678
3212
  <ng-container *ngIf="isTestingAi; else btnContent">
2679
3213
  <mat-spinner diameter="16" class="btn-spinner"></mat-spinner>
2680
- <span class="ai-action-label">Conectando...</span>
3214
+ <span class="ai-action-label">{{ tx('Connecting...') }}</span>
2681
3215
  </ng-container>
2682
3216
  <ng-template #btnContent>
2683
3217
  <mat-icon>{{ aiTestResult?.success ? 'check' : 'bolt' }}</mat-icon>
2684
3218
  <span class="ai-action-label">
2685
- {{ aiTestResult?.success ? 'Conectado' : 'Testar conexão' }}
3219
+ {{ aiTestResult?.success ? tx('Connected') : tx('Test connection') }}
2686
3220
  </span>
2687
3221
  </ng-template>
2688
3222
  </button>
@@ -2724,7 +3258,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2724
3258
  </div>
2725
3259
  <div class="ai-provider-key is-unlocked" *ngIf="selectedProvider?.requiresApiKey === false">
2726
3260
  <mat-icon>check_circle</mat-icon>
2727
- <span>Sem chave necessária</span>
3261
+ <span>{{ tx('No key required') }}</span>
2728
3262
  </div>
2729
3263
  </div>
2730
3264
  <div class="ai-credentials-row">
@@ -2744,12 +3278,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2744
3278
  <div class="ai-group" [class.disabled-group]="!hasApiKey">
2745
3279
  <div class="ai-group-header">
2746
3280
  <div class="ai-group-title">
2747
- <mat-icon>smart_toy</mat-icon> Modelo & Comportamento
3281
+ <mat-icon>smart_toy</mat-icon> {{ tx('Model & Behavior') }}
2748
3282
  </div>
2749
3283
  <div class="ai-header-actions">
2750
- <span class="ai-subtext" *ngIf="hasApiKey">Escolha o modelo após validar a chave.</span>
2751
- <mat-chip-option *ngIf="!hasApiKey" disabled>Requer chave API validada</mat-chip-option>
2752
- <button mat-icon-button (click)="refreshModels(true)" [disabled]="isTestingAi || !hasApiKey" matTooltip="Atualizar lista de modelos" aria-label="Atualizar lista de modelos">
3284
+ <span class="ai-subtext" *ngIf="hasApiKey">{{ tx('Choose the model after validating the key.') }}</span>
3285
+ <mat-chip-option *ngIf="!hasApiKey" disabled>{{ tx('API key validated required') }}</mat-chip-option>
3286
+ <button mat-icon-button (click)="refreshModels(true)" [disabled]="isTestingAi || !hasApiKey" [matTooltip]="tx('Refresh model list')" [attr.aria-label]="tx('Refresh model list')">
2753
3287
  <mat-icon [class.spin]="isRefetchingModels">sync</mat-icon>
2754
3288
  </button>
2755
3289
  </div>
@@ -2768,7 +3302,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2768
3302
 
2769
3303
  <div class="ai-placeholder" *ngIf="!hasApiKey">
2770
3304
  <mat-icon>lock</mat-icon>
2771
- <span>Configure e teste sua chave de API para desbloquear a seleção de modelos.</span>
3305
+ <span>{{ tx('Configure and test your API key to unlock model selection.') }}</span>
2772
3306
  </div>
2773
3307
  </div>
2774
3308
 
@@ -2776,7 +3310,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2776
3310
  <div class="ai-group">
2777
3311
  <div class="ai-group-header">
2778
3312
  <div class="ai-group-title">
2779
- <mat-icon>scatter_plot</mat-icon> Embeddings (RAG)
3313
+ <mat-icon>scatter_plot</mat-icon> {{ tx('Embeddings (RAG)') }}
2780
3314
  </div>
2781
3315
  <div class="ai-header-actions">
2782
3316
  <button
@@ -2785,19 +3319,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2785
3319
  class="ai-action-btn"
2786
3320
  (click)="useLlmForEmbeddings()"
2787
3321
  [disabled]="!canUseLlmForEmbeddings"
2788
- matTooltip="Aplicar provedor e chave do LLM aos embeddings"
3322
+ [matTooltip]="tx('Use the LLM provider and key for embeddings')"
2789
3323
  >
2790
3324
  <mat-icon>merge_type</mat-icon>
2791
- <span class="ai-action-label">Usar mesmo LLM</span>
3325
+ <span class="ai-action-label">{{ tx('Use the LLM provider and key for embeddings') }}</span>
2792
3326
  </button>
2793
- <mat-chip-option *ngIf="!canUseLlmForEmbeddings" disabled>Provedor sem embeddings</mat-chip-option>
3327
+ <mat-chip-option *ngIf="!canUseLlmForEmbeddings" disabled>{{ tx('Provider has no embeddings') }}</mat-chip-option>
2794
3328
  </div>
2795
3329
  </div>
2796
3330
  <div class="ai-subtext">
2797
- Configure o provedor de embeddings para buscas vetoriais (templates e schemas).
3331
+ {{ tx('Configure the embeddings provider for vector search (templates and schemas).') }}
2798
3332
  </div>
2799
3333
  <div class="ai-subtext" *ngIf="embeddingUseSameAsLlm">
2800
- Sincronizado com o LLM. Os campos abaixo acompanham a credencial principal.
3334
+ {{ tx('Synchronized with the LLM. The fields below follow the primary credential.') }}
2801
3335
  </div>
2802
3336
  <div class="ai-embedding-row">
2803
3337
  <ng-template #hostAiEmbedding></ng-template>
@@ -2805,7 +3339,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2805
3339
  <div class="ai-feedback ai-feedback--warn" *ngIf="embeddingDimensionMismatch">
2806
3340
  <mat-icon>warning</mat-icon>
2807
3341
  <span class="error-text">
2808
- Dimensão de embeddings diferente do banco (768). Ajuste para 768 ou refaça a migração.
3342
+ {{ tx('Embedding dimension mismatch with database (768). Adjust to 768 or redo the migration.') }}
2809
3343
  </span>
2810
3344
  </div>
2811
3345
  </div>
@@ -2816,9 +3350,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2816
3350
  <mat-expansion-panel-header>
2817
3351
  <mat-panel-title>
2818
3352
  <mat-icon class="panel-icon">table_chart</mat-icon>
2819
- Tabela
3353
+ {{ tx('Table') }}
2820
3354
  </mat-panel-title>
2821
- <mat-panel-description>Toolbar, aparência e filtro avançado</mat-panel-description>
3355
+ <mat-panel-description>{{ tx('Toolbar, appearance and advanced filter') }}</mat-panel-description>
2822
3356
  </mat-expansion-panel-header>
2823
3357
  <ng-template #hostTable></ng-template>
2824
3358
  </mat-expansion-panel>
@@ -2826,27 +3360,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2826
3360
  <mat-expansion-panel-header>
2827
3361
  <mat-panel-title>
2828
3362
  <mat-icon class="panel-icon">forum</mat-icon>
2829
- Dialog
3363
+ {{ tx('Dialog') }}
2830
3364
  </mat-panel-title>
2831
- <mat-panel-description>Defaults e variants (danger, info, success, question, error)</mat-panel-description>
3365
+ <mat-panel-description>{{ tx('Defaults and variants (danger, info, success, question, error)') }}</mat-panel-description>
2832
3366
  </mat-expansion-panel-header>
2833
3367
  <ng-template #hostDialog></ng-template>
2834
3368
  <!-- Icon UX helpers for Dialog variants -->
2835
3369
  <div class="dlg-icon-helpers">
2836
- <div class="dlg-icon-helpers__head">Ícones por perfil (atalhos)</div>
3370
+ <div class="dlg-icon-helpers__head">{{ tx('Icons by profile (shortcuts)') }}</div>
2837
3371
  <div class="dlg-icon-helpers__row" *ngFor="let k of dialogVariantKeys">
2838
3372
  <div class="dlg-icon-helpers__label">{{ k }}</div>
2839
3373
  <mat-icon aria-hidden="true" [praxisIcon]="getVariantIcon(k) || 'mi:help_outline'" class="dlg-icon-helpers__preview"></mat-icon>
2840
- <button mat-stroked-button color="primary" type="button" (click)="pickVariantIcon(k)" matTooltip="Escolher ícone para {{k}}">
3374
+ <button mat-stroked-button color="primary" type="button" (click)="pickVariantIcon(k)" [matTooltip]="tx('Pick icon for {{key}}', { key: k })">
2841
3375
  <mat-icon>search</mat-icon>
2842
- Escolher ícone
3376
+ {{ tx('Pick icon') }}
2843
3377
  </button>
2844
- <button mat-icon-button type="button" *ngIf="getVariantIcon(k)" (click)="clearVariantIcon(k)" matTooltip="Limpar ícone de {{k}}" aria-label="Limpar ícone">
3378
+ <button mat-icon-button type="button" *ngIf="getVariantIcon(k)" (click)="clearVariantIcon(k)" [matTooltip]="tx('Clear icon for {{key}}', { key: k })" [attr.aria-label]="tx('Clear icon')">
2845
3379
  <mat-icon>backspace</mat-icon>
2846
3380
  </button>
2847
3381
  <span class="dlg-icon-helpers__value" *ngIf="getVariantIcon(k)">{{ getVariantIcon(k) }}</span>
2848
3382
  </div>
2849
- <div class="dlg-icon-helpers__hint">Dica: O campo de texto acima permanece editável. Estes botões apenas facilitam a escolha/limpeza com preview.</div>
3383
+ <div class="dlg-icon-helpers__hint">{{ tx('The text field above remains editable. These buttons only help with selection/clear preview.') }}</div>
2850
3384
  </div>
2851
3385
  </mat-expansion-panel>
2852
3386
  </mat-accordion>
@@ -2885,7 +3419,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2885
3419
  function openGlobalConfigEditor(settings, opts) {
2886
3420
  settings.open({
2887
3421
  id: opts?.id ?? 'global-config',
2888
- title: opts?.title ?? 'Configurações Globais',
3422
+ title: opts?.title,
2889
3423
  titleIcon: opts?.titleIcon ?? 'tune',
2890
3424
  content: { component: GlobalConfigEditorComponent, inputs: {} },
2891
3425
  });
@@ -2929,5 +3463,4 @@ const SETTINGS_PANEL_AI_CAPABILITIES = {
2929
3463
  * Generated bundle index. Do not edit.
2930
3464
  */
2931
3465
 
2932
- export { GLOBAL_CONFIG_DYNAMIC_FORM_COMPONENT, GlobalConfigAdminService, GlobalConfigEditorComponent, SETTINGS_PANEL_AI_CAPABILITIES, SETTINGS_PANEL_DATA, SETTINGS_PANEL_REF, SettingsPanelComponent, SettingsPanelRef, SettingsPanelService, buildGlobalConfigFormConfig, openGlobalConfigEditor };
2933
- //# sourceMappingURL=praxisui-settings-panel.mjs.map
3466
+ export { GLOBAL_CONFIG_DYNAMIC_FORM_COMPONENT, GlobalConfigAdminService, GlobalConfigEditorComponent, SETTINGS_PANEL_AI_CAPABILITIES, SETTINGS_PANEL_DATA, SETTINGS_PANEL_REF, SettingsPanelComponent, SettingsPanelRef, SettingsPanelService, buildGlobalConfigFormConfig, openGlobalConfigEditor, providePraxisSettingsPanelBridge };