@praxisui/settings-panel 8.0.0-beta.8 → 8.0.0-beta.81

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.
@@ -23,9 +23,9 @@ import { ComponentPortal } from '@angular/cdk/portal';
23
23
  import * as i1$2 from '@angular/cdk/overlay';
24
24
  import * as i2$1 from '@angular/material/snack-bar';
25
25
  import { MatSnackBarModule } from '@angular/material/snack-bar';
26
- import * as i4$1 from '@angular/material/expansion';
26
+ import * as i3$1 from '@angular/material/expansion';
27
27
  import { MatExpansionModule } from '@angular/material/expansion';
28
- import * as i9 from '@angular/material/chips';
28
+ import * as i8 from '@angular/material/chips';
29
29
  import { MatChipsModule } from '@angular/material/chips';
30
30
  import { AiBackendApiService } from '@praxisui/ai';
31
31
 
@@ -480,6 +480,12 @@ function providePraxisSettingsPanelI18n() {
480
480
  });
481
481
  }
482
482
 
483
+ const DEFAULT_DIAGNOSTICS = {
484
+ showStatusMessage: true,
485
+ showDisabledReason: true,
486
+ showBusyState: true,
487
+ exposeValidationState: true,
488
+ };
483
489
  class SettingsPanelComponent {
484
490
  cdr;
485
491
  dialog;
@@ -498,6 +504,9 @@ class SettingsPanelComponent {
498
504
  isDirty = false;
499
505
  isValid = true;
500
506
  isBusy = false;
507
+ diagnostics = {
508
+ ...DEFAULT_DIAGNOSTICS,
509
+ };
501
510
  lastSavedAt = null;
502
511
  activePointerId = null;
503
512
  dragStartX = 0;
@@ -541,9 +550,20 @@ class SettingsPanelComponent {
541
550
  }
542
551
  return '';
543
552
  }
553
+ get applySaveDisabledReason() {
554
+ return this.diagnostics.showDisabledReason ? this.disabledReason : '';
555
+ }
556
+ get showStatusMessage() {
557
+ return this.diagnostics.showStatusMessage;
558
+ }
559
+ get showBusyIndicator() {
560
+ return this.isBusy && this.diagnostics.showBusyState;
561
+ }
544
562
  get statusTone() {
545
- if (this.isBusy)
563
+ if (this.isBusy && this.diagnostics.showBusyState)
546
564
  return 'busy';
565
+ if (!this.isValid && this.diagnostics.exposeValidationState)
566
+ return 'invalid';
547
567
  if (this.isDirty)
548
568
  return 'dirty';
549
569
  if (this.lastSavedAt)
@@ -554,6 +574,9 @@ class SettingsPanelComponent {
554
574
  if (this.statusTone === 'busy') {
555
575
  return this.tx('Operation in progress...');
556
576
  }
577
+ if (this.statusTone === 'invalid') {
578
+ return this.tx('The form contains errors that need to be fixed.');
579
+ }
557
580
  if (this.statusTone === 'dirty') {
558
581
  return this.tx('Unsaved changes');
559
582
  }
@@ -726,6 +749,13 @@ class SettingsPanelComponent {
726
749
  this.expanded = false;
727
750
  this.collapsedWidthBeforeExpand = null;
728
751
  }
752
+ configureDiagnostics(config) {
753
+ this.diagnostics = {
754
+ ...DEFAULT_DIAGNOSTICS,
755
+ ...(config ?? {}),
756
+ };
757
+ this.cdr.markForCheck();
758
+ }
729
759
  toggleExpand() {
730
760
  if (!this.expanded) {
731
761
  this.collapsedWidthBeforeExpand = this.width;
@@ -910,10 +940,10 @@ class SettingsPanelComponent {
910
940
  .open(ConfirmDialogComponent, { data: dialogData, autoFocus: false })
911
941
  .afterClosed();
912
942
  }
913
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SettingsPanelComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1.MatDialog }], target: i0.ɵɵFactoryTarget.Component });
914
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: SettingsPanelComponent, isStandalone: true, selector: "praxis-settings-panel", host: { listeners: { "document:keydown": "handleKeydown($event)", "document:pointermove": "onDocumentPointerMove($event)", "document:pointerup": "onDocumentPointerEnd($event)", "document:pointercancel": "onDocumentPointerEnd($event)" } }, providers: [providePraxisSettingsPanelI18n()], viewQueries: [{ propertyName: "contentHost", first: true, predicate: ["contentHost"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: "<div\n class=\"settings-panel\"\n data-testid=\"settings-panel-root\"\n [class.expanded]=\"expanded\"\n [class.is-resizable]=\"showResizeHandle\"\n [ngStyle]=\"panelInlineStyles\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"titleId\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n>\n @if (showResizeHandle) {\n <div\n class=\"settings-panel__resize-handle\"\n role=\"separator\"\n tabindex=\"0\"\n aria-orientation=\"vertical\"\n [attr.aria-label]=\"resizeHandleLabel\"\n (pointerdown)=\"onResizeHandlePointerDown($event)\"\n (keydown)=\"onResizeHandleKeydown($event)\"\n ></div>\n }\n <header class=\"settings-panel-header\">\n <h2 class=\"settings-panel-title\" [id]=\"titleId\">\n <mat-icon *ngIf=\"titleIcon\" class=\"title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n <span>{{ title }}</span>\n </h2>\n <span class=\"spacer\"></span>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-toggle-expand\"\n [attr.aria-label]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n [attr.aria-expanded]=\"expanded\"\n [matTooltip]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n (click)=\"toggleExpand()\"\n >\n <mat-icon [praxisIcon]=\"expanded ? 'close_fullscreen' : 'open_in_full'\"></mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-close-icon\"\n [attr.aria-label]=\"tx('Close')\"\n [matTooltip]=\"tx('Close')\"\n (click)=\"onCancel()\"\n >\n <mat-icon [praxisIcon]=\"'close'\"></mat-icon>\n </button>\n </header>\n <div\n class=\"settings-panel-status\"\n [attr.data-status]=\"statusTone\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <mat-icon\n aria-hidden=\"true\"\n [praxisIcon]=\"\n statusTone === 'dirty'\n ? 'warning'\n : statusTone === 'saved'\n ? 'check_circle'\n : statusTone === 'busy'\n ? 'autorenew'\n : 'info'\n \"\n ></mat-icon>\n <span>{{ statusMessage }}</span>\n </div>\n <div class=\"settings-panel-body\">\n <ng-template #contentHost></ng-template>\n </div>\n <footer class=\"settings-panel-footer\">\n <button\n mat-button\n type=\"button\"\n data-testid=\"settings-panel-reset\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onReset()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'restart_alt'\"></mat-icon>\n <span>{{ tx('Reset') }}</span>\n </button>\n <span class=\"spacer\"></span>\n <button\n mat-button\n type=\"button\"\n data-testid=\"settings-panel-cancel\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onCancel()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'close'\"></mat-icon>\n <span>{{ tx('Cancel') }}</span>\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n data-testid=\"settings-panel-apply\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onApply()\"\n [disabled]=\"!canApply\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canApply\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'done'\"></mat-icon>\n <span>{{ tx('Apply') }}</span>\n </ng-container>\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"settings-panel-save\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onSave()\"\n [disabled]=\"!canSave\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canSave\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'save'\"></mat-icon>\n <span>{{ tx('Save & Close') }}</span>\n </ng-container>\n </button>\n </footer>\n</div>\n", styles: ["@charset \"UTF-8\";praxis-settings-panel{display:block;height:100%;width:100%;min-width:0;flex:1 1 auto}praxis-settings-panel .settings-panel{position:relative;--pfx-settings-panel-surface-container: var( --md-sys-color-surface-container, #ffffff );--pfx-settings-panel-surface-container-high: var( --md-sys-color-surface-container-high, #f7f7f7 );--pfx-settings-panel-surface-container-low: var( --md-sys-color-surface-container-low, #f2f4f7 );--pfx-settings-panel-surface: var(--md-sys-color-surface, #ffffff);--pfx-settings-panel-on-surface: var(--md-sys-color-on-surface, #111827);--pfx-settings-panel-on-surface-variant: var( --md-sys-color-on-surface-variant, rgba(17, 24, 39, .72) );--pfx-settings-panel-outline: var( --md-sys-color-outline, rgba(15, 23, 42, .24) );--pfx-settings-panel-outline-variant: var( --md-sys-color-outline-variant, rgba(15, 23, 42, .12) );--pfx-settings-panel-primary: var(--md-sys-color-primary, #2563eb);--pfx-settings-panel-primary-container: var( --md-sys-color-primary-container, rgba(37, 99, 235, .14) );--pfx-settings-panel-on-primary-container: var( --md-sys-color-on-primary-container, #0f172a );--pfx-settings-panel-secondary: var( --md-sys-color-secondary, var(--pfx-settings-panel-primary) );--pfx-settings-panel-error: var(--md-sys-color-error, #b3261e);--pfx-settings-panel-error-container: var( --md-sys-color-error-container, rgba(179, 38, 30, .14) );display:grid;grid-template-rows:auto auto 1fr auto;grid-template-areas:\"header\" \"status\" \"body\" \"footer\";height:100%;background:var(--pfx-settings-panel-surface-container);color:var(--pfx-settings-panel-on-surface);border-left:1px solid var(--pfx-settings-panel-outline-variant);width:var(--pfx-settings-panel-width, 720px);transition:width .3s ease;box-shadow:var(--pfx-settings-panel-elevation, var(--md-sys-elevation-level2, 0 8px 24px rgba(0, 0, 0, .16)));overflow:hidden}praxis-settings-panel .settings-panel.expanded{width:min(var(--pfx-settings-panel-width-expanded, 95vw),var(--pfx-settings-panel-max-width, 2400px))}praxis-settings-panel .settings-panel__resize-handle{position:absolute;inset:0 auto 0 0;width:12px;cursor:col-resize;touch-action:none;z-index:3;outline:none}praxis-settings-panel .settings-panel__resize-handle:before{content:\"\";position:absolute;inset:0 auto 0 4px;width:2px;background:transparent;transition:background-color var(--pfx-side-panel-motion-duration, .16s) ease,opacity var(--pfx-side-panel-motion-duration, .16s) ease;opacity:0}praxis-settings-panel .settings-panel.is-resizable:hover .settings-panel__resize-handle:before,praxis-settings-panel .settings-panel__resize-handle:focus-visible:before{opacity:1;background:var(--pfx-settings-panel-outline)}praxis-settings-panel .settings-panel-header{grid-area:header;display:flex;align-items:center;gap:var(--pfx-settings-panel-header-gap, 8px);padding:0 var(--pfx-settings-panel-header-padding-x, 16px);height:var(--pfx-settings-panel-header-height, 64px);border-bottom:1px solid var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-high);box-shadow:var(--pfx-settings-panel-header-shadow, var(--md-sys-elevation-level1, 0 2px 6px rgba(0, 0, 0, .08)));flex-shrink:0}praxis-settings-panel .settings-panel-header .spacer{flex:1}praxis-settings-panel .settings-panel-status{grid-area:status;display:flex;align-items:center;gap:8px;min-height:36px;padding:6px 16px;border-bottom:1px solid var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-high);color:var(--pfx-settings-panel-on-surface);font-size:.85rem;font-weight:500}praxis-settings-panel .settings-panel-status mat-icon{width:18px;height:18px;font-size:18px}praxis-settings-panel .settings-panel-status[data-status=dirty]{color:var(--pfx-settings-panel-error);background:color-mix(in srgb,var(--pfx-settings-panel-error-container) 30%,var(--pfx-settings-panel-surface-container-high))}praxis-settings-panel .settings-panel-status[data-status=saved]{color:var(--pfx-settings-panel-primary);background:color-mix(in srgb,var(--pfx-settings-panel-primary-container) 28%,var(--pfx-settings-panel-surface-container-high))}praxis-settings-panel .settings-panel-body{grid-area:body;overflow-y:auto;min-height:0;padding:var(--pfx-settings-panel-body-padding, 8px 8px 24px 8px);background:var(--pfx-settings-panel-surface);display:flex;flex-direction:column}praxis-settings-panel .settings-panel-content{display:block}praxis-settings-panel .settings-panel-footer{grid-area:footer;border-top:1px solid var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-high);box-shadow:var(--pfx-settings-panel-footer-shadow, var(--md-sys-elevation-level1, 0 -2px 6px rgba(0, 0, 0, .08)));display:flex;align-items:center;padding:var(--pfx-settings-panel-footer-padding, 12px 16px);column-gap:var(--pfx-settings-panel-footer-gap, 12px);flex-shrink:0}praxis-settings-panel .spacer{flex:1}praxis-settings-panel .settings-panel-title{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-title-gap, 8px);font-weight:700;letter-spacing:.2px;margin:0}praxis-settings-panel .settings-panel-title mat-icon{color:var(--pfx-settings-panel-primary)}praxis-settings-panel .settings-panel-title .title-icon{width:20px;height:20px;font-size:20px}praxis-settings-panel .settings-panel-footer button+button{margin-left:var(--pfx-settings-panel-footer-gap, 12px)}praxis-settings-panel .settings-panel-footer button{display:inline-flex;align-items:center}praxis-settings-panel .settings-panel-footer button .mat-icon{font-size:20px;width:20px;height:20px;line-height:1;display:inline-flex;align-items:center;justify-content:center}praxis-settings-panel .settings-panel-footer .mat-button-wrapper{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px)}praxis-settings-panel .settings-panel-footer .mat-progress-spinner{margin-right:8px}praxis-settings-panel .settings-panel-footer .mat-flat-button[color=primary]{font-weight:600}praxis-settings-panel .settings-panel .mdc-button__label{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px);line-height:1}praxis-settings-panel .settings-panel .mdc-button__label>span{display:inline-flex;align-items:center;line-height:1}praxis-settings-panel .settings-panel .mdc-button__label .mat-icon{display:inline-flex;align-items:center;justify-content:center;line-height:1}praxis-settings-panel .settings-panel .mat-divider{background-color:var(--pfx-settings-panel-outline-variant)!important}praxis-settings-panel .settings-panel .mat-expansion-panel{background:var(--pfx-settings-panel-surface-container)!important;border:1px solid var(--pfx-settings-panel-outline-variant);border-radius:var(--md-sys-shape-corner-medium, 12px);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08));color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-expansion-panel-header{background:var(--pfx-settings-panel-surface-container)!important;color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-content{color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-expansion-panel:not(.mat-expanded) .mat-expansion-panel-header:hover{background:color-mix(in srgb,var(--pfx-settings-panel-surface-container-high) 84%,var(--pfx-settings-panel-primary) 16%)}praxis-settings-panel .settings-panel .mat-expansion-panel.mat-expanded>.mat-expansion-panel-header{background:var(--pfx-settings-panel-surface-container-high)!important}praxis-settings-panel .settings-panel .mat-expansion-panel-header mat-icon,praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-icon{color:var(--pfx-settings-panel-on-surface)!important}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title,praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title>*,praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description>*{color:inherit}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title small,praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description small{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-indicator,praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-indicator:after{color:var(--pfx-settings-panel-on-surface-variant);border-color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-expansion-panel .mat-action-row{border-top-color:var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-low)}praxis-settings-panel .settings-panel .mat-accordion,praxis-settings-panel .settings-panel .mat-accordion .mat-expansion-panel-spacing{background:transparent!important}praxis-settings-panel .settings-panel .mat-expansion-panel-content,praxis-settings-panel .settings-panel .mat-expansion-panel-content-wrapper,praxis-settings-panel .settings-panel .mat-expansion-panel-body{background:var(--pfx-settings-panel-surface-container)!important;color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-expansion-panel .mat-expansion-panel-body{padding:8px}praxis-settings-panel .settings-panel .mat-expansion-panel .mat-expansion-panel-body>*{color:inherit}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header{border-bottom:1px solid var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-low)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab{min-width:0}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab .mdc-tab__text-label{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab:hover .mdc-tab__text-label,praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab:focus .mdc-tab__text-label{color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab.mdc-tab--active .mdc-tab__text-label{color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination-chevron{border-color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination:not(.mat-mdc-tab-header-pagination-disabled):hover{background:color-mix(in srgb,var(--pfx-settings-panel-surface-container-high) 84%,var(--pfx-settings-panel-primary) 16%)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab-indicator__content--underline{border-color:var(--pfx-settings-panel-primary)}praxis-settings-panel .settings-panel .mat-mdc-card{background:var(--pfx-settings-panel-surface-container-low);border:1px solid var(--pfx-settings-panel-outline-variant);color:var(--pfx-settings-panel-on-surface);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08))}praxis-settings-panel .settings-panel .mat-mdc-card-subtitle,praxis-settings-panel .settings-panel .mat-mdc-card-content{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-mdc-card-title{color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-mdc-form-field{width:100%;--mdc-filled-text-field-container-color: var( --pfx-settings-panel-surface-container );--mdc-filled-text-field-hover-container-color: var( --pfx-settings-panel-surface-container-high );--mdc-filled-text-field-focus-container-color: var( --pfx-settings-panel-surface-container-high );--mdc-filled-text-field-active-indicator-color: var( --pfx-settings-panel-outline-variant );--mdc-filled-text-field-hover-active-indicator-color: var( --pfx-settings-panel-secondary );--mdc-filled-text-field-focus-active-indicator-color: var( --pfx-settings-panel-primary );--mdc-filled-text-field-label-text-color: var( --pfx-settings-panel-on-surface-variant );--mdc-filled-text-field-focus-label-text-color: var( --pfx-settings-panel-primary );--mdc-filled-text-field-input-text-color: var( --pfx-settings-panel-on-surface );--mdc-filled-text-field-caret-color: var(--pfx-settings-panel-primary);--mdc-outlined-text-field-outline-color: var( --pfx-settings-panel-outline-variant );--mdc-outlined-text-field-hover-outline-color: var( --pfx-settings-panel-secondary );--mdc-outlined-text-field-focus-outline-color: var( --pfx-settings-panel-primary );--mdc-outlined-text-field-label-text-color: var( --pfx-settings-panel-on-surface-variant );--mdc-outlined-text-field-focus-label-text-color: var( --pfx-settings-panel-primary );--mdc-outlined-text-field-input-text-color: var( --pfx-settings-panel-on-surface );--mdc-outlined-text-field-caret-color: var(--pfx-settings-panel-primary);--mat-form-field-focus-select-arrow-color: var( --pfx-settings-panel-primary );--mat-form-field-enabled-select-arrow-color: var( --pfx-settings-panel-on-surface-variant )}praxis-settings-panel .settings-panel .mdc-text-field--filled{background-color:var(--pfx-settings-panel-surface-container)!important;border-radius:var(--md-sys-shape-corner-small, 8px) var(--md-sys-shape-corner-small, 8px) 0 0}praxis-settings-panel .settings-panel .mat-mdc-text-field-wrapper,praxis-settings-panel .settings-panel .mat-mdc-form-field-flex,praxis-settings-panel .settings-panel .mat-mdc-form-field-infix,praxis-settings-panel .settings-panel .mat-mdc-form-field-focus-overlay{background:var(--pfx-settings-panel-surface-container)!important;color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-mdc-form-field-focus-overlay{opacity:0}praxis-settings-panel .settings-panel .mdc-text-field--filled .mdc-text-field__input,praxis-settings-panel .settings-panel .mdc-text-field--filled .mdc-floating-label,praxis-settings-panel .settings-panel .mat-mdc-select-value,praxis-settings-panel .settings-panel .mat-mdc-select-arrow,praxis-settings-panel .settings-panel .mat-mdc-form-field .mat-mdc-floating-label{color:var(--pfx-settings-panel-on-surface-variant)!important}praxis-settings-panel .settings-panel .mdc-text-field--filled.mdc-text-field--focused .mdc-floating-label,praxis-settings-panel .settings-panel .mdc-text-field--filled .mdc-text-field__input{color:var(--pfx-settings-panel-on-surface)!important}praxis-settings-panel .settings-panel .mat-mdc-form-field input,praxis-settings-panel .settings-panel .mat-mdc-form-field textarea,praxis-settings-panel .settings-panel .mat-mdc-form-field .mdc-text-field__input{color:var(--pfx-settings-panel-on-surface)!important}praxis-settings-panel .settings-panel .mat-mdc-form-field-subscript-wrapper,praxis-settings-panel .settings-panel .mat-mdc-form-field-hint-wrapper,praxis-settings-panel .settings-panel .mat-mdc-form-field-error-wrapper,praxis-settings-panel .settings-panel .mat-mdc-hint{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-button-toggle-group{border-color:var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-low)}praxis-settings-panel .settings-panel .mat-button-toggle{background:var(--pfx-settings-panel-surface-container-low);color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-button-toggle+.mat-button-toggle{border-left-color:var(--pfx-settings-panel-outline-variant)}praxis-settings-panel .settings-panel .mat-button-toggle-checked{background:var(--pfx-settings-panel-primary-container)!important;color:var(--pfx-settings-panel-on-primary-container)!important}praxis-settings-panel .settings-panel .mat-button-toggle .mat-icon,praxis-settings-panel .settings-panel .mat-button-toggle-checked .mat-icon{color:inherit}praxis-settings-panel .settings-panel .mat-mdc-slide-toggle .mdc-label{color:var(--pfx-settings-panel-on-surface)}.praxis-settings-panel-backdrop{background:var(--pfx-backdrop, var(--md-sys-color-scrim, rgba(8, 15, 26, .42)));backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%)}.praxis-settings-panel-pane{position:fixed!important;top:0!important;right:0!important;display:flex!important;align-items:stretch!important;height:100vh!important;max-height:100vh!important;z-index:var(--praxis-layer-settings-panel, 1220)!important}.praxis-settings-panel-pane .settings-panel{pointer-events:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i6.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatDialogModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
943
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: SettingsPanelComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1.MatDialog }], target: i0.ɵɵFactoryTarget.Component });
944
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: SettingsPanelComponent, isStandalone: true, selector: "praxis-settings-panel", host: { listeners: { "document:keydown": "handleKeydown($event)", "document:pointermove": "onDocumentPointerMove($event)", "document:pointerup": "onDocumentPointerEnd($event)", "document:pointercancel": "onDocumentPointerEnd($event)" } }, providers: [providePraxisSettingsPanelI18n()], viewQueries: [{ propertyName: "contentHost", first: true, predicate: ["contentHost"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: "<div\n class=\"settings-panel\"\n data-testid=\"settings-panel-root\"\n [class.expanded]=\"expanded\"\n [class.is-resizable]=\"showResizeHandle\"\n [ngStyle]=\"panelInlineStyles\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"titleId\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n >\n @if (showResizeHandle) {\n <div\n class=\"settings-panel__resize-handle\"\n role=\"separator\"\n tabindex=\"0\"\n aria-orientation=\"vertical\"\n [attr.aria-label]=\"resizeHandleLabel\"\n (pointerdown)=\"onResizeHandlePointerDown($event)\"\n (keydown)=\"onResizeHandleKeydown($event)\"\n ></div>\n }\n <header class=\"settings-panel-header\">\n <h2 class=\"settings-panel-title\" [id]=\"titleId\">\n @if (titleIcon) {\n <mat-icon class=\"title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n }\n <span>{{ title }}</span>\n </h2>\n <span class=\"spacer\"></span>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-toggle-expand\"\n [attr.aria-label]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n [attr.aria-expanded]=\"expanded\"\n [matTooltip]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n (click)=\"toggleExpand()\"\n >\n <mat-icon [praxisIcon]=\"expanded ? 'close_fullscreen' : 'open_in_full'\"></mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-close-icon\"\n [attr.aria-label]=\"tx('Close')\"\n [matTooltip]=\"tx('Close')\"\n (click)=\"onCancel()\"\n >\n <mat-icon [praxisIcon]=\"'close'\"></mat-icon>\n </button>\n </header>\n @if (showStatusMessage) {\n <div\n class=\"settings-panel-status\"\n [attr.data-status]=\"statusTone\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <mat-icon\n aria-hidden=\"true\"\n [praxisIcon]=\"\n statusTone === 'dirty' || statusTone === 'invalid'\n ? 'warning'\n : statusTone === 'saved'\n ? 'check_circle'\n : statusTone === 'busy'\n ? 'autorenew'\n : 'info'\n \"\n ></mat-icon>\n <span>{{ statusMessage }}</span>\n </div>\n }\n <div class=\"settings-panel-body\">\n <ng-template #contentHost></ng-template>\n </div>\n <footer class=\"settings-panel-footer\">\n <button\n mat-button\n type=\"button\"\n data-testid=\"settings-panel-reset\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onReset()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'restart_alt'\"></mat-icon>\n <span>{{ tx('Reset') }}</span>\n </button>\n <span class=\"spacer\"></span>\n <button\n mat-button\n type=\"button\"\n data-testid=\"settings-panel-cancel\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onCancel()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'close'\"></mat-icon>\n <span>{{ tx('Cancel') }}</span>\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n data-testid=\"settings-panel-apply\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onApply()\"\n [disabled]=\"!canApply\"\n [matTooltip]=\"applySaveDisabledReason\"\n [matTooltipDisabled]=\"canApply || !diagnostics.showDisabledReason\"\n [attr.aria-busy]=\"isBusy\"\n >\n @if (showBusyIndicator) {\n <mat-progress-spinner\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n }\n @if (!showBusyIndicator) {\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'done'\"></mat-icon>\n <span>{{ tx('Apply') }}</span>\n }\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"settings-panel-save\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onSave()\"\n [disabled]=\"!canSave\"\n [matTooltip]=\"applySaveDisabledReason\"\n [matTooltipDisabled]=\"canSave || !diagnostics.showDisabledReason\"\n [attr.aria-busy]=\"isBusy\"\n >\n @if (showBusyIndicator) {\n <mat-progress-spinner\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n }\n @if (!showBusyIndicator) {\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'save'\"></mat-icon>\n <span>{{ tx('Save & Close') }}</span>\n }\n </button>\n </footer>\n</div>\n", styles: ["@charset \"UTF-8\";praxis-settings-panel{display:block;height:100%;width:100%;min-width:0;flex:1 1 auto}praxis-settings-panel .settings-panel{position:relative;--pfx-settings-panel-surface-container: var( --md-sys-color-surface-container, #ffffff );--pfx-settings-panel-surface-container-high: var( --md-sys-color-surface-container-high, #f7f7f7 );--pfx-settings-panel-surface-container-low: var( --md-sys-color-surface-container-low, #f2f4f7 );--pfx-settings-panel-surface: var(--md-sys-color-surface, #ffffff);--pfx-settings-panel-on-surface: var(--md-sys-color-on-surface, #111827);--pfx-settings-panel-on-surface-variant: var( --md-sys-color-on-surface-variant, rgba(17, 24, 39, .72) );--pfx-settings-panel-outline: var( --md-sys-color-outline, rgba(15, 23, 42, .24) );--pfx-settings-panel-outline-variant: var( --md-sys-color-outline-variant, rgba(15, 23, 42, .12) );--pfx-settings-panel-primary: var(--md-sys-color-primary, #2563eb);--pfx-settings-panel-primary-container: var( --md-sys-color-primary-container, rgba(37, 99, 235, .14) );--pfx-settings-panel-on-primary-container: var( --md-sys-color-on-primary-container, #0f172a );--pfx-settings-panel-secondary: var( --md-sys-color-secondary, var(--pfx-settings-panel-primary) );--pfx-settings-panel-error: var(--md-sys-color-error, #b3261e);--pfx-settings-panel-error-container: var( --md-sys-color-error-container, rgba(179, 38, 30, .14) );display:grid;grid-template-rows:auto auto 1fr auto;grid-template-areas:\"header\" \"status\" \"body\" \"footer\";height:100%;background:var(--pfx-settings-panel-surface-container);color:var(--pfx-settings-panel-on-surface);border-left:1px solid var(--pfx-settings-panel-outline-variant);width:var(--pfx-settings-panel-width, 720px);transition:width .3s ease;box-shadow:var(--pfx-settings-panel-elevation, var(--md-sys-elevation-level2, 0 8px 24px rgba(0, 0, 0, .16)));overflow:hidden}praxis-settings-panel .settings-panel.expanded{width:min(var(--pfx-settings-panel-width-expanded, 95vw),var(--pfx-settings-panel-max-width, 2400px))}praxis-settings-panel .settings-panel__resize-handle{position:absolute;inset:0 auto 0 0;width:12px;cursor:col-resize;touch-action:none;z-index:3;outline:none}praxis-settings-panel .settings-panel__resize-handle:before{content:\"\";position:absolute;inset:0 auto 0 4px;width:2px;background:transparent;transition:background-color var(--pfx-side-panel-motion-duration, .16s) ease,opacity var(--pfx-side-panel-motion-duration, .16s) ease;opacity:0}praxis-settings-panel .settings-panel.is-resizable:hover .settings-panel__resize-handle:before,praxis-settings-panel .settings-panel__resize-handle:focus-visible:before{opacity:1;background:var(--pfx-settings-panel-outline)}praxis-settings-panel .settings-panel-header{grid-area:header;display:flex;align-items:center;gap:var(--pfx-settings-panel-header-gap, 8px);padding:0 var(--pfx-settings-panel-header-padding-x, 16px);height:var(--pfx-settings-panel-header-height, 64px);border-bottom:1px solid var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-high);box-shadow:var(--pfx-settings-panel-header-shadow, var(--md-sys-elevation-level1, 0 2px 6px rgba(0, 0, 0, .08)));flex-shrink:0}praxis-settings-panel .settings-panel-header .spacer{flex:1}praxis-settings-panel .settings-panel-status{grid-area:status;display:flex;align-items:center;gap:8px;min-height:36px;padding:6px 16px;border-bottom:1px solid var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-high);color:var(--pfx-settings-panel-on-surface);font-size:.85rem;font-weight:500}praxis-settings-panel .settings-panel-status mat-icon{width:18px;height:18px;font-size:18px}praxis-settings-panel .settings-panel-status[data-status=dirty],praxis-settings-panel .settings-panel-status[data-status=invalid]{color:var(--pfx-settings-panel-error);background:color-mix(in srgb,var(--pfx-settings-panel-error-container) 30%,var(--pfx-settings-panel-surface-container-high))}praxis-settings-panel .settings-panel-status[data-status=saved]{color:var(--pfx-settings-panel-primary);background:color-mix(in srgb,var(--pfx-settings-panel-primary-container) 28%,var(--pfx-settings-panel-surface-container-high))}praxis-settings-panel .settings-panel-body{grid-area:body;overflow-y:auto;min-height:0;padding:var(--pfx-settings-panel-body-padding, 8px 8px 24px 8px);background:var(--pfx-settings-panel-surface);display:flex;flex-direction:column}praxis-settings-panel .settings-panel-content{display:block}praxis-settings-panel .settings-panel-footer{grid-area:footer;border-top:1px solid var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-high);box-shadow:var(--pfx-settings-panel-footer-shadow, var(--md-sys-elevation-level1, 0 -2px 6px rgba(0, 0, 0, .08)));display:flex;align-items:center;padding:var(--pfx-settings-panel-footer-padding, 12px 16px);column-gap:var(--pfx-settings-panel-footer-gap, 12px);flex-shrink:0}praxis-settings-panel .spacer{flex:1}praxis-settings-panel .settings-panel-title{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-title-gap, 8px);font-weight:700;letter-spacing:.2px;margin:0}praxis-settings-panel .settings-panel-title mat-icon{color:var(--pfx-settings-panel-primary)}praxis-settings-panel .settings-panel-title .title-icon{width:20px;height:20px;font-size:20px}praxis-settings-panel .settings-panel-footer button+button{margin-left:var(--pfx-settings-panel-footer-gap, 12px)}praxis-settings-panel .settings-panel-footer button{display:inline-flex;align-items:center}praxis-settings-panel .settings-panel-footer button .mat-icon{font-size:20px;width:20px;height:20px;line-height:1;display:inline-flex;align-items:center;justify-content:center}praxis-settings-panel .settings-panel-footer .mat-button-wrapper{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px)}praxis-settings-panel .settings-panel-footer .mat-progress-spinner{margin-right:8px}praxis-settings-panel .settings-panel-footer .mat-flat-button[color=primary]{font-weight:600}praxis-settings-panel .settings-panel .mdc-button__label{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px);line-height:1}praxis-settings-panel .settings-panel .mdc-button__label>span{display:inline-flex;align-items:center;line-height:1}praxis-settings-panel .settings-panel .mdc-button__label .mat-icon{display:inline-flex;align-items:center;justify-content:center;line-height:1}praxis-settings-panel .settings-panel .mat-divider{background-color:var(--pfx-settings-panel-outline-variant)!important}praxis-settings-panel .settings-panel .mat-expansion-panel{background:var(--pfx-settings-panel-surface-container)!important;border:1px solid var(--pfx-settings-panel-outline-variant);border-radius:var(--md-sys-shape-corner-medium, 12px);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08));color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-expansion-panel-header{background:var(--pfx-settings-panel-surface-container)!important;color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-content{color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-expansion-panel:not(.mat-expanded) .mat-expansion-panel-header:hover{background:color-mix(in srgb,var(--pfx-settings-panel-surface-container-high) 84%,var(--pfx-settings-panel-primary) 16%)}praxis-settings-panel .settings-panel .mat-expansion-panel.mat-expanded>.mat-expansion-panel-header{background:var(--pfx-settings-panel-surface-container-high)!important}praxis-settings-panel .settings-panel .mat-expansion-panel-header mat-icon,praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-icon{color:var(--pfx-settings-panel-on-surface)!important}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title,praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title>*,praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description>*{color:inherit}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title small,praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description small{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-indicator,praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-indicator:after{color:var(--pfx-settings-panel-on-surface-variant);border-color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-expansion-panel .mat-action-row{border-top-color:var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-low)}praxis-settings-panel .settings-panel .mat-accordion,praxis-settings-panel .settings-panel .mat-accordion .mat-expansion-panel-spacing{background:transparent!important}praxis-settings-panel .settings-panel .mat-expansion-panel-content,praxis-settings-panel .settings-panel .mat-expansion-panel-content-wrapper,praxis-settings-panel .settings-panel .mat-expansion-panel-body{background:var(--pfx-settings-panel-surface-container)!important;color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-expansion-panel .mat-expansion-panel-body{padding:8px}praxis-settings-panel .settings-panel .mat-expansion-panel .mat-expansion-panel-body>*{color:inherit}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header{border-bottom:1px solid var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-low)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab{min-width:0}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab .mdc-tab__text-label{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab:hover .mdc-tab__text-label,praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab:focus .mdc-tab__text-label{color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab.mdc-tab--active .mdc-tab__text-label{color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination-chevron{border-color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination:not(.mat-mdc-tab-header-pagination-disabled):hover{background:color-mix(in srgb,var(--pfx-settings-panel-surface-container-high) 84%,var(--pfx-settings-panel-primary) 16%)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab-indicator__content--underline{border-color:var(--pfx-settings-panel-primary)}praxis-settings-panel .settings-panel .mat-mdc-card{background:var(--pfx-settings-panel-surface-container-low);border:1px solid var(--pfx-settings-panel-outline-variant);color:var(--pfx-settings-panel-on-surface);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08))}praxis-settings-panel .settings-panel .mat-mdc-card-subtitle,praxis-settings-panel .settings-panel .mat-mdc-card-content{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-mdc-card-title{color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-mdc-form-field{width:100%;--mdc-filled-text-field-container-color: var( --pfx-settings-panel-surface-container );--mdc-filled-text-field-hover-container-color: var( --pfx-settings-panel-surface-container-high );--mdc-filled-text-field-focus-container-color: var( --pfx-settings-panel-surface-container-high );--mdc-filled-text-field-active-indicator-color: var( --pfx-settings-panel-outline-variant );--mdc-filled-text-field-hover-active-indicator-color: var( --pfx-settings-panel-secondary );--mdc-filled-text-field-focus-active-indicator-color: var( --pfx-settings-panel-primary );--mdc-filled-text-field-label-text-color: var( --pfx-settings-panel-on-surface-variant );--mdc-filled-text-field-focus-label-text-color: var( --pfx-settings-panel-primary );--mdc-filled-text-field-input-text-color: var( --pfx-settings-panel-on-surface );--mdc-filled-text-field-caret-color: var(--pfx-settings-panel-primary);--mdc-outlined-text-field-outline-color: var( --pfx-settings-panel-outline-variant );--mdc-outlined-text-field-hover-outline-color: var( --pfx-settings-panel-secondary );--mdc-outlined-text-field-focus-outline-color: var( --pfx-settings-panel-primary );--mdc-outlined-text-field-label-text-color: var( --pfx-settings-panel-on-surface-variant );--mdc-outlined-text-field-focus-label-text-color: var( --pfx-settings-panel-primary );--mdc-outlined-text-field-input-text-color: var( --pfx-settings-panel-on-surface );--mdc-outlined-text-field-caret-color: var(--pfx-settings-panel-primary);--mat-form-field-focus-select-arrow-color: var( --pfx-settings-panel-primary );--mat-form-field-enabled-select-arrow-color: var( --pfx-settings-panel-on-surface-variant )}praxis-settings-panel .settings-panel .mdc-text-field--filled{background-color:var(--pfx-settings-panel-surface-container)!important;border-radius:var(--md-sys-shape-corner-small, 8px) var(--md-sys-shape-corner-small, 8px) 0 0}praxis-settings-panel .settings-panel .mat-mdc-text-field-wrapper,praxis-settings-panel .settings-panel .mat-mdc-form-field-flex,praxis-settings-panel .settings-panel .mat-mdc-form-field-infix,praxis-settings-panel .settings-panel .mat-mdc-form-field-focus-overlay{background:var(--pfx-settings-panel-surface-container)!important;color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-mdc-form-field-focus-overlay{opacity:0}praxis-settings-panel .settings-panel .mdc-text-field--filled .mdc-text-field__input,praxis-settings-panel .settings-panel .mdc-text-field--filled .mdc-floating-label,praxis-settings-panel .settings-panel .mat-mdc-select-value,praxis-settings-panel .settings-panel .mat-mdc-select-arrow,praxis-settings-panel .settings-panel .mat-mdc-form-field .mat-mdc-floating-label{color:var(--pfx-settings-panel-on-surface-variant)!important}praxis-settings-panel .settings-panel .mdc-text-field--filled.mdc-text-field--focused .mdc-floating-label,praxis-settings-panel .settings-panel .mdc-text-field--filled .mdc-text-field__input{color:var(--pfx-settings-panel-on-surface)!important}praxis-settings-panel .settings-panel .mat-mdc-form-field input,praxis-settings-panel .settings-panel .mat-mdc-form-field textarea,praxis-settings-panel .settings-panel .mat-mdc-form-field .mdc-text-field__input{color:var(--pfx-settings-panel-on-surface)!important}praxis-settings-panel .settings-panel .mat-mdc-form-field-subscript-wrapper,praxis-settings-panel .settings-panel .mat-mdc-form-field-hint-wrapper,praxis-settings-panel .settings-panel .mat-mdc-form-field-error-wrapper,praxis-settings-panel .settings-panel .mat-mdc-hint{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-button-toggle-group{border-color:var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-low)}praxis-settings-panel .settings-panel .mat-button-toggle{background:var(--pfx-settings-panel-surface-container-low);color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-button-toggle+.mat-button-toggle{border-left-color:var(--pfx-settings-panel-outline-variant)}praxis-settings-panel .settings-panel .mat-button-toggle-checked{background:var(--pfx-settings-panel-primary-container)!important;color:var(--pfx-settings-panel-on-primary-container)!important}praxis-settings-panel .settings-panel .mat-button-toggle .mat-icon,praxis-settings-panel .settings-panel .mat-button-toggle-checked .mat-icon{color:inherit}praxis-settings-panel .settings-panel .mat-mdc-slide-toggle .mdc-label{color:var(--pfx-settings-panel-on-surface)}.praxis-settings-panel-backdrop{background:var(--pfx-backdrop, var(--md-sys-color-scrim, rgba(8, 15, 26, .42)));backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%)}.praxis-settings-panel-pane{position:fixed!important;top:0!important;right:0!important;display:flex!important;align-items:stretch!important;height:100vh!important;max-height:100vh!important;z-index:var(--praxis-layer-settings-panel, 1220)!important}.praxis-settings-panel-pane .settings-panel{pointer-events:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i6.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatDialogModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
915
945
  }
916
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SettingsPanelComponent, decorators: [{
946
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: SettingsPanelComponent, decorators: [{
917
947
  type: Component,
918
948
  args: [{ selector: 'praxis-settings-panel', standalone: true, imports: [
919
949
  CommonModule,
@@ -925,7 +955,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
925
955
  CdkTrapFocus,
926
956
  MatProgressSpinnerModule,
927
957
  MatDialogModule,
928
- ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, providers: [providePraxisSettingsPanelI18n()], template: "<div\n class=\"settings-panel\"\n data-testid=\"settings-panel-root\"\n [class.expanded]=\"expanded\"\n [class.is-resizable]=\"showResizeHandle\"\n [ngStyle]=\"panelInlineStyles\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"titleId\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n>\n @if (showResizeHandle) {\n <div\n class=\"settings-panel__resize-handle\"\n role=\"separator\"\n tabindex=\"0\"\n aria-orientation=\"vertical\"\n [attr.aria-label]=\"resizeHandleLabel\"\n (pointerdown)=\"onResizeHandlePointerDown($event)\"\n (keydown)=\"onResizeHandleKeydown($event)\"\n ></div>\n }\n <header class=\"settings-panel-header\">\n <h2 class=\"settings-panel-title\" [id]=\"titleId\">\n <mat-icon *ngIf=\"titleIcon\" class=\"title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n <span>{{ title }}</span>\n </h2>\n <span class=\"spacer\"></span>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-toggle-expand\"\n [attr.aria-label]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n [attr.aria-expanded]=\"expanded\"\n [matTooltip]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n (click)=\"toggleExpand()\"\n >\n <mat-icon [praxisIcon]=\"expanded ? 'close_fullscreen' : 'open_in_full'\"></mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-close-icon\"\n [attr.aria-label]=\"tx('Close')\"\n [matTooltip]=\"tx('Close')\"\n (click)=\"onCancel()\"\n >\n <mat-icon [praxisIcon]=\"'close'\"></mat-icon>\n </button>\n </header>\n <div\n class=\"settings-panel-status\"\n [attr.data-status]=\"statusTone\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <mat-icon\n aria-hidden=\"true\"\n [praxisIcon]=\"\n statusTone === 'dirty'\n ? 'warning'\n : statusTone === 'saved'\n ? 'check_circle'\n : statusTone === 'busy'\n ? 'autorenew'\n : 'info'\n \"\n ></mat-icon>\n <span>{{ statusMessage }}</span>\n </div>\n <div class=\"settings-panel-body\">\n <ng-template #contentHost></ng-template>\n </div>\n <footer class=\"settings-panel-footer\">\n <button\n mat-button\n type=\"button\"\n data-testid=\"settings-panel-reset\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onReset()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'restart_alt'\"></mat-icon>\n <span>{{ tx('Reset') }}</span>\n </button>\n <span class=\"spacer\"></span>\n <button\n mat-button\n type=\"button\"\n data-testid=\"settings-panel-cancel\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onCancel()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'close'\"></mat-icon>\n <span>{{ tx('Cancel') }}</span>\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n data-testid=\"settings-panel-apply\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onApply()\"\n [disabled]=\"!canApply\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canApply\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'done'\"></mat-icon>\n <span>{{ tx('Apply') }}</span>\n </ng-container>\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"settings-panel-save\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onSave()\"\n [disabled]=\"!canSave\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canSave\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'save'\"></mat-icon>\n <span>{{ tx('Save & Close') }}</span>\n </ng-container>\n </button>\n </footer>\n</div>\n", styles: ["@charset \"UTF-8\";praxis-settings-panel{display:block;height:100%;width:100%;min-width:0;flex:1 1 auto}praxis-settings-panel .settings-panel{position:relative;--pfx-settings-panel-surface-container: var( --md-sys-color-surface-container, #ffffff );--pfx-settings-panel-surface-container-high: var( --md-sys-color-surface-container-high, #f7f7f7 );--pfx-settings-panel-surface-container-low: var( --md-sys-color-surface-container-low, #f2f4f7 );--pfx-settings-panel-surface: var(--md-sys-color-surface, #ffffff);--pfx-settings-panel-on-surface: var(--md-sys-color-on-surface, #111827);--pfx-settings-panel-on-surface-variant: var( --md-sys-color-on-surface-variant, rgba(17, 24, 39, .72) );--pfx-settings-panel-outline: var( --md-sys-color-outline, rgba(15, 23, 42, .24) );--pfx-settings-panel-outline-variant: var( --md-sys-color-outline-variant, rgba(15, 23, 42, .12) );--pfx-settings-panel-primary: var(--md-sys-color-primary, #2563eb);--pfx-settings-panel-primary-container: var( --md-sys-color-primary-container, rgba(37, 99, 235, .14) );--pfx-settings-panel-on-primary-container: var( --md-sys-color-on-primary-container, #0f172a );--pfx-settings-panel-secondary: var( --md-sys-color-secondary, var(--pfx-settings-panel-primary) );--pfx-settings-panel-error: var(--md-sys-color-error, #b3261e);--pfx-settings-panel-error-container: var( --md-sys-color-error-container, rgba(179, 38, 30, .14) );display:grid;grid-template-rows:auto auto 1fr auto;grid-template-areas:\"header\" \"status\" \"body\" \"footer\";height:100%;background:var(--pfx-settings-panel-surface-container);color:var(--pfx-settings-panel-on-surface);border-left:1px solid var(--pfx-settings-panel-outline-variant);width:var(--pfx-settings-panel-width, 720px);transition:width .3s ease;box-shadow:var(--pfx-settings-panel-elevation, var(--md-sys-elevation-level2, 0 8px 24px rgba(0, 0, 0, .16)));overflow:hidden}praxis-settings-panel .settings-panel.expanded{width:min(var(--pfx-settings-panel-width-expanded, 95vw),var(--pfx-settings-panel-max-width, 2400px))}praxis-settings-panel .settings-panel__resize-handle{position:absolute;inset:0 auto 0 0;width:12px;cursor:col-resize;touch-action:none;z-index:3;outline:none}praxis-settings-panel .settings-panel__resize-handle:before{content:\"\";position:absolute;inset:0 auto 0 4px;width:2px;background:transparent;transition:background-color var(--pfx-side-panel-motion-duration, .16s) ease,opacity var(--pfx-side-panel-motion-duration, .16s) ease;opacity:0}praxis-settings-panel .settings-panel.is-resizable:hover .settings-panel__resize-handle:before,praxis-settings-panel .settings-panel__resize-handle:focus-visible:before{opacity:1;background:var(--pfx-settings-panel-outline)}praxis-settings-panel .settings-panel-header{grid-area:header;display:flex;align-items:center;gap:var(--pfx-settings-panel-header-gap, 8px);padding:0 var(--pfx-settings-panel-header-padding-x, 16px);height:var(--pfx-settings-panel-header-height, 64px);border-bottom:1px solid var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-high);box-shadow:var(--pfx-settings-panel-header-shadow, var(--md-sys-elevation-level1, 0 2px 6px rgba(0, 0, 0, .08)));flex-shrink:0}praxis-settings-panel .settings-panel-header .spacer{flex:1}praxis-settings-panel .settings-panel-status{grid-area:status;display:flex;align-items:center;gap:8px;min-height:36px;padding:6px 16px;border-bottom:1px solid var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-high);color:var(--pfx-settings-panel-on-surface);font-size:.85rem;font-weight:500}praxis-settings-panel .settings-panel-status mat-icon{width:18px;height:18px;font-size:18px}praxis-settings-panel .settings-panel-status[data-status=dirty]{color:var(--pfx-settings-panel-error);background:color-mix(in srgb,var(--pfx-settings-panel-error-container) 30%,var(--pfx-settings-panel-surface-container-high))}praxis-settings-panel .settings-panel-status[data-status=saved]{color:var(--pfx-settings-panel-primary);background:color-mix(in srgb,var(--pfx-settings-panel-primary-container) 28%,var(--pfx-settings-panel-surface-container-high))}praxis-settings-panel .settings-panel-body{grid-area:body;overflow-y:auto;min-height:0;padding:var(--pfx-settings-panel-body-padding, 8px 8px 24px 8px);background:var(--pfx-settings-panel-surface);display:flex;flex-direction:column}praxis-settings-panel .settings-panel-content{display:block}praxis-settings-panel .settings-panel-footer{grid-area:footer;border-top:1px solid var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-high);box-shadow:var(--pfx-settings-panel-footer-shadow, var(--md-sys-elevation-level1, 0 -2px 6px rgba(0, 0, 0, .08)));display:flex;align-items:center;padding:var(--pfx-settings-panel-footer-padding, 12px 16px);column-gap:var(--pfx-settings-panel-footer-gap, 12px);flex-shrink:0}praxis-settings-panel .spacer{flex:1}praxis-settings-panel .settings-panel-title{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-title-gap, 8px);font-weight:700;letter-spacing:.2px;margin:0}praxis-settings-panel .settings-panel-title mat-icon{color:var(--pfx-settings-panel-primary)}praxis-settings-panel .settings-panel-title .title-icon{width:20px;height:20px;font-size:20px}praxis-settings-panel .settings-panel-footer button+button{margin-left:var(--pfx-settings-panel-footer-gap, 12px)}praxis-settings-panel .settings-panel-footer button{display:inline-flex;align-items:center}praxis-settings-panel .settings-panel-footer button .mat-icon{font-size:20px;width:20px;height:20px;line-height:1;display:inline-flex;align-items:center;justify-content:center}praxis-settings-panel .settings-panel-footer .mat-button-wrapper{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px)}praxis-settings-panel .settings-panel-footer .mat-progress-spinner{margin-right:8px}praxis-settings-panel .settings-panel-footer .mat-flat-button[color=primary]{font-weight:600}praxis-settings-panel .settings-panel .mdc-button__label{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px);line-height:1}praxis-settings-panel .settings-panel .mdc-button__label>span{display:inline-flex;align-items:center;line-height:1}praxis-settings-panel .settings-panel .mdc-button__label .mat-icon{display:inline-flex;align-items:center;justify-content:center;line-height:1}praxis-settings-panel .settings-panel .mat-divider{background-color:var(--pfx-settings-panel-outline-variant)!important}praxis-settings-panel .settings-panel .mat-expansion-panel{background:var(--pfx-settings-panel-surface-container)!important;border:1px solid var(--pfx-settings-panel-outline-variant);border-radius:var(--md-sys-shape-corner-medium, 12px);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08));color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-expansion-panel-header{background:var(--pfx-settings-panel-surface-container)!important;color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-content{color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-expansion-panel:not(.mat-expanded) .mat-expansion-panel-header:hover{background:color-mix(in srgb,var(--pfx-settings-panel-surface-container-high) 84%,var(--pfx-settings-panel-primary) 16%)}praxis-settings-panel .settings-panel .mat-expansion-panel.mat-expanded>.mat-expansion-panel-header{background:var(--pfx-settings-panel-surface-container-high)!important}praxis-settings-panel .settings-panel .mat-expansion-panel-header mat-icon,praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-icon{color:var(--pfx-settings-panel-on-surface)!important}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title,praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title>*,praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description>*{color:inherit}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title small,praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description small{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-indicator,praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-indicator:after{color:var(--pfx-settings-panel-on-surface-variant);border-color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-expansion-panel .mat-action-row{border-top-color:var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-low)}praxis-settings-panel .settings-panel .mat-accordion,praxis-settings-panel .settings-panel .mat-accordion .mat-expansion-panel-spacing{background:transparent!important}praxis-settings-panel .settings-panel .mat-expansion-panel-content,praxis-settings-panel .settings-panel .mat-expansion-panel-content-wrapper,praxis-settings-panel .settings-panel .mat-expansion-panel-body{background:var(--pfx-settings-panel-surface-container)!important;color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-expansion-panel .mat-expansion-panel-body{padding:8px}praxis-settings-panel .settings-panel .mat-expansion-panel .mat-expansion-panel-body>*{color:inherit}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header{border-bottom:1px solid var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-low)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab{min-width:0}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab .mdc-tab__text-label{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab:hover .mdc-tab__text-label,praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab:focus .mdc-tab__text-label{color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab.mdc-tab--active .mdc-tab__text-label{color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination-chevron{border-color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination:not(.mat-mdc-tab-header-pagination-disabled):hover{background:color-mix(in srgb,var(--pfx-settings-panel-surface-container-high) 84%,var(--pfx-settings-panel-primary) 16%)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab-indicator__content--underline{border-color:var(--pfx-settings-panel-primary)}praxis-settings-panel .settings-panel .mat-mdc-card{background:var(--pfx-settings-panel-surface-container-low);border:1px solid var(--pfx-settings-panel-outline-variant);color:var(--pfx-settings-panel-on-surface);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08))}praxis-settings-panel .settings-panel .mat-mdc-card-subtitle,praxis-settings-panel .settings-panel .mat-mdc-card-content{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-mdc-card-title{color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-mdc-form-field{width:100%;--mdc-filled-text-field-container-color: var( --pfx-settings-panel-surface-container );--mdc-filled-text-field-hover-container-color: var( --pfx-settings-panel-surface-container-high );--mdc-filled-text-field-focus-container-color: var( --pfx-settings-panel-surface-container-high );--mdc-filled-text-field-active-indicator-color: var( --pfx-settings-panel-outline-variant );--mdc-filled-text-field-hover-active-indicator-color: var( --pfx-settings-panel-secondary );--mdc-filled-text-field-focus-active-indicator-color: var( --pfx-settings-panel-primary );--mdc-filled-text-field-label-text-color: var( --pfx-settings-panel-on-surface-variant );--mdc-filled-text-field-focus-label-text-color: var( --pfx-settings-panel-primary );--mdc-filled-text-field-input-text-color: var( --pfx-settings-panel-on-surface );--mdc-filled-text-field-caret-color: var(--pfx-settings-panel-primary);--mdc-outlined-text-field-outline-color: var( --pfx-settings-panel-outline-variant );--mdc-outlined-text-field-hover-outline-color: var( --pfx-settings-panel-secondary );--mdc-outlined-text-field-focus-outline-color: var( --pfx-settings-panel-primary );--mdc-outlined-text-field-label-text-color: var( --pfx-settings-panel-on-surface-variant );--mdc-outlined-text-field-focus-label-text-color: var( --pfx-settings-panel-primary );--mdc-outlined-text-field-input-text-color: var( --pfx-settings-panel-on-surface );--mdc-outlined-text-field-caret-color: var(--pfx-settings-panel-primary);--mat-form-field-focus-select-arrow-color: var( --pfx-settings-panel-primary );--mat-form-field-enabled-select-arrow-color: var( --pfx-settings-panel-on-surface-variant )}praxis-settings-panel .settings-panel .mdc-text-field--filled{background-color:var(--pfx-settings-panel-surface-container)!important;border-radius:var(--md-sys-shape-corner-small, 8px) var(--md-sys-shape-corner-small, 8px) 0 0}praxis-settings-panel .settings-panel .mat-mdc-text-field-wrapper,praxis-settings-panel .settings-panel .mat-mdc-form-field-flex,praxis-settings-panel .settings-panel .mat-mdc-form-field-infix,praxis-settings-panel .settings-panel .mat-mdc-form-field-focus-overlay{background:var(--pfx-settings-panel-surface-container)!important;color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-mdc-form-field-focus-overlay{opacity:0}praxis-settings-panel .settings-panel .mdc-text-field--filled .mdc-text-field__input,praxis-settings-panel .settings-panel .mdc-text-field--filled .mdc-floating-label,praxis-settings-panel .settings-panel .mat-mdc-select-value,praxis-settings-panel .settings-panel .mat-mdc-select-arrow,praxis-settings-panel .settings-panel .mat-mdc-form-field .mat-mdc-floating-label{color:var(--pfx-settings-panel-on-surface-variant)!important}praxis-settings-panel .settings-panel .mdc-text-field--filled.mdc-text-field--focused .mdc-floating-label,praxis-settings-panel .settings-panel .mdc-text-field--filled .mdc-text-field__input{color:var(--pfx-settings-panel-on-surface)!important}praxis-settings-panel .settings-panel .mat-mdc-form-field input,praxis-settings-panel .settings-panel .mat-mdc-form-field textarea,praxis-settings-panel .settings-panel .mat-mdc-form-field .mdc-text-field__input{color:var(--pfx-settings-panel-on-surface)!important}praxis-settings-panel .settings-panel .mat-mdc-form-field-subscript-wrapper,praxis-settings-panel .settings-panel .mat-mdc-form-field-hint-wrapper,praxis-settings-panel .settings-panel .mat-mdc-form-field-error-wrapper,praxis-settings-panel .settings-panel .mat-mdc-hint{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-button-toggle-group{border-color:var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-low)}praxis-settings-panel .settings-panel .mat-button-toggle{background:var(--pfx-settings-panel-surface-container-low);color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-button-toggle+.mat-button-toggle{border-left-color:var(--pfx-settings-panel-outline-variant)}praxis-settings-panel .settings-panel .mat-button-toggle-checked{background:var(--pfx-settings-panel-primary-container)!important;color:var(--pfx-settings-panel-on-primary-container)!important}praxis-settings-panel .settings-panel .mat-button-toggle .mat-icon,praxis-settings-panel .settings-panel .mat-button-toggle-checked .mat-icon{color:inherit}praxis-settings-panel .settings-panel .mat-mdc-slide-toggle .mdc-label{color:var(--pfx-settings-panel-on-surface)}.praxis-settings-panel-backdrop{background:var(--pfx-backdrop, var(--md-sys-color-scrim, rgba(8, 15, 26, .42)));backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%)}.praxis-settings-panel-pane{position:fixed!important;top:0!important;right:0!important;display:flex!important;align-items:stretch!important;height:100vh!important;max-height:100vh!important;z-index:var(--praxis-layer-settings-panel, 1220)!important}.praxis-settings-panel-pane .settings-panel{pointer-events:auto}\n"] }]
958
+ ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, providers: [providePraxisSettingsPanelI18n()], template: "<div\n class=\"settings-panel\"\n data-testid=\"settings-panel-root\"\n [class.expanded]=\"expanded\"\n [class.is-resizable]=\"showResizeHandle\"\n [ngStyle]=\"panelInlineStyles\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"titleId\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n >\n @if (showResizeHandle) {\n <div\n class=\"settings-panel__resize-handle\"\n role=\"separator\"\n tabindex=\"0\"\n aria-orientation=\"vertical\"\n [attr.aria-label]=\"resizeHandleLabel\"\n (pointerdown)=\"onResizeHandlePointerDown($event)\"\n (keydown)=\"onResizeHandleKeydown($event)\"\n ></div>\n }\n <header class=\"settings-panel-header\">\n <h2 class=\"settings-panel-title\" [id]=\"titleId\">\n @if (titleIcon) {\n <mat-icon class=\"title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n }\n <span>{{ title }}</span>\n </h2>\n <span class=\"spacer\"></span>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-toggle-expand\"\n [attr.aria-label]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n [attr.aria-expanded]=\"expanded\"\n [matTooltip]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n (click)=\"toggleExpand()\"\n >\n <mat-icon [praxisIcon]=\"expanded ? 'close_fullscreen' : 'open_in_full'\"></mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-close-icon\"\n [attr.aria-label]=\"tx('Close')\"\n [matTooltip]=\"tx('Close')\"\n (click)=\"onCancel()\"\n >\n <mat-icon [praxisIcon]=\"'close'\"></mat-icon>\n </button>\n </header>\n @if (showStatusMessage) {\n <div\n class=\"settings-panel-status\"\n [attr.data-status]=\"statusTone\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <mat-icon\n aria-hidden=\"true\"\n [praxisIcon]=\"\n statusTone === 'dirty' || statusTone === 'invalid'\n ? 'warning'\n : statusTone === 'saved'\n ? 'check_circle'\n : statusTone === 'busy'\n ? 'autorenew'\n : 'info'\n \"\n ></mat-icon>\n <span>{{ statusMessage }}</span>\n </div>\n }\n <div class=\"settings-panel-body\">\n <ng-template #contentHost></ng-template>\n </div>\n <footer class=\"settings-panel-footer\">\n <button\n mat-button\n type=\"button\"\n data-testid=\"settings-panel-reset\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onReset()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'restart_alt'\"></mat-icon>\n <span>{{ tx('Reset') }}</span>\n </button>\n <span class=\"spacer\"></span>\n <button\n mat-button\n type=\"button\"\n data-testid=\"settings-panel-cancel\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onCancel()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'close'\"></mat-icon>\n <span>{{ tx('Cancel') }}</span>\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n data-testid=\"settings-panel-apply\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onApply()\"\n [disabled]=\"!canApply\"\n [matTooltip]=\"applySaveDisabledReason\"\n [matTooltipDisabled]=\"canApply || !diagnostics.showDisabledReason\"\n [attr.aria-busy]=\"isBusy\"\n >\n @if (showBusyIndicator) {\n <mat-progress-spinner\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n }\n @if (!showBusyIndicator) {\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'done'\"></mat-icon>\n <span>{{ tx('Apply') }}</span>\n }\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"settings-panel-save\"\n (mousedown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation(); onSave()\"\n [disabled]=\"!canSave\"\n [matTooltip]=\"applySaveDisabledReason\"\n [matTooltipDisabled]=\"canSave || !diagnostics.showDisabledReason\"\n [attr.aria-busy]=\"isBusy\"\n >\n @if (showBusyIndicator) {\n <mat-progress-spinner\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n }\n @if (!showBusyIndicator) {\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'save'\"></mat-icon>\n <span>{{ tx('Save & Close') }}</span>\n }\n </button>\n </footer>\n</div>\n", styles: ["@charset \"UTF-8\";praxis-settings-panel{display:block;height:100%;width:100%;min-width:0;flex:1 1 auto}praxis-settings-panel .settings-panel{position:relative;--pfx-settings-panel-surface-container: var( --md-sys-color-surface-container, #ffffff );--pfx-settings-panel-surface-container-high: var( --md-sys-color-surface-container-high, #f7f7f7 );--pfx-settings-panel-surface-container-low: var( --md-sys-color-surface-container-low, #f2f4f7 );--pfx-settings-panel-surface: var(--md-sys-color-surface, #ffffff);--pfx-settings-panel-on-surface: var(--md-sys-color-on-surface, #111827);--pfx-settings-panel-on-surface-variant: var( --md-sys-color-on-surface-variant, rgba(17, 24, 39, .72) );--pfx-settings-panel-outline: var( --md-sys-color-outline, rgba(15, 23, 42, .24) );--pfx-settings-panel-outline-variant: var( --md-sys-color-outline-variant, rgba(15, 23, 42, .12) );--pfx-settings-panel-primary: var(--md-sys-color-primary, #2563eb);--pfx-settings-panel-primary-container: var( --md-sys-color-primary-container, rgba(37, 99, 235, .14) );--pfx-settings-panel-on-primary-container: var( --md-sys-color-on-primary-container, #0f172a );--pfx-settings-panel-secondary: var( --md-sys-color-secondary, var(--pfx-settings-panel-primary) );--pfx-settings-panel-error: var(--md-sys-color-error, #b3261e);--pfx-settings-panel-error-container: var( --md-sys-color-error-container, rgba(179, 38, 30, .14) );display:grid;grid-template-rows:auto auto 1fr auto;grid-template-areas:\"header\" \"status\" \"body\" \"footer\";height:100%;background:var(--pfx-settings-panel-surface-container);color:var(--pfx-settings-panel-on-surface);border-left:1px solid var(--pfx-settings-panel-outline-variant);width:var(--pfx-settings-panel-width, 720px);transition:width .3s ease;box-shadow:var(--pfx-settings-panel-elevation, var(--md-sys-elevation-level2, 0 8px 24px rgba(0, 0, 0, .16)));overflow:hidden}praxis-settings-panel .settings-panel.expanded{width:min(var(--pfx-settings-panel-width-expanded, 95vw),var(--pfx-settings-panel-max-width, 2400px))}praxis-settings-panel .settings-panel__resize-handle{position:absolute;inset:0 auto 0 0;width:12px;cursor:col-resize;touch-action:none;z-index:3;outline:none}praxis-settings-panel .settings-panel__resize-handle:before{content:\"\";position:absolute;inset:0 auto 0 4px;width:2px;background:transparent;transition:background-color var(--pfx-side-panel-motion-duration, .16s) ease,opacity var(--pfx-side-panel-motion-duration, .16s) ease;opacity:0}praxis-settings-panel .settings-panel.is-resizable:hover .settings-panel__resize-handle:before,praxis-settings-panel .settings-panel__resize-handle:focus-visible:before{opacity:1;background:var(--pfx-settings-panel-outline)}praxis-settings-panel .settings-panel-header{grid-area:header;display:flex;align-items:center;gap:var(--pfx-settings-panel-header-gap, 8px);padding:0 var(--pfx-settings-panel-header-padding-x, 16px);height:var(--pfx-settings-panel-header-height, 64px);border-bottom:1px solid var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-high);box-shadow:var(--pfx-settings-panel-header-shadow, var(--md-sys-elevation-level1, 0 2px 6px rgba(0, 0, 0, .08)));flex-shrink:0}praxis-settings-panel .settings-panel-header .spacer{flex:1}praxis-settings-panel .settings-panel-status{grid-area:status;display:flex;align-items:center;gap:8px;min-height:36px;padding:6px 16px;border-bottom:1px solid var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-high);color:var(--pfx-settings-panel-on-surface);font-size:.85rem;font-weight:500}praxis-settings-panel .settings-panel-status mat-icon{width:18px;height:18px;font-size:18px}praxis-settings-panel .settings-panel-status[data-status=dirty],praxis-settings-panel .settings-panel-status[data-status=invalid]{color:var(--pfx-settings-panel-error);background:color-mix(in srgb,var(--pfx-settings-panel-error-container) 30%,var(--pfx-settings-panel-surface-container-high))}praxis-settings-panel .settings-panel-status[data-status=saved]{color:var(--pfx-settings-panel-primary);background:color-mix(in srgb,var(--pfx-settings-panel-primary-container) 28%,var(--pfx-settings-panel-surface-container-high))}praxis-settings-panel .settings-panel-body{grid-area:body;overflow-y:auto;min-height:0;padding:var(--pfx-settings-panel-body-padding, 8px 8px 24px 8px);background:var(--pfx-settings-panel-surface);display:flex;flex-direction:column}praxis-settings-panel .settings-panel-content{display:block}praxis-settings-panel .settings-panel-footer{grid-area:footer;border-top:1px solid var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-high);box-shadow:var(--pfx-settings-panel-footer-shadow, var(--md-sys-elevation-level1, 0 -2px 6px rgba(0, 0, 0, .08)));display:flex;align-items:center;padding:var(--pfx-settings-panel-footer-padding, 12px 16px);column-gap:var(--pfx-settings-panel-footer-gap, 12px);flex-shrink:0}praxis-settings-panel .spacer{flex:1}praxis-settings-panel .settings-panel-title{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-title-gap, 8px);font-weight:700;letter-spacing:.2px;margin:0}praxis-settings-panel .settings-panel-title mat-icon{color:var(--pfx-settings-panel-primary)}praxis-settings-panel .settings-panel-title .title-icon{width:20px;height:20px;font-size:20px}praxis-settings-panel .settings-panel-footer button+button{margin-left:var(--pfx-settings-panel-footer-gap, 12px)}praxis-settings-panel .settings-panel-footer button{display:inline-flex;align-items:center}praxis-settings-panel .settings-panel-footer button .mat-icon{font-size:20px;width:20px;height:20px;line-height:1;display:inline-flex;align-items:center;justify-content:center}praxis-settings-panel .settings-panel-footer .mat-button-wrapper{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px)}praxis-settings-panel .settings-panel-footer .mat-progress-spinner{margin-right:8px}praxis-settings-panel .settings-panel-footer .mat-flat-button[color=primary]{font-weight:600}praxis-settings-panel .settings-panel .mdc-button__label{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px);line-height:1}praxis-settings-panel .settings-panel .mdc-button__label>span{display:inline-flex;align-items:center;line-height:1}praxis-settings-panel .settings-panel .mdc-button__label .mat-icon{display:inline-flex;align-items:center;justify-content:center;line-height:1}praxis-settings-panel .settings-panel .mat-divider{background-color:var(--pfx-settings-panel-outline-variant)!important}praxis-settings-panel .settings-panel .mat-expansion-panel{background:var(--pfx-settings-panel-surface-container)!important;border:1px solid var(--pfx-settings-panel-outline-variant);border-radius:var(--md-sys-shape-corner-medium, 12px);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08));color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-expansion-panel-header{background:var(--pfx-settings-panel-surface-container)!important;color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-content{color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-expansion-panel:not(.mat-expanded) .mat-expansion-panel-header:hover{background:color-mix(in srgb,var(--pfx-settings-panel-surface-container-high) 84%,var(--pfx-settings-panel-primary) 16%)}praxis-settings-panel .settings-panel .mat-expansion-panel.mat-expanded>.mat-expansion-panel-header{background:var(--pfx-settings-panel-surface-container-high)!important}praxis-settings-panel .settings-panel .mat-expansion-panel-header mat-icon,praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-icon{color:var(--pfx-settings-panel-on-surface)!important}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title,praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title>*,praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description>*{color:inherit}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title small,praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description small{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-indicator,praxis-settings-panel .settings-panel .mat-expansion-panel-header .mat-expansion-indicator:after{color:var(--pfx-settings-panel-on-surface-variant);border-color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-expansion-panel .mat-action-row{border-top-color:var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-low)}praxis-settings-panel .settings-panel .mat-accordion,praxis-settings-panel .settings-panel .mat-accordion .mat-expansion-panel-spacing{background:transparent!important}praxis-settings-panel .settings-panel .mat-expansion-panel-content,praxis-settings-panel .settings-panel .mat-expansion-panel-content-wrapper,praxis-settings-panel .settings-panel .mat-expansion-panel-body{background:var(--pfx-settings-panel-surface-container)!important;color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-expansion-panel .mat-expansion-panel-body{padding:8px}praxis-settings-panel .settings-panel .mat-expansion-panel .mat-expansion-panel-body>*{color:inherit}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header{border-bottom:1px solid var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-low)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab{min-width:0}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab .mdc-tab__text-label{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab:hover .mdc-tab__text-label,praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab:focus .mdc-tab__text-label{color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab.mdc-tab--active .mdc-tab__text-label{color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination-chevron{border-color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination:not(.mat-mdc-tab-header-pagination-disabled):hover{background:color-mix(in srgb,var(--pfx-settings-panel-surface-container-high) 84%,var(--pfx-settings-panel-primary) 16%)}praxis-settings-panel .settings-panel .mat-mdc-tab-group .mdc-tab-indicator__content--underline{border-color:var(--pfx-settings-panel-primary)}praxis-settings-panel .settings-panel .mat-mdc-card{background:var(--pfx-settings-panel-surface-container-low);border:1px solid var(--pfx-settings-panel-outline-variant);color:var(--pfx-settings-panel-on-surface);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08))}praxis-settings-panel .settings-panel .mat-mdc-card-subtitle,praxis-settings-panel .settings-panel .mat-mdc-card-content{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-mdc-card-title{color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-mdc-form-field{width:100%;--mdc-filled-text-field-container-color: var( --pfx-settings-panel-surface-container );--mdc-filled-text-field-hover-container-color: var( --pfx-settings-panel-surface-container-high );--mdc-filled-text-field-focus-container-color: var( --pfx-settings-panel-surface-container-high );--mdc-filled-text-field-active-indicator-color: var( --pfx-settings-panel-outline-variant );--mdc-filled-text-field-hover-active-indicator-color: var( --pfx-settings-panel-secondary );--mdc-filled-text-field-focus-active-indicator-color: var( --pfx-settings-panel-primary );--mdc-filled-text-field-label-text-color: var( --pfx-settings-panel-on-surface-variant );--mdc-filled-text-field-focus-label-text-color: var( --pfx-settings-panel-primary );--mdc-filled-text-field-input-text-color: var( --pfx-settings-panel-on-surface );--mdc-filled-text-field-caret-color: var(--pfx-settings-panel-primary);--mdc-outlined-text-field-outline-color: var( --pfx-settings-panel-outline-variant );--mdc-outlined-text-field-hover-outline-color: var( --pfx-settings-panel-secondary );--mdc-outlined-text-field-focus-outline-color: var( --pfx-settings-panel-primary );--mdc-outlined-text-field-label-text-color: var( --pfx-settings-panel-on-surface-variant );--mdc-outlined-text-field-focus-label-text-color: var( --pfx-settings-panel-primary );--mdc-outlined-text-field-input-text-color: var( --pfx-settings-panel-on-surface );--mdc-outlined-text-field-caret-color: var(--pfx-settings-panel-primary);--mat-form-field-focus-select-arrow-color: var( --pfx-settings-panel-primary );--mat-form-field-enabled-select-arrow-color: var( --pfx-settings-panel-on-surface-variant )}praxis-settings-panel .settings-panel .mdc-text-field--filled{background-color:var(--pfx-settings-panel-surface-container)!important;border-radius:var(--md-sys-shape-corner-small, 8px) var(--md-sys-shape-corner-small, 8px) 0 0}praxis-settings-panel .settings-panel .mat-mdc-text-field-wrapper,praxis-settings-panel .settings-panel .mat-mdc-form-field-flex,praxis-settings-panel .settings-panel .mat-mdc-form-field-infix,praxis-settings-panel .settings-panel .mat-mdc-form-field-focus-overlay{background:var(--pfx-settings-panel-surface-container)!important;color:var(--pfx-settings-panel-on-surface)}praxis-settings-panel .settings-panel .mat-mdc-form-field-focus-overlay{opacity:0}praxis-settings-panel .settings-panel .mdc-text-field--filled .mdc-text-field__input,praxis-settings-panel .settings-panel .mdc-text-field--filled .mdc-floating-label,praxis-settings-panel .settings-panel .mat-mdc-select-value,praxis-settings-panel .settings-panel .mat-mdc-select-arrow,praxis-settings-panel .settings-panel .mat-mdc-form-field .mat-mdc-floating-label{color:var(--pfx-settings-panel-on-surface-variant)!important}praxis-settings-panel .settings-panel .mdc-text-field--filled.mdc-text-field--focused .mdc-floating-label,praxis-settings-panel .settings-panel .mdc-text-field--filled .mdc-text-field__input{color:var(--pfx-settings-panel-on-surface)!important}praxis-settings-panel .settings-panel .mat-mdc-form-field input,praxis-settings-panel .settings-panel .mat-mdc-form-field textarea,praxis-settings-panel .settings-panel .mat-mdc-form-field .mdc-text-field__input{color:var(--pfx-settings-panel-on-surface)!important}praxis-settings-panel .settings-panel .mat-mdc-form-field-subscript-wrapper,praxis-settings-panel .settings-panel .mat-mdc-form-field-hint-wrapper,praxis-settings-panel .settings-panel .mat-mdc-form-field-error-wrapper,praxis-settings-panel .settings-panel .mat-mdc-hint{color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-button-toggle-group{border-color:var(--pfx-settings-panel-outline-variant);background:var(--pfx-settings-panel-surface-container-low)}praxis-settings-panel .settings-panel .mat-button-toggle{background:var(--pfx-settings-panel-surface-container-low);color:var(--pfx-settings-panel-on-surface-variant)}praxis-settings-panel .settings-panel .mat-button-toggle+.mat-button-toggle{border-left-color:var(--pfx-settings-panel-outline-variant)}praxis-settings-panel .settings-panel .mat-button-toggle-checked{background:var(--pfx-settings-panel-primary-container)!important;color:var(--pfx-settings-panel-on-primary-container)!important}praxis-settings-panel .settings-panel .mat-button-toggle .mat-icon,praxis-settings-panel .settings-panel .mat-button-toggle-checked .mat-icon{color:inherit}praxis-settings-panel .settings-panel .mat-mdc-slide-toggle .mdc-label{color:var(--pfx-settings-panel-on-surface)}.praxis-settings-panel-backdrop{background:var(--pfx-backdrop, var(--md-sys-color-scrim, rgba(8, 15, 26, .42)));backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%)}.praxis-settings-panel-pane{position:fixed!important;top:0!important;right:0!important;display:flex!important;align-items:stretch!important;height:100vh!important;max-height:100vh!important;z-index:var(--praxis-layer-settings-panel, 1220)!important}.praxis-settings-panel-pane .settings-panel{pointer-events:auto}\n"] }]
929
959
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1.MatDialog }], propDecorators: { contentHost: [{
930
960
  type: ViewChild,
931
961
  args: ['contentHost', { read: ViewContainerRef, static: true }]
@@ -1291,10 +1321,10 @@ class BaseSidePanelComponent {
1291
1321
  const parsed = Number.parseFloat(normalized.slice(0, -2));
1292
1322
  return Number.isFinite(parsed) ? parsed : null;
1293
1323
  }
1294
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: BaseSidePanelComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1$1.PraxisI18nService }], target: i0.ɵɵFactoryTarget.Component });
1295
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: BaseSidePanelComponent, isStandalone: true, selector: "praxis-base-side-panel", host: { listeners: { "document:pointermove": "onDocumentPointerMove($event)", "document:pointerup": "onDocumentPointerEnd($event)", "document:pointercancel": "onDocumentPointerEnd($event)" } }, viewQueries: [{ propertyName: "contentHost", first: true, predicate: ["contentHost"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "footerHost", first: true, predicate: ["footerHost"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: "<div\n class=\"pfx-base-side-panel\"\n [class.has-header]=\"hasHeader\"\n [class.has-footer]=\"hasFooter\"\n [class.width-narrow]=\"widthClass === 'width-narrow'\"\n [class.width-default]=\"widthClass === 'width-default'\"\n [class.width-wide]=\"widthClass === 'width-wide'\"\n [class.width-full]=\"widthClass === 'width-full'\"\n [class.use-host-width]=\"useHostWidth\"\n [class.is-resizable]=\"showResizeHandle\"\n [ngClass]=\"hostClasses\"\n [ngStyle]=\"mergedHostStyles\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"hasHeader ? titleId : null\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n>\n @if (showResizeHandle) {\n <div\n class=\"pfx-base-side-panel__resize-handle\"\n role=\"separator\"\n tabindex=\"0\"\n aria-orientation=\"vertical\"\n [attr.aria-label]=\"resizeHandleLabel\"\n (pointerdown)=\"onResizeHandlePointerDown($event)\"\n (keydown)=\"onResizeHandleKeydown($event)\"\n ></div>\n }\n @if (hasHeader) {\n <header class=\"pfx-base-side-panel__header\">\n <div class=\"pfx-base-side-panel__title-block\">\n @if (titleIcon) {\n <mat-icon class=\"pfx-base-side-panel__title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n }\n <div class=\"pfx-base-side-panel__copy\">\n @if (title) {\n <h2 class=\"pfx-base-side-panel__title\" [id]=\"titleId\">{{ title }}</h2>\n }\n @if (subtitle) {\n <p class=\"pfx-base-side-panel__subtitle\">{{ subtitle }}</p>\n }\n </div>\n </div>\n <button\n mat-icon-button\n type=\"button\"\n class=\"pfx-base-side-panel__close\"\n [attr.aria-label]=\"closeLabel\"\n [matTooltip]=\"closeLabel\"\n (click)=\"onClose?.()\"\n >\n <mat-icon [praxisIcon]=\"'close'\"></mat-icon>\n </button>\n </header>\n }\n\n <section class=\"pfx-base-side-panel__body\">\n <ng-template #contentHost></ng-template>\n </section>\n\n <footer class=\"pfx-base-side-panel__footer\" [hidden]=\"!hasFooter\">\n <ng-template #footerHost></ng-template>\n </footer>\n</div>\n", styles: [":host{display:block;height:100%;width:100%;min-width:0;flex:1 1 auto}.pfx-base-side-panel{position:relative;display:grid;grid-template-rows:auto 1fr auto;height:100%;min-width:0;overflow:hidden;background:var(--md-sys-color-surface-container, #fff);color:var(--md-sys-color-on-surface, #111);border-left:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .12));border-radius:var(--pfx-side-panel-border-radius, 0);box-shadow:var(--pfx-side-panel-elevation, var(--md-sys-elevation-level2, 0 8px 24px rgba(0, 0, 0, .16)));width:var(--pfx-side-panel-width, 720px)}.pfx-base-side-panel__resize-handle{position:absolute;inset:0 auto 0 0;width:12px;cursor:col-resize;touch-action:none;z-index:2;outline:none}.pfx-base-side-panel__resize-handle:before{content:\"\";position:absolute;inset:0 auto 0 4px;width:2px;background:transparent;transition:background-color var(--pfx-side-panel-motion-duration, .16s) ease,opacity var(--pfx-side-panel-motion-duration, .16s) ease;opacity:0}.pfx-base-side-panel.is-resizable:hover .pfx-base-side-panel__resize-handle:before,.pfx-base-side-panel__resize-handle:focus-visible:before{opacity:1;background:var(--md-sys-color-outline, rgba(0, 0, 0, .24))}.pfx-base-side-panel.use-host-width{width:100%;max-width:100%}.pfx-base-side-panel.width-narrow{width:var(--pfx-side-panel-width, 420px)}.pfx-base-side-panel.width-default{width:var(--pfx-side-panel-width, 720px)}.pfx-base-side-panel.width-wide{width:var(--pfx-side-panel-width, 960px)}.pfx-base-side-panel.width-full{width:min(100vw,var(--pfx-side-panel-max-width, 100vw))}.pfx-base-side-panel__header{display:flex;align-items:center;gap:var(--pfx-side-panel-header-gap, 8px);min-height:var(--pfx-side-panel-header-height, 64px);padding:0 var(--pfx-side-panel-header-padding-x, 16px);border-bottom:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .12));background:var(--md-sys-color-surface-container-high, #f7f7f7)}.pfx-base-side-panel__title-block{display:flex;align-items:flex-start;gap:10px;min-width:0;flex:1 1 auto}.pfx-base-side-panel__title-icon{color:var(--md-sys-color-primary, currentColor)}.pfx-base-side-panel__copy{min-width:0}.pfx-base-side-panel__title,.pfx-base-side-panel__subtitle{margin:0}.pfx-base-side-panel__title{font-size:1rem;font-weight:600}.pfx-base-side-panel__subtitle{margin-top:4px;color:var(--md-sys-color-on-surface-variant, rgba(0, 0, 0, .64));font-size:.875rem}.pfx-base-side-panel__body{min-height:0;overflow:auto;padding:var(--pfx-side-panel-body-padding, 16px);background:var(--md-sys-color-surface, #fff)}.pfx-base-side-panel__footer{border-top:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .12));background:var(--md-sys-color-surface-container-high, #f7f7f7);padding:var(--pfx-side-panel-footer-padding, 12px 16px)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1324
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BaseSidePanelComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1$1.PraxisI18nService }], target: i0.ɵɵFactoryTarget.Component });
1325
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: BaseSidePanelComponent, isStandalone: true, selector: "praxis-base-side-panel", host: { listeners: { "document:pointermove": "onDocumentPointerMove($event)", "document:pointerup": "onDocumentPointerEnd($event)", "document:pointercancel": "onDocumentPointerEnd($event)" } }, viewQueries: [{ propertyName: "contentHost", first: true, predicate: ["contentHost"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "footerHost", first: true, predicate: ["footerHost"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: "<div\n class=\"pfx-base-side-panel\"\n [class.has-header]=\"hasHeader\"\n [class.has-footer]=\"hasFooter\"\n [class.width-narrow]=\"widthClass === 'width-narrow'\"\n [class.width-default]=\"widthClass === 'width-default'\"\n [class.width-wide]=\"widthClass === 'width-wide'\"\n [class.width-full]=\"widthClass === 'width-full'\"\n [class.use-host-width]=\"useHostWidth\"\n [class.is-resizable]=\"showResizeHandle\"\n [ngClass]=\"hostClasses\"\n [ngStyle]=\"mergedHostStyles\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"hasHeader ? titleId : null\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n>\n @if (showResizeHandle) {\n <div\n class=\"pfx-base-side-panel__resize-handle\"\n role=\"separator\"\n tabindex=\"0\"\n aria-orientation=\"vertical\"\n [attr.aria-label]=\"resizeHandleLabel\"\n (pointerdown)=\"onResizeHandlePointerDown($event)\"\n (keydown)=\"onResizeHandleKeydown($event)\"\n ></div>\n }\n @if (hasHeader) {\n <header class=\"pfx-base-side-panel__header\">\n <div class=\"pfx-base-side-panel__title-block\">\n @if (titleIcon) {\n <mat-icon class=\"pfx-base-side-panel__title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n }\n <div class=\"pfx-base-side-panel__copy\">\n @if (title) {\n <h2 class=\"pfx-base-side-panel__title\" [id]=\"titleId\">{{ title }}</h2>\n }\n @if (subtitle) {\n <p class=\"pfx-base-side-panel__subtitle\">{{ subtitle }}</p>\n }\n </div>\n </div>\n <button\n mat-icon-button\n type=\"button\"\n class=\"pfx-base-side-panel__close\"\n [attr.aria-label]=\"closeLabel\"\n [matTooltip]=\"closeLabel\"\n (click)=\"onClose?.()\"\n >\n <mat-icon [praxisIcon]=\"'close'\"></mat-icon>\n </button>\n </header>\n }\n\n <section class=\"pfx-base-side-panel__body\">\n <ng-template #contentHost></ng-template>\n </section>\n\n <footer class=\"pfx-base-side-panel__footer\" [hidden]=\"!hasFooter\">\n <ng-template #footerHost></ng-template>\n </footer>\n</div>\n", styles: [":host{display:block;height:100%;width:100%;min-width:0;flex:1 1 auto}.pfx-base-side-panel{position:relative;display:grid;grid-template-rows:auto 1fr auto;height:100%;min-width:0;overflow:hidden;background:var(--md-sys-color-surface-container, #fff);color:var(--md-sys-color-on-surface, #111);border-left:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .12));border-radius:var(--pfx-side-panel-border-radius, 0);box-shadow:var(--pfx-side-panel-elevation, var(--md-sys-elevation-level2, 0 8px 24px rgba(0, 0, 0, .16)));width:var(--pfx-side-panel-width, 720px)}.pfx-base-side-panel__resize-handle{position:absolute;inset:0 auto 0 0;width:12px;cursor:col-resize;touch-action:none;z-index:2;outline:none}.pfx-base-side-panel__resize-handle:before{content:\"\";position:absolute;inset:0 auto 0 4px;width:2px;background:transparent;transition:background-color var(--pfx-side-panel-motion-duration, .16s) ease,opacity var(--pfx-side-panel-motion-duration, .16s) ease;opacity:0}.pfx-base-side-panel.is-resizable:hover .pfx-base-side-panel__resize-handle:before,.pfx-base-side-panel__resize-handle:focus-visible:before{opacity:1;background:var(--md-sys-color-outline, rgba(0, 0, 0, .24))}.pfx-base-side-panel.use-host-width{width:100%;max-width:100%}.pfx-base-side-panel.width-narrow{width:var(--pfx-side-panel-width, 420px)}.pfx-base-side-panel.width-default{width:var(--pfx-side-panel-width, 720px)}.pfx-base-side-panel.width-wide{width:var(--pfx-side-panel-width, 960px)}.pfx-base-side-panel.width-full{width:min(100vw,var(--pfx-side-panel-max-width, 100vw))}.pfx-base-side-panel__header{display:flex;align-items:center;gap:var(--pfx-side-panel-header-gap, 8px);min-height:var(--pfx-side-panel-header-height, 64px);padding:0 var(--pfx-side-panel-header-padding-x, 16px);border-bottom:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .12));background:var(--md-sys-color-surface-container-high, #f7f7f7)}.pfx-base-side-panel__title-block{display:flex;align-items:flex-start;gap:10px;min-width:0;flex:1 1 auto}.pfx-base-side-panel__title-icon{color:var(--md-sys-color-primary, currentColor)}.pfx-base-side-panel__copy{min-width:0}.pfx-base-side-panel__title,.pfx-base-side-panel__subtitle{margin:0}.pfx-base-side-panel__title{font-size:1rem;font-weight:600}.pfx-base-side-panel__subtitle{margin-top:4px;color:var(--md-sys-color-on-surface-variant, rgba(0, 0, 0, .64));font-size:.875rem}.pfx-base-side-panel__body{min-height:0;overflow:auto;padding:var(--pfx-side-panel-body-padding, 16px);background:var(--md-sys-color-surface, #fff)}.pfx-base-side-panel__footer{border-top:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .12));background:var(--md-sys-color-surface-container-high, #f7f7f7);padding:var(--pfx-side-panel-footer-padding, 12px 16px)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1296
1326
  }
1297
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: BaseSidePanelComponent, decorators: [{
1327
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BaseSidePanelComponent, decorators: [{
1298
1328
  type: Component,
1299
1329
  args: [{ selector: 'praxis-base-side-panel', standalone: true, imports: [
1300
1330
  CommonModule,
@@ -1554,10 +1584,10 @@ class BaseSidePanelService {
1554
1584
  }
1555
1585
  });
1556
1586
  }
1557
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: BaseSidePanelService, deps: [{ token: i1$2.Overlay }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
1558
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: BaseSidePanelService, providedIn: 'root' });
1587
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BaseSidePanelService, deps: [{ token: i1$2.Overlay }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
1588
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BaseSidePanelService, providedIn: 'root' });
1559
1589
  }
1560
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: BaseSidePanelService, decorators: [{
1590
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: BaseSidePanelService, decorators: [{
1561
1591
  type: Injectable,
1562
1592
  args: [{ providedIn: 'root' }]
1563
1593
  }], ctorParameters: () => [{ type: i1$2.Overlay }, { type: i0.Injector }] });
@@ -1636,6 +1666,7 @@ class SettingsPanelService {
1636
1666
  maxWidth: resolvedConfig.maxWidth,
1637
1667
  expanded: config.expanded || false,
1638
1668
  });
1669
+ panelRef.instance.configureDiagnostics(config.diagnostics);
1639
1670
  this.currentPanelInstance = panelRef.instance;
1640
1671
  const inputs = config.content.inputs;
1641
1672
  const injector = Injector.create({
@@ -1666,10 +1697,10 @@ class SettingsPanelService {
1666
1697
  this.currentRef = undefined;
1667
1698
  this.currentPanelInstance = undefined;
1668
1699
  }
1669
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SettingsPanelService, deps: [{ token: BaseSidePanelService }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
1670
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SettingsPanelService, providedIn: 'root' });
1700
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: SettingsPanelService, deps: [{ token: BaseSidePanelService }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
1701
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: SettingsPanelService, providedIn: 'root' });
1671
1702
  }
1672
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SettingsPanelService, decorators: [{
1703
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: SettingsPanelService, decorators: [{
1673
1704
  type: Injectable,
1674
1705
  args: [{ providedIn: 'root' }]
1675
1706
  }], ctorParameters: () => [{ type: BaseSidePanelService }, { type: i0.Injector }] });
@@ -1746,12 +1777,13 @@ class SurfaceDrawerComponent {
1746
1777
  return;
1747
1778
  }
1748
1779
  const supportedInputs = this.resolveSupportedInputs(componentRef);
1780
+ const canUseSetInput = typeof componentRef.setInput === 'function';
1749
1781
  for (const [key, value] of Object.entries(inputs)) {
1750
- if (supportedInputs && !supportedInputs.has(key)) {
1782
+ if (!canUseSetInput && supportedInputs && !supportedInputs.has(key)) {
1751
1783
  continue;
1752
1784
  }
1753
1785
  try {
1754
- if (typeof componentRef.setInput === 'function') {
1786
+ if (canUseSetInput) {
1755
1787
  componentRef.setInput(key, value);
1756
1788
  }
1757
1789
  else {
@@ -1770,10 +1802,10 @@ class SurfaceDrawerComponent {
1770
1802
  }
1771
1803
  return new Set(Object.keys(declaredInputs));
1772
1804
  }
1773
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SurfaceDrawerComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1$1.PraxisI18nService }], target: i0.ɵɵFactoryTarget.Component });
1774
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.17", type: SurfaceDrawerComponent, isStandalone: true, selector: "praxis-surface-drawer", viewQueries: [{ propertyName: "contentHost", first: true, predicate: ["contentHost"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: "<div\n class=\"pfx-surface-drawer\"\n [class.has-header]=\"hasHeader\"\n [class.width-narrow]=\"widthClass === 'width-narrow'\"\n [class.width-default]=\"widthClass === 'width-default'\"\n [class.width-wide]=\"widthClass === 'width-wide'\"\n [class.width-full]=\"widthClass === 'width-full'\"\n [class.use-host-width]=\"useHostWidth\"\n [ngClass]=\"hostClasses\"\n [ngStyle]=\"hostCssVars\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"hasHeader ? titleId : null\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n>\n @if (hasHeader) {\n <header class=\"pfx-surface-drawer__header\">\n <div class=\"pfx-surface-drawer__title-block\">\n @if (titleIcon) {\n <mat-icon class=\"pfx-surface-drawer__title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n }\n <div class=\"pfx-surface-drawer__copy\">\n @if (title) {\n <h2 class=\"pfx-surface-drawer__title\" [id]=\"titleId\">{{ title }}</h2>\n }\n @if (subtitle) {\n <p class=\"pfx-surface-drawer__subtitle\">{{ subtitle }}</p>\n }\n </div>\n </div>\n <button\n mat-icon-button\n type=\"button\"\n class=\"pfx-surface-drawer__close\"\n [attr.aria-label]=\"closeLabel\"\n [matTooltip]=\"closeLabel\"\n (click)=\"onClose?.()\"\n >\n <mat-icon [praxisIcon]=\"'close'\"></mat-icon>\n </button>\n </header>\n }\n\n <section class=\"pfx-surface-drawer__body\">\n <ng-template #contentHost></ng-template>\n </section>\n</div>\n", styles: [":host{display:block;height:100%;width:100%;min-width:0;flex:1 1 auto}.pfx-surface-drawer{display:grid;grid-template-rows:auto 1fr;height:100%;min-width:0;overflow:hidden;background:var(--md-sys-color-surface-container, #fff);color:var(--md-sys-color-on-surface, #111);border-left:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .12));border-radius:var(--pfx-side-panel-border-radius, 0);box-shadow:var(--pfx-side-panel-elevation, var(--md-sys-elevation-level2, 0 8px 24px rgba(0, 0, 0, .16)));width:var(--pfx-side-panel-width, 720px);transition:width var(--pfx-side-panel-motion-duration-enter, .22s) var(--pfx-side-panel-motion-easing-enter, cubic-bezier(.2, 0, 0, 1)),box-shadow var(--pfx-side-panel-motion-duration-enter, .22s) var(--pfx-side-panel-motion-easing-enter, cubic-bezier(.2, 0, 0, 1))}.pfx-surface-drawer.use-host-width{width:100%;max-width:100%}.pfx-surface-drawer.width-narrow{width:var(--pfx-side-panel-width, 420px)}.pfx-surface-drawer.width-default{width:var(--pfx-side-panel-width, 720px)}.pfx-surface-drawer.width-wide{width:var(--pfx-side-panel-width, 960px)}.pfx-surface-drawer.width-full{width:min(100vw,var(--pfx-side-panel-max-width, 100vw))}.pfx-surface-drawer__header{display:flex;align-items:center;gap:var(--pfx-side-panel-header-gap, 8px);min-height:var(--pfx-side-panel-header-height, 64px);padding:0 var(--pfx-side-panel-header-padding-x, 16px);border-bottom:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .12));background:var(--md-sys-color-surface-container-high, #f7f7f7)}.pfx-surface-drawer__title-block{display:flex;align-items:flex-start;gap:10px;min-width:0;flex:1 1 auto}.pfx-surface-drawer__title-icon{color:var(--md-sys-color-primary, currentColor)}.pfx-surface-drawer__copy{min-width:0}.pfx-surface-drawer__title,.pfx-surface-drawer__subtitle{margin:0}.pfx-surface-drawer__title{font-size:1rem;font-weight:600}.pfx-surface-drawer__subtitle{margin-top:4px;color:var(--md-sys-color-on-surface-variant, rgba(0, 0, 0, .64));font-size:.875rem}.pfx-surface-drawer__body{min-height:0;overflow:auto;padding:var(--pfx-side-panel-body-padding, 16px);background:var(--md-sys-color-surface, #fff)}@media(prefers-reduced-motion:reduce){.pfx-surface-drawer{transition:none}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1805
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: SurfaceDrawerComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1$1.PraxisI18nService }], target: i0.ɵɵFactoryTarget.Component });
1806
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: SurfaceDrawerComponent, isStandalone: true, selector: "praxis-surface-drawer", viewQueries: [{ propertyName: "contentHost", first: true, predicate: ["contentHost"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: "<div\n class=\"pfx-surface-drawer\"\n [class.has-header]=\"hasHeader\"\n [class.width-narrow]=\"widthClass === 'width-narrow'\"\n [class.width-default]=\"widthClass === 'width-default'\"\n [class.width-wide]=\"widthClass === 'width-wide'\"\n [class.width-full]=\"widthClass === 'width-full'\"\n [class.use-host-width]=\"useHostWidth\"\n [ngClass]=\"hostClasses\"\n [ngStyle]=\"hostCssVars\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"hasHeader ? titleId : null\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n>\n @if (hasHeader) {\n <header class=\"pfx-surface-drawer__header\">\n <div class=\"pfx-surface-drawer__title-block\">\n @if (titleIcon) {\n <mat-icon class=\"pfx-surface-drawer__title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n }\n <div class=\"pfx-surface-drawer__copy\">\n @if (title) {\n <h2 class=\"pfx-surface-drawer__title\" [id]=\"titleId\">{{ title }}</h2>\n }\n @if (subtitle) {\n <p class=\"pfx-surface-drawer__subtitle\">{{ subtitle }}</p>\n }\n </div>\n </div>\n <button\n mat-icon-button\n type=\"button\"\n class=\"pfx-surface-drawer__close\"\n [attr.aria-label]=\"closeLabel\"\n [matTooltip]=\"closeLabel\"\n (click)=\"onClose?.()\"\n >\n <mat-icon [praxisIcon]=\"'close'\"></mat-icon>\n </button>\n </header>\n }\n\n <section class=\"pfx-surface-drawer__body\">\n <ng-template #contentHost></ng-template>\n </section>\n</div>\n", styles: [":host{display:block;height:100%;width:100%;min-width:0;flex:1 1 auto}.pfx-surface-drawer{display:grid;grid-template-rows:auto 1fr;height:100%;min-width:0;overflow:hidden;background:var(--md-sys-color-surface-container, #fff);color:var(--md-sys-color-on-surface, #111);border-left:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .12));border-radius:var(--pfx-side-panel-border-radius, 0);box-shadow:var(--pfx-side-panel-elevation, var(--md-sys-elevation-level2, 0 8px 24px rgba(0, 0, 0, .16)));width:var(--pfx-side-panel-width, 720px);transition:width var(--pfx-side-panel-motion-duration-enter, .22s) var(--pfx-side-panel-motion-easing-enter, cubic-bezier(.2, 0, 0, 1)),box-shadow var(--pfx-side-panel-motion-duration-enter, .22s) var(--pfx-side-panel-motion-easing-enter, cubic-bezier(.2, 0, 0, 1))}.pfx-surface-drawer.use-host-width{width:100%;max-width:100%}.pfx-surface-drawer.width-narrow{width:var(--pfx-side-panel-width, 420px)}.pfx-surface-drawer.width-default{width:var(--pfx-side-panel-width, 720px)}.pfx-surface-drawer.width-wide{width:var(--pfx-side-panel-width, 960px)}.pfx-surface-drawer.width-full{width:min(100vw,var(--pfx-side-panel-max-width, 100vw))}.pfx-surface-drawer__header{display:flex;align-items:center;gap:var(--pfx-side-panel-header-gap, 8px);min-height:var(--pfx-side-panel-header-height, 64px);padding:0 var(--pfx-side-panel-header-padding-x, 16px);border-bottom:1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, .12));background:var(--md-sys-color-surface-container-high, #f7f7f7)}.pfx-surface-drawer__title-block{display:flex;align-items:flex-start;gap:10px;min-width:0;flex:1 1 auto}.pfx-surface-drawer__title-icon{color:var(--md-sys-color-primary, currentColor)}.pfx-surface-drawer__copy{min-width:0}.pfx-surface-drawer__title,.pfx-surface-drawer__subtitle{margin:0}.pfx-surface-drawer__title{font-size:1rem;font-weight:600}.pfx-surface-drawer__subtitle{margin-top:4px;color:var(--md-sys-color-on-surface-variant, rgba(0, 0, 0, .64));font-size:.875rem}.pfx-surface-drawer__body{min-height:0;overflow:auto;padding:var(--pfx-side-panel-body-padding, 16px);background:var(--md-sys-color-surface, #fff)}@media(prefers-reduced-motion:reduce){.pfx-surface-drawer{transition:none}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1775
1807
  }
1776
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SurfaceDrawerComponent, decorators: [{
1808
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: SurfaceDrawerComponent, decorators: [{
1777
1809
  type: Component,
1778
1810
  args: [{ selector: 'praxis-surface-drawer', standalone: true, imports: [
1779
1811
  CommonModule,
@@ -1915,10 +1947,10 @@ class GlobalConfigAdminService {
1915
1947
  clearStoredConfig() {
1916
1948
  return this.global.clearGlobalConfig();
1917
1949
  }
1918
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: GlobalConfigAdminService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1919
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: GlobalConfigAdminService, providedIn: 'root' });
1950
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: GlobalConfigAdminService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1951
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: GlobalConfigAdminService, providedIn: 'root' });
1920
1952
  }
1921
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: GlobalConfigAdminService, decorators: [{
1953
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: GlobalConfigAdminService, decorators: [{
1922
1954
  type: Injectable,
1923
1955
  args: [{ providedIn: 'root' }]
1924
1956
  }] });
@@ -3852,8 +3884,8 @@ class GlobalConfigEditorComponent {
3852
3884
  }
3853
3885
  return true;
3854
3886
  }
3855
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: GlobalConfigEditorComponent, deps: [{ token: GlobalConfigAdminService }, { token: i2$1.MatSnackBar }], target: i0.ɵɵFactoryTarget.Component });
3856
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: GlobalConfigEditorComponent, isStandalone: true, selector: "praxis-global-config-editor", viewQueries: [{ propertyName: "hostCrud", first: true, predicate: ["hostCrud"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "hostFields", first: true, predicate: ["hostFields"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "hostCache", first: true, predicate: ["hostCache"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "hostTable", first: true, predicate: ["hostTable"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "hostDialog", first: true, predicate: ["hostDialog"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "hostAiCredentials", first: true, predicate: ["hostAiCredentials"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "hostAiModel", first: true, predicate: ["hostAiModel"], descendants: true, read: ViewContainerRef }, { propertyName: "hostAiEmbedding", first: true, predicate: ["hostAiEmbedding"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: `
3887
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: GlobalConfigEditorComponent, deps: [{ token: GlobalConfigAdminService }, { token: i2$1.MatSnackBar }], target: i0.ɵɵFactoryTarget.Component });
3888
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: GlobalConfigEditorComponent, isStandalone: true, selector: "praxis-global-config-editor", viewQueries: [{ propertyName: "hostCrud", first: true, predicate: ["hostCrud"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "hostFields", first: true, predicate: ["hostFields"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "hostCache", first: true, predicate: ["hostCache"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "hostTable", first: true, predicate: ["hostTable"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "hostDialog", first: true, predicate: ["hostDialog"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "hostAiCredentials", first: true, predicate: ["hostAiCredentials"], descendants: true, read: ViewContainerRef, static: true }, { propertyName: "hostAiModel", first: true, predicate: ["hostAiModel"], descendants: true, read: ViewContainerRef }, { propertyName: "hostAiEmbedding", first: true, predicate: ["hostAiEmbedding"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: `
3857
3889
  <mat-accordion multi>
3858
3890
  <mat-expansion-panel>
3859
3891
  <mat-expansion-panel-header>
@@ -3893,32 +3925,36 @@ class GlobalConfigEditorComponent {
3893
3925
  </mat-panel-title>
3894
3926
  <mat-panel-description>{{ tx('LLM integration') }}</mat-panel-description>
3895
3927
  </mat-expansion-panel-header>
3896
-
3928
+
3897
3929
  <div class="ai-config-container">
3898
3930
  <div class="ai-config-source">
3899
3931
  <div class="ai-config-source__meta">
3900
3932
  <mat-icon>settings_suggest</mat-icon>
3901
3933
  <span>{{ configSourceLabel }}</span>
3902
3934
  </div>
3935
+ @if (hasStoredGlobalConfig) {
3903
3936
  <button
3904
3937
  mat-stroked-button
3905
3938
  type="button"
3906
3939
  class="ai-action-btn ai-action-btn--clear"
3907
- *ngIf="hasStoredGlobalConfig"
3908
3940
  [attr.aria-busy]="isClearingGlobalConfig ? 'true' : null"
3909
3941
  [disabled]="isClearingGlobalConfig"
3910
3942
  (click)="clearStoredConfig()"
3911
- [matTooltip]="tx('Clear saved config and return to server defaults')"
3912
- >
3913
- <ng-container *ngIf="isClearingGlobalConfig; else clearContent">
3914
- <mat-spinner diameter="16" class="btn-spinner"></mat-spinner>
3915
- <span class="ai-action-label">{{ tx('Clearing...') }}</span>
3916
- </ng-container>
3917
- <ng-template #clearContent>
3918
- <mat-icon>delete_sweep</mat-icon>
3919
- <span class="ai-action-label">{{ tx('Clear saved config') }}</span>
3920
- </ng-template>
3921
- </button>
3943
+ [matTooltip]="tx('Clear saved config and return to server defaults')"
3944
+ >
3945
+ @if (isClearingGlobalConfig) {
3946
+ <mat-spinner diameter="16" class="btn-spinner"></mat-spinner>
3947
+ <span class="ai-action-label">{{ tx('Clearing...') }}</span>
3948
+ } @else {
3949
+ <mat-icon>delete_sweep</mat-icon>
3950
+ <span class="ai-action-label">{{ tx('Clear saved config') }}</span>
3951
+ }
3952
+ <ng-template #clearContent>
3953
+ <mat-icon>delete_sweep</mat-icon>
3954
+ <span class="ai-action-label">{{ tx('Clear saved config') }}</span>
3955
+ </ng-template>
3956
+ </button>
3957
+ }
3922
3958
  </div>
3923
3959
  <!-- Group 1: Credentials -->
3924
3960
  <div class="ai-group">
@@ -3926,18 +3962,23 @@ class GlobalConfigEditorComponent {
3926
3962
  <div class="ai-group-title">
3927
3963
  <mat-icon>vpn_key</mat-icon> {{ tx('Credentials') }}
3928
3964
  </div>
3929
- <button mat-stroked-button
3930
- type="button"
3931
- class="ai-action-btn"
3932
- [class.is-success]="aiTestResult?.success"
3933
- [attr.aria-busy]="isTestingAi ? 'true' : null"
3934
- (click)="testAiConnection()"
3935
- [disabled]="isTestingAi || !hasApiKey"
3936
- [matTooltip]="tx('Test connection with the provided key')">
3937
- <ng-container *ngIf="isTestingAi; else btnContent">
3965
+ <button mat-stroked-button
3966
+ type="button"
3967
+ class="ai-action-btn"
3968
+ [class.is-success]="aiTestResult?.success"
3969
+ [attr.aria-busy]="isTestingAi ? 'true' : null"
3970
+ (click)="testAiConnection()"
3971
+ [disabled]="isTestingAi || !hasApiKey"
3972
+ [matTooltip]="tx('Test connection with the provided key')">
3973
+ @if (isTestingAi) {
3938
3974
  <mat-spinner diameter="16" class="btn-spinner"></mat-spinner>
3939
3975
  <span class="ai-action-label">{{ tx('Connecting...') }}</span>
3940
- </ng-container>
3976
+ } @else {
3977
+ <mat-icon>{{ aiTestResult?.success ? 'check' : 'bolt' }}</mat-icon>
3978
+ <span class="ai-action-label">
3979
+ {{ aiTestResult?.success ? tx('Connected') : tx('Test connection') }}
3980
+ </span>
3981
+ }
3941
3982
  <ng-template #btnContent>
3942
3983
  <mat-icon>{{ aiTestResult?.success ? 'check' : 'bolt' }}</mat-icon>
3943
3984
  <span class="ai-action-label">
@@ -3946,59 +3987,76 @@ class GlobalConfigEditorComponent {
3946
3987
  </ng-template>
3947
3988
  </button>
3948
3989
  </div>
3949
- <div class="ai-provider-summary" *ngIf="selectedProvider">
3950
- <span class="ai-provider-icon" aria-hidden="true">
3951
- <ng-container [ngSwitch]="selectedProvider.iconKey">
3952
- <svg *ngSwitchCase="'gemini'" viewBox="0 0 24 24" class="provider-svg" fill="none" stroke="currentColor" stroke-width="1.6">
3953
- <path d="M12 3l2.6 5.4L20 11l-5.4 2.6L12 19l-2.6-5.4L4 11l5.4-2.6L12 3z" />
3954
- </svg>
3955
- <svg *ngSwitchCase="'openai'" viewBox="0 0 24 24" class="provider-svg" fill="none" stroke="currentColor" stroke-width="1.6">
3956
- <polygon points="12,2 20,7 20,17 12,22 4,17 4,7" />
3957
- </svg>
3958
- <svg *ngSwitchCase="'xai'" viewBox="0 0 24 24" class="provider-svg" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round">
3959
- <line x1="5" y1="5" x2="19" y2="19" />
3960
- <line x1="19" y1="5" x2="5" y2="19" />
3961
- </svg>
3962
- <svg *ngSwitchCase="'mock'" viewBox="0 0 24 24" class="provider-svg" fill="none" stroke="currentColor" stroke-width="1.6">
3963
- <rect x="4.5" y="4.5" width="15" height="15" rx="2" ry="2" stroke-dasharray="2 2" />
3964
- </svg>
3965
- <svg *ngSwitchDefault viewBox="0 0 24 24" class="provider-svg" fill="none" stroke="currentColor" stroke-width="1.6">
3966
- <circle cx="12" cy="12" r="7.5" />
3967
- </svg>
3968
- </ng-container>
3969
- </span>
3970
- <div class="ai-provider-meta">
3971
- <div class="ai-provider-name">{{ selectedProvider.label }}</div>
3972
- <div class="ai-provider-desc">{{ selectedProvider.description }}</div>
3973
- </div>
3974
- <div
3975
- class="ai-provider-key"
3976
- *ngIf="selectedProvider?.requiresApiKey !== false"
3977
- [class.is-present]="hasCurrentApiKey"
3978
- [class.is-saved]="!hasCurrentApiKey && (apiKeyLast4 || hasStoredApiKey)"
3979
- [class.is-missing]="!hasCurrentApiKey && !apiKeyLast4 && !hasStoredApiKey"
3980
- >
3981
- <mat-icon>vpn_key</mat-icon>
3982
- <span>{{ apiKeyStatusLabel }}</span>
3983
- </div>
3984
- <div class="ai-provider-key is-unlocked" *ngIf="selectedProvider?.requiresApiKey === false">
3985
- <mat-icon>check_circle</mat-icon>
3986
- <span>{{ tx('No key required') }}</span>
3990
+ @if (selectedProvider) {
3991
+ <div class="ai-provider-summary">
3992
+ <span class="ai-provider-icon" aria-hidden="true">
3993
+ @switch (selectedProvider.iconKey) {
3994
+ @case ('gemini') {
3995
+ <svg viewBox="0 0 24 24" class="provider-svg" fill="none" stroke="currentColor" stroke-width="1.6">
3996
+ <path d="M12 3l2.6 5.4L20 11l-5.4 2.6L12 19l-2.6-5.4L4 11l5.4-2.6L12 3z" />
3997
+ </svg>
3998
+ }
3999
+ @case ('openai') {
4000
+ <svg viewBox="0 0 24 24" class="provider-svg" fill="none" stroke="currentColor" stroke-width="1.6">
4001
+ <polygon points="12,2 20,7 20,17 12,22 4,17 4,7" />
4002
+ </svg>
4003
+ }
4004
+ @case ('xai') {
4005
+ <svg viewBox="0 0 24 24" class="provider-svg" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round">
4006
+ <line x1="5" y1="5" x2="19" y2="19" />
4007
+ <line x1="19" y1="5" x2="5" y2="19" />
4008
+ </svg>
4009
+ }
4010
+ @case ('mock') {
4011
+ <svg viewBox="0 0 24 24" class="provider-svg" fill="none" stroke="currentColor" stroke-width="1.6">
4012
+ <rect x="4.5" y="4.5" width="15" height="15" rx="2" ry="2" stroke-dasharray="2 2" />
4013
+ </svg>
4014
+ }
4015
+ @default {
4016
+ <svg viewBox="0 0 24 24" class="provider-svg" fill="none" stroke="currentColor" stroke-width="1.6">
4017
+ <circle cx="12" cy="12" r="7.5" />
4018
+ </svg>
4019
+ }
4020
+ }
4021
+ </span>
4022
+ <div class="ai-provider-meta">
4023
+ <div class="ai-provider-name">{{ selectedProvider.label }}</div>
4024
+ <div class="ai-provider-desc">{{ selectedProvider.description }}</div>
4025
+ </div>
4026
+ @if (selectedProvider?.requiresApiKey !== false) {
4027
+ <div
4028
+ class="ai-provider-key"
4029
+ [class.is-present]="hasCurrentApiKey"
4030
+ [class.is-saved]="!hasCurrentApiKey && (apiKeyLast4 || hasStoredApiKey)"
4031
+ [class.is-missing]="!hasCurrentApiKey && !apiKeyLast4 && !hasStoredApiKey"
4032
+ >
4033
+ <mat-icon>vpn_key</mat-icon>
4034
+ <span>{{ apiKeyStatusLabel }}</span>
4035
+ </div>
4036
+ }
4037
+ @if (selectedProvider?.requiresApiKey === false) {
4038
+ <div class="ai-provider-key is-unlocked">
4039
+ <mat-icon>check_circle</mat-icon>
4040
+ <span>{{ tx('No key required') }}</span>
4041
+ </div>
4042
+ }
3987
4043
  </div>
3988
- </div>
4044
+ }
3989
4045
  <div class="ai-credentials-row">
3990
4046
  <div class="ai-form-inline">
3991
4047
  <ng-template #hostAiCredentials></ng-template>
3992
4048
  </div>
3993
4049
  </div>
3994
-
4050
+
3995
4051
  <!-- Feedback / Error Message -->
3996
- <div class="ai-feedback" *ngIf="aiTestResult && !aiTestResult.success">
3997
- <mat-icon color="warn">error</mat-icon>
3998
- <span class="error-text">{{ aiTestResult.message }}</span>
3999
- </div>
4052
+ @if (aiTestResult && !aiTestResult.success) {
4053
+ <div class="ai-feedback">
4054
+ <mat-icon color="warn">error</mat-icon>
4055
+ <span class="error-text">{{ aiTestResult.message }}</span>
4056
+ </div>
4057
+ }
4000
4058
  </div>
4001
-
4059
+
4002
4060
  <!-- Group 2: Model & Behavior (Only visible if Key is present) -->
4003
4061
  <div class="ai-group" [class.disabled-group]="!hasApiKey">
4004
4062
  <div class="ai-group-header">
@@ -4006,31 +4064,40 @@ class GlobalConfigEditorComponent {
4006
4064
  <mat-icon>smart_toy</mat-icon> {{ tx('Model & Behavior') }}
4007
4065
  </div>
4008
4066
  <div class="ai-header-actions">
4009
- <span class="ai-subtext" *ngIf="hasApiKey">{{ tx('Choose the model after validating the key.') }}</span>
4010
- <mat-chip-option *ngIf="!hasApiKey" disabled>{{ tx('API key validated required') }}</mat-chip-option>
4067
+ @if (hasApiKey) {
4068
+ <span class="ai-subtext">{{ tx('Choose the model after validating the key.') }}</span>
4069
+ }
4070
+ @if (!hasApiKey) {
4071
+ <mat-chip-option disabled>{{ tx('API key validated required') }}</mat-chip-option>
4072
+ }
4011
4073
  <button mat-icon-button (click)="refreshModels(true)" [disabled]="isTestingAi || !hasApiKey" [matTooltip]="tx('Refresh model list')" [attr.aria-label]="tx('Refresh model list')">
4012
4074
  <mat-icon [class.spin]="isRefetchingModels">sync</mat-icon>
4013
4075
  </button>
4014
4076
  </div>
4015
4077
  </div>
4016
-
4017
- <div class="ai-model-content" *ngIf="hasApiKey">
4018
- <div class="ai-model-controls">
4019
- <ng-template #hostAiModel></ng-template>
4078
+
4079
+ @if (hasApiKey) {
4080
+ <div class="ai-model-content">
4081
+ <div class="ai-model-controls">
4082
+ <ng-template #hostAiModel></ng-template>
4083
+ </div>
4084
+ <!-- Model Details (Placeholder for future metadata) -->
4085
+ @if (selectedModelDetails) {
4086
+ <div class="ai-model-details">
4087
+ <mat-icon inline>info</mat-icon> {{ selectedModelDetails }}
4088
+ </div>
4089
+ }
4020
4090
  </div>
4021
-
4022
- <!-- Model Details (Placeholder for future metadata) -->
4023
- <div class="ai-model-details" *ngIf="selectedModelDetails">
4024
- <mat-icon inline>info</mat-icon> {{ selectedModelDetails }}
4091
+ }
4092
+
4093
+ @if (!hasApiKey) {
4094
+ <div class="ai-placeholder">
4095
+ <mat-icon>lock</mat-icon>
4096
+ <span>{{ tx('Configure and test your API key to unlock model selection.') }}</span>
4025
4097
  </div>
4026
- </div>
4027
-
4028
- <div class="ai-placeholder" *ngIf="!hasApiKey">
4029
- <mat-icon>lock</mat-icon>
4030
- <span>{{ tx('Configure and test your API key to unlock model selection.') }}</span>
4031
- </div>
4098
+ }
4032
4099
  </div>
4033
-
4100
+
4034
4101
  <!-- Group 3: Embeddings (RAG) -->
4035
4102
  <div class="ai-group">
4036
4103
  <div class="ai-group-header">
@@ -4045,31 +4112,37 @@ class GlobalConfigEditorComponent {
4045
4112
  (click)="useLlmForEmbeddings()"
4046
4113
  [disabled]="!canUseLlmForEmbeddings"
4047
4114
  [matTooltip]="tx('Use the LLM provider and key for embeddings')"
4048
- >
4115
+ >
4049
4116
  <mat-icon>merge_type</mat-icon>
4050
4117
  <span class="ai-action-label">{{ tx('Use the LLM provider and key for embeddings') }}</span>
4051
4118
  </button>
4052
- <mat-chip-option *ngIf="!canUseLlmForEmbeddings" disabled>{{ tx('Provider has no embeddings') }}</mat-chip-option>
4119
+ @if (!canUseLlmForEmbeddings) {
4120
+ <mat-chip-option disabled>{{ tx('Provider has no embeddings') }}</mat-chip-option>
4121
+ }
4053
4122
  </div>
4054
4123
  </div>
4055
4124
  <div class="ai-subtext">
4056
4125
  {{ tx('Configure the embeddings provider for vector search (templates and schemas).') }}
4057
4126
  </div>
4058
- <div class="ai-subtext" *ngIf="embeddingUseSameAsLlm">
4059
- {{ tx('Synchronized with the LLM. The fields below follow the primary credential.') }}
4060
- </div>
4127
+ @if (embeddingUseSameAsLlm) {
4128
+ <div class="ai-subtext">
4129
+ {{ tx('Synchronized with the LLM. The fields below follow the primary credential.') }}
4130
+ </div>
4131
+ }
4061
4132
  <div class="ai-embedding-row">
4062
4133
  <ng-template #hostAiEmbedding></ng-template>
4063
4134
  </div>
4064
- <div class="ai-feedback ai-feedback--warn" *ngIf="embeddingDimensionMismatch">
4065
- <mat-icon>warning</mat-icon>
4066
- <span class="error-text">
4067
- {{ tx('Embedding dimension mismatch with database (768). Adjust to 768 or redo the migration.') }}
4068
- </span>
4069
- </div>
4135
+ @if (embeddingDimensionMismatch) {
4136
+ <div class="ai-feedback ai-feedback--warn">
4137
+ <mat-icon>warning</mat-icon>
4138
+ <span class="error-text">
4139
+ {{ tx('Embedding dimension mismatch with database (768). Adjust to 768 or redo the migration.') }}
4140
+ </span>
4141
+ </div>
4142
+ }
4070
4143
  </div>
4071
4144
  </div>
4072
-
4145
+
4073
4146
  </mat-expansion-panel>
4074
4147
  <mat-expansion-panel>
4075
4148
  <mat-expansion-panel-header>
@@ -4093,27 +4166,33 @@ class GlobalConfigEditorComponent {
4093
4166
  <!-- Icon UX helpers for Dialog variants -->
4094
4167
  <div class="dlg-icon-helpers">
4095
4168
  <div class="dlg-icon-helpers__head">{{ tx('Icons by profile (shortcuts)') }}</div>
4096
- <div class="dlg-icon-helpers__row" *ngFor="let k of dialogVariantKeys">
4097
- <div class="dlg-icon-helpers__label">{{ k }}</div>
4098
- <mat-icon aria-hidden="true" [praxisIcon]="getVariantIcon(k) || 'mi:help_outline'" class="dlg-icon-helpers__preview"></mat-icon>
4099
- <button mat-stroked-button color="primary" type="button" (click)="pickVariantIcon(k)" [matTooltip]="tx('Pick icon for {{key}}', { key: k })">
4100
- <mat-icon>search</mat-icon>
4101
- {{ tx('Pick icon') }}
4102
- </button>
4103
- <button mat-icon-button type="button" *ngIf="getVariantIcon(k)" (click)="clearVariantIcon(k)" [matTooltip]="tx('Clear icon for {{key}}', { key: k })" [attr.aria-label]="tx('Clear icon')">
4104
- <mat-icon>backspace</mat-icon>
4105
- </button>
4106
- <span class="dlg-icon-helpers__value" *ngIf="getVariantIcon(k)">{{ getVariantIcon(k) }}</span>
4107
- </div>
4169
+ @for (k of dialogVariantKeys; track k) {
4170
+ <div class="dlg-icon-helpers__row">
4171
+ <div class="dlg-icon-helpers__label">{{ k }}</div>
4172
+ <mat-icon aria-hidden="true" [praxisIcon]="getVariantIcon(k) || 'mi:help_outline'" class="dlg-icon-helpers__preview"></mat-icon>
4173
+ <button mat-stroked-button color="primary" type="button" (click)="pickVariantIcon(k)" [matTooltip]="tx('Pick icon for {{key}}', { key: k })">
4174
+ <mat-icon>search</mat-icon>
4175
+ {{ tx('Pick icon') }}
4176
+ </button>
4177
+ @if (getVariantIcon(k)) {
4178
+ <button mat-icon-button type="button" (click)="clearVariantIcon(k)" [matTooltip]="tx('Clear icon for {{key}}', { key: k })" [attr.aria-label]="tx('Clear icon')">
4179
+ <mat-icon>backspace</mat-icon>
4180
+ </button>
4181
+ }
4182
+ @if (getVariantIcon(k)) {
4183
+ <span class="dlg-icon-helpers__value">{{ getVariantIcon(k) }}</span>
4184
+ }
4185
+ </div>
4186
+ }
4108
4187
  <div class="dlg-icon-helpers__hint">{{ tx('The text field above remains editable. These buttons only help with selection/clear preview.') }}</div>
4109
4188
  </div>
4110
4189
  </mat-expansion-panel>
4111
4190
  </mat-accordion>
4112
- `, isInline: true, styles: [".dlg-icon-helpers{margin:12px 16px 4px;padding:12px;border:1px dashed var(--md-sys-color-outline-variant);border-radius:var(--md-sys-shape-corner-small, 8px)}.dlg-icon-helpers__head{font-weight:600;margin-bottom:8px;opacity:.8}.dlg-icon-helpers__row{display:flex;align-items:center;gap:12px;padding:6px 0}.dlg-icon-helpers__label{text-transform:capitalize;width:96px;opacity:.8}.dlg-icon-helpers__preview{width:24px;height:24px}.dlg-icon-helpers__value{font-family:monospace;font-size:12px;opacity:.8}.dlg-icon-helpers__hint{margin-top:8px;font-size:12px;opacity:.7}.panel-icon{margin-right:12px;color:var(--md-sys-color-primary)}.ai-config-container{padding:16px;display:flex;flex-direction:column;gap:24px}.ai-config-source{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;border-radius:var(--md-sys-shape-corner-medium, 10px);border:1px dashed var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low);font-size:12px;color:var(--md-sys-color-on-surface-variant);flex-wrap:wrap}.ai-config-source__meta{display:inline-flex;align-items:center;gap:8px}.ai-action-btn--clear{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container);--mdc-outlined-button-container-color: var(--md-sys-color-error-container);--mdc-outlined-button-label-text-color: var(--md-sys-color-on-error-container);--mdc-outlined-button-outline-color: var(--md-sys-color-error);--mat-mdc-button-persistent-ripple-color: var(--md-sys-color-on-error-container);--mat-mdc-button-ripple-color: var(--md-sys-color-on-error-container)}.ai-action-btn--clear:hover{background:var(--md-sys-color-error-container)}.ai-group-title{display:flex;align-items:center;gap:8px;font-weight:500;font-size:14px;color:var(--md-sys-color-primary);margin-bottom:12px;border-bottom:1px solid var(--md-sys-color-outline-variant);padding-bottom:4px}.ai-group-title mat-icon{font-size:20px;width:20px;height:20px}.ai-group ::ng-deep .form-actions,.ai-group ::ng-deep button[type=submit]{display:none!important}.ai-credentials-row{display:flex;align-items:flex-start;gap:16px;flex-wrap:wrap}.ai-group-header{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.ai-provider-summary{display:flex;align-items:center;gap:12px;padding:8px 12px;border-radius:var(--md-sys-shape-corner-medium, 10px);background:var(--md-sys-color-surface-variant);border:1px solid var(--md-sys-color-outline-variant);margin:8px 0 4px;flex-wrap:wrap}.ai-provider-icon{width:32px;height:32px;border-radius:var(--md-sys-shape-corner-small, 8px);border:1px solid var(--md-sys-color-outline-variant);display:flex;align-items:center;justify-content:center;background:var(--md-sys-color-surface);color:var(--md-sys-color-primary);flex:0 0 auto}.provider-svg{width:18px;height:18px}.ai-provider-meta{display:flex;flex-direction:column;gap:2px;min-width:200px;flex:1 1 auto}.ai-provider-name{font-weight:600;font-size:13px}.ai-provider-desc{font-size:12px;opacity:.75}.ai-provider-key{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-lowest);color:var(--md-sys-color-on-surface-variant);font-size:11px;font-weight:600;letter-spacing:.2px;box-shadow:var(--md-sys-elevation-level1, 0 1px 2px rgba(0,0,0,.2))}.ai-provider-key mat-icon{width:16px;height:16px;font-size:16px}.ai-provider-key.is-present{background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container);border-color:var(--md-sys-color-primary)}.ai-provider-key.is-saved{background:var(--md-sys-color-secondary-container);color:var(--md-sys-color-on-secondary-container);border-color:var(--md-sys-color-secondary)}.ai-provider-key.is-missing{background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface-variant);border-style:dashed;opacity:.9}.ai-provider-key.is-unlocked{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container);border-color:var(--md-sys-color-tertiary)}.ai-action-btn{display:inline-flex;align-items:center;gap:8px;height:40px;padding:0 14px;min-width:168px;border-radius:var(--md-sys-shape-corner-medium, 12px);white-space:nowrap;background:var(--md-sys-color-secondary-container);color:var(--md-sys-color-on-secondary-container);border-color:var(--md-sys-color-outline-variant);box-shadow:var(--md-sys-elevation-level1, 0 1px 2px rgba(0,0,0,.2));transition:background-color .15s ease,box-shadow .15s ease,transform .06s ease;--mdc-outlined-button-container-color: var(--md-sys-color-secondary-container);--mdc-outlined-button-label-text-color: var(--md-sys-color-on-secondary-container);--mdc-outlined-button-outline-color: var(--md-sys-color-outline-variant);--mat-mdc-button-persistent-ripple-color: var(--md-sys-color-on-secondary-container);--mat-mdc-button-ripple-color: var(--md-sys-color-on-secondary-container)}.ai-action-btn .mat-button-wrapper,.ai-action-btn .mdc-button__label{display:inline-flex;align-items:center;gap:8px;line-height:1;height:100%}.ai-action-btn .mat-icon{width:18px;height:18px;font-size:18px;line-height:18px}.ai-action-btn .ai-action-label{display:inline-flex;align-items:center;line-height:1}.ai-action-btn .mat-progress-spinner,.ai-action-btn .btn-spinner{display:inline-flex;align-items:center;justify-content:center;vertical-align:middle}.ai-action-btn:hover{background:var(--md-sys-color-secondary-container);box-shadow:var(--md-sys-elevation-level2, 0 2px 6px rgba(0,0,0,.25))}.ai-action-btn:active{transform:translateY(1px)}.ai-action-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.ai-action-btn.is-success{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container);border-color:var(--md-sys-color-tertiary);--mdc-outlined-button-container-color: var(--md-sys-color-tertiary-container);--mdc-outlined-button-label-text-color: var(--md-sys-color-on-tertiary-container);--mdc-outlined-button-outline-color: var(--md-sys-color-tertiary);--mat-mdc-button-persistent-ripple-color: var(--md-sys-color-on-tertiary-container);--mat-mdc-button-ripple-color: var(--md-sys-color-on-tertiary-container)}.ai-action-btn.is-success:hover{background:var(--md-sys-color-tertiary-container)}.ai-action-btn:disabled{background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface-variant);border-color:var(--md-sys-color-outline-variant);box-shadow:none;opacity:.7}.ai-form-inline{flex:1;min-width:300px}.ai-credentials-row button{margin-top:4px;height:40px}.btn-spinner{margin-right:8px}.ai-feedback{margin-top:8px;display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container);border-radius:var(--md-sys-shape-corner-extra-small, 4px);font-size:13px}.ai-feedback--warn{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container)}.disabled-group{opacity:.6;pointer-events:none}.ai-model-content{display:flex;flex-direction:column;gap:8px}.ai-model-controls{display:flex;align-items:flex-start;gap:8px}.ai-model-controls ::ng-deep praxis-dynamic-form{flex:1}.ai-model-details{font-size:12px;color:var(--md-sys-color-on-surface-variant);margin-left:4px}.ai-subtext{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.ai-header-actions{display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap}.ai-placeholder{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:24px;background:var(--md-sys-color-surface-container-low);border-radius:var(--md-sys-shape-corner-small, 8px);color:var(--md-sys-color-outline);gap:8px;text-align:center;font-size:13px}.spin{animation:spin 1s linear infinite}@keyframes spin{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i2.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i2.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "directive", type: i4$1.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i4$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i4$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i4$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i4$1.MatExpansionPanelDescription, selector: "mat-panel-description" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i6.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i9.MatChipOption, selector: "mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]", inputs: ["selectable", "selected"], outputs: ["selectionChange"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }] });
4191
+ `, isInline: true, styles: [".dlg-icon-helpers{margin:12px 16px 4px;padding:12px;border:1px dashed var(--md-sys-color-outline-variant);border-radius:var(--md-sys-shape-corner-small, 8px)}.dlg-icon-helpers__head{font-weight:600;margin-bottom:8px;opacity:.8}.dlg-icon-helpers__row{display:flex;align-items:center;gap:12px;padding:6px 0}.dlg-icon-helpers__label{text-transform:capitalize;width:96px;opacity:.8}.dlg-icon-helpers__preview{width:24px;height:24px}.dlg-icon-helpers__value{font-family:monospace;font-size:12px;opacity:.8}.dlg-icon-helpers__hint{margin-top:8px;font-size:12px;opacity:.7}.panel-icon{margin-right:12px;color:var(--md-sys-color-primary)}.ai-config-container{padding:16px;display:flex;flex-direction:column;gap:24px}.ai-config-source{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;border-radius:var(--md-sys-shape-corner-medium, 10px);border:1px dashed var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low);font-size:12px;color:var(--md-sys-color-on-surface-variant);flex-wrap:wrap}.ai-config-source__meta{display:inline-flex;align-items:center;gap:8px}.ai-action-btn--clear{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container);--mdc-outlined-button-container-color: var(--md-sys-color-error-container);--mdc-outlined-button-label-text-color: var(--md-sys-color-on-error-container);--mdc-outlined-button-outline-color: var(--md-sys-color-error);--mat-mdc-button-persistent-ripple-color: var(--md-sys-color-on-error-container);--mat-mdc-button-ripple-color: var(--md-sys-color-on-error-container)}.ai-action-btn--clear:hover{background:var(--md-sys-color-error-container)}.ai-group-title{display:flex;align-items:center;gap:8px;font-weight:500;font-size:14px;color:var(--md-sys-color-primary);margin-bottom:12px;border-bottom:1px solid var(--md-sys-color-outline-variant);padding-bottom:4px}.ai-group-title mat-icon{font-size:20px;width:20px;height:20px}.ai-group ::ng-deep .form-actions,.ai-group ::ng-deep button[type=submit]{display:none!important}.ai-credentials-row{display:flex;align-items:flex-start;gap:16px;flex-wrap:wrap}.ai-group-header{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.ai-provider-summary{display:flex;align-items:center;gap:12px;padding:8px 12px;border-radius:var(--md-sys-shape-corner-medium, 10px);background:var(--md-sys-color-surface-variant);border:1px solid var(--md-sys-color-outline-variant);margin:8px 0 4px;flex-wrap:wrap}.ai-provider-icon{width:32px;height:32px;border-radius:var(--md-sys-shape-corner-small, 8px);border:1px solid var(--md-sys-color-outline-variant);display:flex;align-items:center;justify-content:center;background:var(--md-sys-color-surface);color:var(--md-sys-color-primary);flex:0 0 auto}.provider-svg{width:18px;height:18px}.ai-provider-meta{display:flex;flex-direction:column;gap:2px;min-width:200px;flex:1 1 auto}.ai-provider-name{font-weight:600;font-size:13px}.ai-provider-desc{font-size:12px;opacity:.75}.ai-provider-key{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-lowest);color:var(--md-sys-color-on-surface-variant);font-size:11px;font-weight:600;letter-spacing:.2px;box-shadow:var(--md-sys-elevation-level1, 0 1px 2px rgba(0,0,0,.2))}.ai-provider-key mat-icon{width:16px;height:16px;font-size:16px}.ai-provider-key.is-present{background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container);border-color:var(--md-sys-color-primary)}.ai-provider-key.is-saved{background:var(--md-sys-color-secondary-container);color:var(--md-sys-color-on-secondary-container);border-color:var(--md-sys-color-secondary)}.ai-provider-key.is-missing{background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface-variant);border-style:dashed;opacity:.9}.ai-provider-key.is-unlocked{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container);border-color:var(--md-sys-color-tertiary)}.ai-action-btn{display:inline-flex;align-items:center;gap:8px;height:40px;padding:0 14px;min-width:168px;border-radius:var(--md-sys-shape-corner-medium, 12px);white-space:nowrap;background:var(--md-sys-color-secondary-container);color:var(--md-sys-color-on-secondary-container);border-color:var(--md-sys-color-outline-variant);box-shadow:var(--md-sys-elevation-level1, 0 1px 2px rgba(0,0,0,.2));transition:background-color .15s ease,box-shadow .15s ease,transform .06s ease;--mdc-outlined-button-container-color: var(--md-sys-color-secondary-container);--mdc-outlined-button-label-text-color: var(--md-sys-color-on-secondary-container);--mdc-outlined-button-outline-color: var(--md-sys-color-outline-variant);--mat-mdc-button-persistent-ripple-color: var(--md-sys-color-on-secondary-container);--mat-mdc-button-ripple-color: var(--md-sys-color-on-secondary-container)}.ai-action-btn .mat-button-wrapper,.ai-action-btn .mdc-button__label{display:inline-flex;align-items:center;gap:8px;line-height:1;height:100%}.ai-action-btn .mat-icon{width:18px;height:18px;font-size:18px;line-height:18px}.ai-action-btn .ai-action-label{display:inline-flex;align-items:center;line-height:1}.ai-action-btn .mat-progress-spinner,.ai-action-btn .btn-spinner{display:inline-flex;align-items:center;justify-content:center;vertical-align:middle}.ai-action-btn:hover{background:var(--md-sys-color-secondary-container);box-shadow:var(--md-sys-elevation-level2, 0 2px 6px rgba(0,0,0,.25))}.ai-action-btn:active{transform:translateY(1px)}.ai-action-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.ai-action-btn.is-success{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container);border-color:var(--md-sys-color-tertiary);--mdc-outlined-button-container-color: var(--md-sys-color-tertiary-container);--mdc-outlined-button-label-text-color: var(--md-sys-color-on-tertiary-container);--mdc-outlined-button-outline-color: var(--md-sys-color-tertiary);--mat-mdc-button-persistent-ripple-color: var(--md-sys-color-on-tertiary-container);--mat-mdc-button-ripple-color: var(--md-sys-color-on-tertiary-container)}.ai-action-btn.is-success:hover{background:var(--md-sys-color-tertiary-container)}.ai-action-btn:disabled{background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface-variant);border-color:var(--md-sys-color-outline-variant);box-shadow:none;opacity:.7}.ai-form-inline{flex:1;min-width:300px}.ai-credentials-row button{margin-top:4px;height:40px}.btn-spinner{margin-right:8px}.ai-feedback{margin-top:8px;display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container);border-radius:var(--md-sys-shape-corner-extra-small, 4px);font-size:13px}.ai-feedback--warn{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container)}.disabled-group{opacity:.6;pointer-events:none}.ai-model-content{display:flex;flex-direction:column;gap:8px}.ai-model-controls{display:flex;align-items:flex-start;gap:8px}.ai-model-controls ::ng-deep praxis-dynamic-form{flex:1}.ai-model-details{font-size:12px;color:var(--md-sys-color-on-surface-variant);margin-left:4px}.ai-subtext{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.ai-header-actions{display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap}.ai-placeholder{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:24px;background:var(--md-sys-color-surface-container-low);border-radius:var(--md-sys-shape-corner-small, 8px);color:var(--md-sys-color-outline);gap:8px;text-align:center;font-size:13px}.spin{animation:spin 1s linear infinite}@keyframes spin{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}\n"], dependencies: [{ kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: MatExpansionModule }, { kind: "directive", type: i3$1.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i3$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i3$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i3$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i3$1.MatExpansionPanelDescription, selector: "mat-panel-description" }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i6.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatChipsModule }, { kind: "component", type: i8.MatChipOption, selector: "mat-basic-chip-option, [mat-basic-chip-option], mat-chip-option, [mat-chip-option]", inputs: ["selectable", "selected"], outputs: ["selectionChange"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }] });
4113
4192
  }
4114
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: GlobalConfigEditorComponent, decorators: [{
4193
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: GlobalConfigEditorComponent, decorators: [{
4115
4194
  type: Component,
4116
- args: [{ selector: 'praxis-global-config-editor', standalone: true, imports: [CommonModule, MatSnackBarModule, MatExpansionModule, MatIconModule, MatButtonModule, MatTooltipModule, MatProgressSpinnerModule, MatChipsModule, PraxisIconDirective], template: `
4195
+ args: [{ selector: 'praxis-global-config-editor', standalone: true, imports: [MatSnackBarModule, MatExpansionModule, MatIconModule, MatButtonModule, MatTooltipModule, MatProgressSpinnerModule, MatChipsModule, PraxisIconDirective], template: `
4117
4196
  <mat-accordion multi>
4118
4197
  <mat-expansion-panel>
4119
4198
  <mat-expansion-panel-header>
@@ -4153,32 +4232,36 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
4153
4232
  </mat-panel-title>
4154
4233
  <mat-panel-description>{{ tx('LLM integration') }}</mat-panel-description>
4155
4234
  </mat-expansion-panel-header>
4156
-
4235
+
4157
4236
  <div class="ai-config-container">
4158
4237
  <div class="ai-config-source">
4159
4238
  <div class="ai-config-source__meta">
4160
4239
  <mat-icon>settings_suggest</mat-icon>
4161
4240
  <span>{{ configSourceLabel }}</span>
4162
4241
  </div>
4242
+ @if (hasStoredGlobalConfig) {
4163
4243
  <button
4164
4244
  mat-stroked-button
4165
4245
  type="button"
4166
4246
  class="ai-action-btn ai-action-btn--clear"
4167
- *ngIf="hasStoredGlobalConfig"
4168
4247
  [attr.aria-busy]="isClearingGlobalConfig ? 'true' : null"
4169
4248
  [disabled]="isClearingGlobalConfig"
4170
4249
  (click)="clearStoredConfig()"
4171
- [matTooltip]="tx('Clear saved config and return to server defaults')"
4172
- >
4173
- <ng-container *ngIf="isClearingGlobalConfig; else clearContent">
4174
- <mat-spinner diameter="16" class="btn-spinner"></mat-spinner>
4175
- <span class="ai-action-label">{{ tx('Clearing...') }}</span>
4176
- </ng-container>
4177
- <ng-template #clearContent>
4178
- <mat-icon>delete_sweep</mat-icon>
4179
- <span class="ai-action-label">{{ tx('Clear saved config') }}</span>
4180
- </ng-template>
4181
- </button>
4250
+ [matTooltip]="tx('Clear saved config and return to server defaults')"
4251
+ >
4252
+ @if (isClearingGlobalConfig) {
4253
+ <mat-spinner diameter="16" class="btn-spinner"></mat-spinner>
4254
+ <span class="ai-action-label">{{ tx('Clearing...') }}</span>
4255
+ } @else {
4256
+ <mat-icon>delete_sweep</mat-icon>
4257
+ <span class="ai-action-label">{{ tx('Clear saved config') }}</span>
4258
+ }
4259
+ <ng-template #clearContent>
4260
+ <mat-icon>delete_sweep</mat-icon>
4261
+ <span class="ai-action-label">{{ tx('Clear saved config') }}</span>
4262
+ </ng-template>
4263
+ </button>
4264
+ }
4182
4265
  </div>
4183
4266
  <!-- Group 1: Credentials -->
4184
4267
  <div class="ai-group">
@@ -4186,18 +4269,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
4186
4269
  <div class="ai-group-title">
4187
4270
  <mat-icon>vpn_key</mat-icon> {{ tx('Credentials') }}
4188
4271
  </div>
4189
- <button mat-stroked-button
4190
- type="button"
4191
- class="ai-action-btn"
4192
- [class.is-success]="aiTestResult?.success"
4193
- [attr.aria-busy]="isTestingAi ? 'true' : null"
4194
- (click)="testAiConnection()"
4195
- [disabled]="isTestingAi || !hasApiKey"
4196
- [matTooltip]="tx('Test connection with the provided key')">
4197
- <ng-container *ngIf="isTestingAi; else btnContent">
4272
+ <button mat-stroked-button
4273
+ type="button"
4274
+ class="ai-action-btn"
4275
+ [class.is-success]="aiTestResult?.success"
4276
+ [attr.aria-busy]="isTestingAi ? 'true' : null"
4277
+ (click)="testAiConnection()"
4278
+ [disabled]="isTestingAi || !hasApiKey"
4279
+ [matTooltip]="tx('Test connection with the provided key')">
4280
+ @if (isTestingAi) {
4198
4281
  <mat-spinner diameter="16" class="btn-spinner"></mat-spinner>
4199
4282
  <span class="ai-action-label">{{ tx('Connecting...') }}</span>
4200
- </ng-container>
4283
+ } @else {
4284
+ <mat-icon>{{ aiTestResult?.success ? 'check' : 'bolt' }}</mat-icon>
4285
+ <span class="ai-action-label">
4286
+ {{ aiTestResult?.success ? tx('Connected') : tx('Test connection') }}
4287
+ </span>
4288
+ }
4201
4289
  <ng-template #btnContent>
4202
4290
  <mat-icon>{{ aiTestResult?.success ? 'check' : 'bolt' }}</mat-icon>
4203
4291
  <span class="ai-action-label">
@@ -4206,59 +4294,76 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
4206
4294
  </ng-template>
4207
4295
  </button>
4208
4296
  </div>
4209
- <div class="ai-provider-summary" *ngIf="selectedProvider">
4210
- <span class="ai-provider-icon" aria-hidden="true">
4211
- <ng-container [ngSwitch]="selectedProvider.iconKey">
4212
- <svg *ngSwitchCase="'gemini'" viewBox="0 0 24 24" class="provider-svg" fill="none" stroke="currentColor" stroke-width="1.6">
4213
- <path d="M12 3l2.6 5.4L20 11l-5.4 2.6L12 19l-2.6-5.4L4 11l5.4-2.6L12 3z" />
4214
- </svg>
4215
- <svg *ngSwitchCase="'openai'" viewBox="0 0 24 24" class="provider-svg" fill="none" stroke="currentColor" stroke-width="1.6">
4216
- <polygon points="12,2 20,7 20,17 12,22 4,17 4,7" />
4217
- </svg>
4218
- <svg *ngSwitchCase="'xai'" viewBox="0 0 24 24" class="provider-svg" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round">
4219
- <line x1="5" y1="5" x2="19" y2="19" />
4220
- <line x1="19" y1="5" x2="5" y2="19" />
4221
- </svg>
4222
- <svg *ngSwitchCase="'mock'" viewBox="0 0 24 24" class="provider-svg" fill="none" stroke="currentColor" stroke-width="1.6">
4223
- <rect x="4.5" y="4.5" width="15" height="15" rx="2" ry="2" stroke-dasharray="2 2" />
4224
- </svg>
4225
- <svg *ngSwitchDefault viewBox="0 0 24 24" class="provider-svg" fill="none" stroke="currentColor" stroke-width="1.6">
4226
- <circle cx="12" cy="12" r="7.5" />
4227
- </svg>
4228
- </ng-container>
4229
- </span>
4230
- <div class="ai-provider-meta">
4231
- <div class="ai-provider-name">{{ selectedProvider.label }}</div>
4232
- <div class="ai-provider-desc">{{ selectedProvider.description }}</div>
4233
- </div>
4234
- <div
4235
- class="ai-provider-key"
4236
- *ngIf="selectedProvider?.requiresApiKey !== false"
4237
- [class.is-present]="hasCurrentApiKey"
4238
- [class.is-saved]="!hasCurrentApiKey && (apiKeyLast4 || hasStoredApiKey)"
4239
- [class.is-missing]="!hasCurrentApiKey && !apiKeyLast4 && !hasStoredApiKey"
4240
- >
4241
- <mat-icon>vpn_key</mat-icon>
4242
- <span>{{ apiKeyStatusLabel }}</span>
4243
- </div>
4244
- <div class="ai-provider-key is-unlocked" *ngIf="selectedProvider?.requiresApiKey === false">
4245
- <mat-icon>check_circle</mat-icon>
4246
- <span>{{ tx('No key required') }}</span>
4297
+ @if (selectedProvider) {
4298
+ <div class="ai-provider-summary">
4299
+ <span class="ai-provider-icon" aria-hidden="true">
4300
+ @switch (selectedProvider.iconKey) {
4301
+ @case ('gemini') {
4302
+ <svg viewBox="0 0 24 24" class="provider-svg" fill="none" stroke="currentColor" stroke-width="1.6">
4303
+ <path d="M12 3l2.6 5.4L20 11l-5.4 2.6L12 19l-2.6-5.4L4 11l5.4-2.6L12 3z" />
4304
+ </svg>
4305
+ }
4306
+ @case ('openai') {
4307
+ <svg viewBox="0 0 24 24" class="provider-svg" fill="none" stroke="currentColor" stroke-width="1.6">
4308
+ <polygon points="12,2 20,7 20,17 12,22 4,17 4,7" />
4309
+ </svg>
4310
+ }
4311
+ @case ('xai') {
4312
+ <svg viewBox="0 0 24 24" class="provider-svg" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round">
4313
+ <line x1="5" y1="5" x2="19" y2="19" />
4314
+ <line x1="19" y1="5" x2="5" y2="19" />
4315
+ </svg>
4316
+ }
4317
+ @case ('mock') {
4318
+ <svg viewBox="0 0 24 24" class="provider-svg" fill="none" stroke="currentColor" stroke-width="1.6">
4319
+ <rect x="4.5" y="4.5" width="15" height="15" rx="2" ry="2" stroke-dasharray="2 2" />
4320
+ </svg>
4321
+ }
4322
+ @default {
4323
+ <svg viewBox="0 0 24 24" class="provider-svg" fill="none" stroke="currentColor" stroke-width="1.6">
4324
+ <circle cx="12" cy="12" r="7.5" />
4325
+ </svg>
4326
+ }
4327
+ }
4328
+ </span>
4329
+ <div class="ai-provider-meta">
4330
+ <div class="ai-provider-name">{{ selectedProvider.label }}</div>
4331
+ <div class="ai-provider-desc">{{ selectedProvider.description }}</div>
4332
+ </div>
4333
+ @if (selectedProvider?.requiresApiKey !== false) {
4334
+ <div
4335
+ class="ai-provider-key"
4336
+ [class.is-present]="hasCurrentApiKey"
4337
+ [class.is-saved]="!hasCurrentApiKey && (apiKeyLast4 || hasStoredApiKey)"
4338
+ [class.is-missing]="!hasCurrentApiKey && !apiKeyLast4 && !hasStoredApiKey"
4339
+ >
4340
+ <mat-icon>vpn_key</mat-icon>
4341
+ <span>{{ apiKeyStatusLabel }}</span>
4342
+ </div>
4343
+ }
4344
+ @if (selectedProvider?.requiresApiKey === false) {
4345
+ <div class="ai-provider-key is-unlocked">
4346
+ <mat-icon>check_circle</mat-icon>
4347
+ <span>{{ tx('No key required') }}</span>
4348
+ </div>
4349
+ }
4247
4350
  </div>
4248
- </div>
4351
+ }
4249
4352
  <div class="ai-credentials-row">
4250
4353
  <div class="ai-form-inline">
4251
4354
  <ng-template #hostAiCredentials></ng-template>
4252
4355
  </div>
4253
4356
  </div>
4254
-
4357
+
4255
4358
  <!-- Feedback / Error Message -->
4256
- <div class="ai-feedback" *ngIf="aiTestResult && !aiTestResult.success">
4257
- <mat-icon color="warn">error</mat-icon>
4258
- <span class="error-text">{{ aiTestResult.message }}</span>
4259
- </div>
4359
+ @if (aiTestResult && !aiTestResult.success) {
4360
+ <div class="ai-feedback">
4361
+ <mat-icon color="warn">error</mat-icon>
4362
+ <span class="error-text">{{ aiTestResult.message }}</span>
4363
+ </div>
4364
+ }
4260
4365
  </div>
4261
-
4366
+
4262
4367
  <!-- Group 2: Model & Behavior (Only visible if Key is present) -->
4263
4368
  <div class="ai-group" [class.disabled-group]="!hasApiKey">
4264
4369
  <div class="ai-group-header">
@@ -4266,31 +4371,40 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
4266
4371
  <mat-icon>smart_toy</mat-icon> {{ tx('Model & Behavior') }}
4267
4372
  </div>
4268
4373
  <div class="ai-header-actions">
4269
- <span class="ai-subtext" *ngIf="hasApiKey">{{ tx('Choose the model after validating the key.') }}</span>
4270
- <mat-chip-option *ngIf="!hasApiKey" disabled>{{ tx('API key validated required') }}</mat-chip-option>
4374
+ @if (hasApiKey) {
4375
+ <span class="ai-subtext">{{ tx('Choose the model after validating the key.') }}</span>
4376
+ }
4377
+ @if (!hasApiKey) {
4378
+ <mat-chip-option disabled>{{ tx('API key validated required') }}</mat-chip-option>
4379
+ }
4271
4380
  <button mat-icon-button (click)="refreshModels(true)" [disabled]="isTestingAi || !hasApiKey" [matTooltip]="tx('Refresh model list')" [attr.aria-label]="tx('Refresh model list')">
4272
4381
  <mat-icon [class.spin]="isRefetchingModels">sync</mat-icon>
4273
4382
  </button>
4274
4383
  </div>
4275
4384
  </div>
4276
-
4277
- <div class="ai-model-content" *ngIf="hasApiKey">
4278
- <div class="ai-model-controls">
4279
- <ng-template #hostAiModel></ng-template>
4385
+
4386
+ @if (hasApiKey) {
4387
+ <div class="ai-model-content">
4388
+ <div class="ai-model-controls">
4389
+ <ng-template #hostAiModel></ng-template>
4390
+ </div>
4391
+ <!-- Model Details (Placeholder for future metadata) -->
4392
+ @if (selectedModelDetails) {
4393
+ <div class="ai-model-details">
4394
+ <mat-icon inline>info</mat-icon> {{ selectedModelDetails }}
4395
+ </div>
4396
+ }
4280
4397
  </div>
4281
-
4282
- <!-- Model Details (Placeholder for future metadata) -->
4283
- <div class="ai-model-details" *ngIf="selectedModelDetails">
4284
- <mat-icon inline>info</mat-icon> {{ selectedModelDetails }}
4398
+ }
4399
+
4400
+ @if (!hasApiKey) {
4401
+ <div class="ai-placeholder">
4402
+ <mat-icon>lock</mat-icon>
4403
+ <span>{{ tx('Configure and test your API key to unlock model selection.') }}</span>
4285
4404
  </div>
4286
- </div>
4287
-
4288
- <div class="ai-placeholder" *ngIf="!hasApiKey">
4289
- <mat-icon>lock</mat-icon>
4290
- <span>{{ tx('Configure and test your API key to unlock model selection.') }}</span>
4291
- </div>
4405
+ }
4292
4406
  </div>
4293
-
4407
+
4294
4408
  <!-- Group 3: Embeddings (RAG) -->
4295
4409
  <div class="ai-group">
4296
4410
  <div class="ai-group-header">
@@ -4305,31 +4419,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
4305
4419
  (click)="useLlmForEmbeddings()"
4306
4420
  [disabled]="!canUseLlmForEmbeddings"
4307
4421
  [matTooltip]="tx('Use the LLM provider and key for embeddings')"
4308
- >
4422
+ >
4309
4423
  <mat-icon>merge_type</mat-icon>
4310
4424
  <span class="ai-action-label">{{ tx('Use the LLM provider and key for embeddings') }}</span>
4311
4425
  </button>
4312
- <mat-chip-option *ngIf="!canUseLlmForEmbeddings" disabled>{{ tx('Provider has no embeddings') }}</mat-chip-option>
4426
+ @if (!canUseLlmForEmbeddings) {
4427
+ <mat-chip-option disabled>{{ tx('Provider has no embeddings') }}</mat-chip-option>
4428
+ }
4313
4429
  </div>
4314
4430
  </div>
4315
4431
  <div class="ai-subtext">
4316
4432
  {{ tx('Configure the embeddings provider for vector search (templates and schemas).') }}
4317
4433
  </div>
4318
- <div class="ai-subtext" *ngIf="embeddingUseSameAsLlm">
4319
- {{ tx('Synchronized with the LLM. The fields below follow the primary credential.') }}
4320
- </div>
4434
+ @if (embeddingUseSameAsLlm) {
4435
+ <div class="ai-subtext">
4436
+ {{ tx('Synchronized with the LLM. The fields below follow the primary credential.') }}
4437
+ </div>
4438
+ }
4321
4439
  <div class="ai-embedding-row">
4322
4440
  <ng-template #hostAiEmbedding></ng-template>
4323
4441
  </div>
4324
- <div class="ai-feedback ai-feedback--warn" *ngIf="embeddingDimensionMismatch">
4325
- <mat-icon>warning</mat-icon>
4326
- <span class="error-text">
4327
- {{ tx('Embedding dimension mismatch with database (768). Adjust to 768 or redo the migration.') }}
4328
- </span>
4329
- </div>
4442
+ @if (embeddingDimensionMismatch) {
4443
+ <div class="ai-feedback ai-feedback--warn">
4444
+ <mat-icon>warning</mat-icon>
4445
+ <span class="error-text">
4446
+ {{ tx('Embedding dimension mismatch with database (768). Adjust to 768 or redo the migration.') }}
4447
+ </span>
4448
+ </div>
4449
+ }
4330
4450
  </div>
4331
4451
  </div>
4332
-
4452
+
4333
4453
  </mat-expansion-panel>
4334
4454
  <mat-expansion-panel>
4335
4455
  <mat-expansion-panel-header>
@@ -4353,23 +4473,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
4353
4473
  <!-- Icon UX helpers for Dialog variants -->
4354
4474
  <div class="dlg-icon-helpers">
4355
4475
  <div class="dlg-icon-helpers__head">{{ tx('Icons by profile (shortcuts)') }}</div>
4356
- <div class="dlg-icon-helpers__row" *ngFor="let k of dialogVariantKeys">
4357
- <div class="dlg-icon-helpers__label">{{ k }}</div>
4358
- <mat-icon aria-hidden="true" [praxisIcon]="getVariantIcon(k) || 'mi:help_outline'" class="dlg-icon-helpers__preview"></mat-icon>
4359
- <button mat-stroked-button color="primary" type="button" (click)="pickVariantIcon(k)" [matTooltip]="tx('Pick icon for {{key}}', { key: k })">
4360
- <mat-icon>search</mat-icon>
4361
- {{ tx('Pick icon') }}
4362
- </button>
4363
- <button mat-icon-button type="button" *ngIf="getVariantIcon(k)" (click)="clearVariantIcon(k)" [matTooltip]="tx('Clear icon for {{key}}', { key: k })" [attr.aria-label]="tx('Clear icon')">
4364
- <mat-icon>backspace</mat-icon>
4365
- </button>
4366
- <span class="dlg-icon-helpers__value" *ngIf="getVariantIcon(k)">{{ getVariantIcon(k) }}</span>
4367
- </div>
4476
+ @for (k of dialogVariantKeys; track k) {
4477
+ <div class="dlg-icon-helpers__row">
4478
+ <div class="dlg-icon-helpers__label">{{ k }}</div>
4479
+ <mat-icon aria-hidden="true" [praxisIcon]="getVariantIcon(k) || 'mi:help_outline'" class="dlg-icon-helpers__preview"></mat-icon>
4480
+ <button mat-stroked-button color="primary" type="button" (click)="pickVariantIcon(k)" [matTooltip]="tx('Pick icon for {{key}}', { key: k })">
4481
+ <mat-icon>search</mat-icon>
4482
+ {{ tx('Pick icon') }}
4483
+ </button>
4484
+ @if (getVariantIcon(k)) {
4485
+ <button mat-icon-button type="button" (click)="clearVariantIcon(k)" [matTooltip]="tx('Clear icon for {{key}}', { key: k })" [attr.aria-label]="tx('Clear icon')">
4486
+ <mat-icon>backspace</mat-icon>
4487
+ </button>
4488
+ }
4489
+ @if (getVariantIcon(k)) {
4490
+ <span class="dlg-icon-helpers__value">{{ getVariantIcon(k) }}</span>
4491
+ }
4492
+ </div>
4493
+ }
4368
4494
  <div class="dlg-icon-helpers__hint">{{ tx('The text field above remains editable. These buttons only help with selection/clear preview.') }}</div>
4369
4495
  </div>
4370
4496
  </mat-expansion-panel>
4371
4497
  </mat-accordion>
4372
- `, styles: [".dlg-icon-helpers{margin:12px 16px 4px;padding:12px;border:1px dashed var(--md-sys-color-outline-variant);border-radius:var(--md-sys-shape-corner-small, 8px)}.dlg-icon-helpers__head{font-weight:600;margin-bottom:8px;opacity:.8}.dlg-icon-helpers__row{display:flex;align-items:center;gap:12px;padding:6px 0}.dlg-icon-helpers__label{text-transform:capitalize;width:96px;opacity:.8}.dlg-icon-helpers__preview{width:24px;height:24px}.dlg-icon-helpers__value{font-family:monospace;font-size:12px;opacity:.8}.dlg-icon-helpers__hint{margin-top:8px;font-size:12px;opacity:.7}.panel-icon{margin-right:12px;color:var(--md-sys-color-primary)}.ai-config-container{padding:16px;display:flex;flex-direction:column;gap:24px}.ai-config-source{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;border-radius:var(--md-sys-shape-corner-medium, 10px);border:1px dashed var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low);font-size:12px;color:var(--md-sys-color-on-surface-variant);flex-wrap:wrap}.ai-config-source__meta{display:inline-flex;align-items:center;gap:8px}.ai-action-btn--clear{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container);--mdc-outlined-button-container-color: var(--md-sys-color-error-container);--mdc-outlined-button-label-text-color: var(--md-sys-color-on-error-container);--mdc-outlined-button-outline-color: var(--md-sys-color-error);--mat-mdc-button-persistent-ripple-color: var(--md-sys-color-on-error-container);--mat-mdc-button-ripple-color: var(--md-sys-color-on-error-container)}.ai-action-btn--clear:hover{background:var(--md-sys-color-error-container)}.ai-group-title{display:flex;align-items:center;gap:8px;font-weight:500;font-size:14px;color:var(--md-sys-color-primary);margin-bottom:12px;border-bottom:1px solid var(--md-sys-color-outline-variant);padding-bottom:4px}.ai-group-title mat-icon{font-size:20px;width:20px;height:20px}.ai-group ::ng-deep .form-actions,.ai-group ::ng-deep button[type=submit]{display:none!important}.ai-credentials-row{display:flex;align-items:flex-start;gap:16px;flex-wrap:wrap}.ai-group-header{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.ai-provider-summary{display:flex;align-items:center;gap:12px;padding:8px 12px;border-radius:var(--md-sys-shape-corner-medium, 10px);background:var(--md-sys-color-surface-variant);border:1px solid var(--md-sys-color-outline-variant);margin:8px 0 4px;flex-wrap:wrap}.ai-provider-icon{width:32px;height:32px;border-radius:var(--md-sys-shape-corner-small, 8px);border:1px solid var(--md-sys-color-outline-variant);display:flex;align-items:center;justify-content:center;background:var(--md-sys-color-surface);color:var(--md-sys-color-primary);flex:0 0 auto}.provider-svg{width:18px;height:18px}.ai-provider-meta{display:flex;flex-direction:column;gap:2px;min-width:200px;flex:1 1 auto}.ai-provider-name{font-weight:600;font-size:13px}.ai-provider-desc{font-size:12px;opacity:.75}.ai-provider-key{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-lowest);color:var(--md-sys-color-on-surface-variant);font-size:11px;font-weight:600;letter-spacing:.2px;box-shadow:var(--md-sys-elevation-level1, 0 1px 2px rgba(0,0,0,.2))}.ai-provider-key mat-icon{width:16px;height:16px;font-size:16px}.ai-provider-key.is-present{background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container);border-color:var(--md-sys-color-primary)}.ai-provider-key.is-saved{background:var(--md-sys-color-secondary-container);color:var(--md-sys-color-on-secondary-container);border-color:var(--md-sys-color-secondary)}.ai-provider-key.is-missing{background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface-variant);border-style:dashed;opacity:.9}.ai-provider-key.is-unlocked{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container);border-color:var(--md-sys-color-tertiary)}.ai-action-btn{display:inline-flex;align-items:center;gap:8px;height:40px;padding:0 14px;min-width:168px;border-radius:var(--md-sys-shape-corner-medium, 12px);white-space:nowrap;background:var(--md-sys-color-secondary-container);color:var(--md-sys-color-on-secondary-container);border-color:var(--md-sys-color-outline-variant);box-shadow:var(--md-sys-elevation-level1, 0 1px 2px rgba(0,0,0,.2));transition:background-color .15s ease,box-shadow .15s ease,transform .06s ease;--mdc-outlined-button-container-color: var(--md-sys-color-secondary-container);--mdc-outlined-button-label-text-color: var(--md-sys-color-on-secondary-container);--mdc-outlined-button-outline-color: var(--md-sys-color-outline-variant);--mat-mdc-button-persistent-ripple-color: var(--md-sys-color-on-secondary-container);--mat-mdc-button-ripple-color: var(--md-sys-color-on-secondary-container)}.ai-action-btn .mat-button-wrapper,.ai-action-btn .mdc-button__label{display:inline-flex;align-items:center;gap:8px;line-height:1;height:100%}.ai-action-btn .mat-icon{width:18px;height:18px;font-size:18px;line-height:18px}.ai-action-btn .ai-action-label{display:inline-flex;align-items:center;line-height:1}.ai-action-btn .mat-progress-spinner,.ai-action-btn .btn-spinner{display:inline-flex;align-items:center;justify-content:center;vertical-align:middle}.ai-action-btn:hover{background:var(--md-sys-color-secondary-container);box-shadow:var(--md-sys-elevation-level2, 0 2px 6px rgba(0,0,0,.25))}.ai-action-btn:active{transform:translateY(1px)}.ai-action-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.ai-action-btn.is-success{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container);border-color:var(--md-sys-color-tertiary);--mdc-outlined-button-container-color: var(--md-sys-color-tertiary-container);--mdc-outlined-button-label-text-color: var(--md-sys-color-on-tertiary-container);--mdc-outlined-button-outline-color: var(--md-sys-color-tertiary);--mat-mdc-button-persistent-ripple-color: var(--md-sys-color-on-tertiary-container);--mat-mdc-button-ripple-color: var(--md-sys-color-on-tertiary-container)}.ai-action-btn.is-success:hover{background:var(--md-sys-color-tertiary-container)}.ai-action-btn:disabled{background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface-variant);border-color:var(--md-sys-color-outline-variant);box-shadow:none;opacity:.7}.ai-form-inline{flex:1;min-width:300px}.ai-credentials-row button{margin-top:4px;height:40px}.btn-spinner{margin-right:8px}.ai-feedback{margin-top:8px;display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container);border-radius:var(--md-sys-shape-corner-extra-small, 4px);font-size:13px}.ai-feedback--warn{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container)}.disabled-group{opacity:.6;pointer-events:none}.ai-model-content{display:flex;flex-direction:column;gap:8px}.ai-model-controls{display:flex;align-items:flex-start;gap:8px}.ai-model-controls ::ng-deep praxis-dynamic-form{flex:1}.ai-model-details{font-size:12px;color:var(--md-sys-color-on-surface-variant);margin-left:4px}.ai-subtext{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.ai-header-actions{display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap}.ai-placeholder{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:24px;background:var(--md-sys-color-surface-container-low);border-radius:var(--md-sys-shape-corner-small, 8px);color:var(--md-sys-color-outline);gap:8px;text-align:center;font-size:13px}.spin{animation:spin 1s linear infinite}@keyframes spin{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}\n"] }]
4498
+ `, styles: [".dlg-icon-helpers{margin:12px 16px 4px;padding:12px;border:1px dashed var(--md-sys-color-outline-variant);border-radius:var(--md-sys-shape-corner-small, 8px)}.dlg-icon-helpers__head{font-weight:600;margin-bottom:8px;opacity:.8}.dlg-icon-helpers__row{display:flex;align-items:center;gap:12px;padding:6px 0}.dlg-icon-helpers__label{text-transform:capitalize;width:96px;opacity:.8}.dlg-icon-helpers__preview{width:24px;height:24px}.dlg-icon-helpers__value{font-family:monospace;font-size:12px;opacity:.8}.dlg-icon-helpers__hint{margin-top:8px;font-size:12px;opacity:.7}.panel-icon{margin-right:12px;color:var(--md-sys-color-primary)}.ai-config-container{padding:16px;display:flex;flex-direction:column;gap:24px}.ai-config-source{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;border-radius:var(--md-sys-shape-corner-medium, 10px);border:1px dashed var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low);font-size:12px;color:var(--md-sys-color-on-surface-variant);flex-wrap:wrap}.ai-config-source__meta{display:inline-flex;align-items:center;gap:8px}.ai-action-btn--clear{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container);--mdc-outlined-button-container-color: var(--md-sys-color-error-container);--mdc-outlined-button-label-text-color: var(--md-sys-color-on-error-container);--mdc-outlined-button-outline-color: var(--md-sys-color-error);--mat-mdc-button-persistent-ripple-color: var(--md-sys-color-on-error-container);--mat-mdc-button-ripple-color: var(--md-sys-color-on-error-container)}.ai-action-btn--clear:hover{background:var(--md-sys-color-error-container)}.ai-group-title{display:flex;align-items:center;gap:8px;font-weight:500;font-size:14px;color:var(--md-sys-color-primary);margin-bottom:12px;border-bottom:1px solid var(--md-sys-color-outline-variant);padding-bottom:4px}.ai-group-title mat-icon{font-size:20px;width:20px;height:20px}.ai-group ::ng-deep .form-actions,.ai-group ::ng-deep button[type=submit]{display:none!important}.ai-credentials-row{display:flex;align-items:flex-start;gap:16px;flex-wrap:wrap}.ai-group-header{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.ai-provider-summary{display:flex;align-items:center;gap:12px;padding:8px 12px;border-radius:var(--md-sys-shape-corner-medium, 10px);background:var(--md-sys-color-surface-variant);border:1px solid var(--md-sys-color-outline-variant);margin:8px 0 4px;flex-wrap:wrap}.ai-provider-icon{width:32px;height:32px;border-radius:var(--md-sys-shape-corner-small, 8px);border:1px solid var(--md-sys-color-outline-variant);display:flex;align-items:center;justify-content:center;background:var(--md-sys-color-surface);color:var(--md-sys-color-primary);flex:0 0 auto}.provider-svg{width:18px;height:18px}.ai-provider-meta{display:flex;flex-direction:column;gap:2px;min-width:200px;flex:1 1 auto}.ai-provider-name{font-weight:600;font-size:13px}.ai-provider-desc{font-size:12px;opacity:.75}.ai-provider-key{display:inline-flex;align-items:center;gap:6px;padding:4px 10px;border-radius:999px;border:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-lowest);color:var(--md-sys-color-on-surface-variant);font-size:11px;font-weight:600;letter-spacing:.2px;box-shadow:var(--md-sys-elevation-level1, 0 1px 2px rgba(0,0,0,.2))}.ai-provider-key mat-icon{width:16px;height:16px;font-size:16px}.ai-provider-key.is-present{background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container);border-color:var(--md-sys-color-primary)}.ai-provider-key.is-saved{background:var(--md-sys-color-secondary-container);color:var(--md-sys-color-on-secondary-container);border-color:var(--md-sys-color-secondary)}.ai-provider-key.is-missing{background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface-variant);border-style:dashed;opacity:.9}.ai-provider-key.is-unlocked{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container);border-color:var(--md-sys-color-tertiary)}.ai-action-btn{display:inline-flex;align-items:center;gap:8px;height:40px;padding:0 14px;min-width:168px;border-radius:var(--md-sys-shape-corner-medium, 12px);white-space:nowrap;background:var(--md-sys-color-secondary-container);color:var(--md-sys-color-on-secondary-container);border-color:var(--md-sys-color-outline-variant);box-shadow:var(--md-sys-elevation-level1, 0 1px 2px rgba(0,0,0,.2));transition:background-color .15s ease,box-shadow .15s ease,transform .06s ease;--mdc-outlined-button-container-color: var(--md-sys-color-secondary-container);--mdc-outlined-button-label-text-color: var(--md-sys-color-on-secondary-container);--mdc-outlined-button-outline-color: var(--md-sys-color-outline-variant);--mat-mdc-button-persistent-ripple-color: var(--md-sys-color-on-secondary-container);--mat-mdc-button-ripple-color: var(--md-sys-color-on-secondary-container)}.ai-action-btn .mat-button-wrapper,.ai-action-btn .mdc-button__label{display:inline-flex;align-items:center;gap:8px;line-height:1;height:100%}.ai-action-btn .mat-icon{width:18px;height:18px;font-size:18px;line-height:18px}.ai-action-btn .ai-action-label{display:inline-flex;align-items:center;line-height:1}.ai-action-btn .mat-progress-spinner,.ai-action-btn .btn-spinner{display:inline-flex;align-items:center;justify-content:center;vertical-align:middle}.ai-action-btn:hover{background:var(--md-sys-color-secondary-container);box-shadow:var(--md-sys-elevation-level2, 0 2px 6px rgba(0,0,0,.25))}.ai-action-btn:active{transform:translateY(1px)}.ai-action-btn:focus-visible{outline:2px solid var(--md-sys-color-primary);outline-offset:2px}.ai-action-btn.is-success{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container);border-color:var(--md-sys-color-tertiary);--mdc-outlined-button-container-color: var(--md-sys-color-tertiary-container);--mdc-outlined-button-label-text-color: var(--md-sys-color-on-tertiary-container);--mdc-outlined-button-outline-color: var(--md-sys-color-tertiary);--mat-mdc-button-persistent-ripple-color: var(--md-sys-color-on-tertiary-container);--mat-mdc-button-ripple-color: var(--md-sys-color-on-tertiary-container)}.ai-action-btn.is-success:hover{background:var(--md-sys-color-tertiary-container)}.ai-action-btn:disabled{background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface-variant);border-color:var(--md-sys-color-outline-variant);box-shadow:none;opacity:.7}.ai-form-inline{flex:1;min-width:300px}.ai-credentials-row button{margin-top:4px;height:40px}.btn-spinner{margin-right:8px}.ai-feedback{margin-top:8px;display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container);border-radius:var(--md-sys-shape-corner-extra-small, 4px);font-size:13px}.ai-feedback--warn{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container)}.disabled-group{opacity:.6;pointer-events:none}.ai-model-content{display:flex;flex-direction:column;gap:8px}.ai-model-controls{display:flex;align-items:flex-start;gap:8px}.ai-model-controls ::ng-deep praxis-dynamic-form{flex:1}.ai-model-details{font-size:12px;color:var(--md-sys-color-on-surface-variant);margin-left:4px}.ai-subtext{font-size:12px;color:var(--md-sys-color-on-surface-variant)}.ai-header-actions{display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap}.ai-placeholder{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:24px;background:var(--md-sys-color-surface-container-low);border-radius:var(--md-sys-shape-corner-small, 8px);color:var(--md-sys-color-outline);gap:8px;text-align:center;font-size:13px}.spin{animation:spin 1s linear infinite}@keyframes spin{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}\n"] }]
4373
4499
  }], ctorParameters: () => [{ type: GlobalConfigAdminService }, { type: i2$1.MatSnackBar }], propDecorators: { hostCrud: [{
4374
4500
  type: ViewChild,
4375
4501
  args: ['hostCrud', { read: ViewContainerRef, static: true }]
@@ -4426,6 +4552,15 @@ const SETTINGS_PANEL_AI_CAPABILITIES = {
4426
4552
  { path: 'title', category: 'identity', valueKind: 'string', description: 'Titulo do painel.' },
4427
4553
  { path: 'titleIcon', category: 'identity', valueKind: 'string', description: 'Icone do titulo (Material icon name).' },
4428
4554
  { path: 'expanded', category: 'behavior', valueKind: 'boolean', description: 'Estado inicial expandido.' },
4555
+ { path: 'resizable', category: 'behavior', valueKind: 'boolean', description: 'Habilita resize horizontal do painel autoral.' },
4556
+ { path: 'persistSizeKey', category: 'behavior', valueKind: 'string', description: 'Chave estavel para persistencia de largura.' },
4557
+ { path: 'minWidth', category: 'layout', valueKind: 'string', description: 'Largura minima do painel.' },
4558
+ { path: 'maxWidth', category: 'layout', valueKind: 'string', description: 'Largura maxima do painel.' },
4559
+ { path: 'diagnostics', category: 'behavior', valueKind: 'object', description: 'Configuracao opcional de visibilidade dos diagnosticos do shell.' },
4560
+ { path: 'diagnostics.showStatusMessage', category: 'behavior', valueKind: 'boolean', description: 'Controla a faixa de status do painel.' },
4561
+ { path: 'diagnostics.showDisabledReason', category: 'behavior', valueKind: 'boolean', description: 'Controla tooltips com motivo de acoes desabilitadas.' },
4562
+ { path: 'diagnostics.showBusyState', category: 'behavior', valueKind: 'boolean', description: 'Controla indicadores visuais de busy no shell.' },
4563
+ { path: 'diagnostics.exposeValidationState', category: 'behavior', valueKind: 'boolean', description: 'Controla a exposicao visual de invalidade do provider.' },
4429
4564
  { path: 'content', category: 'content', valueKind: 'object', description: 'Conteudo do painel.' },
4430
4565
  {
4431
4566
  path: 'content.component',
@@ -4444,8 +4579,314 @@ const SETTINGS_PANEL_AI_CAPABILITIES = {
4444
4579
  ],
4445
4580
  };
4446
4581
 
4582
+ const panelSizeSchema = {
4583
+ type: 'object',
4584
+ minProperties: 1,
4585
+ properties: {
4586
+ width: { oneOf: [{ type: 'string' }, { type: 'number' }] },
4587
+ minWidth: { type: 'string' },
4588
+ maxWidth: { type: 'string' },
4589
+ resizable: { type: 'boolean' },
4590
+ persistSizeKey: { type: 'string' },
4591
+ expanded: { type: 'boolean' },
4592
+ },
4593
+ };
4594
+ const panelShellSchema = {
4595
+ type: 'object',
4596
+ minProperties: 1,
4597
+ properties: {
4598
+ id: { type: 'string' },
4599
+ title: { type: 'string' },
4600
+ titleIcon: { type: 'string' },
4601
+ },
4602
+ };
4603
+ const editorHostSchema = {
4604
+ type: 'object',
4605
+ required: ['componentId'],
4606
+ properties: {
4607
+ componentId: { type: 'string' },
4608
+ inputs: { type: 'object' },
4609
+ configManifestComponentId: { type: 'string' },
4610
+ editorContract: {
4611
+ enum: ['SettingsValueProvider'],
4612
+ },
4613
+ },
4614
+ };
4615
+ const behaviorSchema = {
4616
+ type: 'object',
4617
+ minProperties: 1,
4618
+ properties: {
4619
+ requireDirty: { type: 'boolean' },
4620
+ requireValid: { type: 'boolean' },
4621
+ blockWhileBusy: { type: 'boolean' },
4622
+ useProviderHook: { type: 'boolean' },
4623
+ closeAfterSave: { type: 'boolean' },
4624
+ },
4625
+ };
4626
+ const PRAXIS_SETTINGS_PANEL_AUTHORING_MANIFEST = {
4627
+ schemaVersion: '1.0.0',
4628
+ componentId: 'praxis-settings-panel',
4629
+ ownerPackage: '@praxisui/settings-panel',
4630
+ configSchemaId: 'SettingsPanelConfig',
4631
+ manifestVersion: '1.0.0',
4632
+ runtimeInputs: [
4633
+ { name: 'config', type: 'SettingsPanelConfig', description: 'Canonical settings panel shell config passed to SettingsPanelService.open.' },
4634
+ { name: 'config.diagnostics', type: 'SettingsPanelDiagnosticsConfig', description: 'Optional shell diagnostics visibility config for status, disabled reason, busy state and validation state.' },
4635
+ { name: 'content.component', type: 'Type<SettingsValueProvider>', description: 'Standalone editor component hosted inside the authoring shell.' },
4636
+ { name: 'content.inputs', type: 'Record<string, unknown>', description: 'Initial editor inputs owned by the consumer component manifest.' },
4637
+ { name: 'ref', type: 'SettingsPanelRef', description: 'Panel ref that emits applied, saved, reset, closed and sizeChanged events.' },
4638
+ { name: 'provider', type: 'SettingsValueProvider', description: 'Editor protocol for dirty, valid, busy, getSettingsValue, onSave, reset and close hooks.' },
4639
+ ],
4640
+ editableTargets: [
4641
+ { kind: 'panelShell', resolver: 'settings-panel-config-root', description: 'Top-level shell identity and title fields owned by SettingsPanelConfig.' },
4642
+ { kind: 'openMode', resolver: 'settings-panel-open-mode', description: 'Initial expanded state, replacement mediation and close behavior for an authoring panel.' },
4643
+ { kind: 'panelSize', resolver: 'settings-panel-size-and-resize', description: 'Width, min/max width, resize enablement and persisted size key.' },
4644
+ { kind: 'applyBehavior', resolver: 'settings-value-provider-apply-contract', description: 'Apply availability and payload emission through getSettingsValue().' },
4645
+ { kind: 'saveBehavior', resolver: 'settings-value-provider-save-contract', description: 'Save availability, onSave fallback and close-after-save behavior.' },
4646
+ { kind: 'resetBehavior', resolver: 'settings-value-provider-reset-contract', description: 'Reset confirmation, provider reset hook and reset event emission.' },
4647
+ { kind: 'editorHost', resolver: 'settings-panel-editor-host', description: 'Hosted editor component and serializable input envelope.' },
4648
+ { kind: 'diagnostics', resolver: 'settings-panel-state-diagnostics', description: 'Dirty, valid, busy, disabled reason and status message visibility.' },
4649
+ ],
4650
+ operations: [
4651
+ {
4652
+ operationId: 'panel.shell.configure',
4653
+ title: 'Configure panel shell identity',
4654
+ scope: 'global',
4655
+ targetKind: 'panelShell',
4656
+ target: { kind: 'panelShell', resolver: 'settings-panel-config-root', ambiguityPolicy: 'fail', required: true },
4657
+ inputSchema: panelShellSchema,
4658
+ effects: [{ kind: 'merge-object', path: 'config' }],
4659
+ validators: ['panel-id-stable', 'title-i18n-compatible', 'settings-panel-round-trip'],
4660
+ affectedPaths: ['config.id', 'config.title', 'config.titleIcon'],
4661
+ submissionImpact: 'config-only',
4662
+ destructive: false,
4663
+ requiresConfirmation: false,
4664
+ preconditions: ['config-initialized'],
4665
+ },
4666
+ {
4667
+ operationId: 'panel.openMode.set',
4668
+ title: 'Set panel open mode',
4669
+ scope: 'global',
4670
+ targetKind: 'openMode',
4671
+ target: { kind: 'openMode', resolver: 'settings-panel-open-mode', ambiguityPolicy: 'fail', required: false },
4672
+ inputSchema: {
4673
+ type: 'object',
4674
+ minProperties: 1,
4675
+ properties: {
4676
+ expanded: { type: 'boolean' },
4677
+ replacementPolicy: { enum: ['consult-current-editor', 'reject-when-dirty', 'force-close-disallowed'] },
4678
+ },
4679
+ },
4680
+ effects: [{ kind: 'merge-object', path: 'config' }],
4681
+ validators: ['replacement-mediates-before-close', 'settings-panel-round-trip'],
4682
+ affectedPaths: ['config.expanded'],
4683
+ submissionImpact: 'config-only',
4684
+ destructive: false,
4685
+ requiresConfirmation: false,
4686
+ preconditions: ['config-initialized'],
4687
+ },
4688
+ {
4689
+ operationId: 'panel.size.set',
4690
+ title: 'Set panel size and resize policy',
4691
+ scope: 'layout',
4692
+ targetKind: 'panelSize',
4693
+ target: { kind: 'panelSize', resolver: 'settings-panel-size-and-resize', ambiguityPolicy: 'fail', required: false },
4694
+ inputSchema: panelSizeSchema,
4695
+ effects: [{ kind: 'compile-domain-patch', handler: 'settings-panel-size-set', handlerContract: {
4696
+ reads: ['SettingsPanelConfig', 'SettingsPanelRef.sizeChanged$', 'persistSizeKey'],
4697
+ writes: ['config.minWidth', 'config.maxWidth', 'config.resizable', 'config.persistSizeKey', 'runtime.size'],
4698
+ identityKeys: ['config.id', 'persistSizeKey'],
4699
+ inputSchema: panelSizeSchema,
4700
+ failureModes: ['invalid-css-size', 'min-width-greater-than-max-width', 'persist-size-key-missing-for-persistence'],
4701
+ description: 'Applies shell size configuration and open-panel width changes through SettingsPanelRef.updateSize without changing hosted editor config semantics.',
4702
+ } }],
4703
+ validators: ['panel-size-safe', 'panel-min-max-consistent', 'resize-persistence-explicit', 'settings-panel-round-trip'],
4704
+ affectedPaths: ['config.minWidth', 'config.maxWidth', 'config.resizable', 'config.persistSizeKey', 'runtime.width'],
4705
+ submissionImpact: 'visual-only',
4706
+ destructive: false,
4707
+ requiresConfirmation: false,
4708
+ preconditions: ['config-initialized'],
4709
+ },
4710
+ {
4711
+ operationId: 'panel.applyBehavior.set',
4712
+ title: 'Set apply behavior',
4713
+ scope: 'interaction',
4714
+ targetKind: 'applyBehavior',
4715
+ target: { kind: 'applyBehavior', resolver: 'settings-value-provider-apply-contract', ambiguityPolicy: 'fail', required: false },
4716
+ inputSchema: behaviorSchema,
4717
+ effects: [{ kind: 'compile-domain-patch', handler: 'settings-panel-apply-behavior-set', handlerContract: {
4718
+ reads: ['SettingsValueProvider.isDirty$', 'SettingsValueProvider.isValid$', 'SettingsValueProvider.isBusy$', 'SettingsValueProvider.getSettingsValue'],
4719
+ writes: ['SettingsPanelRef.applied$'],
4720
+ identityKeys: ['config.id', 'provider.getSettingsValue'],
4721
+ inputSchema: behaviorSchema,
4722
+ failureModes: ['provider-missing-get-settings-value', 'apply-while-invalid', 'apply-while-busy', 'apply-without-dirty-state'],
4723
+ description: 'Keeps Apply gated by dirty, valid and busy state and emits the provider value through SettingsPanelRef.applied$.',
4724
+ } }],
4725
+ validators: ['apply-requires-provider-value', 'dirty-valid-busy-gates-preserved', 'apply-does-not-close-panel', 'settings-panel-round-trip'],
4726
+ affectedPaths: ['provider.isDirty$', 'provider.isValid$', 'provider.isBusy$', 'ref.applied$'],
4727
+ submissionImpact: 'none',
4728
+ destructive: false,
4729
+ requiresConfirmation: false,
4730
+ preconditions: ['settings-value-provider-attached'],
4731
+ },
4732
+ {
4733
+ operationId: 'panel.saveBehavior.set',
4734
+ title: 'Set save behavior',
4735
+ scope: 'interaction',
4736
+ targetKind: 'saveBehavior',
4737
+ target: { kind: 'saveBehavior', resolver: 'settings-value-provider-save-contract', ambiguityPolicy: 'fail', required: false },
4738
+ inputSchema: behaviorSchema,
4739
+ effects: [{ kind: 'compile-domain-patch', handler: 'settings-panel-save-behavior-set', handlerContract: {
4740
+ reads: ['SettingsValueProvider.onSave', 'SettingsValueProvider.getSettingsValue', 'SettingsValueProvider.isDirty$', 'SettingsValueProvider.isValid$', 'SettingsValueProvider.isBusy$'],
4741
+ writes: ['SettingsPanelRef.saved$', 'SettingsPanelRef.closed$'],
4742
+ identityKeys: ['config.id', 'provider.onSave'],
4743
+ inputSchema: behaviorSchema,
4744
+ failureModes: ['save-while-invalid', 'save-while-busy', 'save-result-undefined', 'provider-save-rejected'],
4745
+ description: 'Keeps Save gated by dirty, valid and busy state, prefers onSave when present, falls back to getSettingsValue and closes with reason save.',
4746
+ } }],
4747
+ validators: ['save-prefers-provider-hook', 'save-fallback-value-preserved', 'save-closes-with-save-reason', 'dirty-valid-busy-gates-preserved'],
4748
+ affectedPaths: ['provider.onSave', 'provider.getSettingsValue', 'ref.saved$', 'ref.closed$'],
4749
+ submissionImpact: 'config-only',
4750
+ destructive: false,
4751
+ requiresConfirmation: false,
4752
+ preconditions: ['settings-value-provider-attached'],
4753
+ },
4754
+ {
4755
+ operationId: 'panel.resetBehavior.set',
4756
+ title: 'Set reset behavior',
4757
+ scope: 'interaction',
4758
+ targetKind: 'resetBehavior',
4759
+ target: { kind: 'resetBehavior', resolver: 'settings-value-provider-reset-contract', ambiguityPolicy: 'fail', required: false },
4760
+ inputSchema: {
4761
+ type: 'object',
4762
+ minProperties: 1,
4763
+ properties: {
4764
+ requireConfirmation: { type: 'boolean' },
4765
+ callProviderReset: { type: 'boolean' },
4766
+ emitResetEvent: { type: 'boolean' },
4767
+ clearSavedStatus: { type: 'boolean' },
4768
+ },
4769
+ },
4770
+ effects: [{ kind: 'compile-domain-patch', handler: 'settings-panel-reset-behavior-set', handlerContract: {
4771
+ reads: ['SettingsValueProvider.reset', 'SettingsPanelRef.reset$', 'ConfirmDialogComponent'],
4772
+ writes: ['provider.reset()', 'SettingsPanelRef.reset$', 'panel.lastSavedAt'],
4773
+ identityKeys: ['config.id', 'provider.reset'],
4774
+ inputSchema: { type: 'object', properties: { requireConfirmation: { type: 'boolean' }, callProviderReset: { type: 'boolean' }, emitResetEvent: { type: 'boolean' } } },
4775
+ failureModes: ['reset-confirmation-missing', 'provider-reset-failed', 'reset-event-not-emitted'],
4776
+ description: 'Preserves Reset as a confirmed shell action that calls the provider reset hook and emits SettingsPanelRef.reset$.',
4777
+ } }],
4778
+ destructive: true,
4779
+ requiresConfirmation: true,
4780
+ validators: ['reset-requires-confirmation', 'reset-calls-provider-reset', 'reset-event-emitted', 'settings-panel-round-trip'],
4781
+ affectedPaths: ['provider.reset', 'ref.reset$', 'panel.lastSavedAt'],
4782
+ submissionImpact: 'config-only',
4783
+ preconditions: ['settings-value-provider-attached', 'confirmation-collected'],
4784
+ },
4785
+ {
4786
+ operationId: 'editor.host.configure',
4787
+ title: 'Configure hosted editor',
4788
+ scope: 'templating',
4789
+ targetKind: 'editorHost',
4790
+ target: { kind: 'editorHost', resolver: 'settings-panel-editor-host', ambiguityPolicy: 'fail', required: true },
4791
+ inputSchema: editorHostSchema,
4792
+ effects: [{ kind: 'compile-domain-patch', handler: 'settings-panel-editor-host-configure', handlerContract: {
4793
+ reads: ['ComponentMetadataRegistry', 'SettingsPanelConfig.content', 'SettingsValueProvider', 'component.authoringManifest'],
4794
+ writes: ['config.content.component', 'config.content.inputs', 'delegatedConsumerPatch'],
4795
+ identityKeys: ['componentId', 'configManifestComponentId'],
4796
+ inputSchema: editorHostSchema,
4797
+ failureModes: ['editor-component-not-registered', 'settings-value-provider-missing', 'consumer-manifest-required', 'consumer-config-boundary-violation'],
4798
+ description: 'Configures the hosted editor component and input envelope while delegating consumer-specific config edits to the owning component manifest.',
4799
+ } }],
4800
+ validators: ['editor-component-registered', 'settings-value-provider-contract-present', 'consumer-config-delegated', 'editor-inputs-serializable'],
4801
+ affectedPaths: ['config.content.component', 'config.content.inputs', 'delegatedConsumerPatch'],
4802
+ submissionImpact: 'config-only',
4803
+ destructive: false,
4804
+ requiresConfirmation: false,
4805
+ preconditions: ['component-metadata-resolved'],
4806
+ },
4807
+ {
4808
+ operationId: 'diagnostics.visibility.set',
4809
+ title: 'Set panel diagnostics visibility',
4810
+ scope: 'interaction',
4811
+ targetKind: 'diagnostics',
4812
+ target: { kind: 'diagnostics', resolver: 'settings-panel-state-diagnostics', ambiguityPolicy: 'fail', required: false },
4813
+ inputSchema: {
4814
+ type: 'object',
4815
+ minProperties: 1,
4816
+ properties: {
4817
+ showStatusMessage: { type: 'boolean' },
4818
+ showDisabledReason: { type: 'boolean' },
4819
+ showBusyState: { type: 'boolean' },
4820
+ exposeValidationState: { type: 'boolean' },
4821
+ },
4822
+ },
4823
+ effects: [{ kind: 'merge-object', path: 'config.diagnostics' }],
4824
+ validators: ['diagnostics-follow-provider-state', 'diagnostics-i18n-compatible', 'busy-valid-dirty-visible-when-needed', 'settings-panel-round-trip'],
4825
+ affectedPaths: [
4826
+ 'config.diagnostics.showStatusMessage',
4827
+ 'config.diagnostics.showDisabledReason',
4828
+ 'config.diagnostics.showBusyState',
4829
+ 'config.diagnostics.exposeValidationState',
4830
+ 'panel.statusMessage',
4831
+ 'panel.disabledReason',
4832
+ 'panel.isDirty',
4833
+ 'panel.isValid',
4834
+ 'panel.isBusy',
4835
+ ],
4836
+ submissionImpact: 'visual-only',
4837
+ destructive: false,
4838
+ requiresConfirmation: false,
4839
+ preconditions: ['settings-value-provider-attached'],
4840
+ },
4841
+ ],
4842
+ validators: [
4843
+ { validatorId: 'panel-id-stable', level: 'error', code: 'SETTINGS_PANEL_ID_STABLE', description: 'Panel id must remain stable for replacement, persistence and diagnostics.' },
4844
+ { validatorId: 'replacement-mediates-before-close', level: 'error', code: 'SETTINGS_PANEL_REPLACEMENT_MEDIATES_CLOSE', description: 'Opening a replacement panel must consult the current editor onBeforeClose before closing.' },
4845
+ { validatorId: 'title-i18n-compatible', level: 'warning', code: 'SETTINGS_PANEL_TITLE_I18N_COMPATIBLE', description: 'Authoring chrome text must remain compatible with the settings panel i18n namespace.' },
4846
+ { validatorId: 'panel-size-safe', level: 'error', code: 'SETTINGS_PANEL_SIZE_SAFE', description: 'Panel sizes must be finite numbers or safe CSS size strings.' },
4847
+ { validatorId: 'panel-min-max-consistent', level: 'error', code: 'SETTINGS_PANEL_MIN_MAX_CONSISTENT', description: 'Minimum width must not exceed maximum width.' },
4848
+ { validatorId: 'resize-persistence-explicit', level: 'error', code: 'SETTINGS_PANEL_RESIZE_PERSISTENCE_EXPLICIT', description: 'Persisted resize must use a stable persistSizeKey or be explicitly transient.' },
4849
+ { validatorId: 'apply-requires-provider-value', level: 'error', code: 'SETTINGS_PANEL_APPLY_REQUIRES_PROVIDER_VALUE', description: 'Apply must obtain payloads from SettingsValueProvider.getSettingsValue().' },
4850
+ { validatorId: 'apply-does-not-close-panel', level: 'error', code: 'SETTINGS_PANEL_APPLY_DOES_NOT_CLOSE', description: 'Apply emits preview payloads without closing the panel.' },
4851
+ { validatorId: 'dirty-valid-busy-gates-preserved', level: 'error', code: 'SETTINGS_PANEL_STATE_GATES_PRESERVED', description: 'Apply and Save remain gated by dirty, valid and busy state.' },
4852
+ { validatorId: 'save-prefers-provider-hook', level: 'error', code: 'SETTINGS_PANEL_SAVE_PREFERS_PROVIDER_HOOK', description: 'Save must call onSave when provided before falling back to getSettingsValue().' },
4853
+ { validatorId: 'save-fallback-value-preserved', level: 'error', code: 'SETTINGS_PANEL_SAVE_FALLBACK_PRESERVED', description: 'Save fallback must preserve getSettingsValue payload semantics.' },
4854
+ { validatorId: 'save-closes-with-save-reason', level: 'error', code: 'SETTINGS_PANEL_SAVE_CLOSE_REASON', description: 'Successful save must emit saved$ and close with reason save.' },
4855
+ { validatorId: 'reset-requires-confirmation', level: 'error', code: 'SETTINGS_PANEL_RESET_REQUIRES_CONFIRMATION', description: 'Reset is destructive and must remain confirmed before provider reset is called.' },
4856
+ { validatorId: 'reset-calls-provider-reset', level: 'error', code: 'SETTINGS_PANEL_RESET_CALLS_PROVIDER', description: 'Reset must call the hosted provider reset hook when present.' },
4857
+ { validatorId: 'reset-event-emitted', level: 'error', code: 'SETTINGS_PANEL_RESET_EVENT_EMITTED', description: 'Reset must emit SettingsPanelRef.reset$ so hosts can coordinate state.' },
4858
+ { validatorId: 'editor-component-registered', level: 'error', code: 'SETTINGS_PANEL_EDITOR_REGISTERED', description: 'Hosted editor component must resolve from governed component metadata or host registration.' },
4859
+ { validatorId: 'settings-value-provider-contract-present', level: 'error', code: 'SETTINGS_PANEL_PROVIDER_CONTRACT_PRESENT', description: 'Hosted authoring editors must implement SettingsValueProvider state and value contract.' },
4860
+ { validatorId: 'consumer-config-delegated', level: 'error', code: 'SETTINGS_PANEL_CONSUMER_CONFIG_DELEGATED', description: 'Consumer-specific config operations must delegate to the owning component manifest.' },
4861
+ { validatorId: 'editor-inputs-serializable', level: 'error', code: 'SETTINGS_PANEL_EDITOR_INPUTS_SERIALIZABLE', description: 'Persisted editor host inputs must be serializable safe values.' },
4862
+ { validatorId: 'diagnostics-follow-provider-state', level: 'error', code: 'SETTINGS_PANEL_DIAGNOSTICS_PROVIDER_STATE', description: 'Diagnostics must reflect provider dirty, valid and busy state.' },
4863
+ { validatorId: 'diagnostics-i18n-compatible', level: 'warning', code: 'SETTINGS_PANEL_DIAGNOSTICS_I18N_COMPATIBLE', description: 'Diagnostics copy must use the settings panel i18n namespace.' },
4864
+ { validatorId: 'busy-valid-dirty-visible-when-needed', level: 'error', code: 'SETTINGS_PANEL_STATE_VISIBLE', description: 'Busy, invalid and dirty states must remain visible or explain disabled actions.' },
4865
+ { validatorId: 'settings-panel-round-trip', level: 'error', code: 'SETTINGS_PANEL_ROUND_TRIP', description: 'Open, edit, apply, save, reset and reopen must preserve the shell protocol without mutating consumer config semantics.' },
4866
+ ],
4867
+ roundTripRequirements: [
4868
+ 'SettingsPanelConfig owns only shell fields: identity, title, size, resize persistence and hosted editor envelope.',
4869
+ 'SettingsValueProvider owns apply/save/reset payload extraction, while consumer config semantics stay delegated to the owning component manifest.',
4870
+ 'Apply must emit applied$ without closing; Save must emit saved$ and close with reason save; Reset must be confirmed and emit reset$.',
4871
+ 'Busy, dirty and valid state must gate Apply and Save consistently and remain visible through diagnostics.',
4872
+ 'Opening a replacement panel must pass through the current editor onBeforeClose and dirty-discard mediation.',
4873
+ ],
4874
+ examples: [
4875
+ { id: 'open-component-editor', request: 'Open the table settings editor in the settings panel.', operationId: 'editor.host.configure', params: { componentId: 'praxis-table', inputs: { tableId: 'orders' }, configManifestComponentId: 'praxis-table', editorContract: 'SettingsValueProvider' }, isPositive: true },
4876
+ { id: 'start-expanded', request: 'Open this settings panel expanded.', operationId: 'panel.openMode.set', params: { expanded: true }, isPositive: true },
4877
+ { id: 'set-resizable-width', request: 'Make the settings drawer resizable between 420px and 960px.', operationId: 'panel.size.set', params: { resizable: true, minWidth: '420px', maxWidth: '960px', persistSizeKey: 'settings-panel:orders' }, isPositive: true },
4878
+ { id: 'apply-preview', request: 'Apply the editor change without saving or closing.', operationId: 'panel.applyBehavior.set', params: { requireDirty: true, requireValid: true, blockWhileBusy: true }, isPositive: true },
4879
+ { id: 'save-and-close', request: 'Save this editor value and close the panel.', operationId: 'panel.saveBehavior.set', params: { requireDirty: true, requireValid: true, blockWhileBusy: true, useProviderHook: true, closeAfterSave: true }, isPositive: true },
4880
+ { id: 'reset-to-original', request: 'Reset this editor to its original value.', operationId: 'panel.resetBehavior.set', params: { requireConfirmation: true, callProviderReset: true, emitResetEvent: true }, isPositive: true },
4881
+ { id: 'show-busy-diagnostics', request: 'Show why Apply and Save are disabled while the editor is busy.', operationId: 'diagnostics.visibility.set', params: { showStatusMessage: true, showDisabledReason: true, showBusyState: true }, isPositive: true },
4882
+ { id: 'reject-table-column-direct-edit', request: 'Use settings-panel to rename a table column directly.', operationId: 'editor.host.configure', params: { componentId: 'praxis-settings-panel', inputs: { columns: [{ field: 'name', label: 'Name' }] } }, isPositive: false },
4883
+ { id: 'reject-missing-provider', request: 'Host a component that has no dirty, valid, busy or getSettingsValue contract.', operationId: 'editor.host.configure', params: { componentId: 'plain-info-card' }, isPositive: false },
4884
+ { id: 'reject-reset-without-confirmation', request: 'Reset the editor immediately without confirmation.', operationId: 'panel.resetBehavior.set', params: { requireConfirmation: false, callProviderReset: true }, isPositive: false },
4885
+ ],
4886
+ };
4887
+
4447
4888
  /**
4448
4889
  * Generated bundle index. Do not edit.
4449
4890
  */
4450
4891
 
4451
- export { BASE_SIDE_PANEL_DATA, BASE_SIDE_PANEL_REF, BaseSidePanelComponent, BaseSidePanelOverlayRef, BaseSidePanelService, DeferredSettingsPanelRef, GLOBAL_CONFIG_DYNAMIC_FORM_COMPONENT, GlobalConfigAdminService, GlobalConfigEditorComponent, SETTINGS_PANEL_AI_CAPABILITIES, SETTINGS_PANEL_DATA, SETTINGS_PANEL_REF, SIDE_PANEL_THEME_CSS_VARS, SettingsPanelComponent, SettingsPanelRef, SettingsPanelService, SurfaceDrawerComponent, buildGlobalConfigFormConfig, openGlobalConfigEditor, providePraxisSettingsPanelBridge, providePraxisSurfaceDrawerBridge };
4892
+ export { BASE_SIDE_PANEL_DATA, BASE_SIDE_PANEL_REF, BaseSidePanelComponent, BaseSidePanelOverlayRef, BaseSidePanelService, DeferredSettingsPanelRef, GLOBAL_CONFIG_DYNAMIC_FORM_COMPONENT, GlobalConfigAdminService, GlobalConfigEditorComponent, PRAXIS_SETTINGS_PANEL_AUTHORING_MANIFEST, SETTINGS_PANEL_AI_CAPABILITIES, SETTINGS_PANEL_DATA, SETTINGS_PANEL_REF, SIDE_PANEL_THEME_CSS_VARS, SettingsPanelComponent, SettingsPanelRef, SettingsPanelService, SurfaceDrawerComponent, buildGlobalConfigFormConfig, openGlobalConfigEditor, providePraxisSettingsPanelBridge, providePraxisSurfaceDrawerBridge };