@praxisui/page-builder 9.0.0-beta.85 → 9.0.0-beta.88

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.
@@ -7,7 +7,7 @@ import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
7
7
  import * as i2$1 from '@angular/material/icon';
8
8
  import { MatIconModule } from '@angular/material/icon';
9
9
  import * as i2$2 from '@praxisui/core';
10
- import { PraxisIconDirective, BUILTIN_SHELL_PRESETS, providePraxisI18n, PraxisI18nService, GLOBAL_ACTION_CATALOG, getGlobalActionCatalog, PRAXIS_GLOBAL_ACTION_CATALOG, SurfaceOpenActionEditorComponent, BUILTIN_PAGE_LAYOUT_PRESETS, BUILTIN_PAGE_THEME_PRESETS, SETTINGS_PANEL_DATA as SETTINGS_PANEL_DATA$1, deepMerge, generateId, ComponentMetadataRegistry, ObservabilityDashboardService, PraxisRuntimeComponentObservationRegistryService, domainKnowledgeTimelineToRichContentDocument, DomainRuleService, DomainKnowledgeService, SETTINGS_PANEL_BRIDGE, DynamicWidgetPageComponent, DYNAMIC_PAGE_SHELL_EDITOR } from '@praxisui/core';
10
+ import { PraxisIconDirective, BUILTIN_SHELL_PRESETS, PraxisIconButtonComponent, providePraxisI18n, PraxisI18nService, GLOBAL_ACTION_CATALOG, getGlobalActionCatalog, PRAXIS_GLOBAL_ACTION_CATALOG, SurfaceOpenActionEditorComponent, BUILTIN_PAGE_LAYOUT_PRESETS, BUILTIN_PAGE_THEME_PRESETS, SETTINGS_PANEL_DATA as SETTINGS_PANEL_DATA$1, deepMerge, generateId, ComponentMetadataRegistry, ObservabilityDashboardService, PraxisRuntimeComponentObservationRegistryService, domainKnowledgeTimelineToRichContentDocument, DomainRuleService, DomainKnowledgeService, SETTINGS_PANEL_BRIDGE, DynamicWidgetPageComponent, DYNAMIC_PAGE_SHELL_EDITOR } from '@praxisui/core';
11
11
  import * as i3 from '@angular/material/tooltip';
12
12
  import { MatTooltipModule } from '@angular/material/tooltip';
13
13
  import * as i3$1 from '@angular/material/form-field';
