@praxisui/manual-form 9.0.0-beta.9 → 9.0.0-beta.90

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.
@@ -1,11 +1,11 @@
1
- import { ensureIds, DynamicFormService, ASYNC_CONFIG_STORAGE, RULE_PROPERTY_SCHEMA, deepMerge, resolveControlTypeAlias, FieldControlType, normalizeControlTypeKey, FormHooksRegistry, ComponentKeyService, FIELD_SELECTOR_REGISTRY_DISABLE_DEFAULTS, DEFAULT_FIELD_SELECTOR_CONTROL_TYPE_MAP, FieldSelectorRegistry, providePraxisI18n, PraxisI18nService, API_URL } from '@praxisui/core';
1
+ import { ensureIds, DynamicFormService, ASYNC_CONFIG_STORAGE, RULE_PROPERTY_SCHEMA, deepMerge, resolveControlTypeAlias, FieldControlType, normalizeControlTypeKey, PraxisIconButtonComponent, FormHooksRegistry, ComponentKeyService, FIELD_SELECTOR_REGISTRY_DISABLE_DEFAULTS, DEFAULT_FIELD_SELECTOR_CONTROL_TYPE_MAP, FieldSelectorRegistry, providePraxisI18n, PraxisI18nService, API_URL, ComponentMetadataRegistry } from '@praxisui/core';
2
2
  import * as i0 from '@angular/core';
3
- import { inject, Optional, Inject, Injectable, input, output, ChangeDetectionStrategy, Component, Input, InjectionToken, isDevMode, EventEmitter, HostListener, ViewChild, Output, ChangeDetectorRef, DestroyRef, PLATFORM_ID, effect, ContentChildren, signal, computed, Directive } from '@angular/core';
3
+ import { inject, Optional, Inject, Injectable, input, output, ChangeDetectionStrategy, Component, Input, InjectionToken, isDevMode, EventEmitter, HostListener, ViewChild, Output, ChangeDetectorRef, DestroyRef, PLATFORM_ID, effect, ContentChildren, signal, computed, Directive, ENVIRONMENT_INITIALIZER } from '@angular/core';
4
4
  import { BehaviorSubject, firstValueFrom, debounceTime, fromEvent, of } from 'rxjs';
5
5
  import { take } from 'rxjs/operators';
6
6
  import { ActivatedRoute } from '@angular/router';
7
7
  import { isPlatformBrowser } from '@angular/common';
8
- import { BaseAiAdapter, shouldRoutePromptToGovernedDecision, AiBackendApiService, PraxisAssistantSessionRegistryService, PraxisAssistantTurnOrchestratorService, createPraxisAssistantViewportLayout, PraxisAiAssistantShellComponent } from '@praxisui/ai';
8
+ import { BaseAiAdapter, withAuthoringScopePolicy, shouldRoutePromptToGovernedDecision, AiBackendApiService, PraxisAssistantSessionRegistryService, PraxisAssistantTurnOrchestratorService, createPraxisAssistantViewportLayout, PraxisAiAssistantShellComponent } from '@praxisui/ai';
9
9
  import * as i1 from '@angular/forms';
10
10
  import { FormGroupDirective, FormGroup, FormControl, Validators, ReactiveFormsModule, FormControlName, ControlContainer, FormsModule } from '@angular/forms';