@@ -1560,7 +1560,7 @@ class WidgetShellEditorComponent {
1560
1560
  </div>
1561
1561
  </div>
1562
1562
  <div class="shell-preview-actions">
1563
- @for (action of previewHeaderActions(); track action) {
1563
+ @for (action of previewHeaderActions(); track action.id) {
1564
1564
  <button
1565
1565
  type="button"
1566
1566
  [disabled]="action.disabled"
@@ -1575,31 +1575,37 @@ class WidgetShellEditorComponent {
1575
1575
  }
1576
1576
  @if (previewOverflowCount()) {
1577
1577
  <button
1578
- mat-icon-button
1578
+ praxisIconButton="more_horiz"
1579
+ size="compact"
1579
1580
  type="button"
1580
1581
  aria-label="Mais ações"
1581
- >
1582
- <mat-icon>more_horiz</mat-icon>
1583
- </button>
1582
+ ></button>
1584
1583
  }
1585
- @for (action of previewWindowActions(); track action) {
1586
- <button
1587
- mat-icon-button
1588
- type="button"
1589
- [disabled]="action.disabled"
1590
- [attr.aria-label]="action.label || action.id"
1591
- >
1592
- @if (action.icon) {
1593
- <mat-icon [praxisIcon]="action.icon"></mat-icon>
1594
- }
1595
- </button>
1584
+ @for (action of previewWindowActions(); track action.id) {
1585
+ @if (action.icon) {
1586
+ <button
1587
+ [praxisIconButton]="action.icon"
1588
+ size="compact"
1589
+ type="button"
1590
+ [disabled]="action.disabled"
1591
+ [attr.aria-label]="action.label || action.id"
1592
+ ></button>
1593
+ }
1596
1594
  }
1597
- <button mat-icon-button type="button" (click)="togglePreviewCollapse()" aria-label="Recolher">
1598
- <mat-icon>{{ previewCollapsed ? 'unfold_more' : 'unfold_less' }}</mat-icon>
1599
- </button>
1600
- <button mat-icon-button type="button" (click)="togglePreviewExpand()" aria-label="Expandir">
1601
- <mat-icon>{{ previewExpanded ? 'close_fullscreen' : 'open_in_full' }}</mat-icon>
1602
- </button>
1595
+ <button
1596
+ [praxisIconButton]="previewCollapsed ? 'unfold_more' : 'unfold_less'"
1597
+ size="compact"
1598
+ type="button"
1599
+ (click)="togglePreviewCollapse()"
1600
+ aria-label="Recolher"
1601
+ ></button>
1602
+ <button
1603
+ [praxisIconButton]="previewExpanded ? 'close_fullscreen' : 'open_in_full'"
1604
+ size="compact"
1605
+ type="button"
1606
+ (click)="togglePreviewExpand()"
1607
+ aria-label="Expandir"
1608
+ ></button>
1603
1609
  </div>
1604
1610
  </div>
1605
1611
  }
@@ -1803,7 +1809,7 @@ class WidgetShellEditorComponent {
1803
1809
  }
1804
1810
 
1805
1811
  </div>
1806
- `, isInline: true, styles: [".shell-editor{display:grid;gap:12px;padding:16px;background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:12px}.shell-head{display:flex;align-items:center;gap:12px}.shell-title{font-weight:600}.shell-subtitle{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-section{font-weight:600;margin-top:8px}.shell-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.shell-flags{display:flex;gap:16px;align-items:center}.shell-actions-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.shell-available{display:grid;gap:12px}.shell-available-toolbar{display:grid;gap:12px;grid-template-columns:minmax(200px,1fr)}.shell-available-filters{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.shell-available-list{display:grid;gap:10px}.shell-available-item{display:grid;gap:8px;grid-template-columns:1fr auto;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;padding:12px;background:var(--md-sys-color-surface-container-low)}.shell-available-title{display:flex;align-items:center;gap:10px}.shell-available-text{display:grid;gap:4px}.shell-available-label{font-weight:600}.shell-available-meta{display:flex;gap:8px;flex-wrap:wrap;font-size:11px;color:var(--md-sys-color-on-surface-variant)}.shell-available-desc{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-available-payload{font-size:11px;color:var(--md-sys-color-on-surface-variant)}.shell-available-warning{font-size:11px;color:var(--md-sys-color-error)}.shell-available-actions{display:flex;align-items:flex-start;justify-content:flex-end}.shell-badge{padding:2px 6px;border-radius:999px;border:1px solid var(--md-sys-color-outline-variant);font-size:10px}.shell-code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.shell-actions{display:grid;gap:12px}.shell-action{border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;padding:12px;display:grid;gap:8px}.shell-action-row{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}.shell-action-actions{display:flex;justify-content:flex-end}.shell-action-hint,.shell-empty{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-hint{align-self:center;font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-quick-inputs{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));align-items:end}.shell-input-path{font-size:12px;color:var(--md-sys-color-on-surface-variant);align-self:center}.shell-editor .mat-mdc-form-field{width:100%}.shell-preview{border:1px dashed var(--md-sys-color-outline-variant);border-radius:12px;padding:12px}.shell-preview-card{background:var(--pdx-shell-card-bg, var(--md-sys-color-surface));border:1px solid var(--pdx-shell-card-border, var(--md-sys-color-outline-variant));border-radius:var(--pdx-shell-card-radius, 14px);box-shadow:var(--pdx-shell-card-shadow, var(--mat-elevation-level2));overflow:hidden}.shell-preview-card.no-shell{background:transparent;border:none;box-shadow:none}.shell-preview-card.expanded{box-shadow:var(--pdx-shell-card-shadow, 0 16px 34px rgba(0,0,0,.32));transform:scale(1.01)}.shell-preview-header{display:flex;align-items:center;gap:12px;padding:10px 12px 8px;border-bottom:1px solid var(--pdx-shell-header-border, var(--md-sys-color-outline-variant));background:var(--pdx-shell-header-bg, var(--md-sys-color-surface-container))}.shell-preview-title{display:flex;align-items:center;gap:10px;min-width:0;flex:1;color:var(--pdx-shell-title-color, inherit)}.shell-preview-title mat-icon{color:var(--pdx-shell-icon-color, currentColor)}.shell-preview-text{min-width:0}.shell-preview-title-text{font-weight:var(--pdx-shell-title-weight, 600);font-size:var(--pdx-shell-title-size, 14px);line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.shell-preview-subtitle{font-size:var(--pdx-shell-subtitle-size, 12px);opacity:.75;color:var(--pdx-shell-subtitle-color, currentColor);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.shell-preview-actions{display:flex;align-items:center;gap:6px}.pdx-preview-text{padding:0 8px}.pdx-preview-outlined{border:1px solid var(--md-sys-color-outline-variant);border-radius:999px;padding:0 10px}.pdx-preview-label{font-size:12px;font-weight:500}.shell-preview-body{padding:var(--pdx-shell-body-padding, 10px 12px 12px 12px);background:var(--pdx-shell-body-bg, transparent);color:var(--pdx-shell-body-color, inherit);font-size:12px}.shell-preview-body.hidden{display:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i5$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i6.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.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: i6$1.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: i6$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1812
+ `, isInline: true, styles: [".shell-editor{display:grid;gap:12px;padding:16px;background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:12px}.shell-head{display:flex;align-items:center;gap:12px}.shell-title{font-weight:600}.shell-subtitle{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-section{font-weight:600;margin-top:8px}.shell-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.shell-flags{display:flex;gap:16px;align-items:center}.shell-actions-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.shell-available{display:grid;gap:12px}.shell-available-toolbar{display:grid;gap:12px;grid-template-columns:minmax(200px,1fr)}.shell-available-filters{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.shell-available-list{display:grid;gap:10px}.shell-available-item{display:grid;gap:8px;grid-template-columns:1fr auto;border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;padding:12px;background:var(--md-sys-color-surface-container-low)}.shell-available-title{display:flex;align-items:center;gap:10px}.shell-available-text{display:grid;gap:4px}.shell-available-label{font-weight:600}.shell-available-meta{display:flex;gap:8px;flex-wrap:wrap;font-size:11px;color:var(--md-sys-color-on-surface-variant)}.shell-available-desc{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-available-payload{font-size:11px;color:var(--md-sys-color-on-surface-variant)}.shell-available-warning{font-size:11px;color:var(--md-sys-color-error)}.shell-available-actions{display:flex;align-items:flex-start;justify-content:flex-end}.shell-badge{padding:2px 6px;border-radius:999px;border:1px solid var(--md-sys-color-outline-variant);font-size:10px}.shell-code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.shell-actions{display:grid;gap:12px}.shell-action{border:1px solid var(--md-sys-color-outline-variant);border-radius:12px;padding:12px;display:grid;gap:8px}.shell-action-row{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}.shell-action-actions{display:flex;justify-content:flex-end}.shell-action-hint,.shell-empty{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-hint{align-self:center;font-size:12px;color:var(--md-sys-color-on-surface-variant)}.shell-quick-inputs{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));align-items:end}.shell-input-path{font-size:12px;color:var(--md-sys-color-on-surface-variant);align-self:center}.shell-editor .mat-mdc-form-field{width:100%}.shell-preview{border:1px dashed var(--md-sys-color-outline-variant);border-radius:12px;padding:12px}.shell-preview-card{background:var(--pdx-shell-card-bg, var(--md-sys-color-surface));border:1px solid var(--pdx-shell-card-border, var(--md-sys-color-outline-variant));border-radius:var(--pdx-shell-card-radius, 14px);box-shadow:var(--pdx-shell-card-shadow, var(--mat-elevation-level2));overflow:hidden}.shell-preview-card.no-shell{background:transparent;border:none;box-shadow:none}.shell-preview-card.expanded{box-shadow:var(--pdx-shell-card-shadow, 0 16px 34px rgba(0,0,0,.32));transform:scale(1.01)}.shell-preview-header{display:flex;align-items:center;gap:12px;padding:10px 12px 8px;border-bottom:1px solid var(--pdx-shell-header-border, var(--md-sys-color-outline-variant));background:var(--pdx-shell-header-bg, var(--md-sys-color-surface-container))}.shell-preview-title{display:flex;align-items:center;gap:10px;min-width:0;flex:1;color:var(--pdx-shell-title-color, inherit)}.shell-preview-title mat-icon{color:var(--pdx-shell-icon-color, currentColor)}.shell-preview-text{min-width:0}.shell-preview-title-text{font-weight:var(--pdx-shell-title-weight, 600);font-size:var(--pdx-shell-title-size, 14px);line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.shell-preview-subtitle{font-size:var(--pdx-shell-subtitle-size, 12px);opacity:.75;color:var(--pdx-shell-subtitle-color, currentColor);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.shell-preview-actions{display:flex;align-items:center;gap:6px}.pdx-preview-text{padding:0 8px}.pdx-preview-outlined{border:1px solid var(--md-sys-color-outline-variant);border-radius:999px;padding:0 10px}.pdx-preview-label{font-size:12px;font-weight:500}.shell-preview-body{padding:var(--pdx-shell-body-padding, 10px 12px 12px 12px);background:var(--pdx-shell-body-bg, transparent);color:var(--pdx-shell-body-color, inherit);font-size:12px}.shell-preview-body.hidden{display:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: MatCheckboxModule }, { kind: "component", type: i5$1.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatDividerModule }, { kind: "component", type: i6.MatDivider, selector: "mat-divider", inputs: ["vertical", "inset"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.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: i6$1.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: i6$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1807
1813
  }
1808
1814
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: WidgetShellEditorComponent, decorators: [{
1809
1815
  type: Component,
@@ -1818,6 +1824,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
1818
1824
  MatIconModule,
1819
1825
  MatInputModule,
1820
1826
  MatSelectModule,
1827
+ PraxisIconButtonComponent,
1821
1828
  PraxisIconDirective,
1822
1829
  ], template: `
1823
1830
  <div class="shell-editor">
@@ -1967,7 +1974,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
1967
1974
  </div>
1968
1975
  </div>
1969
1976
  <div class="shell-preview-actions">
1970
- @for (action of previewHeaderActions(); track action) {
1977
+ @for (action of previewHeaderActions(); track action.id) {
1971
1978
  <button
1972
1979
  type="button"
1973
1980
  [disabled]="action.disabled"
@@ -1982,31 +1989,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
1982
1989
  }
1983
1990
  @if (previewOverflowCount()) {
1984
1991
  <button
1985
- mat-icon-button
1992
+ praxisIconButton="more_horiz"
1993
+ size="compact"
1986
1994
  type="button"
1987
1995
  aria-label="Mais ações"
1988
- >
1989
- <mat-icon>more_horiz</mat-icon>
1990
- </button>
1996
+ ></button>
1991
1997
  }
1992
- @for (action of previewWindowActions(); track action) {
1993
- <button
1994
- mat-icon-button
1995
- type="button"
1996
- [disabled]="action.disabled"
1997
- [attr.aria-label]="action.label || action.id"
1998
- >
1999
- @if (action.icon) {
2000
- <mat-icon [praxisIcon]="action.icon"></mat-icon>
2001
- }
2002
- </button>
1998
+ @for (action of previewWindowActions(); track action.id) {
1999
+ @if (action.icon) {
2000
+ <button
2001
+ [praxisIconButton]="action.icon"
2002
+ size="compact"
2003
+ type="button"
2004
+ [disabled]="action.disabled"
2005
+ [attr.aria-label]="action.label || action.id"
2006
+ ></button>
2007
+ }
2003
2008
  }
2004
- <button mat-icon-button type="button" (click)="togglePreviewCollapse()" aria-label="Recolher">
2005
- <mat-icon>{{ previewCollapsed ? 'unfold_more' : 'unfold_less' }}</mat-icon>
2006
- </button>
2007
- <button mat-icon-button type="button" (click)="togglePreviewExpand()" aria-label="Expandir">
2008
- <mat-icon>{{ previewExpanded ? 'close_fullscreen' : 'open_in_full' }}</mat-icon>
2009
- </button>
2009
+ <button
2010
+ [praxisIconButton]="previewCollapsed ? 'unfold_more' : 'unfold_less'"
2011
+ size="compact"
2012
+ type="button"
2013
+ (click)="togglePreviewCollapse()"
2014
+ aria-label="Recolher"
2015
+ ></button>
2016
+ <button
2017
+ [praxisIconButton]="previewExpanded ? 'close_fullscreen' : 'open_in_full'"
2018
+ size="compact"
2019
+ type="button"
2020
+ (click)="togglePreviewExpand()"
2021
+ aria-label="Expandir"
2022
+ ></button>
2010
2023
  </div>
2011
2024
  </div>
2012
2025
  }
@@ -10419,17 +10432,17 @@ class DynamicPageConfigEditorComponent {
10419
10432
  </div>
10420
10433
  <mat-tab-group [selectedIndex]="selectedTabIndex" (selectedIndexChange)="onSelectedTabIndexChange($event)">
10421
10434
  <mat-tab [label]="tx('editor.tabs.layout', 'Layout')"><div class="grid"><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.cols', 'Columns') }}</mat-label><input matInput type="number" min="1" [formControl]="canvasColumnsControl" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.rowUnit', 'Row unit') }}</mat-label><input matInput [formControl]="canvasRowUnitControl" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.gap', 'Gap') }}</mat-label><input matInput [formControl]="canvasGapControl" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.autoRows', 'Auto rows') }}</mat-label><mat-select [formControl]="canvasAutoRowsControl"><mat-option value="fixed">fixed</mat-option><mat-option value="content">content</mat-option></mat-select></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.layoutPreset', 'Layout preset') }}</mat-label><mat-select [formControl]="layoutPresetControl"><mat-option value="">{{ tx('editor.layout.none', 'None') }}</mat-option>@for (preset of layoutPresetEntries; track preset.id) {<mat-option [value]="preset.id">{{ preset.label }}</mat-option>}</mat-select></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.themePreset', 'Theme preset') }}</mat-label><mat-select [formControl]="themePresetControl"><mat-option value="">{{ tx('editor.layout.layoutDefault', 'Layout default') }}</mat-option>@for (preset of themePresetEntries; track preset.id) {<mat-option [value]="preset.id">{{ preset.label }}</mat-option>}</mat-select></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.shellPreset', 'Shell visual preset') }}</mat-label><mat-select [formControl]="shellPresetControl"><mat-option value="">{{ tx('editor.layout.none', 'None') }}</mat-option>@for (preset of shellPresetEntries; track preset.id) {<mat-option [value]="preset.id">{{ preset.label }}</mat-option>}</mat-select></mat-form-field></div>@if (selectedLayoutPresetEntry; as preset) {<div class="preset-preview"><div class="preset-preview__head"><div><span class="eyebrow">{{ tx('editor.preview.layoutEyebrow', 'Layout template') }} · {{ preset.category }}</span><strong>{{ preset.label }}</strong></div><span>{{ layoutPresetSummary(preset) }}</span></div><p>{{ preset.description || tx('editor.preview.layoutFallbackDescription', 'Page organization preset.') }}</p><div class="preset-preview__chips"><span>{{ slotsSummary(preset) }}</span><span>{{ requiredSlotsSummary(preset) }}</span><span>{{ devicePolicySummary(preset) }}</span></div>@if (recommendedThemePresetEntry; as theme) {<div class="preset-preview__recommendation"><div><span class="eyebrow">{{ tx('editor.preview.inheritedTheme', 'Inherited theme') }}</span><strong>{{ theme.label }}</strong><span>{{ inheritedThemeHelp(theme) }}</span></div><button mat-stroked-button type="button" [disabled]="themePresetControl.value === theme.id" (click)="pinRecommendedThemePreset()">{{ tx('editor.preview.pinTheme', 'Pin theme') }}</button></div>}</div>} @if (selectedThemePresetEntry; as theme) {<div class="preset-preview preset-preview--theme"><div class="preset-preview__head"><div><span class="eyebrow">{{ tx('editor.preview.explicitTheme', 'Explicit theme') }}</span><strong>{{ theme.label }}</strong></div><span>{{ theme.density }} · {{ theme.motion }}</span></div><p>{{ theme.description || tx('editor.preview.themeFallbackDescription', 'Page visual preset.') }}</p><div class="preset-preview__chips"><span>{{ tx('editor.preview.shellChip', 'Shell: {{value}}').replace('{{value}}', theme.shellPreset) }}</span><span>{{ tx('editor.preview.chartsChip', 'Charts: {{value}}').replace('{{value}}', theme.chartThemePreset) }}</span></div></div>}</mat-tab>
10422
- <mat-tab [label]="tx('editor.tabs.grouping', 'Grouping')"><div class="panel"><div class="head"><span>{{ tx('editor.grouping.title', 'Grouping') }}</span><button mat-stroked-button type="button" (click)="addGroupingNode()"><mat-icon>add</mat-icon>{{ tx('editor.grouping.add', 'Add group') }}</button></div>@for (group of groupingNodes.controls; track $index) {<div class="card" [formGroup]="group"><div class="head"><span>{{ groupLabel($index) }}</span><button mat-icon-button type="button" [attr.aria-label]="tx('editor.grouping.removeAria', 'Remove group')" (click)="removeGroupingNode($index)"><mat-icon>delete</mat-icon></button></div><div class="grid"><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.kind', 'Kind') }}</mat-label><mat-select formControlName="kind"><mat-option value="section">section</mat-option><mat-option value="tabs">tabs</mat-option><mat-option value="hero">hero</mat-option><mat-option value="rail">rail</mat-option></mat-select></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.id', 'Id') }}</mat-label><input matInput formControlName="id" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.label', 'Label') }}</mat-label><input matInput formControlName="label" /></mat-form-field>@if (group.controls.kind.value === 'section') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.layout', 'Layout') }}</mat-label><mat-select formControlName="layout"><mat-option value="">{{ tx('editor.grouping.layoutDefault', 'Default') }}</mat-option><mat-option value="stack">stack</mat-option><mat-option value="grid">grid</mat-option><mat-option value="row">row</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'hero') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.emphasis', 'Emphasis') }}</mat-label><mat-select formControlName="emphasis"><mat-option value="">{{ tx('editor.grouping.layoutDefault', 'Default') }}</mat-option><mat-option value="high">high</mat-option><mat-option value="medium">medium</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'rail') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.side', 'Side') }}</mat-label><mat-select formControlName="side"><mat-option value="left">left</mat-option><mat-option value="right">right</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'tabs') {<mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.grouping.tabsJson', 'Tabs (JSON)') }}</mat-label><textarea matInput rows="4" formControlName="tabsJson"></textarea></mat-form-field>} @else {<mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.grouping.widgetKeysCsv', 'Widget keys (comma-separated)') }}</mat-label><input matInput formControlName="widgetKeysCsv" /></mat-form-field>}</div></div>}</div></mat-tab>
10435
+ <mat-tab [label]="tx('editor.tabs.grouping', 'Grouping')"><div class="panel"><div class="head"><span>{{ tx('editor.grouping.title', 'Grouping') }}</span><button mat-stroked-button type="button" (click)="addGroupingNode()"><mat-icon>add</mat-icon>{{ tx('editor.grouping.add', 'Add group') }}</button></div>@for (group of groupingNodes.controls; track $index) {<div class="card" [formGroup]="group"><div class="head"><span>{{ groupLabel($index) }}</span><button praxisIconButton="delete" size="compact" type="button" [attr.aria-label]="tx('editor.grouping.removeAria', 'Remove group')" (click)="removeGroupingNode($index)"></button></div><div class="grid"><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.kind', 'Kind') }}</mat-label><mat-select formControlName="kind"><mat-option value="section">section</mat-option><mat-option value="tabs">tabs</mat-option><mat-option value="hero">hero</mat-option><mat-option value="rail">rail</mat-option></mat-select></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.id', 'Id') }}</mat-label><input matInput formControlName="id" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.label', 'Label') }}</mat-label><input matInput formControlName="label" /></mat-form-field>@if (group.controls.kind.value === 'section') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.layout', 'Layout') }}</mat-label><mat-select formControlName="layout"><mat-option value="">{{ tx('editor.grouping.layoutDefault', 'Default') }}</mat-option><mat-option value="stack">stack</mat-option><mat-option value="grid">grid</mat-option><mat-option value="row">row</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'hero') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.emphasis', 'Emphasis') }}</mat-label><mat-select formControlName="emphasis"><mat-option value="">{{ tx('editor.grouping.layoutDefault', 'Default') }}</mat-option><mat-option value="high">high</mat-option><mat-option value="medium">medium</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'rail') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.side', 'Side') }}</mat-label><mat-select formControlName="side"><mat-option value="left">left</mat-option><mat-option value="right">right</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'tabs') {<mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.grouping.tabsJson', 'Tabs (JSON)') }}</mat-label><textarea matInput rows="4" formControlName="tabsJson"></textarea></mat-form-field>} @else {<mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.grouping.widgetKeysCsv', 'Widget keys (comma-separated)') }}</mat-label><input matInput formControlName="widgetKeysCsv" /></mat-form-field>}</div></div>}</div></mat-tab>
10423
10436
  <mat-tab [label]="tx('editor.tabs.devices', 'Device Layouts')"><div class="panel">@for (device of deviceKeys; track device) {<div class="card"><div>{{ deviceLabels[device] }}</div><div class="grid"><mat-form-field appearance="outline"><mat-label>{{ tx('editor.devices.columns', 'Columns') }}</mat-label><input matInput type="number" min="1" [formControl]="deviceForms[device].controls.columns" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.rowUnit', 'Row unit') }}</mat-label><input matInput [formControl]="deviceForms[device].controls.rowUnit" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.devices.gap', 'Gap') }}</mat-label><input matInput [formControl]="deviceForms[device].controls.gap" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.autoRows', 'Auto rows') }}</mat-label><mat-select [formControl]="deviceForms[device].controls.autoRows"><mat-option value="">{{ tx('editor.devices.orientationDefault', 'Default') }}</mat-option><mat-option value="fixed">fixed</mat-option><mat-option value="content">content</mat-option></mat-select></mat-form-field><mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.devices.groupingOverridesJson', 'Grouping overrides (JSON)') }}</mat-label><textarea matInput rows="4" [formControl]="deviceForms[device].controls.groupingOverrides"></textarea></mat-form-field><mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.devices.widgetOverridesJson', 'Widget overrides (JSON)') }}</mat-label><textarea matInput rows="4" [formControl]="deviceForms[device].controls.widgetOverrides"></textarea></mat-form-field></div><div class="hint">{{ tx('editor.preview.deviceCanvasHint', 'Existing canvas item overrides are preserved.') }}</div></div>}</div></mat-tab>
10424
10437
  <mat-tab [label]="tx('editor.tabs.context', 'Context')"><mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.context.json', 'Context (JSON)') }}</mat-label><textarea matInput rows="12" [formControl]="contextControl"></textarea></mat-form-field></mat-tab>
10425
10438
  <mat-tab [label]="tx('editor.tabs.state', 'Page State')"><mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.state.valuesJson', 'Primary values (JSON)') }}</mat-label><textarea matInput rows="14" [formControl]="stateControl"></textarea></mat-form-field></mat-tab>
10426
10439
  </mat-tab-group>
10427
10440
  </div>
10428
- `, isInline: true, styles: [".editor,.panel{display:grid;gap:16px;color:var(--md-sys-color-on-surface, currentColor)}.head,.summary{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.summary{color:var(--md-sys-color-on-surface-variant, currentColor)}.grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.card{display:grid;gap:12px;padding:16px;border:1px solid var(--md-sys-color-outline-variant, color-mix(in srgb, currentColor 18%, transparent));border-radius:8px;background:var(--md-sys-color-surface-container-low, var(--md-sys-color-surface-container, transparent));color:var(--md-sys-color-on-surface, currentColor)}.wide{grid-column:1 / -1}.hint{color:var(--md-sys-color-on-surface-variant, currentColor);font-size:12px}.preset-preview{display:grid;gap:12px;margin-top:12px;padding:16px;border:1px solid var(--md-sys-color-outline-variant, color-mix(in srgb, currentColor 18%, transparent));border-radius:8px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low, transparent) 88%,var(--md-sys-color-primary-container, transparent) 12%)}.preset-preview--theme{background:var(--md-sys-color-surface-container-low, transparent)}.preset-preview__head,.preset-preview__recommendation{display:flex;justify-content:space-between;align-items:center;gap:16px}.preset-preview__head>div,.preset-preview__recommendation>div{display:grid;gap:4px;min-width:0}.preset-preview p{margin:0;color:var(--md-sys-color-on-surface-variant, currentColor);line-height:1.45}.preset-preview__chips{display:flex;flex-wrap:wrap;gap:8px}.preset-preview__chips span{padding:5px 9px;border-radius:999px;background:var(--md-sys-color-surface-container, color-mix(in srgb, currentColor 8%, transparent));color:var(--md-sys-color-on-surface-variant, currentColor);font-size:12px;line-height:1.3}.preset-preview__recommendation{padding-top:12px;border-top:1px solid var(--md-sys-color-outline-variant, color-mix(in srgb, currentColor 14%, transparent))}.preset-preview__recommendation span:last-child{color:var(--md-sys-color-on-surface-variant, currentColor);font-size:13px;line-height:1.35}.eyebrow{color:var(--md-sys-color-primary, currentColor);font-size:11px;font-weight:700;letter-spacing:0;text-transform:uppercase}:host ::ng-deep .mat-mdc-tab-body-content{padding-top:12px;box-sizing:border-box}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.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: i6$1.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: i6$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i7.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i7.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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
10441
+ `, isInline: true, styles: [".editor,.panel{display:grid;gap:16px;color:var(--md-sys-color-on-surface, currentColor)}.head,.summary{display:flex;gap:12px;align-items:center;flex-wrap:wrap}.summary{color:var(--md-sys-color-on-surface-variant, currentColor)}.grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}.card{display:grid;gap:12px;padding:16px;border:1px solid var(--md-sys-color-outline-variant, color-mix(in srgb, currentColor 18%, transparent));border-radius:8px;background:var(--md-sys-color-surface-container-low, var(--md-sys-color-surface-container, transparent));color:var(--md-sys-color-on-surface, currentColor)}.wide{grid-column:1 / -1}.hint{color:var(--md-sys-color-on-surface-variant, currentColor);font-size:12px}.preset-preview{display:grid;gap:12px;margin-top:12px;padding:16px;border:1px solid var(--md-sys-color-outline-variant, color-mix(in srgb, currentColor 18%, transparent));border-radius:8px;background:color-mix(in srgb,var(--md-sys-color-surface-container-low, transparent) 88%,var(--md-sys-color-primary-container, transparent) 12%)}.preset-preview--theme{background:var(--md-sys-color-surface-container-low, transparent)}.preset-preview__head,.preset-preview__recommendation{display:flex;justify-content:space-between;align-items:center;gap:16px}.preset-preview__head>div,.preset-preview__recommendation>div{display:grid;gap:4px;min-width:0}.preset-preview p{margin:0;color:var(--md-sys-color-on-surface-variant, currentColor);line-height:1.45}.preset-preview__chips{display:flex;flex-wrap:wrap;gap:8px}.preset-preview__chips span{padding:5px 9px;border-radius:999px;background:var(--md-sys-color-surface-container, color-mix(in srgb, currentColor 8%, transparent));color:var(--md-sys-color-on-surface-variant, currentColor);font-size:12px;line-height:1.3}.preset-preview__recommendation{padding-top:12px;border-top:1px solid var(--md-sys-color-outline-variant, color-mix(in srgb, currentColor 14%, transparent))}.preset-preview__recommendation span:last-child{color:var(--md-sys-color-on-surface-variant, currentColor);font-size:13px;line-height:1.35}.eyebrow{color:var(--md-sys-color-primary, currentColor);font-size:11px;font-weight:700;letter-spacing:0;text-transform:uppercase}:host ::ng-deep .mat-mdc-tab-body-content{padding-top:12px;box-sizing:border-box}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: MatFormFieldModule }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.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: i6$1.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: i6$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i7.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i7.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: "component", type: PraxisIconButtonComponent, selector: "button[praxisIconButton]", inputs: ["praxisIconButton", "size", "appearance", "presentation", "pressed", "busy"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
10429
10442
  }
10430
10443
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DynamicPageConfigEditorComponent, decorators: [{
10431
10444
  type: Component,
10432
- args: [{ selector: 'praxis-dynamic-page-config-editor', standalone: true, imports: [ReactiveFormsModule, MatButtonModule, MatFormFieldModule, MatIconModule, MatInputModule, MatSelectModule, MatTabsModule], providers: [providePraxisPageBuilderI18n()], template: `
10445
+ args: [{ selector: 'praxis-dynamic-page-config-editor', standalone: true, imports: [ReactiveFormsModule, MatButtonModule, MatFormFieldModule, MatIconModule, MatInputModule, MatSelectModule, MatTabsModule, PraxisIconButtonComponent], providers: [providePraxisPageBuilderI18n()], template: `
10433
10446
  <div class="editor">
10434
10447
  <div class="head">
10435
10448
  <div>{{ tx('editor.title', 'Page settings') }}</div>
@@ -10444,7 +10457,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
10444
10457
  </div>
10445
10458
  <mat-tab-group [selectedIndex]="selectedTabIndex" (selectedIndexChange)="onSelectedTabIndexChange($event)">
10446
10459
  <mat-tab [label]="tx('editor.tabs.layout', 'Layout')"><div class="grid"><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.cols', 'Columns') }}</mat-label><input matInput type="number" min="1" [formControl]="canvasColumnsControl" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.rowUnit', 'Row unit') }}</mat-label><input matInput [formControl]="canvasRowUnitControl" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.gap', 'Gap') }}</mat-label><input matInput [formControl]="canvasGapControl" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.autoRows', 'Auto rows') }}</mat-label><mat-select [formControl]="canvasAutoRowsControl"><mat-option value="fixed">fixed</mat-option><mat-option value="content">content</mat-option></mat-select></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.layoutPreset', 'Layout preset') }}</mat-label><mat-select [formControl]="layoutPresetControl"><mat-option value="">{{ tx('editor.layout.none', 'None') }}</mat-option>@for (preset of layoutPresetEntries; track preset.id) {<mat-option [value]="preset.id">{{ preset.label }}</mat-option>}</mat-select></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.themePreset', 'Theme preset') }}</mat-label><mat-select [formControl]="themePresetControl"><mat-option value="">{{ tx('editor.layout.layoutDefault', 'Layout default') }}</mat-option>@for (preset of themePresetEntries; track preset.id) {<mat-option [value]="preset.id">{{ preset.label }}</mat-option>}</mat-select></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.shellPreset', 'Shell visual preset') }}</mat-label><mat-select [formControl]="shellPresetControl"><mat-option value="">{{ tx('editor.layout.none', 'None') }}</mat-option>@for (preset of shellPresetEntries; track preset.id) {<mat-option [value]="preset.id">{{ preset.label }}</mat-option>}</mat-select></mat-form-field></div>@if (selectedLayoutPresetEntry; as preset) {<div class="preset-preview"><div class="preset-preview__head"><div><span class="eyebrow">{{ tx('editor.preview.layoutEyebrow', 'Layout template') }} · {{ preset.category }}</span><strong>{{ preset.label }}</strong></div><span>{{ layoutPresetSummary(preset) }}</span></div><p>{{ preset.description || tx('editor.preview.layoutFallbackDescription', 'Page organization preset.') }}</p><div class="preset-preview__chips"><span>{{ slotsSummary(preset) }}</span><span>{{ requiredSlotsSummary(preset) }}</span><span>{{ devicePolicySummary(preset) }}</span></div>@if (recommendedThemePresetEntry; as theme) {<div class="preset-preview__recommendation"><div><span class="eyebrow">{{ tx('editor.preview.inheritedTheme', 'Inherited theme') }}</span><strong>{{ theme.label }}</strong><span>{{ inheritedThemeHelp(theme) }}</span></div><button mat-stroked-button type="button" [disabled]="themePresetControl.value === theme.id" (click)="pinRecommendedThemePreset()">{{ tx('editor.preview.pinTheme', 'Pin theme') }}</button></div>}</div>} @if (selectedThemePresetEntry; as theme) {<div class="preset-preview preset-preview--theme"><div class="preset-preview__head"><div><span class="eyebrow">{{ tx('editor.preview.explicitTheme', 'Explicit theme') }}</span><strong>{{ theme.label }}</strong></div><span>{{ theme.density }} · {{ theme.motion }}</span></div><p>{{ theme.description || tx('editor.preview.themeFallbackDescription', 'Page visual preset.') }}</p><div class="preset-preview__chips"><span>{{ tx('editor.preview.shellChip', 'Shell: {{value}}').replace('{{value}}', theme.shellPreset) }}</span><span>{{ tx('editor.preview.chartsChip', 'Charts: {{value}}').replace('{{value}}', theme.chartThemePreset) }}</span></div></div>}</mat-tab>
10447
- <mat-tab [label]="tx('editor.tabs.grouping', 'Grouping')"><div class="panel"><div class="head"><span>{{ tx('editor.grouping.title', 'Grouping') }}</span><button mat-stroked-button type="button" (click)="addGroupingNode()"><mat-icon>add</mat-icon>{{ tx('editor.grouping.add', 'Add group') }}</button></div>@for (group of groupingNodes.controls; track $index) {<div class="card" [formGroup]="group"><div class="head"><span>{{ groupLabel($index) }}</span><button mat-icon-button type="button" [attr.aria-label]="tx('editor.grouping.removeAria', 'Remove group')" (click)="removeGroupingNode($index)"><mat-icon>delete</mat-icon></button></div><div class="grid"><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.kind', 'Kind') }}</mat-label><mat-select formControlName="kind"><mat-option value="section">section</mat-option><mat-option value="tabs">tabs</mat-option><mat-option value="hero">hero</mat-option><mat-option value="rail">rail</mat-option></mat-select></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.id', 'Id') }}</mat-label><input matInput formControlName="id" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.label', 'Label') }}</mat-label><input matInput formControlName="label" /></mat-form-field>@if (group.controls.kind.value === 'section') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.layout', 'Layout') }}</mat-label><mat-select formControlName="layout"><mat-option value="">{{ tx('editor.grouping.layoutDefault', 'Default') }}</mat-option><mat-option value="stack">stack</mat-option><mat-option value="grid">grid</mat-option><mat-option value="row">row</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'hero') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.emphasis', 'Emphasis') }}</mat-label><mat-select formControlName="emphasis"><mat-option value="">{{ tx('editor.grouping.layoutDefault', 'Default') }}</mat-option><mat-option value="high">high</mat-option><mat-option value="medium">medium</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'rail') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.side', 'Side') }}</mat-label><mat-select formControlName="side"><mat-option value="left">left</mat-option><mat-option value="right">right</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'tabs') {<mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.grouping.tabsJson', 'Tabs (JSON)') }}</mat-label><textarea matInput rows="4" formControlName="tabsJson"></textarea></mat-form-field>} @else {<mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.grouping.widgetKeysCsv', 'Widget keys (comma-separated)') }}</mat-label><input matInput formControlName="widgetKeysCsv" /></mat-form-field>}</div></div>}</div></mat-tab>
10460
+ <mat-tab [label]="tx('editor.tabs.grouping', 'Grouping')"><div class="panel"><div class="head"><span>{{ tx('editor.grouping.title', 'Grouping') }}</span><button mat-stroked-button type="button" (click)="addGroupingNode()"><mat-icon>add</mat-icon>{{ tx('editor.grouping.add', 'Add group') }}</button></div>@for (group of groupingNodes.controls; track $index) {<div class="card" [formGroup]="group"><div class="head"><span>{{ groupLabel($index) }}</span><button praxisIconButton="delete" size="compact" type="button" [attr.aria-label]="tx('editor.grouping.removeAria', 'Remove group')" (click)="removeGroupingNode($index)"></button></div><div class="grid"><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.kind', 'Kind') }}</mat-label><mat-select formControlName="kind"><mat-option value="section">section</mat-option><mat-option value="tabs">tabs</mat-option><mat-option value="hero">hero</mat-option><mat-option value="rail">rail</mat-option></mat-select></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.id', 'Id') }}</mat-label><input matInput formControlName="id" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.label', 'Label') }}</mat-label><input matInput formControlName="label" /></mat-form-field>@if (group.controls.kind.value === 'section') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.layout', 'Layout') }}</mat-label><mat-select formControlName="layout"><mat-option value="">{{ tx('editor.grouping.layoutDefault', 'Default') }}</mat-option><mat-option value="stack">stack</mat-option><mat-option value="grid">grid</mat-option><mat-option value="row">row</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'hero') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.emphasis', 'Emphasis') }}</mat-label><mat-select formControlName="emphasis"><mat-option value="">{{ tx('editor.grouping.layoutDefault', 'Default') }}</mat-option><mat-option value="high">high</mat-option><mat-option value="medium">medium</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'rail') {<mat-form-field appearance="outline"><mat-label>{{ tx('editor.grouping.side', 'Side') }}</mat-label><mat-select formControlName="side"><mat-option value="left">left</mat-option><mat-option value="right">right</mat-option></mat-select></mat-form-field>}@if (group.controls.kind.value === 'tabs') {<mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.grouping.tabsJson', 'Tabs (JSON)') }}</mat-label><textarea matInput rows="4" formControlName="tabsJson"></textarea></mat-form-field>} @else {<mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.grouping.widgetKeysCsv', 'Widget keys (comma-separated)') }}</mat-label><input matInput formControlName="widgetKeysCsv" /></mat-form-field>}</div></div>}</div></mat-tab>
10448
10461
  <mat-tab [label]="tx('editor.tabs.devices', 'Device Layouts')"><div class="panel">@for (device of deviceKeys; track device) {<div class="card"><div>{{ deviceLabels[device] }}</div><div class="grid"><mat-form-field appearance="outline"><mat-label>{{ tx('editor.devices.columns', 'Columns') }}</mat-label><input matInput type="number" min="1" [formControl]="deviceForms[device].controls.columns" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.rowUnit', 'Row unit') }}</mat-label><input matInput [formControl]="deviceForms[device].controls.rowUnit" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.devices.gap', 'Gap') }}</mat-label><input matInput [formControl]="deviceForms[device].controls.gap" /></mat-form-field><mat-form-field appearance="outline"><mat-label>{{ tx('editor.layout.autoRows', 'Auto rows') }}</mat-label><mat-select [formControl]="deviceForms[device].controls.autoRows"><mat-option value="">{{ tx('editor.devices.orientationDefault', 'Default') }}</mat-option><mat-option value="fixed">fixed</mat-option><mat-option value="content">content</mat-option></mat-select></mat-form-field><mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.devices.groupingOverridesJson', 'Grouping overrides (JSON)') }}</mat-label><textarea matInput rows="4" [formControl]="deviceForms[device].controls.groupingOverrides"></textarea></mat-form-field><mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.devices.widgetOverridesJson', 'Widget overrides (JSON)') }}</mat-label><textarea matInput rows="4" [formControl]="deviceForms[device].controls.widgetOverrides"></textarea></mat-form-field></div><div class="hint">{{ tx('editor.preview.deviceCanvasHint', 'Existing canvas item overrides are preserved.') }}</div></div>}</div></mat-tab>
10449
10462
  <mat-tab [label]="tx('editor.tabs.context', 'Context')"><mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.context.json', 'Context (JSON)') }}</mat-label><textarea matInput rows="12" [formControl]="contextControl"></textarea></mat-form-field></mat-tab>
10450
10463
  <mat-tab [label]="tx('editor.tabs.state', 'Page State')"><mat-form-field appearance="outline" class="wide"><mat-label>{{ tx('editor.state.valuesJson', 'Primary values (JSON)') }}</mat-label><textarea matInput rows="14" [formControl]="stateControl"></textarea></mat-form-field></mat-tab>
@@ -11387,9 +11400,7 @@ class PageConfigEditorComponent {
11387
11400
  <mat-option [value]="preset.id">{{ preset.label }}</mat-option>
11388
11401
  }
11389
11402
  </mat-select>
11390
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="layoutPresetHint" matTooltipPosition="above">
11391
- <mat-icon>help_outline</mat-icon>
11392
- </button>
11403
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="layoutPresetHint" [attr.aria-label]="layoutPresetHint" matTooltipPosition="above"></button>
11393
11404
  </mat-form-field>
11394
11405
  <mat-form-field appearance="outline">
11395
11406
  <mat-label>{{ tx('editor.layout.themePreset', 'Theme preset') }}</mat-label>
@@ -11399,9 +11410,7 @@ class PageConfigEditorComponent {
11399
11410
  <mat-option [value]="preset.id">{{ preset.label }}</mat-option>
11400
11411
  }
11401
11412
  </mat-select>
11402
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="themePresetHint" matTooltipPosition="above">
11403
- <mat-icon>help_outline</mat-icon>
11404
- </button>
11413
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="themePresetHint" [attr.aria-label]="themePresetHint" matTooltipPosition="above"></button>
11405
11414
  </mat-form-field>
11406
11415
  <mat-form-field appearance="outline">
11407
11416
  <mat-label>{{ tx('editor.layout.shellPreset', 'Shell visual preset') }}</mat-label>
@@ -11411,9 +11420,7 @@ class PageConfigEditorComponent {
11411
11420
  <mat-option [value]="preset.id">{{ preset.label }}</mat-option>
11412
11421
  }
11413
11422
  </mat-select>
11414
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="shellPresetHint" matTooltipPosition="above">
11415
- <mat-icon>help_outline</mat-icon>
11416
- </button>
11423
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="shellPresetHint" [attr.aria-label]="shellPresetHint" matTooltipPosition="above"></button>
11417
11424
  </mat-form-field>
11418
11425
  </div>
11419
11426
  </div>
@@ -11438,9 +11445,7 @@ class PageConfigEditorComponent {
11438
11445
  <div class="state-card" [formGroup]="group">
11439
11446
  <div class="state-card-head">
11440
11447
  <div class="state-card-title">{{ tx('editor.grouping.groupN', 'Group {{index}}').replace('{{index}}', '' + ($index + 1)) }}</div>
11441
- <button mat-icon-button type="button" (click)="removeGroupingNode($index)" [attr.aria-label]="tx('editor.grouping.removeAria', 'Remove group')">
11442
- <mat-icon>delete</mat-icon>
11443
- </button>
11448
+ <button praxisIconButton="delete" size="compact" type="button" (click)="removeGroupingNode($index)" [attr.aria-label]="tx('editor.grouping.removeAria', 'Remove group')"></button>
11444
11449
  </div>
11445
11450
  <div class="state-card-grid">
11446
11451
  <mat-form-field appearance="outline">
@@ -11493,9 +11498,7 @@ class PageConfigEditorComponent {
11493
11498
  <mat-form-field appearance="outline" class="span-3">
11494
11499
  <mat-label>{{ tx('editor.grouping.tabsJson', 'Tabs (JSON)') }}</mat-label>
11495
11500
  <textarea matInput rows="5" formControlName="tabsJson"></textarea>
11496
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="tabsJsonHint" matTooltipPosition="above">
11497
- <mat-icon>help_outline</mat-icon>
11498
- </button>
11501
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="tabsJsonHint" [attr.aria-label]="tabsJsonHint" matTooltipPosition="above"></button>
11499
11502
  </mat-form-field>
11500
11503
  } @else {
11501
11504
  <mat-form-field appearance="outline" class="span-3">
@@ -11541,23 +11544,17 @@ class PageConfigEditorComponent {
11541
11544
  <mat-form-field appearance="outline" class="span-3">
11542
11545
  <mat-label>{{ tx('editor.devices.breakpointsJson', 'Breakpoints (JSON)') }}</mat-label>
11543
11546
  <textarea matInput rows="3" formControlName="breakpointsJson"></textarea>
11544
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="breakpointsHint" matTooltipPosition="above">
11545
- <mat-icon>help_outline</mat-icon>
11546
- </button>
11547
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="breakpointsHint" [attr.aria-label]="breakpointsHint" matTooltipPosition="above"></button>
11547
11548
  </mat-form-field>
11548
11549
  <mat-form-field appearance="outline" class="span-3">
11549
11550
  <mat-label>{{ tx('editor.devices.groupingOverridesJson', 'Grouping overrides (JSON)') }}</mat-label>
11550
11551
  <textarea matInput rows="4" formControlName="groupingOverridesJson"></textarea>
11551
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="groupingOverridesHint" matTooltipPosition="above">
11552
- <mat-icon>help_outline</mat-icon>
11553
- </button>
11552
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="groupingOverridesHint" [attr.aria-label]="groupingOverridesHint" matTooltipPosition="above"></button>
11554
11553
  </mat-form-field>
11555
11554
  <mat-form-field appearance="outline" class="span-3">
11556
11555
  <mat-label>{{ tx('editor.devices.widgetOverridesJson', 'Widget overrides (JSON)') }}</mat-label>
11557
11556
  <textarea matInput rows="4" formControlName="widgetOverridesJson"></textarea>
11558
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="widgetOverridesHint" matTooltipPosition="above">
11559
- <mat-icon>help_outline</mat-icon>
11560
- </button>
11557
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="widgetOverridesHint" [attr.aria-label]="widgetOverridesHint" matTooltipPosition="above"></button>
11561
11558
  </mat-form-field>
11562
11559
  </div>
11563
11560
  </div>
@@ -11583,23 +11580,17 @@ class PageConfigEditorComponent {
11583
11580
  <mat-form-field appearance="outline" class="span-3">
11584
11581
  <mat-label>{{ tx('editor.devices.breakpointsJson', 'Breakpoints (JSON)') }}</mat-label>
11585
11582
  <textarea matInput rows="3" formControlName="breakpointsJson"></textarea>
11586
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="breakpointsHint" matTooltipPosition="above">
11587
- <mat-icon>help_outline</mat-icon>
11588
- </button>
11583
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="breakpointsHint" [attr.aria-label]="breakpointsHint" matTooltipPosition="above"></button>
11589
11584
  </mat-form-field>
11590
11585
  <mat-form-field appearance="outline" class="span-3">
11591
11586
  <mat-label>{{ tx('editor.devices.groupingOverridesJson', 'Grouping overrides (JSON)') }}</mat-label>
11592
11587
  <textarea matInput rows="4" formControlName="groupingOverridesJson"></textarea>
11593
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="groupingOverridesHint" matTooltipPosition="above">
11594
- <mat-icon>help_outline</mat-icon>
11595
- </button>
11588
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="groupingOverridesHint" [attr.aria-label]="groupingOverridesHint" matTooltipPosition="above"></button>
11596
11589
  </mat-form-field>
11597
11590
  <mat-form-field appearance="outline" class="span-3">
11598
11591
  <mat-label>{{ tx('editor.devices.widgetOverridesJson', 'Widget overrides (JSON)') }}</mat-label>
11599
11592
  <textarea matInput rows="4" formControlName="widgetOverridesJson"></textarea>
11600
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="widgetOverridesHint" matTooltipPosition="above">
11601
- <mat-icon>help_outline</mat-icon>
11602
- </button>
11593
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="widgetOverridesHint" [attr.aria-label]="widgetOverridesHint" matTooltipPosition="above"></button>
11603
11594
  </mat-form-field>
11604
11595
  </div>
11605
11596
  </div>
@@ -11625,23 +11616,17 @@ class PageConfigEditorComponent {
11625
11616
  <mat-form-field appearance="outline" class="span-3">
11626
11617
  <mat-label>{{ tx('editor.devices.breakpointsJson', 'Breakpoints (JSON)') }}</mat-label>
11627
11618
  <textarea matInput rows="3" formControlName="breakpointsJson"></textarea>
11628
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="breakpointsHint" matTooltipPosition="above">
11629
- <mat-icon>help_outline</mat-icon>
11630
- </button>
11619
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="breakpointsHint" [attr.aria-label]="breakpointsHint" matTooltipPosition="above"></button>
11631
11620
  </mat-form-field>
11632
11621
  <mat-form-field appearance="outline" class="span-3">
11633
11622
  <mat-label>{{ tx('editor.devices.groupingOverridesJson', 'Grouping overrides (JSON)') }}</mat-label>
11634
11623
  <textarea matInput rows="4" formControlName="groupingOverridesJson"></textarea>
11635
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="groupingOverridesHint" matTooltipPosition="above">
11636
- <mat-icon>help_outline</mat-icon>
11637
- </button>
11624
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="groupingOverridesHint" [attr.aria-label]="groupingOverridesHint" matTooltipPosition="above"></button>
11638
11625
  </mat-form-field>
11639
11626
  <mat-form-field appearance="outline" class="span-3">
11640
11627
  <mat-label>{{ tx('editor.devices.widgetOverridesJson', 'Widget overrides (JSON)') }}</mat-label>
11641
11628
  <textarea matInput rows="4" formControlName="widgetOverridesJson"></textarea>
11642
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="widgetOverridesHint" matTooltipPosition="above">
11643
- <mat-icon>help_outline</mat-icon>
11644
- </button>
11629
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="widgetOverridesHint" [attr.aria-label]="widgetOverridesHint" matTooltipPosition="above"></button>
11645
11630
  </mat-form-field>
11646
11631
  </div>
11647
11632
  </div>
@@ -11660,15 +11645,15 @@ class PageConfigEditorComponent {
11660
11645
  <mat-label>{{ tx('editor.context.json', 'Context (JSON)') }}</mat-label>
11661
11646
  <textarea matInput rows="10" [formControl]="contextControl"></textarea>
11662
11647
  <button
11663
- mat-icon-button
11648
+ praxisIconButton="help_outline"
11649
+ size="dense"
11664
11650
  matSuffix
11665
11651
  class="help-icon-button"
11666
11652
  type="button"
11667
11653
  [matTooltip]="contextExample"
11654
+ [attr.aria-label]="contextExample"
11668
11655
  matTooltipPosition="above"
11669
- >
11670
- <mat-icon>help_outline</mat-icon>
11671
- </button>
11656
+ ></button>
11672
11657
  </mat-form-field>
11673
11658
  @if (contextError()) {
11674
11659
  <div class="page-error" role="alert">{{ contextError() }}</div>
@@ -11684,15 +11669,15 @@ class PageConfigEditorComponent {
11684
11669
  <mat-label>{{ tx('editor.state.valuesJson', 'Primary values (JSON)') }}</mat-label>
11685
11670
  <textarea matInput rows="10" [formControl]="stateValuesControl"></textarea>
11686
11671
  <button
11687
- mat-icon-button
11672
+ praxisIconButton="help_outline"
11673
+ size="dense"
11688
11674
  matSuffix
11689
11675
  class="help-icon-button"
11690
11676
  type="button"
11691
11677
  [matTooltip]="stateValuesExample"
11678
+ [attr.aria-label]="stateValuesExample"
11692
11679
  matTooltipPosition="above"
11693
- >
11694
- <mat-icon>help_outline</mat-icon>
11695
- </button>
11680
+ ></button>
11696
11681
  </mat-form-field>
11697
11682
 
11698
11683
  <div class="state-group">
@@ -11712,9 +11697,7 @@ class PageConfigEditorComponent {
11712
11697
  <div class="state-card" [formGroup]="group">
11713
11698
  <div class="state-card-head">
11714
11699
  <div class="state-card-title">{{ tx('editor.state.nodeN', 'Node {{index}}').replace('{{index}}', '' + ($index + 1)) }}</div>
11715
- <button mat-icon-button type="button" (click)="removeSchemaNode($index)" [attr.aria-label]="tx('editor.state.removeSchemaAria', 'Remove schema node')">
11716
- <mat-icon>delete</mat-icon>
11717
- </button>
11700
+ <button praxisIconButton="delete" size="compact" type="button" (click)="removeSchemaNode($index)" [attr.aria-label]="tx('editor.state.removeSchemaAria', 'Remove schema node')"></button>
11718
11701
  </div>
11719
11702
  <div class="state-card-grid">
11720
11703
  <mat-form-field appearance="outline">
@@ -11754,9 +11737,7 @@ class PageConfigEditorComponent {
11754
11737
  <mat-form-field appearance="outline" class="span-2">
11755
11738
  <mat-label>{{ tx('editor.state.initialJson', 'Initial (JSON)') }}</mat-label>
11756
11739
  <textarea matInput rows="4" formControlName="initialJson"></textarea>
11757
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="schemaInitialHint" matTooltipPosition="above">
11758
- <mat-icon>help_outline</mat-icon>
11759
- </button>
11740
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="schemaInitialHint" [attr.aria-label]="schemaInitialHint" matTooltipPosition="above"></button>
11760
11741
  </mat-form-field>
11761
11742
  } @else if (group.controls.initialMode.value === 'boolean') {
11762
11743
  <mat-form-field appearance="outline" class="span-2">
@@ -11804,9 +11785,7 @@ class PageConfigEditorComponent {
11804
11785
  <div class="state-card" [formGroup]="group">
11805
11786
  <div class="state-card-head">
11806
11787
  <div class="state-card-title">{{ tx('editor.state.derivedN', 'Derived {{index}}').replace('{{index}}', '' + ($index + 1)) }}</div>
11807
- <button mat-icon-button type="button" (click)="removeDerivedNode($index)" [attr.aria-label]="tx('editor.state.removeDerivedAria', 'Remove derivation')">
11808
- <mat-icon>delete</mat-icon>
11809
- </button>
11788
+ <button praxisIconButton="delete" size="compact" type="button" (click)="removeDerivedNode($index)" [attr.aria-label]="tx('editor.state.removeDerivedAria', 'Remove derivation')"></button>
11810
11789
  </div>
11811
11790
  <div class="state-card-grid">
11812
11791
  <mat-form-field appearance="outline">
@@ -11864,9 +11843,7 @@ class PageConfigEditorComponent {
11864
11843
  <mat-form-field appearance="outline" class="span-2">
11865
11844
  <mat-label>{{ tx('editor.state.templateJson', 'Template (JSON)') }}</mat-label>
11866
11845
  <textarea matInput rows="4" formControlName="templateJson"></textarea>
11867
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="templateJsonHint" matTooltipPosition="above">
11868
- <mat-icon>help_outline</mat-icon>
11869
- </button>
11846
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="templateJsonHint" [attr.aria-label]="templateJsonHint" matTooltipPosition="above"></button>
11870
11847
  </mat-form-field>
11871
11848
  } @else if (group.controls.templateMode.value === 'boolean') {
11872
11849
  <mat-form-field appearance="outline" class="span-2">
@@ -11902,9 +11879,7 @@ class PageConfigEditorComponent {
11902
11879
  <mat-form-field appearance="outline" class="span-2">
11903
11880
  <mat-label>{{ tx('editor.state.optionsJson', 'Options (JSON)') }}</mat-label>
11904
11881
  <textarea matInput rows="4" formControlName="options"></textarea>
11905
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="transformerOptionsHint" matTooltipPosition="above">
11906
- <mat-icon>help_outline</mat-icon>
11907
- </button>
11882
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="transformerOptionsHint" [attr.aria-label]="transformerOptionsHint" matTooltipPosition="above"></button>
11908
11883
  </mat-form-field>
11909
11884
  }
11910
11885
  <mat-form-field appearance="outline" class="span-3">
@@ -11930,7 +11905,7 @@ class PageConfigEditorComponent {
11930
11905
  </div>
11931
11906
  }
11932
11907
  </div>
11933
- `, isInline: true, styles: [".page-config{display:grid;gap:12px;padding:16px;background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:12px}.page-head{display:flex;align-items:flex-start;gap:12px}.page-head__copy{display:grid;gap:4px}.page-title{font-weight:600;font-size:18px;line-height:1.2}.page-subtitle{max-width:72ch;color:var(--md-sys-color-on-surface-variant);font-size:13px;line-height:1.45}.spacer{flex:1}.page-summary{display:flex;flex-wrap:wrap;gap:8px}.page-tabs{border-radius:16px;overflow:hidden;background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 78%,transparent);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 82%,transparent)}.page-tabs ::ng-deep .mdc-tab{min-width:120px}.page-tabs ::ng-deep .mdc-tab__text-label{font-size:13px;font-weight:600}.page-tab-panel{display:grid;gap:12px;padding:16px 4px 4px}.page-chip{display:inline-flex;align-items:center;gap:8px;min-height:36px;padding:0 12px;border-radius:999px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 88%,transparent);background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 82%,transparent);color:var(--md-sys-color-on-surface-variant);font-size:12px;font-weight:500}.page-chip mat-icon{width:18px;height:18px;font-size:18px}.page-chip--accent{border-color:color-mix(in srgb,var(--md-sys-color-primary) 35%,var(--md-sys-color-outline-variant));color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 30%,transparent)}.page-chip--warning{border-color:color-mix(in srgb,var(--md-sys-color-error) 35%,var(--md-sys-color-outline-variant));color:var(--md-sys-color-error);background:color-mix(in srgb,var(--md-sys-color-error-container) 35%,transparent)}.page-identity{display:grid;gap:4px;padding:12px 14px;border-radius:12px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 82%,transparent);background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 70%,transparent);font-size:13px;color:var(--md-sys-color-on-surface-variant)}.page-section{font-weight:600;margin-top:8px}.page-section-help{margin-top:-4px;color:var(--md-sys-color-on-surface-variant);font-size:13px;line-height:1.45}.grid-form{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.state-grid{display:grid;gap:16px}.state-values,.state-group{padding:14px;border:1px solid var(--md-sys-color-outline-variant);border-radius:14px;background:linear-gradient(180deg,color-mix(in srgb,var(--md-sys-color-surface-container-low) 92%,transparent),transparent),var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface))}.state-group{display:grid;gap:12px}.state-group-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.state-group-title{font-weight:600}.state-group-help{color:var(--md-sys-color-on-surface-variant);font-size:13px}.state-list{display:grid;gap:12px}.state-card{display:grid;gap:12px;padding:12px;border-radius:12px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 80%,transparent);background:color-mix(in srgb,var(--md-sys-color-surface-container) 84%,transparent)}.state-card-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.state-card-title{font-weight:600}.state-card-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.span-2{grid-column:span 2}.span-3{grid-column:1 / -1}.state-empty{color:var(--md-sys-color-on-surface-variant);font-size:13px}.state-operator-hint{display:flex;align-items:center;gap:8px;min-height:48px;padding:0 12px;border-radius:12px;color:var(--md-sys-color-on-surface-variant);background:color-mix(in srgb,var(--md-sys-color-secondary-container) 35%,transparent)}.state-operator-hint mat-icon{color:var(--md-sys-color-secondary)}.page-actions{display:flex;justify-content:flex-end}.page-error{color:var(--md-sys-color-error);font-size:13px}.page-config .mat-mdc-form-field{width:100%;--mdc-outlined-text-field-container-height: 48px;--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-outline);--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-error-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-focus-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-hover-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-label-text-color: var(--md-sys-color-on-surface-variant);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-supporting-text-color: var(--md-sys-color-on-surface-variant)}.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}@media(max-width:720px){.span-2,.span-3{grid-column:auto}.page-head{flex-direction:column}.state-group-head{align-items:stretch;flex-direction:column}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: i2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.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: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6$1.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: i6$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i7.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i7.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: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
11908
+ `, isInline: true, styles: [".page-config{display:grid;gap:12px;padding:16px;background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:12px}.page-head{display:flex;align-items:flex-start;gap:12px}.page-head__copy{display:grid;gap:4px}.page-title{font-weight:600;font-size:18px;line-height:1.2}.page-subtitle{max-width:72ch;color:var(--md-sys-color-on-surface-variant);font-size:13px;line-height:1.45}.spacer{flex:1}.page-summary{display:flex;flex-wrap:wrap;gap:8px}.page-tabs{border-radius:16px;overflow:hidden;background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 78%,transparent);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 82%,transparent)}.page-tabs ::ng-deep .mdc-tab{min-width:120px}.page-tabs ::ng-deep .mdc-tab__text-label{font-size:13px;font-weight:600}.page-tab-panel{display:grid;gap:12px;padding:16px 4px 4px}.page-chip{display:inline-flex;align-items:center;gap:8px;min-height:36px;padding:0 12px;border-radius:999px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 88%,transparent);background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 82%,transparent);color:var(--md-sys-color-on-surface-variant);font-size:12px;font-weight:500}.page-chip mat-icon{width:18px;height:18px;font-size:18px}.page-chip--accent{border-color:color-mix(in srgb,var(--md-sys-color-primary) 35%,var(--md-sys-color-outline-variant));color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 30%,transparent)}.page-chip--warning{border-color:color-mix(in srgb,var(--md-sys-color-error) 35%,var(--md-sys-color-outline-variant));color:var(--md-sys-color-error);background:color-mix(in srgb,var(--md-sys-color-error-container) 35%,transparent)}.page-identity{display:grid;gap:4px;padding:12px 14px;border-radius:12px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 82%,transparent);background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 70%,transparent);font-size:13px;color:var(--md-sys-color-on-surface-variant)}.page-section{font-weight:600;margin-top:8px}.page-section-help{margin-top:-4px;color:var(--md-sys-color-on-surface-variant);font-size:13px;line-height:1.45}.grid-form{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.state-grid{display:grid;gap:16px}.state-values,.state-group{padding:14px;border:1px solid var(--md-sys-color-outline-variant);border-radius:14px;background:linear-gradient(180deg,color-mix(in srgb,var(--md-sys-color-surface-container-low) 92%,transparent),transparent),var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface))}.state-group{display:grid;gap:12px}.state-group-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.state-group-title{font-weight:600}.state-group-help{color:var(--md-sys-color-on-surface-variant);font-size:13px}.state-list{display:grid;gap:12px}.state-card{display:grid;gap:12px;padding:12px;border-radius:12px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 80%,transparent);background:color-mix(in srgb,var(--md-sys-color-surface-container) 84%,transparent)}.state-card-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.state-card-title{font-weight:600}.state-card-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.span-2{grid-column:span 2}.span-3{grid-column:1 / -1}.state-empty{color:var(--md-sys-color-on-surface-variant);font-size:13px}.state-operator-hint{display:flex;align-items:center;gap:8px;min-height:48px;padding:0 12px;border-radius:12px;color:var(--md-sys-color-on-surface-variant);background:color-mix(in srgb,var(--md-sys-color-secondary-container) 35%,transparent)}.state-operator-hint mat-icon{color:var(--md-sys-color-secondary)}.page-actions{display:flex;justify-content:flex-end}.page-error{color:var(--md-sys-color-error);font-size:13px}.page-config .mat-mdc-form-field{width:100%;--mdc-outlined-text-field-container-height: 48px;--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-outline);--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-error-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-focus-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-hover-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-label-text-color: var(--md-sys-color-on-surface-variant);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-supporting-text-color: var(--md-sys-color-on-surface-variant)}.help-icon-button{margin-right:-4px}.mat-mdc-form-field-icon-suffix{align-self:center}@media(max-width:720px){.span-2,.span-3{grid-column:auto}.page-head{flex-direction:column}.state-group-head{align-items:stretch;flex-direction:column}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.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: MatFormFieldModule }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i3$1.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i5.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: MatIconModule }, { kind: "component", type: i2$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i6$1.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: i6$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i7.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i7.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: MatTooltipModule }, { kind: "directive", type: i3.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"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
11934
11909
  }
11935
11910
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PageConfigEditorComponent, decorators: [{
11936
11911
  type: Component,
@@ -11942,7 +11917,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
11942
11917
  MatIconModule,
11943
11918
  MatSelectModule,
11944
11919
  MatTabsModule,
11945
- MatTooltipModule
11920
+ MatTooltipModule,
11921
+ PraxisIconButtonComponent,
11946
11922
  ], providers: [providePraxisPageBuilderI18n()], template: `
11947
11923
  <div class="page-config">
11948
11924
  <div class="page-head">
@@ -12018,9 +11994,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12018
11994
  <mat-option [value]="preset.id">{{ preset.label }}</mat-option>
12019
11995
  }
12020
11996
  </mat-select>
12021
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="layoutPresetHint" matTooltipPosition="above">
12022
- <mat-icon>help_outline</mat-icon>
12023
- </button>
11997
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="layoutPresetHint" [attr.aria-label]="layoutPresetHint" matTooltipPosition="above"></button>
12024
11998
  </mat-form-field>
12025
11999
  <mat-form-field appearance="outline">
12026
12000
  <mat-label>{{ tx('editor.layout.themePreset', 'Theme preset') }}</mat-label>
@@ -12030,9 +12004,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12030
12004
  <mat-option [value]="preset.id">{{ preset.label }}</mat-option>
12031
12005
  }
12032
12006
  </mat-select>
12033
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="themePresetHint" matTooltipPosition="above">
12034
- <mat-icon>help_outline</mat-icon>
12035
- </button>
12007
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="themePresetHint" [attr.aria-label]="themePresetHint" matTooltipPosition="above"></button>
12036
12008
  </mat-form-field>
12037
12009
  <mat-form-field appearance="outline">
12038
12010
  <mat-label>{{ tx('editor.layout.shellPreset', 'Shell visual preset') }}</mat-label>
@@ -12042,9 +12014,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12042
12014
  <mat-option [value]="preset.id">{{ preset.label }}</mat-option>
12043
12015
  }
12044
12016
  </mat-select>
12045
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="shellPresetHint" matTooltipPosition="above">
12046
- <mat-icon>help_outline</mat-icon>
12047
- </button>
12017
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="shellPresetHint" [attr.aria-label]="shellPresetHint" matTooltipPosition="above"></button>
12048
12018
  </mat-form-field>
12049
12019
  </div>
12050
12020
  </div>
@@ -12069,9 +12039,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12069
12039
  <div class="state-card" [formGroup]="group">
12070
12040
  <div class="state-card-head">
12071
12041
  <div class="state-card-title">{{ tx('editor.grouping.groupN', 'Group {{index}}').replace('{{index}}', '' + ($index + 1)) }}</div>
12072
- <button mat-icon-button type="button" (click)="removeGroupingNode($index)" [attr.aria-label]="tx('editor.grouping.removeAria', 'Remove group')">
12073
- <mat-icon>delete</mat-icon>
12074
- </button>
12042
+ <button praxisIconButton="delete" size="compact" type="button" (click)="removeGroupingNode($index)" [attr.aria-label]="tx('editor.grouping.removeAria', 'Remove group')"></button>
12075
12043
  </div>
12076
12044
  <div class="state-card-grid">
12077
12045
  <mat-form-field appearance="outline">
@@ -12124,9 +12092,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12124
12092
  <mat-form-field appearance="outline" class="span-3">
12125
12093
  <mat-label>{{ tx('editor.grouping.tabsJson', 'Tabs (JSON)') }}</mat-label>
12126
12094
  <textarea matInput rows="5" formControlName="tabsJson"></textarea>
12127
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="tabsJsonHint" matTooltipPosition="above">
12128
- <mat-icon>help_outline</mat-icon>
12129
- </button>
12095
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="tabsJsonHint" [attr.aria-label]="tabsJsonHint" matTooltipPosition="above"></button>
12130
12096
  </mat-form-field>
12131
12097
  } @else {
12132
12098
  <mat-form-field appearance="outline" class="span-3">
@@ -12172,23 +12138,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12172
12138
  <mat-form-field appearance="outline" class="span-3">
12173
12139
  <mat-label>{{ tx('editor.devices.breakpointsJson', 'Breakpoints (JSON)') }}</mat-label>
12174
12140
  <textarea matInput rows="3" formControlName="breakpointsJson"></textarea>
12175
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="breakpointsHint" matTooltipPosition="above">
12176
- <mat-icon>help_outline</mat-icon>
12177
- </button>
12141
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="breakpointsHint" [attr.aria-label]="breakpointsHint" matTooltipPosition="above"></button>
12178
12142
  </mat-form-field>
12179
12143
  <mat-form-field appearance="outline" class="span-3">
12180
12144
  <mat-label>{{ tx('editor.devices.groupingOverridesJson', 'Grouping overrides (JSON)') }}</mat-label>
12181
12145
  <textarea matInput rows="4" formControlName="groupingOverridesJson"></textarea>
12182
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="groupingOverridesHint" matTooltipPosition="above">
12183
- <mat-icon>help_outline</mat-icon>
12184
- </button>
12146
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="groupingOverridesHint" [attr.aria-label]="groupingOverridesHint" matTooltipPosition="above"></button>
12185
12147
  </mat-form-field>
12186
12148
  <mat-form-field appearance="outline" class="span-3">
12187
12149
  <mat-label>{{ tx('editor.devices.widgetOverridesJson', 'Widget overrides (JSON)') }}</mat-label>
12188
12150
  <textarea matInput rows="4" formControlName="widgetOverridesJson"></textarea>
12189
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="widgetOverridesHint" matTooltipPosition="above">
12190
- <mat-icon>help_outline</mat-icon>
12191
- </button>
12151
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="widgetOverridesHint" [attr.aria-label]="widgetOverridesHint" matTooltipPosition="above"></button>
12192
12152
  </mat-form-field>
12193
12153
  </div>
12194
12154
  </div>
@@ -12214,23 +12174,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12214
12174
  <mat-form-field appearance="outline" class="span-3">
12215
12175
  <mat-label>{{ tx('editor.devices.breakpointsJson', 'Breakpoints (JSON)') }}</mat-label>
12216
12176
  <textarea matInput rows="3" formControlName="breakpointsJson"></textarea>
12217
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="breakpointsHint" matTooltipPosition="above">
12218
- <mat-icon>help_outline</mat-icon>
12219
- </button>
12177
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="breakpointsHint" [attr.aria-label]="breakpointsHint" matTooltipPosition="above"></button>
12220
12178
  </mat-form-field>
12221
12179
  <mat-form-field appearance="outline" class="span-3">
12222
12180
  <mat-label>{{ tx('editor.devices.groupingOverridesJson', 'Grouping overrides (JSON)') }}</mat-label>
12223
12181
  <textarea matInput rows="4" formControlName="groupingOverridesJson"></textarea>
12224
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="groupingOverridesHint" matTooltipPosition="above">
12225
- <mat-icon>help_outline</mat-icon>
12226
- </button>
12182
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="groupingOverridesHint" [attr.aria-label]="groupingOverridesHint" matTooltipPosition="above"></button>
12227
12183
  </mat-form-field>
12228
12184
  <mat-form-field appearance="outline" class="span-3">
12229
12185
  <mat-label>{{ tx('editor.devices.widgetOverridesJson', 'Widget overrides (JSON)') }}</mat-label>
12230
12186
  <textarea matInput rows="4" formControlName="widgetOverridesJson"></textarea>
12231
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="widgetOverridesHint" matTooltipPosition="above">
12232
- <mat-icon>help_outline</mat-icon>
12233
- </button>
12187
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="widgetOverridesHint" [attr.aria-label]="widgetOverridesHint" matTooltipPosition="above"></button>
12234
12188
  </mat-form-field>
12235
12189
  </div>
12236
12190
  </div>
@@ -12256,23 +12210,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12256
12210
  <mat-form-field appearance="outline" class="span-3">
12257
12211
  <mat-label>{{ tx('editor.devices.breakpointsJson', 'Breakpoints (JSON)') }}</mat-label>
12258
12212
  <textarea matInput rows="3" formControlName="breakpointsJson"></textarea>
12259
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="breakpointsHint" matTooltipPosition="above">
12260
- <mat-icon>help_outline</mat-icon>
12261
- </button>
12213
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="breakpointsHint" [attr.aria-label]="breakpointsHint" matTooltipPosition="above"></button>
12262
12214
  </mat-form-field>
12263
12215
  <mat-form-field appearance="outline" class="span-3">
12264
12216
  <mat-label>{{ tx('editor.devices.groupingOverridesJson', 'Grouping overrides (JSON)') }}</mat-label>
12265
12217
  <textarea matInput rows="4" formControlName="groupingOverridesJson"></textarea>
12266
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="groupingOverridesHint" matTooltipPosition="above">
12267
- <mat-icon>help_outline</mat-icon>
12268
- </button>
12218
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="groupingOverridesHint" [attr.aria-label]="groupingOverridesHint" matTooltipPosition="above"></button>
12269
12219
  </mat-form-field>
12270
12220
  <mat-form-field appearance="outline" class="span-3">
12271
12221
  <mat-label>{{ tx('editor.devices.widgetOverridesJson', 'Widget overrides (JSON)') }}</mat-label>
12272
12222
  <textarea matInput rows="4" formControlName="widgetOverridesJson"></textarea>
12273
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="widgetOverridesHint" matTooltipPosition="above">
12274
- <mat-icon>help_outline</mat-icon>
12275
- </button>
12223
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="widgetOverridesHint" [attr.aria-label]="widgetOverridesHint" matTooltipPosition="above"></button>
12276
12224
  </mat-form-field>
12277
12225
  </div>
12278
12226
  </div>
@@ -12291,15 +12239,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12291
12239
  <mat-label>{{ tx('editor.context.json', 'Context (JSON)') }}</mat-label>
12292
12240
  <textarea matInput rows="10" [formControl]="contextControl"></textarea>
12293
12241
  <button
12294
- mat-icon-button
12242
+ praxisIconButton="help_outline"
12243
+ size="dense"
12295
12244
  matSuffix
12296
12245
  class="help-icon-button"
12297
12246
  type="button"
12298
12247
  [matTooltip]="contextExample"
12248
+ [attr.aria-label]="contextExample"
12299
12249
  matTooltipPosition="above"
12300
- >
12301
- <mat-icon>help_outline</mat-icon>
12302
- </button>
12250
+ ></button>
12303
12251
  </mat-form-field>
12304
12252
  @if (contextError()) {
12305
12253
  <div class="page-error" role="alert">{{ contextError() }}</div>
@@ -12315,15 +12263,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12315
12263
  <mat-label>{{ tx('editor.state.valuesJson', 'Primary values (JSON)') }}</mat-label>
12316
12264
  <textarea matInput rows="10" [formControl]="stateValuesControl"></textarea>
12317
12265
  <button
12318
- mat-icon-button
12266
+ praxisIconButton="help_outline"
12267
+ size="dense"
12319
12268
  matSuffix
12320
12269
  class="help-icon-button"
12321
12270
  type="button"
12322
12271
  [matTooltip]="stateValuesExample"
12272
+ [attr.aria-label]="stateValuesExample"
12323
12273
  matTooltipPosition="above"
12324
- >
12325
- <mat-icon>help_outline</mat-icon>
12326
- </button>
12274
+ ></button>
12327
12275
  </mat-form-field>
12328
12276
 
12329
12277
  <div class="state-group">
@@ -12343,9 +12291,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12343
12291
  <div class="state-card" [formGroup]="group">
12344
12292
  <div class="state-card-head">
12345
12293
  <div class="state-card-title">{{ tx('editor.state.nodeN', 'Node {{index}}').replace('{{index}}', '' + ($index + 1)) }}</div>
12346
- <button mat-icon-button type="button" (click)="removeSchemaNode($index)" [attr.aria-label]="tx('editor.state.removeSchemaAria', 'Remove schema node')">
12347
- <mat-icon>delete</mat-icon>
12348
- </button>
12294
+ <button praxisIconButton="delete" size="compact" type="button" (click)="removeSchemaNode($index)" [attr.aria-label]="tx('editor.state.removeSchemaAria', 'Remove schema node')"></button>
12349
12295
  </div>
12350
12296
  <div class="state-card-grid">
12351
12297
  <mat-form-field appearance="outline">
@@ -12385,9 +12331,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12385
12331
  <mat-form-field appearance="outline" class="span-2">
12386
12332
  <mat-label>{{ tx('editor.state.initialJson', 'Initial (JSON)') }}</mat-label>
12387
12333
  <textarea matInput rows="4" formControlName="initialJson"></textarea>
12388
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="schemaInitialHint" matTooltipPosition="above">
12389
- <mat-icon>help_outline</mat-icon>
12390
- </button>
12334
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="schemaInitialHint" [attr.aria-label]="schemaInitialHint" matTooltipPosition="above"></button>
12391
12335
  </mat-form-field>
12392
12336
  } @else if (group.controls.initialMode.value === 'boolean') {
12393
12337
  <mat-form-field appearance="outline" class="span-2">
@@ -12435,9 +12379,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12435
12379
  <div class="state-card" [formGroup]="group">
12436
12380
  <div class="state-card-head">
12437
12381
  <div class="state-card-title">{{ tx('editor.state.derivedN', 'Derived {{index}}').replace('{{index}}', '' + ($index + 1)) }}</div>
12438
- <button mat-icon-button type="button" (click)="removeDerivedNode($index)" [attr.aria-label]="tx('editor.state.removeDerivedAria', 'Remove derivation')">
12439
- <mat-icon>delete</mat-icon>
12440
- </button>
12382
+ <button praxisIconButton="delete" size="compact" type="button" (click)="removeDerivedNode($index)" [attr.aria-label]="tx('editor.state.removeDerivedAria', 'Remove derivation')"></button>
12441
12383
  </div>
12442
12384
  <div class="state-card-grid">
12443
12385
  <mat-form-field appearance="outline">
@@ -12495,9 +12437,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12495
12437
  <mat-form-field appearance="outline" class="span-2">
12496
12438
  <mat-label>{{ tx('editor.state.templateJson', 'Template (JSON)') }}</mat-label>
12497
12439
  <textarea matInput rows="4" formControlName="templateJson"></textarea>
12498
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="templateJsonHint" matTooltipPosition="above">
12499
- <mat-icon>help_outline</mat-icon>
12500
- </button>
12440
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="templateJsonHint" [attr.aria-label]="templateJsonHint" matTooltipPosition="above"></button>
12501
12441
  </mat-form-field>
12502
12442
  } @else if (group.controls.templateMode.value === 'boolean') {
12503
12443
  <mat-form-field appearance="outline" class="span-2">
@@ -12533,9 +12473,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12533
12473
  <mat-form-field appearance="outline" class="span-2">
12534
12474
  <mat-label>{{ tx('editor.state.optionsJson', 'Options (JSON)') }}</mat-label>
12535
12475
  <textarea matInput rows="4" formControlName="options"></textarea>
12536
- <button mat-icon-button matSuffix class="help-icon-button" type="button" [matTooltip]="transformerOptionsHint" matTooltipPosition="above">
12537
- <mat-icon>help_outline</mat-icon>
12538
- </button>
12476
+ <button praxisIconButton="help_outline" size="dense" matSuffix class="help-icon-button" type="button" [matTooltip]="transformerOptionsHint" [attr.aria-label]="transformerOptionsHint" matTooltipPosition="above"></button>
12539
12477
  </mat-form-field>
12540
12478
  }
12541
12479
  <mat-form-field appearance="outline" class="span-3">
@@ -12561,7 +12499,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
12561
12499
  </div>
12562
12500
  }
12563
12501
  </div>
12564
- `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".page-config{display:grid;gap:12px;padding:16px;background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:12px}.page-head{display:flex;align-items:flex-start;gap:12px}.page-head__copy{display:grid;gap:4px}.page-title{font-weight:600;font-size:18px;line-height:1.2}.page-subtitle{max-width:72ch;color:var(--md-sys-color-on-surface-variant);font-size:13px;line-height:1.45}.spacer{flex:1}.page-summary{display:flex;flex-wrap:wrap;gap:8px}.page-tabs{border-radius:16px;overflow:hidden;background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 78%,transparent);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 82%,transparent)}.page-tabs ::ng-deep .mdc-tab{min-width:120px}.page-tabs ::ng-deep .mdc-tab__text-label{font-size:13px;font-weight:600}.page-tab-panel{display:grid;gap:12px;padding:16px 4px 4px}.page-chip{display:inline-flex;align-items:center;gap:8px;min-height:36px;padding:0 12px;border-radius:999px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 88%,transparent);background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 82%,transparent);color:var(--md-sys-color-on-surface-variant);font-size:12px;font-weight:500}.page-chip mat-icon{width:18px;height:18px;font-size:18px}.page-chip--accent{border-color:color-mix(in srgb,var(--md-sys-color-primary) 35%,var(--md-sys-color-outline-variant));color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 30%,transparent)}.page-chip--warning{border-color:color-mix(in srgb,var(--md-sys-color-error) 35%,var(--md-sys-color-outline-variant));color:var(--md-sys-color-error);background:color-mix(in srgb,var(--md-sys-color-error-container) 35%,transparent)}.page-identity{display:grid;gap:4px;padding:12px 14px;border-radius:12px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 82%,transparent);background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 70%,transparent);font-size:13px;color:var(--md-sys-color-on-surface-variant)}.page-section{font-weight:600;margin-top:8px}.page-section-help{margin-top:-4px;color:var(--md-sys-color-on-surface-variant);font-size:13px;line-height:1.45}.grid-form{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.state-grid{display:grid;gap:16px}.state-values,.state-group{padding:14px;border:1px solid var(--md-sys-color-outline-variant);border-radius:14px;background:linear-gradient(180deg,color-mix(in srgb,var(--md-sys-color-surface-container-low) 92%,transparent),transparent),var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface))}.state-group{display:grid;gap:12px}.state-group-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.state-group-title{font-weight:600}.state-group-help{color:var(--md-sys-color-on-surface-variant);font-size:13px}.state-list{display:grid;gap:12px}.state-card{display:grid;gap:12px;padding:12px;border-radius:12px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 80%,transparent);background:color-mix(in srgb,var(--md-sys-color-surface-container) 84%,transparent)}.state-card-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.state-card-title{font-weight:600}.state-card-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.span-2{grid-column:span 2}.span-3{grid-column:1 / -1}.state-empty{color:var(--md-sys-color-on-surface-variant);font-size:13px}.state-operator-hint{display:flex;align-items:center;gap:8px;min-height:48px;padding:0 12px;border-radius:12px;color:var(--md-sys-color-on-surface-variant);background:color-mix(in srgb,var(--md-sys-color-secondary-container) 35%,transparent)}.state-operator-hint mat-icon{color:var(--md-sys-color-secondary)}.page-actions{display:flex;justify-content:flex-end}.page-error{color:var(--md-sys-color-error);font-size:13px}.page-config .mat-mdc-form-field{width:100%;--mdc-outlined-text-field-container-height: 48px;--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-outline);--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-error-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-focus-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-hover-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-label-text-color: var(--md-sys-color-on-surface-variant);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-supporting-text-color: var(--md-sys-color-on-surface-variant)}.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}@media(max-width:720px){.span-2,.span-3{grid-column:auto}.page-head{flex-direction:column}.state-group-head{align-items:stretch;flex-direction:column}}\n"] }]
12502
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".page-config{display:grid;gap:12px;padding:16px;background:var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface));border:1px solid var(--md-sys-color-outline-variant);border-radius:12px}.page-head{display:flex;align-items:flex-start;gap:12px}.page-head__copy{display:grid;gap:4px}.page-title{font-weight:600;font-size:18px;line-height:1.2}.page-subtitle{max-width:72ch;color:var(--md-sys-color-on-surface-variant);font-size:13px;line-height:1.45}.spacer{flex:1}.page-summary{display:flex;flex-wrap:wrap;gap:8px}.page-tabs{border-radius:16px;overflow:hidden;background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 78%,transparent);border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 82%,transparent)}.page-tabs ::ng-deep .mdc-tab{min-width:120px}.page-tabs ::ng-deep .mdc-tab__text-label{font-size:13px;font-weight:600}.page-tab-panel{display:grid;gap:12px;padding:16px 4px 4px}.page-chip{display:inline-flex;align-items:center;gap:8px;min-height:36px;padding:0 12px;border-radius:999px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 88%,transparent);background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 82%,transparent);color:var(--md-sys-color-on-surface-variant);font-size:12px;font-weight:500}.page-chip mat-icon{width:18px;height:18px;font-size:18px}.page-chip--accent{border-color:color-mix(in srgb,var(--md-sys-color-primary) 35%,var(--md-sys-color-outline-variant));color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 30%,transparent)}.page-chip--warning{border-color:color-mix(in srgb,var(--md-sys-color-error) 35%,var(--md-sys-color-outline-variant));color:var(--md-sys-color-error);background:color-mix(in srgb,var(--md-sys-color-error-container) 35%,transparent)}.page-identity{display:grid;gap:4px;padding:12px 14px;border-radius:12px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 82%,transparent);background:color-mix(in srgb,var(--md-sys-color-surface-container-low) 70%,transparent);font-size:13px;color:var(--md-sys-color-on-surface-variant)}.page-section{font-weight:600;margin-top:8px}.page-section-help{margin-top:-4px;color:var(--md-sys-color-on-surface-variant);font-size:13px;line-height:1.45}.grid-form{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.state-grid{display:grid;gap:16px}.state-values,.state-group{padding:14px;border:1px solid var(--md-sys-color-outline-variant);border-radius:14px;background:linear-gradient(180deg,color-mix(in srgb,var(--md-sys-color-surface-container-low) 92%,transparent),transparent),var(--md-sys-color-surface-container-lowest, var(--md-sys-color-surface))}.state-group{display:grid;gap:12px}.state-group-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.state-group-title{font-weight:600}.state-group-help{color:var(--md-sys-color-on-surface-variant);font-size:13px}.state-list{display:grid;gap:12px}.state-card{display:grid;gap:12px;padding:12px;border-radius:12px;border:1px solid color-mix(in srgb,var(--md-sys-color-outline-variant) 80%,transparent);background:color-mix(in srgb,var(--md-sys-color-surface-container) 84%,transparent)}.state-card-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.state-card-title{font-weight:600}.state-card-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.span-2{grid-column:span 2}.span-3{grid-column:1 / -1}.state-empty{color:var(--md-sys-color-on-surface-variant);font-size:13px}.state-operator-hint{display:flex;align-items:center;gap:8px;min-height:48px;padding:0 12px;border-radius:12px;color:var(--md-sys-color-on-surface-variant);background:color-mix(in srgb,var(--md-sys-color-secondary-container) 35%,transparent)}.state-operator-hint mat-icon{color:var(--md-sys-color-secondary)}.page-actions{display:flex;justify-content:flex-end}.page-error{color:var(--md-sys-color-error);font-size:13px}.page-config .mat-mdc-form-field{width:100%;--mdc-outlined-text-field-container-height: 48px;--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-outline);--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-error-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-focus-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-error-hover-outline-color: var(--md-sys-color-error);--mdc-outlined-text-field-label-text-color: var(--md-sys-color-on-surface-variant);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-supporting-text-color: var(--md-sys-color-on-surface-variant)}.help-icon-button{margin-right:-4px}.mat-mdc-form-field-icon-suffix{align-self:center}@media(max-width:720px){.span-2,.span-3{grid-column:auto}.page-head{flex-direction:column}.state-group-head{align-items:stretch;flex-direction:column}}\n"] }]
12565
12503
  }], ctorParameters: () => [], propDecorators: { page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: false }] }], identity: [{ type: i0.Input, args: [{ isSignal: true, alias: "identity", required: false }] }], layoutCompatOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "layoutCompatOptions", required: false }] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }] } });
12566
12504
 
12567
12505
  /**
@@ -14808,10 +14746,12 @@ class PageBuilderAiAdapter {
14808
14746
  const { [LEGACY_OPTIONS_KEY]: _legacyOptions, ...rest } = cloned;
14809
14747
  const widgets = (cloned.widgets || [])
14810
14748
  .map((w) => normalizePageBuilderRuntimeWidget(this.stripLegacyLayoutFields(w)));
14749
+ const canvas = this.ensureCanvasLayout(cloned);
14811
14750
  return {
14812
14751
  ...rest,
14813
14752
  widgets,
14814
- canvas: this.ensureCanvasLayout(cloned),
14753
+ canvas,
14754
+ deviceLayouts: this.ensureDeviceLayouts(cloned.deviceLayouts, canvas),
14815
14755
  };
14816
14756
  }
14817
14757
  normalizeConfigForRuntime(config, warnings) {
@@ -14829,10 +14769,12 @@ class PageBuilderAiAdapter {
14829
14769
  const { [LEGACY_OPTIONS_KEY]: _legacyOptions, ...rest } = cloned;
14830
14770
  const widgets = (cloned.widgets || [])
14831
14771
  .map((w) => normalizePageBuilderRuntimeWidget(this.stripLegacyLayoutFields(w)));
14772
+ const canvas = this.ensureCanvasLayout(cloned, warnings);
14832
14773
  return {
14833
14774
  ...rest,
14834
14775
  widgets,
14835
- canvas: this.ensureCanvasLayout(cloned, warnings),
14776
+ canvas,
14777
+ deviceLayouts: this.ensureDeviceLayouts(cloned.deviceLayouts, canvas, warnings),
14836
14778
  };
14837
14779
  }
14838
14780
  normalizePatch(patch, base, warnings) {
@@ -15051,16 +14993,123 @@ class PageBuilderAiAdapter {
15051
14993
  nextRow = items[key].row + items[key].rowSpan - 1;
15052
14994
  warnings.push(`Widget "${key}" missing canvas placement; using defaults.`);
15053
14995
  }
14996
+ const columns = Math.max(1, this.coerceInteger(baseCanvas.columns, legacyOptions?.cols || 12));
14997
+ const normalizedItems = this.resolveCanvasCollisions(items, columns, warnings, 'base');
15054
14998
  return {
15055
14999
  mode: 'grid',
15056
- columns: baseCanvas.columns || legacyOptions?.cols || 12,
15000
+ columns,
15057
15001
  rowUnit: baseCanvas.rowUnit || (typeof legacyOptions?.rowHeight === 'number' ? `${legacyOptions.rowHeight}px` : '80px'),
15058
15002
  gap: baseCanvas.gap || (typeof legacyOptions?.gap === 'number' ? `${legacyOptions.gap}px` : '16px'),
15059
15003
  autoRows: baseCanvas.autoRows || 'fixed',
15060
15004
  collisionPolicy: baseCanvas.collisionPolicy,
15061
- items,
15005
+ items: normalizedItems,
15062
15006
  };
15063
15007
  }
15008
+ ensureDeviceLayouts(deviceLayouts, baseCanvas, warnings = []) {
15009
+ if (!deviceLayouts)
15010
+ return deviceLayouts;
15011
+ const normalized = this.clone(deviceLayouts);
15012
+ for (const device of ['desktop', 'tablet', 'mobile']) {
15013
+ const variant = normalized[device];
15014
+ if (!variant?.canvas)
15015
+ continue;
15016
+ const columns = Math.max(1, this.coerceInteger(variant.canvas.columns, baseCanvas.columns));
15017
+ const overrides = variant.canvas.items || {};
15018
+ const effectiveItems = {};
15019
+ for (const [key, baseItem] of Object.entries(baseCanvas.items)) {
15020
+ const override = overrides[key];
15021
+ if (override?.hidden)
15022
+ continue;
15023
+ effectiveItems[key] = {
15024
+ ...baseItem,
15025
+ ...(override || {}),
15026
+ col: this.coerceInteger(override?.col, baseItem.col),
15027
+ row: this.coerceInteger(override?.row, baseItem.row),
15028
+ colSpan: this.coerceInteger(override?.colSpan, baseItem.colSpan),
15029
+ rowSpan: this.coerceInteger(override?.rowSpan, baseItem.rowSpan),
15030
+ };
15031
+ }
15032
+ const resolvedItems = this.resolveCanvasCollisions(effectiveItems, columns, warnings, device);
15033
+ const nextOverrides = this.clone(overrides);
15034
+ for (const [key, item] of Object.entries(resolvedItems)) {
15035
+ const baseItem = baseCanvas.items[key];
15036
+ const currentOverride = overrides[key];
15037
+ const differsFromBase = !baseItem
15038
+ || item.col !== baseItem.col
15039
+ || item.row !== baseItem.row
15040
+ || item.colSpan !== baseItem.colSpan
15041
+ || item.rowSpan !== baseItem.rowSpan;
15042
+ if (!currentOverride && !differsFromBase)
15043
+ continue;
15044
+ nextOverrides[key] = {
15045
+ ...(currentOverride || {}),
15046
+ col: item.col,
15047
+ row: item.row,
15048
+ colSpan: item.colSpan,
15049
+ rowSpan: item.rowSpan,
15050
+ };
15051
+ }
15052
+ variant.canvas = {
15053
+ ...variant.canvas,
15054
+ columns,
15055
+ items: nextOverrides,
15056
+ };
15057
+ }
15058
+ return normalized;
15059
+ }
15060
+ resolveCanvasCollisions(items, columns, warnings, scope) {
15061
+ const candidates = Object.entries(items)
15062
+ .map(([key, source], index) => {
15063
+ const colSpan = Math.min(columns, Math.max(1, this.coerceInteger(source.colSpan, 1)));
15064
+ return {
15065
+ key,
15066
+ index,
15067
+ source,
15068
+ desired: {
15069
+ col: Math.min(Math.max(1, this.coerceInteger(source.col, 1)), Math.max(1, columns - colSpan + 1)),
15070
+ row: Math.max(1, this.coerceInteger(source.row, 1)),
15071
+ colSpan,
15072
+ rowSpan: Math.max(1, this.coerceInteger(source.rowSpan, 1)),
15073
+ },
15074
+ };
15075
+ })
15076
+ .sort((left, right) => left.desired.row - right.desired.row
15077
+ || left.desired.col - right.desired.col
15078
+ || left.index - right.index);
15079
+ const placed = [];
15080
+ const resolved = {};
15081
+ for (const candidate of candidates) {
15082
+ const item = this.findAvailableCanvasPosition(candidate.desired, placed, columns);
15083
+ resolved[candidate.key] = { ...candidate.source, ...item };
15084
+ placed.push({ key: candidate.key, item: resolved[candidate.key] });
15085
+ if (item.col !== candidate.desired.col || item.row !== candidate.desired.row) {
15086
+ warnings.push(`Widget "${candidate.key}" overlapped another widget in the ${scope} canvas; moved to row ${item.row}, column ${item.col}.`);
15087
+ }
15088
+ }
15089
+ return resolved;
15090
+ }
15091
+ findAvailableCanvasPosition(desired, placed, columns) {
15092
+ const maxCol = Math.max(1, columns - desired.colSpan + 1);
15093
+ const candidateCols = [
15094
+ desired.col,
15095
+ ...Array.from({ length: maxCol }, (_, index) => index + 1)
15096
+ .filter((col) => col !== desired.col),
15097
+ ];
15098
+ for (let row = desired.row;; row += 1) {
15099
+ for (const col of candidateCols) {
15100
+ const candidate = { ...desired, col, row };
15101
+ const collides = placed.some(({ item }) => this.canvasItemsOverlap(candidate, item));
15102
+ if (!collides)
15103
+ return candidate;
15104
+ }
15105
+ }
15106
+ }
15107
+ canvasItemsOverlap(left, right) {
15108
+ return left.col < right.col + right.colSpan
15109
+ && left.col + left.colSpan > right.col
15110
+ && left.row < right.row + right.rowSpan
15111
+ && left.row + left.rowSpan > right.row;
15112
+ }
15064
15113
  stripLegacyLayoutFields(widget) {
15065
15114
  const { x: _x, y: _y, cols: _cols, rows: _rows, minItemCols: _minItemCols, minItemRows: _minItemRows, maxItemCols: _maxItemCols, maxItemRows: _maxItemRows, ...rest } = widget;
15066
15115
  delete rest[LEGACY_LAYOUT_KEY];
@@ -15180,6 +15229,10 @@ class PageBuilderAiAdapter {
15180
15229
  coerceNumber(value, fallback) {
15181
15230
  return typeof value === 'number' && !Number.isNaN(value) ? value : fallback;
15182
15231
  }
15232
+ coerceInteger(value, fallback) {
15233
+ const numeric = this.coerceNumber(value, fallback);
15234
+ return Number.isFinite(numeric) ? Math.trunc(numeric) : fallback;
15235
+ }
15183
15236
  parsePixelValue(value) {
15184
15237
  if (typeof value !== 'string')
15185
15238
  return undefined;
@@ -15218,6 +15271,11 @@ class PageBuilderAgenticAuthoringTurnFlow {
15218
15271
  phase: 'capture',
15219
15272
  });
15220
15273
  }
15274
+ if (this.context.enableTurnStream?.() === true && this.service.streamTurn) {
15275
+ if (this.hasCanonicalSemanticDecision(request)) {
15276
+ return this.submitWithTurnStream(request, prompt);
15277
+ }
15278
+ }
15221
15279
  const contextualActionContext = this.contextualPreviewActionContextFromRequest(request);
15222
15280
  if (contextualActionContext) {
15223
15281
  return from(this.submitContextualPreviewAction(request, prompt, contextualActionContext));
@@ -15415,6 +15473,9 @@ class PageBuilderAgenticAuthoringTurnFlow {
15415
15473
  clientTurnId: request.clientTurnId ?? event.turnId,
15416
15474
  })))), share());
15417
15475
  return merge(of(this.toTurnStreamPreparationStatusResult()), preparedRequest$.pipe(map(({ diagnostics }) => this.toTurnStreamPreparedStatusResult(diagnostics))), timer(TURN_STREAM_PREPARATION_WAITING_MS).pipe(map(() => this.toTurnStreamPreparationWaitingResult()), takeUntil(stream$)), stream$).pipe(catchError((error) => {
15476
+ if (this.hasCanonicalSemanticDecision(request)) {
15477
+ return of(this.toTurnStreamTransportErrorResult(error));
15478
+ }
15418
15479
  if (this.shouldFailClosedFromTurnStreamError(request, prompt, error)) {
15419
15480
  return of(this.toGovernedRouteFailClosedResult(request, prompt, error));
15420
15481
  }
@@ -16123,10 +16184,10 @@ class PageBuilderAgenticAuthoringTurnFlow {
16123
16184
  queryContext: { filters: {} },
16124
16185
  enableCustomization: true,
16125
16186
  chartDocument: {
16126
- version: '1.0.0',
16187
+ version: '0.1.0',
16127
16188
  chartId: chartKey,
16128
16189
  kind: 'bar',
16129
- preset: 'dashboard-breakdown',
16190
+ preset: 'distribution',
16130
16191
  title: `Distribuicao por ${this.humanizeFieldName(categoryField)}`,
16131
16192
  subtitle: 'Clique em uma barra para refinar os detalhes',
16132
16193
  source: {
@@ -16134,14 +16195,12 @@ class PageBuilderAgenticAuthoringTurnFlow {
16134
16195
  resource: resourcePath,
16135
16196
  operation: 'group-by',
16136
16197
  options: {
16137
- groupBy: categoryField,
16138
- metric: 'count',
16139
16198
  orderBy: 'value-desc',
16140
16199
  limit: 12,
16141
16200
  },
16142
16201
  },
16143
16202
  dimensions: [{ field: categoryField, label: this.humanizeFieldName(categoryField), role: 'category' }],
16144
- metrics: [{ field: 'count', label: 'Registros', aggregation: 'count', seriesKind: 'bar' }],
16203
+ metrics: [{ field: 'total', label: 'Registros', aggregation: 'count', seriesKind: 'bar' }],
16145
16204
  legend: false,
16146
16205
  labels: true,
16147
16206
  tooltip: true,
@@ -17049,7 +17108,13 @@ class PageBuilderAgenticAuthoringTurnFlow {
17049
17108
  const contextualExecutableIntent = request
17050
17109
  ? this.toExecutableContextualComponentIntent(intentResolution, request)
17051
17110
  : null;
17052
- if (!backendCanApply && contextualExecutableIntent && request && prompt) {
17111
+ if (!backendCanApply
17112
+ && contextualExecutableIntent
17113
+ && request
17114
+ && prompt
17115
+ && (!this.hasCanonicalSemanticDecision(request)
17116
+ && !this.hasStructuredContinuationAction(request)
17117
+ || this.hasMaterializablePreview(preview))) {
17053
17118
  return this.completeExecutableStreamPreview(request, prompt, contextualExecutableIntent, preview, preparedContextHints);
17054
17119
  }
17055
17120
  if (!canApply) {
@@ -17105,7 +17170,10 @@ class PageBuilderAgenticAuthoringTurnFlow {
17105
17170
  || (!!prompt && this.shouldAutoGenerateGuidedPreview(intentResolution, quickReplies, prompt)))
17106
17171
  && !this.hasBlockingResourceQuickReply(quickReplies)
17107
17172
  && request
17108
- && prompt) {
17173
+ && prompt
17174
+ && (!this.hasCanonicalSemanticDecision(request)
17175
+ && !this.hasStructuredContinuationAction(request)
17176
+ || this.hasMaterializablePreview(preview))) {
17109
17177
  return this.completeExecutableStreamPreview(request, prompt, intentResolution, preview, preparedContextHints);
17110
17178
  }
17111
17179
  const requiresChoice = quickReplies.length > 0 && !this.isConsultativeCatalogIntent(intentResolution);
@@ -18004,7 +18072,7 @@ class PageBuilderAgenticAuthoringTurnFlow {
18004
18072
  return status;
18005
18073
  }
18006
18074
  const suffix = this.context
18007
- .tx('agentic.dashboardQuality.degraded', 'Dashboard created with quality warnings: {warnings}. Review diagnostics before saving.')
18075
+ .tx('agentic.dashboardQuality.degraded', 'A prévia está pronta para revisão. Antes de salvar, recomendo completar: {warnings}. Você pode pedir esse ajuste agora pelas ações sugeridas.')
18008
18076
  .replace('{warnings}', labels.join(', '));
18009
18077
  const normalized = status.trim();
18010
18078
  return normalized ? `${normalized} ${suffix}` : suffix;
@@ -18486,6 +18554,15 @@ class PageBuilderAgenticAuthoringTurnFlow {
18486
18554
  return this.shouldFallbackFromTurnStreamError(error)
18487
18555
  && this.isGovernedSemanticDecisionRequest(request, prompt);
18488
18556
  }
18557
+ hasCanonicalSemanticDecision(request) {
18558
+ return this.toJsonObject(request.activeSemanticDecision) !== null;
18559
+ }
18560
+ hasStructuredContinuationAction(request) {
18561
+ const action = this.toJsonObject(request.action);
18562
+ return action !== null
18563
+ && !!this.readString(action, 'id')
18564
+ && !!this.readString(action, 'kind');
18565
+ }
18489
18566
  isGovernedSemanticDecisionRequest(request, prompt) {
18490
18567
  const contextHints = this.buildContextHints(request);
18491
18568
  const domainCatalog = this.toJsonObject(contextHints?.['domainCatalog']);
@@ -19329,8 +19406,10 @@ class PageBuilderAgenticAuthoringTurnFlow {
19329
19406
  description: reply.description,
19330
19407
  icon: reply.icon,
19331
19408
  tone: reply.tone,
19332
- presentation: null,
19409
+ presentation: this.toJsonObject(reply.presentation)
19410
+ ?? this.toJsonObject(this.toJsonObject(reply.contextHints)?.['presentation']),
19333
19411
  contextHints: this.toJsonObject(reply.contextHints),
19412
+ canonicalAction: this.toJsonObject(reply.canonicalAction),
19334
19413
  semanticDecision: this.toJsonObject(reply.semanticDecision),
19335
19414
  value: reply.value,
19336
19415
  }));
@@ -20065,7 +20144,7 @@ function projectKnowledgeTarget(changeSet, conceptKey) {
20065
20144
  };
20066
20145
  }
20067
20146
 
20068
- const AGENTIC_AUTHORING_TURN_INACTIVITY_TIMEOUT_MS = 90_000;
20147
+ const AGENTIC_AUTHORING_TURN_INACTIVITY_TIMEOUT_MS = 180_000;
20069
20148
  const AGENTIC_PAGE_COMPOSITION_REQUEST_OUTPUT = 'agenticPageCompositionRequested';
20070
20149
  class DynamicPageBuilderComponent {
20071
20150
  dialog;
@@ -20168,6 +20247,7 @@ class DynamicPageBuilderComponent {
20168
20247
  }), ...(ngDevMode ? [{ debugName: "agenticAuthoringPanelLayout" }] : /* istanbul ignore next */ []));
20169
20248
  previewMode = false;
20170
20249
  runtimePreviewPageCache;
20250
+ runtimeAuthoringPageCache;
20171
20251
  agenticComponentCapabilities;
20172
20252
  agenticComponentCapabilitiesPromise;
20173
20253
  agenticTurnController;
@@ -20241,7 +20321,12 @@ class DynamicPageBuilderComponent {
20241
20321
  runtimePage() {
20242
20322
  const page = this.currentPage();
20243
20323
  if (!this.previewMode) {
20244
- return page;
20324
+ if (this.runtimeAuthoringPageCache?.source === page) {
20325
+ return this.runtimeAuthoringPageCache.value;
20326
+ }
20327
+ const value = this.toAuthoringRuntimePage(page);
20328
+ this.runtimeAuthoringPageCache = { source: page, value };
20329
+ return value;
20245
20330
  }
20246
20331
  if (this.runtimePreviewPageCache?.source === page) {
20247
20332
  return this.runtimePreviewPageCache.value;
@@ -21562,6 +21647,9 @@ class DynamicPageBuilderComponent {
21562
21647
  return;
21563
21648
  }
21564
21649
  const contextHints = reply.contextHints ? { ...reply.contextHints } : undefined;
21650
+ const canonicalAction = reply.canonicalAction
21651
+ ? { ...reply.canonicalAction }
21652
+ : undefined;
21565
21653
  const activeSemanticDecision = reply.semanticDecision
21566
21654
  ? { ...reply.semanticDecision }
21567
21655
  : undefined;
@@ -21600,8 +21688,12 @@ class DynamicPageBuilderComponent {
21600
21688
  id: reply.id,
21601
21689
  value: this.cloneValue(reply.value ?? reply.prompt),
21602
21690
  displayPrompt: visiblePrompt,
21603
- contextHints: activeSemanticDecision
21604
- ? { ...(contextHints ?? {}), semanticDecision: activeSemanticDecision }
21691
+ contextHints: canonicalAction || activeSemanticDecision
21692
+ ? {
21693
+ ...(contextHints ?? {}),
21694
+ ...(canonicalAction ? { canonicalAction } : {}),
21695
+ ...(activeSemanticDecision ? { semanticDecision: activeSemanticDecision } : {}),
21696
+ }
21605
21697
  : contextHints,
21606
21698
  activeSemanticDecision,
21607
21699
  });
@@ -22875,6 +22967,26 @@ class DynamicPageBuilderComponent {
22875
22967
  cloned.widgets = (cloned.widgets || []).map((widget) => this.toPresentationRuntimeWidget(widget));
22876
22968
  return cloned;
22877
22969
  }
22970
+ toAuthoringRuntimePage(page) {
22971
+ const cloned = this.clonePage(page);
22972
+ cloned.widgets = (cloned.widgets || []).map((widget) => {
22973
+ const metadata = this.componentMetadata.get(widget.definition?.id);
22974
+ if (!metadata?.inputs?.some((input) => input.name === 'enableCustomization')) {
22975
+ return widget;
22976
+ }
22977
+ return {
22978
+ ...widget,
22979
+ definition: {
22980
+ ...widget.definition,
22981
+ inputs: {
22982
+ ...(widget.definition?.inputs || {}),
22983
+ enableCustomization: true,
22984
+ },
22985
+ },
22986
+ };
22987
+ });
22988
+ return cloned;
22989
+ }
22878
22990
  toPresentationRuntimeWidget(widget) {
22879
22991
  const inputs = widget.definition?.inputs;
22880
22992
  if (!inputs || inputs['enableCustomization'] !== true) {