11
11
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@@ -586,13 +586,15 @@ function generateRulePropertyCapabilities(targetType, properties) {
586
586
  properties.forEach((prop) => {
587
587
  const valueKind = prop.type === 'object'
588
588
  ? 'object'
589
- : prop.type === 'enum'
590
- ? 'enum'
591
- : prop.type === 'boolean'
592
- ? 'boolean'
593
- : prop.type === 'number'
594
- ? 'number'
595
- : 'string';
589
+ : prop.type === 'array'
590
+ ? 'array'
591
+ : prop.type === 'enum'
592
+ ? 'enum'
593
+ : prop.type === 'boolean'
594
+ ? 'boolean'
595
+ : prop.type === 'number'
596
+ ? 'number'
597
+ : 'string';
596
598
  const capability = {
597
599
  path: `formRules[].effect.properties.${prop.name}`,
598
600
  category: 'rules',
@@ -605,7 +607,7 @@ function generateRulePropertyCapabilities(targetType, properties) {
605
607
  : prop.type === 'enum'
606
608
  ? (prop.enumValues?.[0]?.value || 'valor')
607
609
  : 'valor',
608
- safetyNotes: prop.type === 'object' ? 'O objeto deve seguir o schema esperado pelo componente alvo.' : undefined,
610
+ safetyNotes: prop.type === 'object' ? 'O objeto deve seguir o schema esperado pelo componente alvo.' : prop.type === 'array' ? 'A lista deve seguir o schema esperado pelo componente alvo.' : undefined,
609
611
  };
610
612
  if (prop.enumValues) {
611
613
  capability.allowedValues = prop.enumValues.map((ev) => ev.value);
@@ -619,13 +621,15 @@ function generateRulePropertyWhenFalseCapabilities(targetType, properties) {
619
621
  properties.forEach((prop) => {
620
622
  const valueKind = prop.type === 'object'
621
623
  ? 'object'
622
- : prop.type === 'enum'
623
- ? 'enum'
624
- : prop.type === 'boolean'
625
- ? 'boolean'
626
- : prop.type === 'number'
627
- ? 'number'
628
- : 'string';
624
+ : prop.type === 'array'
625
+ ? 'array'
626
+ : prop.type === 'enum'
627
+ ? 'enum'
628
+ : prop.type === 'boolean'
629
+ ? 'boolean'
630
+ : prop.type === 'number'
631
+ ? 'number'
632
+ : 'string';
629
633
  const capability = {
630
634
  path: `formRules[].effect.propertiesWhenFalse.${prop.name}`,
631
635
  category: 'rules',
@@ -638,7 +642,7 @@ function generateRulePropertyWhenFalseCapabilities(targetType, properties) {
638
642
  : prop.type === 'enum'
639
643
  ? (prop.enumValues?.[0]?.value || 'valor')
640
644
  : 'valor',
641
- safetyNotes: prop.type === 'object' ? 'O objeto deve seguir o schema esperado pelo componente alvo.' : undefined,
645
+ safetyNotes: prop.type === 'object' ? 'O objeto deve seguir o schema esperado pelo componente alvo.' : prop.type === 'array' ? 'A lista deve seguir o schema esperado pelo componente alvo.' : undefined,
642
646
  };
643
647
  if (prop.enumValues) {
644
648
  capability.allowedValues = prop.enumValues.map((ev) => ev.value);
@@ -1025,7 +1029,13 @@ const MANUAL_FORM_AI_CAPABILITIES = {
1025
1029
  ...generateRulePropertyWhenFalseCapabilities('action', RULE_PROPERTY_SCHEMA.action),
1026
1030
  ...generateRulePropertyWhenFalseCapabilities('row', RULE_PROPERTY_SCHEMA.row),
1027
1031
  ...generateRulePropertyWhenFalseCapabilities('column', RULE_PROPERTY_SCHEMA.column),
1028
- { path: 'formRulesState', category: 'rules', valueKind: 'object', description: 'Estado bruto do visual builder.' },
1032
+ {
1033
+ path: 'formRulesState',
1034
+ category: 'metadata',
1035
+ valueKind: 'object',
1036
+ description: 'Estado interno do builder visual de regras, gerado apenas para round-trip do editor.',
1037
+ safetyNotes: 'Read-only/internal. Nao use como alvo de autoria, patch, exemplo ou operacao de IA; gere somente formRules e deixe o editor materializar formRulesState.',
1038
+ },
1029
1039
  { path: 'hooks', category: 'hooks', valueKind: 'object', description: 'Declaracoes de hooks por estagio.' },
1030
1040
  { path: 'hooks.beforeInit', category: 'hooks', valueKind: 'array', description: 'Hooks beforeInit.' },
1031
1041
  { path: 'hooks.beforeInit[]', category: 'hooks', valueKind: 'array', description: 'Hooks beforeInit.' },
@@ -1251,7 +1261,7 @@ class ManualFormAgenticAuthoringTurnFlow {
1251
1261
  if (this.shouldRouteToGovernedDecision(prompt, contextHints)) {
1252
1262
  return this.toGovernedDecisionHandoff(prompt, request);
1253
1263
  }
1254
- const response = await firstValueFrom(this.aiApi.getPatch({
1264
+ const response = await firstValueFrom(this.aiApi.getPatch(withAuthoringScopePolicy({
1255
1265
  componentId,
1256
1266
  componentType,
1257
1267
  userPrompt: prompt,
@@ -1268,7 +1278,11 @@ class ManualFormAgenticAuthoringTurnFlow {
1268
1278
  ...(runtimeState ? { runtimeState } : {}),
1269
1279
  ...(schemaFields?.length ? { schemaFields } : {}),
1270
1280
  ...(contextHints ? { contextHints } : {}),
1271
- }));
1281
+ }, {
1282
+ componentId,
1283
+ componentType,
1284
+ componentLabel: this.adapter.componentName || 'formulario',
1285
+ })));
1272
1286
  return this.toTurnResult(this.compileAdapterResponse(response), request);
1273
1287
  }
1274
1288
  async apply(_request) {
@@ -1820,76 +1834,69 @@ class ManualFieldToolbarComponent {
1820
1834
  <button
1821
1835
  #firstAction
1822
1836
  type="button"
1837
+ [praxisIconButton]="'mso:star'"
1838
+ size="dense"
1839
+ [pressed]="isRequired"
1823
1840
  class="toolbar-btn"
1824
- [attr.aria-pressed]="isRequired ? 'true' : 'false'"
1825
1841
  aria-label="Alternar obrigatorio"
1826
1842
  title="Alternar obrigatorio"
1827
1843
  (click)="toggleRequired.emit()"
1828
- >
1829
- <span class="material-symbols-outlined toolbar-icon" [class.toolbar-icon--filled]="isRequired">
1830
- star
1831
- </span>
1832
- </button>
1844
+ ></button>
1833
1845
  <button
1834
1846
  type="button"
1847
+ [praxisIconButton]="isReadOnly ? 'mso:lock' : 'mso:lock_open'"
1848
+ size="dense"
1849
+ [pressed]="isReadOnly"
1835
1850
  class="toolbar-btn"
1836
- [attr.aria-pressed]="isReadOnly ? 'true' : 'false'"
1837
1851
  aria-label="Alternar somente leitura"
1838
1852
  title="Alternar somente leitura"
1839
1853
  (click)="toggleReadonly.emit()"
1840
- >
1841
- <span class="material-symbols-outlined">
1842
- {{ isReadOnly ? 'lock' : 'lock_open' }}
1843
- </span>
1844
- </button>
1854
+ ></button>
1845
1855
  <button
1846
1856
  type="button"
1857
+ [praxisIconButton]="metadata?.hidden ? 'mso:visibility_off' : 'mso:visibility'"
1858
+ size="dense"
1859
+ [pressed]="metadata?.hidden === true"
1847
1860
  class="toolbar-btn"
1848
- [attr.aria-pressed]="metadata?.hidden ? 'true' : 'false'"
1849
1861
  aria-label="Alternar visibilidade"
1850
1862
  title="Alternar visibilidade"
1851
1863
  (click)="toggleHidden.emit()"
1852
- >
1853
- <span class="material-symbols-outlined">
1854
- {{ metadata?.hidden ? 'visibility_off' : 'visibility' }}
1855
- </span>
1856
- </button>
1864
+ ></button>
1857
1865
  <button
1858
1866
  type="button"
1867
+ [praxisIconButton]="metadata?.disabled ? 'mso:toggle_on' : 'mso:toggle_off'"
1868
+ size="dense"
1869
+ [pressed]="metadata?.disabled === true"
1859
1870
  class="toolbar-btn"
1860
- [attr.aria-pressed]="metadata?.disabled ? 'true' : 'false'"
1861
1871
  aria-label="Alternar desabilitado"
1862
1872
  title="Alternar desabilitado"
1863
1873
  (click)="toggleDisabled.emit()"
1864
- >
1865
- <span class="material-symbols-outlined">
1866
- {{ metadata?.disabled ? 'toggle_on' : 'toggle_off' }}
1867
- </span>
1868
- </button>
1874
+ ></button>
1869
1875
  <button
1870
1876
  type="button"
1877
+ [praxisIconButton]="'mso:tune'"
1878
+ size="dense"
1879
+ appearance="filled"
1871
1880
  class="toolbar-btn toolbar-btn--accent"
1872
1881
  aria-label="Configurar campo"
1873
1882
  title="Configurar campo"
1874
1883
  (click)="openEditor.emit()"
1875
- >
1876
- <span class="material-symbols-outlined">tune</span>
1877
- </button>
1884
+ ></button>
1878
1885
  <button
1879
1886
  type="button"
1887
+ [praxisIconButton]="'mso:close'"
1888
+ size="dense"
1880
1889
  class="toolbar-btn toolbar-btn--ghost"
1881
1890
  aria-label="Fechar"
1882
1891
  title="Fechar"
1883
1892
  (click)="requestClose.emit()"
1884
- >
1885
- <span class="material-symbols-outlined">close</span>
1886
- </button>
1893
+ ></button>
1887
1894
  </div>
1888
- `, isInline: true, styles: [":host{display:block;pointer-events:auto;font-family:inherit}.pdx-manual-field-toolbar{display:inline-flex;align-items:center;gap:6px;padding:6px;border-radius:12px;background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);box-shadow:var(--md-sys-elevation-level3);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}.toolbar-btn{appearance:none;border:0;background:transparent;color:var(--md-sys-color-on-surface);display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:10px;cursor:pointer;transition:background-color .12s ease,transform .12s ease}.toolbar-btn:hover{background:var(--md-sys-color-primary-container)}.toolbar-btn:active{transform:scale(.97)}.toolbar-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.toolbar-btn--accent{background:var(--md-sys-color-primary-container)}.toolbar-btn--ghost{opacity:.7}.material-symbols-outlined{font-size:18px;line-height:18px}.toolbar-icon{font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 20}.toolbar-icon--filled{font-variation-settings:\"FILL\" 1,\"wght\" 600,\"GRAD\" 0,\"opsz\" 20}\n"] });
1895
+ `, isInline: true, styles: [":host{display:block;pointer-events:auto;font-family:inherit}.pdx-manual-field-toolbar{display:inline-flex;align-items:center;gap:6px;padding:6px;border-radius:12px;background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);box-shadow:var(--md-sys-elevation-level3);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}.toolbar-btn{color:var(--md-sys-color-on-surface)}.toolbar-btn--accent{--praxis-icon-button-filled-background: var(--md-sys-color-primary-container);--praxis-icon-button-filled-foreground: var(--md-sys-color-on-primary-container)}.toolbar-btn--ghost{opacity:.7}\n"], dependencies: [{ kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }] });
1889
1896
  }
1890
1897
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ManualFieldToolbarComponent, decorators: [{
1891
1898
  type: Component,
1892
- args: [{ selector: 'praxis-manual-field-toolbar', standalone: true, imports: [], template: `
1899
+ args: [{ selector: 'praxis-manual-field-toolbar', standalone: true, imports: [PraxisIconButtonComponent], template: `
1893
1900
  <div
1894
1901
  class="pdx-manual-field-toolbar"
1895
1902
  role="toolbar"
@@ -1898,72 +1905,65 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
1898
1905
  <button
1899
1906
  #firstAction
1900
1907
  type="button"
1908
+ [praxisIconButton]="'mso:star'"
1909
+ size="dense"
1910
+ [pressed]="isRequired"
1901
1911
  class="toolbar-btn"
1902
- [attr.aria-pressed]="isRequired ? 'true' : 'false'"
1903
1912
  aria-label="Alternar obrigatorio"
1904
1913
  title="Alternar obrigatorio"
1905
1914
  (click)="toggleRequired.emit()"
1906
- >
1907
- <span class="material-symbols-outlined toolbar-icon" [class.toolbar-icon--filled]="isRequired">
1908
- star
1909
- </span>
1910
- </button>
1915
+ ></button>
1911
1916
  <button
1912
1917
  type="button"
1918
+ [praxisIconButton]="isReadOnly ? 'mso:lock' : 'mso:lock_open'"
1919
+ size="dense"
1920
+ [pressed]="isReadOnly"
1913
1921
  class="toolbar-btn"
1914
- [attr.aria-pressed]="isReadOnly ? 'true' : 'false'"
1915
1922
  aria-label="Alternar somente leitura"
1916
1923
  title="Alternar somente leitura"
1917
1924
  (click)="toggleReadonly.emit()"
1918
- >
1919
- <span class="material-symbols-outlined">
1920
- {{ isReadOnly ? 'lock' : 'lock_open' }}
1921
- </span>
1922
- </button>
1925
+ ></button>
1923
1926
  <button
1924
1927
  type="button"
1928
+ [praxisIconButton]="metadata?.hidden ? 'mso:visibility_off' : 'mso:visibility'"
1929
+ size="dense"
1930
+ [pressed]="metadata?.hidden === true"
1925
1931
  class="toolbar-btn"
1926
- [attr.aria-pressed]="metadata?.hidden ? 'true' : 'false'"
1927
1932
  aria-label="Alternar visibilidade"
1928
1933
  title="Alternar visibilidade"
1929
1934
  (click)="toggleHidden.emit()"
1930
- >
1931
- <span class="material-symbols-outlined">
1932
- {{ metadata?.hidden ? 'visibility_off' : 'visibility' }}
1933
- </span>
1934
- </button>
1935
+ ></button>
1935
1936
  <button
1936
1937
  type="button"
1938
+ [praxisIconButton]="metadata?.disabled ? 'mso:toggle_on' : 'mso:toggle_off'"
1939
+ size="dense"
1940
+ [pressed]="metadata?.disabled === true"
1937
1941
  class="toolbar-btn"
1938
- [attr.aria-pressed]="metadata?.disabled ? 'true' : 'false'"
1939
1942
  aria-label="Alternar desabilitado"
1940
1943
  title="Alternar desabilitado"
1941
1944
  (click)="toggleDisabled.emit()"
1942
- >
1943
- <span class="material-symbols-outlined">
1944
- {{ metadata?.disabled ? 'toggle_on' : 'toggle_off' }}
1945
- </span>
1946
- </button>
1945
+ ></button>
1947
1946
  <button
1948
1947
  type="button"
1948
+ [praxisIconButton]="'mso:tune'"
1949
+ size="dense"
1950
+ appearance="filled"
1949
1951
  class="toolbar-btn toolbar-btn--accent"
1950
1952
  aria-label="Configurar campo"
1951
1953
  title="Configurar campo"
1952
1954
  (click)="openEditor.emit()"
1953
- >
1954
- <span class="material-symbols-outlined">tune</span>
1955
- </button>
1955
+ ></button>
1956
1956
  <button
1957
1957
  type="button"
1958
+ [praxisIconButton]="'mso:close'"
1959
+ size="dense"
1958
1960
  class="toolbar-btn toolbar-btn--ghost"
1959
1961
  aria-label="Fechar"
1960
1962
  title="Fechar"
1961
1963
  (click)="requestClose.emit()"
1962
- >
1963
- <span class="material-symbols-outlined">close</span>
1964
- </button>
1964
+ ></button>
1965
1965
  </div>
1966
- `, styles: [":host{display:block;pointer-events:auto;font-family:inherit}.pdx-manual-field-toolbar{display:inline-flex;align-items:center;gap:6px;padding:6px;border-radius:12px;background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);box-shadow:var(--md-sys-elevation-level3);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}.toolbar-btn{appearance:none;border:0;background:transparent;color:var(--md-sys-color-on-surface);display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:10px;cursor:pointer;transition:background-color .12s ease,transform .12s ease}.toolbar-btn:hover{background:var(--md-sys-color-primary-container)}.toolbar-btn:active{transform:scale(.97)}.toolbar-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.toolbar-btn--accent{background:var(--md-sys-color-primary-container)}.toolbar-btn--ghost{opacity:.7}.material-symbols-outlined{font-size:18px;line-height:18px}.toolbar-icon{font-variation-settings:\"FILL\" 0,\"wght\" 400,\"GRAD\" 0,\"opsz\" 20}.toolbar-icon--filled{font-variation-settings:\"FILL\" 1,\"wght\" 600,\"GRAD\" 0,\"opsz\" 20}\n"] }]
1966
+ `, styles: [":host{display:block;pointer-events:auto;font-family:inherit}.pdx-manual-field-toolbar{display:inline-flex;align-items:center;gap:6px;padding:6px;border-radius:12px;background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);box-shadow:var(--md-sys-elevation-level3);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}.toolbar-btn{color:var(--md-sys-color-on-surface)}.toolbar-btn--accent{--praxis-icon-button-filled-background: var(--md-sys-color-primary-container);--praxis-icon-button-filled-foreground: var(--md-sys-color-on-primary-container)}.toolbar-btn--ghost{opacity:.7}\n"] }]
1967
1967
  }], propDecorators: { metadata: [{
1968
1968
  type: Input
1969
1969
  }], toggleRequired: [{
@@ -3359,6 +3359,7 @@ const MANUAL_FORM_EN_US = {
3359
3359
  'praxis.manualForm.editor.actions.customTitle': 'Custom actions',
3360
3360
  'praxis.manualForm.editor.actions.customHelp': 'Create extra buttons with their own event.',
3361
3361
  'praxis.manualForm.editor.actions.addCustom': 'Add action',
3362
+ 'praxis.manualForm.editor.actions.removeCustom': 'Remove action',
3362
3363
  'praxis.manualForm.editor.actions.emptyCustom': 'No custom actions registered.',
3363
3364
  'praxis.manualForm.editor.actions.field.visible': 'Visible',
3364
3365
  'praxis.manualForm.editor.actions.field.label': 'Label',
@@ -3446,10 +3447,11 @@ const MANUAL_FORM_EN_US = {
3446
3447
  'praxis.manualForm.editor.rules.apply': 'Apply rules',
3447
3448
  'praxis.manualForm.editor.rules.note': 'Persisted in config; execution depends on the host.',
3448
3449
  'praxis.manualForm.editor.rules.formRulesLabel': 'formRules (JSON)',
3449
- 'praxis.manualForm.editor.rules.formRulesStateLabel': 'formRulesState (JSON)',
3450
- 'praxis.manualForm.editor.rules.formRulesPlaceholder': '[{"id":"rule-1","name":"Required rule","targetType":"field","targets":["field"],"effect":{"condition":"{field} != null","properties":{"required":true}}}]',
3450
+ 'praxis.manualForm.editor.rules.formRulesStateLabel': 'Visual editor internal state (formRulesState - JSON)',
3451
+ 'praxis.manualForm.editor.rules.formRulesPlaceholder': '[{"id":"rule-1","name":"Required rule","targetType":"field","targets":["field"],"effect":{"condition":{"!==":[{"var":"field"},null]},"properties":{"required":true}}}]',
3451
3452
  'praxis.manualForm.editor.rules.tooltip': 'JSON array with rules (id, targetType, targets, effect).',
3452
- 'praxis.manualForm.editor.rules.stateTooltip': 'Raw state from the visual editor (optional).',
3453
+ 'praxis.manualForm.editor.rules.stateTooltip': 'Read-only internal round-trip state for the visual editor; edit formRules instead.',
3454
+ 'praxis.manualForm.editor.rules.formRulesStateReadonlyHint': 'Generated by the visual editor for round-trip; edit formRules instead.',
3453
3455
  'praxis.manualForm.editor.cascade.title': 'Field dependencies',
3454
3456
  'praxis.manualForm.editor.cascade.help': 'Configure native cascades based on metadata.',
3455
3457
  'praxis.manualForm.editor.cascade.note': 'Changes update metadata and are saved in the draft.',
@@ -3497,6 +3499,7 @@ const MANUAL_FORM_PT_BR = {
3497
3499
  'praxis.manualForm.editor.actions.customTitle': 'Ações customizadas',
3498
3500
  'praxis.manualForm.editor.actions.customHelp': 'Crie botões extras com eventos próprios.',
3499
3501
  'praxis.manualForm.editor.actions.addCustom': 'Adicionar ação',
3502
+ 'praxis.manualForm.editor.actions.removeCustom': 'Remover ação',
3500
3503
  'praxis.manualForm.editor.actions.emptyCustom': 'Nenhuma ação customizada cadastrada.',
3501
3504
  'praxis.manualForm.editor.actions.field.visible': 'Visível',
3502
3505
  'praxis.manualForm.editor.actions.field.label': 'Rótulo (Label)',
@@ -3584,10 +3587,11 @@ const MANUAL_FORM_PT_BR = {
3584
3587
  'praxis.manualForm.editor.rules.apply': 'Aplicar regras',
3585
3588
  'praxis.manualForm.editor.rules.note': 'Persistido no config; a execução depende do host.',
3586
3589
  'praxis.manualForm.editor.rules.formRulesLabel': 'Regras do formulário (formRules - JSON)',
3587
- 'praxis.manualForm.editor.rules.formRulesStateLabel': 'Estado das regras (formRulesState - JSON)',
3588
- 'praxis.manualForm.editor.rules.formRulesPlaceholder': '[{"id":"rule-1","name":"Obrigatoriedade","targetType":"field","targets":["campo"],"effect":{"condition":"{campo} != null","properties":{"required":true}}}]',
3590
+ 'praxis.manualForm.editor.rules.formRulesStateLabel': 'Estado interno do editor visual (formRulesState - JSON)',
3591
+ 'praxis.manualForm.editor.rules.formRulesPlaceholder': '[{"id":"rule-1","name":"Obrigatoriedade","targetType":"field","targets":["campo"],"effect":{"condition":{"!==":[{"var":"campo"},null]},"properties":{"required":true}}}]',
3589
3592
  'praxis.manualForm.editor.rules.tooltip': 'Array JSON com regras (id, targetType, targets, effect).',
3590
- 'praxis.manualForm.editor.rules.stateTooltip': 'Estado bruto do editor visual (opcional).',
3593
+ 'praxis.manualForm.editor.rules.stateTooltip': 'Estado interno somente leitura para round-trip do editor visual; edite formRules.',
3594
+ 'praxis.manualForm.editor.rules.formRulesStateReadonlyHint': 'Gerado pelo editor visual para round-trip; edite formRules.',
3591
3595
  'praxis.manualForm.editor.cascade.title': 'Dependências entre campos',
3592
3596
  'praxis.manualForm.editor.cascade.help': 'Configure cascatas nativas baseadas em metadados.',
3593
3597
  'praxis.manualForm.editor.cascade.note': 'As alterações atualizam metadados e são salvas no rascunho (draft).',
@@ -3687,7 +3691,7 @@ class ManualFormConfigEditorComponent {
3687
3691
  return this.tx('editor.hooks.tooltip', `JSON array of hooks for ${stage} (id, priority, timeoutMs, args).`).replace('{{stage}}', stage);
3688
3692
  }
3689
3693
  formRulesPlaceholder() {
3690
- return this.tx('editor.rules.formRulesPlaceholder', '[{"id":"rule-1","name":"Required rule","targetType":"field","targets":["field"],"effect":{"condition":"{field} != null","properties":{"required":true}}}]');
3694
+ return this.tx('editor.rules.formRulesPlaceholder', '[{"id":"rule-1","name":"Required rule","targetType":"field","targets":["field"],"effect":{"condition":{"!==":[{"var":"field"},null]},"properties":{"required":true}}}]');
3691
3695
  }
3692
3696
  ngOnInit() {
3693
3697
  this.refresh();
@@ -4244,9 +4248,13 @@ class ManualFormConfigEditorComponent {
4244
4248
  <div class="mf-custom-card">
4245
4249
  <div class="mf-custom-card__header">
4246
4250
  <div class="mf-custom-card__title">{{ action.label || action.id }}</div>
4247
- <button mat-icon-button type="button" (click)="removeCustomAction(action)">
4248
- <mat-icon>delete</mat-icon>
4249
- </button>
4251
+ <button
4252
+ praxisIconButton="delete"
4253
+ size="dense"
4254
+ type="button"
4255
+ (click)="removeCustomAction(action)"
4256
+ [attr.aria-label]="tx('editor.actions.removeCustom', 'Remove action') + ': ' + (action.label || action.id)"
4257
+ ></button>
4250
4258
  </div>
4251
4259
  <div class="mf-custom-card__grid">
4252
4260
  <mat-form-field appearance="outline">
@@ -4692,15 +4700,14 @@ class ManualFormConfigEditorComponent {
4692
4700
  [placeholder]="hookPlaceholder()"
4693
4701
  ></textarea>
4694
4702
  <button
4695
- mat-icon-button
4703
+ praxisIconButton="help_outline"
4704
+ size="dense"
4696
4705
  matSuffix
4697
- class="help-icon-button"
4698
4706
  type="button"
4699
4707
  [matTooltip]="hookTooltip(stage)"
4708
+ [attr.aria-label]="hookTooltip(stage)"
4700
4709
  matTooltipPosition="above"
4701
- >
4702
- <mat-icon>help_outline</mat-icon>
4703
- </button>
4710
+ ></button>
4704
4711
  @if (hooksErrors[stage]) {
4705
4712
  <mat-error>{{ hooksErrors[stage] }}</mat-error>
4706
4713
  }
@@ -4736,15 +4743,14 @@ class ManualFormConfigEditorComponent {
4736
4743
  [placeholder]="formRulesPlaceholder()"
4737
4744
  ></textarea>
4738
4745
  <button
4739
- mat-icon-button
4746
+ praxisIconButton="help_outline"
4747
+ size="dense"
4740
4748
  matSuffix
4741
- class="help-icon-button"
4742
4749
  type="button"
4743
4750
  [matTooltip]="tx('editor.rules.tooltip', 'JSON array with rules (id, targetType, targets, effect).')"
4751
+ [attr.aria-label]="tx('editor.rules.tooltip', 'JSON array with rules (id, targetType, targets, effect).')"
4744
4752
  matTooltipPosition="above"
4745
- >
4746
- <mat-icon>help_outline</mat-icon>
4747
- </button>
4753
+ ></button>
4748
4754
  @if (formRulesError) {
4749
4755
  <mat-error>{{ formRulesError }}</mat-error>
4750
4756
  }
@@ -4755,19 +4761,21 @@ class ManualFormConfigEditorComponent {
4755
4761
  matInput
4756
4762
  rows="6"
4757
4763
  [value]="formRulesStateText"
4764
+ readonly
4765
+ aria-readonly="true"
4758
4766
  (input)="onRulesStateTextChange($any($event.target).value)"
4759
4767
  placeholder='{"nodes":[],"edges":[]}'
4760
4768
  ></textarea>
4769
+ <mat-hint>{{ tx('editor.rules.formRulesStateReadonlyHint', 'Generated by the visual editor for round-trip; edit formRules instead.') }}</mat-hint>
4761
4770
  <button
4762
- mat-icon-button
4771
+ praxisIconButton="help_outline"
4772
+ size="dense"
4763
4773
  matSuffix
4764
- class="help-icon-button"
4765
4774
  type="button"
4766
4775
  [matTooltip]="tx('editor.rules.stateTooltip', 'Raw state from the visual editor (optional).')"
4776
+ [attr.aria-label]="tx('editor.rules.stateTooltip', 'Raw state from the visual editor (optional).')"
4767
4777
  matTooltipPosition="above"
4768
- >
4769
- <mat-icon>help_outline</mat-icon>
4770
- </button>
4778
+ ></button>
4771
4779
  @if (formRulesStateError) {
4772
4780
  <mat-error>{{ formRulesStateError }}</mat-error>
4773
4781
  }
@@ -4800,7 +4808,7 @@ class ManualFormConfigEditorComponent {
4800
4808
  </mat-tab>
4801
4809
  </mat-tab-group>
4802
4810
  </div>
4803
- `, isInline: true, styles: [".mf-editor{display:grid;gap:16px;padding:16px;color:var(--md-sys-color-on-surface)}.mf-editor__toolbar{display:flex;align-items:center;gap:12px;padding:12px 14px;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;background:var(--md-sys-color-surface-container)}.mf-editor__title{display:flex;flex-direction:column;gap:2px;font-weight:600}.mf-editor__subtitle{font-weight:400;color:var(--md-sys-color-on-surface-variant)}.mf-editor__toolbar .spacer{flex:1}.mf-tabs{min-height:420px}.mf-tab{display:grid;gap:16px;padding:14px 6px 0}.mf-editor__list{display:grid;gap:8px;overflow-x:auto}.mf-editor__row{display:grid;grid-template-columns:2.2fr 2fr 1fr repeat(4,minmax(90px,.8fr));align-items:center;gap:8px;padding:10px 12px;border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;background:var(--md-sys-color-surface);min-width:720px}.mf-editor__row--head{font-weight:600;color:var(--md-sys-color-on-surface);background:var(--md-sys-color-surface-container)}.mf-editor__filter{display:inline-flex;align-items:center;gap:8px;font-weight:500}.mf-editor__filter input{accent-color:var(--md-sys-color-primary)}.col code{display:inline-block;font-family:inherit;font-size:.85rem;padding:2px 8px;border-radius:6px;background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface-variant)}.mf-btn{min-height:36px;padding:0 12px;border-radius:8px;border:1px solid var(--md-sys-color-outline);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);cursor:pointer;transition:background-color .18s ease,border-color .18s ease,color .18s ease}.mf-btn:hover{background:var(--md-sys-color-surface-container)}.mf-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.toggle{display:inline-flex;align-items:center;gap:6px;color:var(--md-sys-color-on-surface-variant)}.mf-section{display:grid;gap:12px;padding:16px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface)}.mf-section--full{padding:10px}.mf-section__header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.mf-section__header h3{margin:0;font-size:1.05rem;font-weight:600}.mf-help{margin:6px 0 0;font-size:.9rem;color:var(--md-sys-color-on-surface-variant)}.mf-note{padding:10px 12px;border-radius:10px;border:1px dashed var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface-variant);font-size:.9rem}.help-icon-button{--mdc-icon-button-state-layer-size: 28px;--mdc-icon-button-icon-size: 18px;width:28px;height:28px;padding:0;display:inline-flex;align-items:center;justify-content:center;margin-right:-4px}.help-icon-button mat-icon{font-size:18px;width:18px;height:18px}.mat-mdc-form-field-icon-suffix{align-self:center}.mf-actions-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px}.mf-action-card,.mf-custom-card,.mf-custom-message-card{display:grid;gap:10px;padding:14px;border-radius:10px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high)}.mf-action-card__title,.mf-custom-card__title{font-weight:600;color:var(--md-sys-color-on-surface)}.mf-actions-layout{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;align-items:center}.mf-toggle-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px;align-items:start}.mf-hooks-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px}.mf-hook-field textarea{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:.85rem}.mf-actions-row{display:inline-flex;gap:8px;align-items:center}.mf-actions-row--end{justify-content:flex-end;width:100%}.mf-custom-actions,.mf-custom-messages{display:grid;gap:12px}.mf-custom-card__header{display:flex;align-items:center;justify-content:space-between;gap:8px}.mf-custom-card__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;align-items:center}.mf-messages-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px}.mf-empty{margin:0;color:var(--md-sys-color-on-surface-variant)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i2.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i6.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i7.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: i7.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i9.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: CascadeManagerTabComponent, selector: "praxis-cascade-manager-tab", inputs: ["fields", "connections"], outputs: ["apply", "cancel"] }] });
4811
+ `, isInline: true, styles: [".mf-editor{display:grid;gap:16px;padding:16px;color:var(--md-sys-color-on-surface)}.mf-editor__toolbar{display:flex;align-items:center;gap:12px;padding:12px 14px;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;background:var(--md-sys-color-surface-container)}.mf-editor__title{display:flex;flex-direction:column;gap:2px;font-weight:600}.mf-editor__subtitle{font-weight:400;color:var(--md-sys-color-on-surface-variant)}.mf-editor__toolbar .spacer{flex:1}.mf-tabs{min-height:420px}.mf-tab{display:grid;gap:16px;padding:14px 6px 0}.mf-editor__list{display:grid;gap:8px;overflow-x:auto}.mf-editor__row{display:grid;grid-template-columns:2.2fr 2fr 1fr repeat(4,minmax(90px,.8fr));align-items:center;gap:8px;padding:10px 12px;border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;background:var(--md-sys-color-surface);min-width:720px}.mf-editor__row--head{font-weight:600;color:var(--md-sys-color-on-surface);background:var(--md-sys-color-surface-container)}.mf-editor__filter{display:inline-flex;align-items:center;gap:8px;font-weight:500}.mf-editor__filter input{accent-color:var(--md-sys-color-primary)}.col code{display:inline-block;font-family:inherit;font-size:.85rem;padding:2px 8px;border-radius:6px;background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface-variant)}.mf-btn{min-height:36px;padding:0 12px;border-radius:8px;border:1px solid var(--md-sys-color-outline);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);cursor:pointer;transition:background-color .18s ease,border-color .18s ease,color .18s ease}.mf-btn:hover{background:var(--md-sys-color-surface-container)}.mf-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.toggle{display:inline-flex;align-items:center;gap:6px;color:var(--md-sys-color-on-surface-variant)}.mf-section{display:grid;gap:12px;padding:16px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface)}.mf-section--full{padding:10px}.mf-section__header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.mf-section__header h3{margin:0;font-size:1.05rem;font-weight:600}.mf-help{margin:6px 0 0;font-size:.9rem;color:var(--md-sys-color-on-surface-variant)}.mf-note{padding:10px 12px;border-radius:10px;border:1px dashed var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface-variant);font-size:.9rem}.mf-actions-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px}.mf-action-card,.mf-custom-card,.mf-custom-message-card{display:grid;gap:10px;padding:14px;border-radius:10px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high)}.mf-action-card__title,.mf-custom-card__title{font-weight:600;color:var(--md-sys-color-on-surface)}.mf-actions-layout{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;align-items:center}.mf-toggle-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px;align-items:start}.mf-hooks-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px}.mf-hook-field textarea{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:.85rem}.mf-actions-row{display:inline-flex;gap:8px;align-items:center}.mf-actions-row--end{justify-content:flex-end;width:100%}.mf-custom-actions,.mf-custom-messages{display:grid;gap:12px}.mf-custom-card__header{display:flex;align-items:center;justify-content:space-between;gap:8px}.mf-custom-card__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;align-items:center}.mf-messages-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px}.mf-empty{margin:0;color:var(--md-sys-color-on-surface-variant)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i2.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i3.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i3.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i5.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatSlideToggleModule }, { kind: "component", type: i6.MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i7.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: "ngmodule", type: MatIconModule }, { kind: "component", type: i8.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i9.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }, { kind: "component", type: CascadeManagerTabComponent, selector: "praxis-cascade-manager-tab", inputs: ["fields", "connections"], outputs: ["apply", "cancel"] }] });
4804
4812
  }
4805
4813
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ManualFormConfigEditorComponent, decorators: [{
4806
4814
  type: Component,
@@ -4814,6 +4822,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
4814
4822
  MatButtonModule,
4815
4823
  MatIconModule,
4816
4824
  MatTooltipModule,
4825
+ PraxisIconButtonComponent,
4817
4826
  CascadeManagerTabComponent
4818
4827
  ], providers: [providePraxisManualFormI18n()], template: `
4819
4828
  <div class="mf-editor">
@@ -5038,9 +5047,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
5038
5047
  <div class="mf-custom-card">
5039
5048
  <div class="mf-custom-card__header">
5040
5049
  <div class="mf-custom-card__title">{{ action.label || action.id }}</div>
5041
- <button mat-icon-button type="button" (click)="removeCustomAction(action)">
5042
- <mat-icon>delete</mat-icon>
5043
- </button>
5050
+ <button
5051
+ praxisIconButton="delete"
5052
+ size="dense"
5053
+ type="button"
5054
+ (click)="removeCustomAction(action)"
5055
+ [attr.aria-label]="tx('editor.actions.removeCustom', 'Remove action') + ': ' + (action.label || action.id)"
5056
+ ></button>
5044
5057
  </div>
5045
5058
  <div class="mf-custom-card__grid">
5046
5059
  <mat-form-field appearance="outline">
@@ -5486,15 +5499,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
5486
5499
  [placeholder]="hookPlaceholder()"
5487
5500
  ></textarea>
5488
5501
  <button
5489
- mat-icon-button
5502
+ praxisIconButton="help_outline"
5503
+ size="dense"
5490
5504
  matSuffix
5491
- class="help-icon-button"
5492
5505
  type="button"
5493
5506
  [matTooltip]="hookTooltip(stage)"
5507
+ [attr.aria-label]="hookTooltip(stage)"
5494
5508
  matTooltipPosition="above"
5495
- >
5496
- <mat-icon>help_outline</mat-icon>
5497
- </button>
5509
+ ></button>
5498
5510
  @if (hooksErrors[stage]) {
5499
5511
  <mat-error>{{ hooksErrors[stage] }}</mat-error>
5500
5512
  }
@@ -5530,15 +5542,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
5530
5542
  [placeholder]="formRulesPlaceholder()"
5531
5543
  ></textarea>
5532
5544
  <button
5533
- mat-icon-button
5545
+ praxisIconButton="help_outline"
5546
+ size="dense"
5534
5547
  matSuffix
5535
- class="help-icon-button"
5536
5548
  type="button"
5537
5549
  [matTooltip]="tx('editor.rules.tooltip', 'JSON array with rules (id, targetType, targets, effect).')"
5550
+ [attr.aria-label]="tx('editor.rules.tooltip', 'JSON array with rules (id, targetType, targets, effect).')"
5538
5551
  matTooltipPosition="above"
5539
- >
5540
- <mat-icon>help_outline</mat-icon>
5541
- </button>
5552
+ ></button>
5542
5553
  @if (formRulesError) {
5543
5554
  <mat-error>{{ formRulesError }}</mat-error>
5544
5555
  }
@@ -5549,19 +5560,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
5549
5560
  matInput
5550
5561
  rows="6"
5551
5562
  [value]="formRulesStateText"
5563
+ readonly
5564
+ aria-readonly="true"
5552
5565
  (input)="onRulesStateTextChange($any($event.target).value)"
5553
5566
  placeholder='{"nodes":[],"edges":[]}'
5554
5567
  ></textarea>
5568
+ <mat-hint>{{ tx('editor.rules.formRulesStateReadonlyHint', 'Generated by the visual editor for round-trip; edit formRules instead.') }}</mat-hint>
5555
5569
  <button
5556
- mat-icon-button
5570
+ praxisIconButton="help_outline"
5571
+ size="dense"
5557
5572
  matSuffix
5558
- class="help-icon-button"
5559
5573
  type="button"
5560
5574
  [matTooltip]="tx('editor.rules.stateTooltip', 'Raw state from the visual editor (optional).')"
5575
+ [attr.aria-label]="tx('editor.rules.stateTooltip', 'Raw state from the visual editor (optional).')"
5561
5576
  matTooltipPosition="above"
5562
- >
5563
- <mat-icon>help_outline</mat-icon>
5564
- </button>
5577
+ ></button>
5565
5578
  @if (formRulesStateError) {
5566
5579
  <mat-error>{{ formRulesStateError }}</mat-error>
5567
5580
  }
@@ -5594,7 +5607,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
5594
5607
  </mat-tab>
5595
5608
  </mat-tab-group>
5596
5609
  </div>
5597
- `, styles: [".mf-editor{display:grid;gap:16px;padding:16px;color:var(--md-sys-color-on-surface)}.mf-editor__toolbar{display:flex;align-items:center;gap:12px;padding:12px 14px;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;background:var(--md-sys-color-surface-container)}.mf-editor__title{display:flex;flex-direction:column;gap:2px;font-weight:600}.mf-editor__subtitle{font-weight:400;color:var(--md-sys-color-on-surface-variant)}.mf-editor__toolbar .spacer{flex:1}.mf-tabs{min-height:420px}.mf-tab{display:grid;gap:16px;padding:14px 6px 0}.mf-editor__list{display:grid;gap:8px;overflow-x:auto}.mf-editor__row{display:grid;grid-template-columns:2.2fr 2fr 1fr repeat(4,minmax(90px,.8fr));align-items:center;gap:8px;padding:10px 12px;border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;background:var(--md-sys-color-surface);min-width:720px}.mf-editor__row--head{font-weight:600;color:var(--md-sys-color-on-surface);background:var(--md-sys-color-surface-container)}.mf-editor__filter{display:inline-flex;align-items:center;gap:8px;font-weight:500}.mf-editor__filter input{accent-color:var(--md-sys-color-primary)}.col code{display:inline-block;font-family:inherit;font-size:.85rem;padding:2px 8px;border-radius:6px;background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface-variant)}.mf-btn{min-height:36px;padding:0 12px;border-radius:8px;border:1px solid var(--md-sys-color-outline);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);cursor:pointer;transition:background-color .18s ease,border-color .18s ease,color .18s ease}.mf-btn:hover{background:var(--md-sys-color-surface-container)}.mf-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.toggle{display:inline-flex;align-items:center;gap:6px;color:var(--md-sys-color-on-surface-variant)}.mf-section{display:grid;gap:12px;padding:16px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface)}.mf-section--full{padding:10px}.mf-section__header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.mf-section__header h3{margin:0;font-size:1.05rem;font-weight:600}.mf-help{margin:6px 0 0;font-size:.9rem;color:var(--md-sys-color-on-surface-variant)}.mf-note{padding:10px 12px;border-radius:10px;border:1px dashed var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface-variant);font-size:.9rem}.help-icon-button{--mdc-icon-button-state-layer-size: 28px;--mdc-icon-button-icon-size: 18px;width:28px;height:28px;padding:0;display:inline-flex;align-items:center;justify-content:center;margin-right:-4px}.help-icon-button mat-icon{font-size:18px;width:18px;height:18px}.mat-mdc-form-field-icon-suffix{align-self:center}.mf-actions-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px}.mf-action-card,.mf-custom-card,.mf-custom-message-card{display:grid;gap:10px;padding:14px;border-radius:10px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high)}.mf-action-card__title,.mf-custom-card__title{font-weight:600;color:var(--md-sys-color-on-surface)}.mf-actions-layout{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;align-items:center}.mf-toggle-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px;align-items:start}.mf-hooks-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px}.mf-hook-field textarea{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:.85rem}.mf-actions-row{display:inline-flex;gap:8px;align-items:center}.mf-actions-row--end{justify-content:flex-end;width:100%}.mf-custom-actions,.mf-custom-messages{display:grid;gap:12px}.mf-custom-card__header{display:flex;align-items:center;justify-content:space-between;gap:8px}.mf-custom-card__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;align-items:center}.mf-messages-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px}.mf-empty{margin:0;color:var(--md-sys-color-on-surface-variant)}\n"] }]
5610
+ `, styles: [".mf-editor{display:grid;gap:16px;padding:16px;color:var(--md-sys-color-on-surface)}.mf-editor__toolbar{display:flex;align-items:center;gap:12px;padding:12px 14px;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;background:var(--md-sys-color-surface-container)}.mf-editor__title{display:flex;flex-direction:column;gap:2px;font-weight:600}.mf-editor__subtitle{font-weight:400;color:var(--md-sys-color-on-surface-variant)}.mf-editor__toolbar .spacer{flex:1}.mf-tabs{min-height:420px}.mf-tab{display:grid;gap:16px;padding:14px 6px 0}.mf-editor__list{display:grid;gap:8px;overflow-x:auto}.mf-editor__row{display:grid;grid-template-columns:2.2fr 2fr 1fr repeat(4,minmax(90px,.8fr));align-items:center;gap:8px;padding:10px 12px;border:1px solid var(--md-sys-color-outline-variant);border-radius:10px;background:var(--md-sys-color-surface);min-width:720px}.mf-editor__row--head{font-weight:600;color:var(--md-sys-color-on-surface);background:var(--md-sys-color-surface-container)}.mf-editor__filter{display:inline-flex;align-items:center;gap:8px;font-weight:500}.mf-editor__filter input{accent-color:var(--md-sys-color-primary)}.col code{display:inline-block;font-family:inherit;font-size:.85rem;padding:2px 8px;border-radius:6px;background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface-variant)}.mf-btn{min-height:36px;padding:0 12px;border-radius:8px;border:1px solid var(--md-sys-color-outline);background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);cursor:pointer;transition:background-color .18s ease,border-color .18s ease,color .18s ease}.mf-btn:hover{background:var(--md-sys-color-surface-container)}.mf-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.toggle{display:inline-flex;align-items:center;gap:6px;color:var(--md-sys-color-on-surface-variant)}.mf-section{display:grid;gap:12px;padding:16px;border-radius:12px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface)}.mf-section--full{padding:10px}.mf-section__header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.mf-section__header h3{margin:0;font-size:1.05rem;font-weight:600}.mf-help{margin:6px 0 0;font-size:.9rem;color:var(--md-sys-color-on-surface-variant)}.mf-note{padding:10px 12px;border-radius:10px;border:1px dashed var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface-variant);font-size:.9rem}.mf-actions-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px}.mf-action-card,.mf-custom-card,.mf-custom-message-card{display:grid;gap:10px;padding:14px;border-radius:10px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high)}.mf-action-card__title,.mf-custom-card__title{font-weight:600;color:var(--md-sys-color-on-surface)}.mf-actions-layout{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;align-items:center}.mf-toggle-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px;align-items:start}.mf-hooks-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px}.mf-hook-field textarea{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:.85rem}.mf-actions-row{display:inline-flex;gap:8px;align-items:center}.mf-actions-row--end{justify-content:flex-end;width:100%}.mf-custom-actions,.mf-custom-messages{display:grid;gap:12px}.mf-custom-card__header{display:flex;align-items:center;justify-content:space-between;gap:8px}.mf-custom-card__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;align-items:center}.mf-messages-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px}.mf-empty{margin:0;color:var(--md-sys-color-on-surface-variant)}\n"] }]
5598
5611
  }], ctorParameters: () => [{ type: undefined, decorators: [{
5599
5612
  type: Inject,
5600
5613
  args: [SETTINGS_PANEL_DATA]
@@ -6412,6 +6425,28 @@ const PRAXIS_MANUAL_FORM_AUTHORING_MANIFEST = {
6412
6425
  ],
6413
6426
  };
6414
6427
 
6428
+ /** Metadata for Praxis Manual Form component */
6429
+ const PRAXIS_MANUAL_FORM_COMPONENT_METADATA = {
6430
+ id: 'praxis-manual-form',
6431
+ selector: 'praxis-manual-form',
6432
+ component: ManualFormComponent,
6433
+ friendlyName: 'Praxis Manual Form',
6434
+ description: 'Form builder with manual field selection and layout controls.',
6435
+ tags: ['widget', 'form', 'configurable'],
6436
+ lib: '@praxisui/manual-form',
6437
+ };
6438
+ /** Provider para auto-registrar metadados do componente Manual Form. */
6439
+ function providePraxisManualFormMetadata() {
6440
+ return {
6441
+ provide: ENVIRONMENT_INITIALIZER,
6442
+ multi: true,
6443
+ useFactory: (registry) => () => {
6444
+ registry.register(PRAXIS_MANUAL_FORM_COMPONENT_METADATA);
6445
+ },
6446
+ deps: [ComponentMetadataRegistry],
6447
+ };
6448
+ }
6449
+
6415
6450
  /*
6416
6451
  * Public API Surface of praxis-manual-form
6417
6452
  */
@@ -6420,4 +6455,4 @@ const PRAXIS_MANUAL_FORM_AUTHORING_MANIFEST = {
6420
6455
  * Generated bundle index. Do not edit.
6421
6456
  */
6422
6457
 
6423
- export { MANUAL_FORM_AI_CAPABILITIES, MANUAL_FORM_CONSTRUCTOR_TO_CONTROL_TYPE, MANUAL_FORM_SELECTOR_TO_CONTROL_TYPE, ManualFieldDirective, ManualFieldEditorOnDblclickDirective, ManualFieldKeyService, ManualFieldMetadataBridgeService, ManualFormActionsComponent, ManualFormComponent, ManualFormConfigEditorComponent, ManualFormDocExampleComponent, ManualFormDocExampleShowcaseComponent, ManualFormHeaderComponent, ManualFormInstance, ManualFormInstanceFactory, PRAXIS_MANUAL_FORM_AUTHORING_MANIFEST, createManualFormSeed, toFieldMetadataMap };
6458
+ export { MANUAL_FORM_AI_CAPABILITIES, MANUAL_FORM_CONSTRUCTOR_TO_CONTROL_TYPE, MANUAL_FORM_SELECTOR_TO_CONTROL_TYPE, ManualFieldDirective, ManualFieldEditorOnDblclickDirective, ManualFieldKeyService, ManualFieldMetadataBridgeService, ManualFormActionsComponent, ManualFormComponent, ManualFormConfigEditorComponent, ManualFormDocExampleComponent, ManualFormDocExampleShowcaseComponent, ManualFormHeaderComponent, ManualFormInstance, ManualFormInstanceFactory, PRAXIS_MANUAL_FORM_AUTHORING_MANIFEST, PRAXIS_MANUAL_FORM_COMPONENT_METADATA, createManualFormSeed, providePraxisManualFormMetadata, toFieldMetadataMap };