@praxisui/settings-panel 3.0.0-beta.8 → 4.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { inject, DestroyRef, ViewContainerRef, HostListener, ViewChild, ChangeDetectionStrategy, Component, InjectionToken, Injector, Injectable, ChangeDetectorRef } from '@angular/core';
3
- import { ComponentPortal } from '@angular/cdk/portal';
4
- import * as i3 from '@angular/common';
5
- import { CommonModule } from '@angular/common';
3
+ import * as i2 from '@angular/common';
4
+ import { CommonModule, NgStyle, NgClass } from '@angular/common';
6
5
  import { CdkTrapFocus } from '@angular/cdk/a11y';
7
- import * as i3$1 from '@angular/material/button';
6
+ import * as i3 from '@angular/material/button';
8
7
  import { MatButtonModule } from '@angular/material/button';
9
8
  import * as i1 from '@angular/material/dialog';
10
9
  import { MatDialogModule } from '@angular/material/dialog';
@@ -17,10 +16,12 @@ import { MatTooltipModule } from '@angular/material/tooltip';
17
16
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
18
17
  import { filter, switchMap, debounceTime, distinctUntilChanged } from 'rxjs/operators';
19
18
  import { isObservable, firstValueFrom, of, Subject, BehaviorSubject } from 'rxjs';
20
- import { providePraxisI18n, PraxisI18nService, PraxisIconDirective, SETTINGS_PANEL_DATA as SETTINGS_PANEL_DATA$1, SETTINGS_PANEL_BRIDGE, GlobalConfigService, FieldControlType, IconPickerService, LoggerService } from '@praxisui/core';
19
+ import * as i1$1 from '@praxisui/core';
20
+ import { providePraxisI18n, PraxisI18nService, PraxisIconDirective, SETTINGS_PANEL_DATA as SETTINGS_PANEL_DATA$1, PraxisLayerScaleStyleService, SETTINGS_PANEL_BRIDGE, SURFACE_DRAWER_BRIDGE, GlobalConfigService, FieldControlType, IconPickerService, LoggerService } from '@praxisui/core';
21
21
  import { ConfirmDialogComponent } from '@praxisui/dynamic-fields';
22
- import * as i1$1 from '@angular/cdk/overlay';
23
- import * as i2 from '@angular/material/snack-bar';
22
+ import { ComponentPortal } from '@angular/cdk/portal';
23
+ import * as i1$2 from '@angular/cdk/overlay';
24
+ import * as i2$1 from '@angular/material/snack-bar';
24
25
  import { MatSnackBarModule } from '@angular/material/snack-bar';
25
26
  import * as i4$1 from '@angular/material/expansion';
26
27
  import { MatExpansionModule } from '@angular/material/expansion';
@@ -484,6 +485,11 @@ class SettingsPanelComponent {
484
485
  dialog;
485
486
  title = '';
486
487
  titleIcon;
488
+ width = '720px';
489
+ minWidth;
490
+ maxWidth;
491
+ resizable = false;
492
+ persistSizeKey;
487
493
  expanded = false;
488
494
  ref;
489
495
  contentRef;
@@ -493,6 +499,10 @@ class SettingsPanelComponent {
493
499
  isValid = true;
494
500
  isBusy = false;
495
501
  lastSavedAt = null;
502
+ activePointerId = null;
503
+ dragStartX = 0;
504
+ dragStartWidth = 0;
505
+ collapsedWidthBeforeExpand = null;
496
506
  destroyRef = inject(DestroyRef);
497
507
  i18n = inject(PraxisI18nService);
498
508
  contentHost;
@@ -503,6 +513,19 @@ class SettingsPanelComponent {
503
513
  get canApply() {
504
514
  return this.isDirty && this.isValid && !this.isBusy;
505
515
  }
516
+ get panelInlineStyles() {
517
+ return {
518
+ width: this.width ?? null,
519
+ minWidth: this.minWidth ?? null,
520
+ maxWidth: this.maxWidth ?? null,
521
+ };
522
+ }
523
+ get showResizeHandle() {
524
+ return this.resizable;
525
+ }
526
+ get resizeHandleLabel() {
527
+ return this.tx('Resize panel');
528
+ }
506
529
  get canSave() {
507
530
  return this.isDirty && this.isValid && !this.isBusy;
508
531
  }
@@ -692,9 +715,73 @@ class SettingsPanelComponent {
692
715
  }
693
716
  }
694
717
  toggleExpand() {
695
- this.expanded = !this.expanded;
718
+ if (!this.expanded) {
719
+ this.collapsedWidthBeforeExpand = this.width;
720
+ const expandedWidth = this.computeExpandedWidth();
721
+ this.width = expandedWidth;
722
+ this.ref.updateSize(expandedWidth);
723
+ this.expanded = true;
724
+ }
725
+ else {
726
+ const restoredWidth = this.collapsedWidthBeforeExpand ?? this.width;
727
+ this.width = restoredWidth;
728
+ this.ref.updateSize(restoredWidth);
729
+ this.expanded = false;
730
+ }
696
731
  this.cdr.markForCheck();
697
732
  }
733
+ onResizeHandlePointerDown(event) {
734
+ if (!this.showResizeHandle || event.button !== 0) {
735
+ return;
736
+ }
737
+ const currentWidth = this.parseWidthPx(this.width);
738
+ if (currentWidth === null) {
739
+ return;
740
+ }
741
+ this.activePointerId = event.pointerId;
742
+ this.dragStartX = event.clientX;
743
+ this.dragStartWidth = currentWidth;
744
+ const target = event.currentTarget;
745
+ try {
746
+ target?.setPointerCapture(event.pointerId);
747
+ }
748
+ catch { }
749
+ event.preventDefault();
750
+ }
751
+ onResizeHandleKeydown(event) {
752
+ if (!this.showResizeHandle) {
753
+ return;
754
+ }
755
+ const step = event.shiftKey ? 48 : 24;
756
+ switch (event.key) {
757
+ case 'ArrowLeft':
758
+ this.applyWidthPx(this.getCurrentWidthPx() + step);
759
+ event.preventDefault();
760
+ return;
761
+ case 'ArrowRight':
762
+ this.applyWidthPx(this.getCurrentWidthPx() - step);
763
+ event.preventDefault();
764
+ return;
765
+ case 'Home': {
766
+ const min = this.parseWidthPx(this.minWidth);
767
+ if (min !== null) {
768
+ this.applyWidthPx(min);
769
+ event.preventDefault();
770
+ }
771
+ return;
772
+ }
773
+ case 'End': {
774
+ const max = this.parseWidthPx(this.maxWidth);
775
+ if (max !== null) {
776
+ this.applyWidthPx(max);
777
+ event.preventDefault();
778
+ }
779
+ return;
780
+ }
781
+ default:
782
+ return;
783
+ }
784
+ }
698
785
  onCancel() {
699
786
  of(this.isDirty)
700
787
  .pipe(switchMap((dirty) => {
@@ -732,13 +819,58 @@ class SettingsPanelComponent {
732
819
  this.onSave();
733
820
  }
734
821
  }
822
+ onDocumentPointerMove(event) {
823
+ if (event.pointerId !== this.activePointerId) {
824
+ return;
825
+ }
826
+ const deltaX = this.dragStartX - event.clientX;
827
+ this.applyWidthPx(this.dragStartWidth + deltaX);
828
+ }
829
+ onDocumentPointerEnd(event) {
830
+ if (event.pointerId !== this.activePointerId) {
831
+ return;
832
+ }
833
+ this.activePointerId = null;
834
+ }
835
+ getCurrentWidthPx() {
836
+ return this.parseWidthPx(this.width) ?? 720;
837
+ }
838
+ applyWidthPx(value) {
839
+ const width = `${this.clampWidthPx(value)}px`;
840
+ this.width = width;
841
+ this.ref.updateSize(width);
842
+ this.cdr.markForCheck();
843
+ }
844
+ clampWidthPx(value) {
845
+ const viewportMax = typeof window !== 'undefined' ? window.innerWidth : value;
846
+ const min = this.parseWidthPx(this.minWidth) ?? 320;
847
+ const max = Math.min(this.parseWidthPx(this.maxWidth) ?? viewportMax, viewportMax);
848
+ return Math.min(Math.max(Math.round(value), min), Math.max(min, max));
849
+ }
850
+ computeExpandedWidth() {
851
+ const viewportWidth = typeof window !== 'undefined' ? window.innerWidth : this.getCurrentWidthPx();
852
+ const desired = viewportWidth * 0.95;
853
+ return `${this.clampWidthPx(desired)}px`;
854
+ }
855
+ parseWidthPx(value) {
856
+ if (!value) {
857
+ return null;
858
+ }
859
+ const normalized = value.trim().toLowerCase();
860
+ if (!normalized.endsWith('px')) {
861
+ return null;
862
+ }
863
+ const parsed = Number.parseFloat(normalized.slice(0, -2));
864
+ return Number.isFinite(parsed) ? parsed : null;
865
+ }
735
866
  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 });
736
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: SettingsPanelComponent, isStandalone: true, selector: "praxis-settings-panel", host: { listeners: { "document:keydown": "handleKeydown($event)" } }, providers: [providePraxisSettingsPanelI18n()], viewQueries: [{ propertyName: "contentHost", first: true, predicate: ["contentHost"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: "<div\n class=\"settings-panel\"\n data-testid=\"settings-panel-root\"\n [class.expanded]=\"expanded\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"titleId\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n>\n <header class=\"settings-panel-header\">\n <h2 class=\"settings-panel-title\" [id]=\"titleId\">\n <mat-icon *ngIf=\"titleIcon\" class=\"title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n <span>{{ title }}</span>\n </h2>\n <span class=\"spacer\"></span>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-toggle-expand\"\n [attr.aria-label]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n [attr.aria-expanded]=\"expanded\"\n [matTooltip]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n (click)=\"toggleExpand()\"\n >\n <mat-icon [praxisIcon]=\"expanded ? 'close_fullscreen' : 'open_in_full'\"></mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-close-icon\"\n [attr.aria-label]=\"tx('Close')\"\n [matTooltip]=\"tx('Close')\"\n (click)=\"onCancel()\"\n >\n <mat-icon [praxisIcon]=\"'close'\"></mat-icon>\n </button>\n </header>\n <div\n class=\"settings-panel-status\"\n [attr.data-status]=\"statusTone\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <mat-icon\n aria-hidden=\"true\"\n [praxisIcon]=\"\n statusTone === 'dirty'\n ? 'warning'\n : statusTone === 'saved'\n ? 'check_circle'\n : statusTone === 'busy'\n ? 'autorenew'\n : 'info'\n \"\n ></mat-icon>\n <span>{{ statusMessage }}</span>\n </div>\n <div class=\"settings-panel-body\">\n <ng-template #contentHost></ng-template>\n </div>\n <footer class=\"settings-panel-footer\">\n <button mat-button type=\"button\" data-testid=\"settings-panel-reset\" (click)=\"onReset()\" [disabled]=\"isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'restart_alt'\"></mat-icon>\n <span>{{ tx('Reset') }}</span>\n </button>\n <span class=\"spacer\"></span>\n <button\n mat-button\n type=\"button\"\n data-testid=\"settings-panel-cancel\"\n (click)=\"onCancel()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'close'\"></mat-icon>\n <span>{{ tx('Cancel') }}</span>\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n data-testid=\"settings-panel-apply\"\n (click)=\"onApply()\"\n [disabled]=\"!canApply\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canApply\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'done'\"></mat-icon>\n <span>{{ tx('Apply') }}</span>\n </ng-container>\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"settings-panel-save\"\n (click)=\"onSave()\"\n [disabled]=\"!canSave\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canSave\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'save'\"></mat-icon>\n <span>{{ tx('Save & Close') }}</span>\n </ng-container>\n </button>\n </footer>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100%}.settings-panel{display:grid;grid-template-rows:auto auto 1fr auto;grid-template-areas:\"header\" \"status\" \"body\" \"footer\";height:100%;background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface);border-left:1px solid var(--md-sys-color-outline-variant);width:var(--pfx-settings-panel-width, 720px);transition:width .3s ease;overflow:hidden}.settings-panel.expanded{width:min(var(--pfx-settings-panel-width-expanded, 95vw),var(--pfx-settings-panel-max-width, 2400px))}.settings-panel-header{grid-area:header;display:flex;align-items:center;gap:var(--pfx-settings-panel-header-gap, 8px);padding:0 var(--pfx-settings-panel-header-padding-x, 16px);height:var(--pfx-settings-panel-header-height, 64px);border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-header-shadow, var(--md-sys-elevation-level1, 0 2px 6px rgba(0, 0, 0, .08)));flex-shrink:0}.settings-panel-header .spacer{flex:1}.settings-panel-status{grid-area:status;display:flex;align-items:center;gap:8px;min-height:36px;padding:6px 16px;border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface);font-size:.85rem;font-weight:500}.settings-panel-status mat-icon{width:18px;height:18px;font-size:18px}.settings-panel-status[data-status=dirty]{color:var(--md-sys-color-error);background:color-mix(in srgb,var(--md-sys-color-error-container) 30%,var(--md-sys-color-surface-container-high))}.settings-panel-status[data-status=saved]{color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 28%,var(--md-sys-color-surface-container-high))}.settings-panel-body{grid-area:body;overflow-y:auto;min-height:0;padding:var(--pfx-settings-panel-body-padding, 8px 8px 24px 8px);background:var(--md-sys-color-surface);display:flex;flex-direction:column}.settings-panel-content{display:block}.settings-panel-footer{grid-area:footer;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-footer-shadow, var(--md-sys-elevation-level1, 0 -2px 6px rgba(0, 0, 0, .08)));display:flex;align-items:center;padding:var(--pfx-settings-panel-footer-padding, 12px 16px);column-gap:var(--pfx-settings-panel-footer-gap, 12px);flex-shrink:0}.spacer{flex:1}.settings-panel-title{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-title-gap, 8px);font-weight:700;letter-spacing:.2px;margin:0}.settings-panel-title mat-icon{color:var(--md-sys-color-primary)}.settings-panel-title .title-icon{width:20px;height:20px;font-size:20px}.settings-panel-footer button+button{margin-left:var(--pfx-settings-panel-footer-gap, 12px)}.settings-panel-footer button{display:inline-flex;align-items:center}.settings-panel-footer button .mat-icon{font-size:20px;width:20px;height:20px;line-height:1;display:inline-flex;align-items:center;justify-content:center}.settings-panel-footer .mat-button-wrapper{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px)}.settings-panel-footer .mat-progress-spinner{margin-right:8px}.settings-panel-footer .mat-flat-button[color=primary]{font-weight:600}:host ::ng-deep .settings-panel .mdc-button__label{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px);line-height:1}:host ::ng-deep .settings-panel .mdc-button__label>span{display:inline-flex;align-items:center;line-height:1}:host ::ng-deep .settings-panel .mdc-button__label .mat-icon{display:inline-flex;align-items:center;justify-content:center;line-height:1}:host ::ng-deep .praxis-settings-panel-backdrop{background:var(--pfx-backdrop, var(--md-sys-color-scrim, rgba(0, 0, 0, .45)));backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%)}.settings-panel .mat-divider{background-color:var(--md-sys-color-outline-variant)!important}.settings-panel .mat-expansion-panel{background:var(--md-sys-color-surface-container);border:1px solid var(--md-sys-color-outline-variant);border-radius:var(--md-sys-shape-corner-medium, 12px);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08))}.settings-panel .mat-expansion-panel-header{background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface)}.settings-panel .mat-expansion-panel-header mat-icon,.settings-panel .mat-expansion-panel-header .mat-icon{color:var(--md-sys-color-on-surface)!important}.settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title,.settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--md-sys-color-on-surface)}.settings-panel .mat-expansion-panel-header .mat-expansion-indicator,.settings-panel .mat-expansion-panel-header .mat-expansion-indicator:after{color:var(--md-sys-color-on-surface-variant);border-color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-expansion-panel-body{padding:8px}.settings-panel .mat-mdc-tab-group .mat-mdc-tab-header{border-bottom:1px solid var(--md-sys-color-outline-variant)}.settings-panel .mat-mdc-tab-group .mdc-tab .mdc-tab__text-label{color:var(--md-sys-color-on-surface-variant)}.settings-panel .mat-mdc-tab-group .mdc-tab.mdc-tab--active .mdc-tab__text-label{color:var(--md-sys-color-on-surface)}.settings-panel .mat-mdc-tab-group .mdc-tab-indicator__content--underline{border-color:var(--md-sys-color-primary)}.settings-panel .mat-mdc-form-field{--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-secondary, var(--md-sys-color-primary));--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary)}:host ::ng-deep .praxis-settings-panel-pane{position:fixed!important;top:0!important;right:0!important;height:100vh!important;z-index:1000}:host ::ng-deep .praxis-settings-panel-pane .settings-panel{pointer-events:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i3$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "ngmodule", type: MatProgressSpinnerModule }, { kind: "component", type: i6.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "ngmodule", type: MatDialogModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
867
+ 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 mat-button type=\"button\" data-testid=\"settings-panel-reset\" (click)=\"onReset()\" [disabled]=\"isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'restart_alt'\"></mat-icon>\n <span>{{ tx('Reset') }}</span>\n </button>\n <span class=\"spacer\"></span>\n <button\n mat-button\n type=\"button\"\n data-testid=\"settings-panel-cancel\"\n (click)=\"onCancel()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'close'\"></mat-icon>\n <span>{{ tx('Cancel') }}</span>\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n data-testid=\"settings-panel-apply\"\n (click)=\"onApply()\"\n [disabled]=\"!canApply\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canApply\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'done'\"></mat-icon>\n <span>{{ tx('Apply') }}</span>\n </ng-container>\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"settings-panel-save\"\n (click)=\"onSave()\"\n [disabled]=\"!canSave\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canSave\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'save'\"></mat-icon>\n <span>{{ tx('Save & Close') }}</span>\n </ng-container>\n </button>\n </footer>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100%;width:100%;min-width:0;flex:1 1 auto}.settings-panel{position:relative;display:grid;grid-template-rows:auto auto 1fr auto;grid-template-areas:\"header\" \"status\" \"body\" \"footer\";height:100%;background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface);border-left:1px solid var(--md-sys-color-outline-variant);width:var(--pfx-settings-panel-width, 720px);transition:width .3s ease;overflow:hidden}.settings-panel.expanded{width:min(var(--pfx-settings-panel-width-expanded, 95vw),var(--pfx-settings-panel-max-width, 2400px))}.settings-panel__resize-handle{position:absolute;inset:0 auto 0 0;width:12px;cursor:col-resize;touch-action:none;z-index:3;outline:none}.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}.settings-panel.is-resizable:hover .settings-panel__resize-handle:before,.settings-panel__resize-handle:focus-visible:before{opacity:1;background:var(--md-sys-color-outline, rgba(0, 0, 0, .24))}.settings-panel-header{grid-area:header;display:flex;align-items:center;gap:var(--pfx-settings-panel-header-gap, 8px);padding:0 var(--pfx-settings-panel-header-padding-x, 16px);height:var(--pfx-settings-panel-header-height, 64px);border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-header-shadow, var(--md-sys-elevation-level1, 0 2px 6px rgba(0, 0, 0, .08)));flex-shrink:0}.settings-panel-header .spacer{flex:1}.settings-panel-status{grid-area:status;display:flex;align-items:center;gap:8px;min-height:36px;padding:6px 16px;border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface);font-size:.85rem;font-weight:500}.settings-panel-status mat-icon{width:18px;height:18px;font-size:18px}.settings-panel-status[data-status=dirty]{color:var(--md-sys-color-error);background:color-mix(in srgb,var(--md-sys-color-error-container) 30%,var(--md-sys-color-surface-container-high))}.settings-panel-status[data-status=saved]{color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 28%,var(--md-sys-color-surface-container-high))}.settings-panel-body{grid-area:body;overflow-y:auto;min-height:0;padding:var(--pfx-settings-panel-body-padding, 8px 8px 24px 8px);background:var(--md-sys-color-surface);display:flex;flex-direction:column}.settings-panel-content{display:block}.settings-panel-footer{grid-area:footer;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-footer-shadow, var(--md-sys-elevation-level1, 0 -2px 6px rgba(0, 0, 0, .08)));display:flex;align-items:center;padding:var(--pfx-settings-panel-footer-padding, 12px 16px);column-gap:var(--pfx-settings-panel-footer-gap, 12px);flex-shrink:0}.spacer{flex:1}.settings-panel-title{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-title-gap, 8px);font-weight:700;letter-spacing:.2px;margin:0}.settings-panel-title mat-icon{color:var(--md-sys-color-primary)}.settings-panel-title .title-icon{width:20px;height:20px;font-size:20px}.settings-panel-footer button+button{margin-left:var(--pfx-settings-panel-footer-gap, 12px)}.settings-panel-footer button{display:inline-flex;align-items:center}.settings-panel-footer button .mat-icon{font-size:20px;width:20px;height:20px;line-height:1;display:inline-flex;align-items:center;justify-content:center}.settings-panel-footer .mat-button-wrapper{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px)}.settings-panel-footer .mat-progress-spinner{margin-right:8px}.settings-panel-footer .mat-flat-button[color=primary]{font-weight:600}:host ::ng-deep .settings-panel .mdc-button__label{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px);line-height:1}:host ::ng-deep .settings-panel .mdc-button__label>span{display:inline-flex;align-items:center;line-height:1}:host ::ng-deep .settings-panel .mdc-button__label .mat-icon{display:inline-flex;align-items:center;justify-content:center;line-height:1}:host ::ng-deep .praxis-settings-panel-backdrop{background:var(--pfx-backdrop, var(--md-sys-color-scrim, rgba(0, 0, 0, .45)));backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%)}:host ::ng-deep .settings-panel .mat-divider{background-color:var(--md-sys-color-outline-variant)!important}:host ::ng-deep .settings-panel .mat-expansion-panel{background:var(--md-sys-color-surface-container)!important;border:1px solid var(--md-sys-color-outline-variant);border-radius:var(--md-sys-shape-corner-medium, 12px);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08));color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel-header{background:var(--md-sys-color-surface-container)!important;color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-content{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel:not(.mat-expanded) .mat-expansion-panel-header:hover{background:color-mix(in srgb,var(--md-sys-color-surface-container-high) 84%,var(--md-sys-color-primary) 16%)}:host ::ng-deep .settings-panel .mat-expansion-panel.mat-expanded>.mat-expansion-panel-header{background:var(--md-sys-color-surface-container-high)!important}:host ::ng-deep .settings-panel .mat-expansion-panel-header mat-icon,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-icon{color:var(--md-sys-color-on-surface)!important}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title>*,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description>*{color:inherit}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title small,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description small{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-indicator,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-indicator:after{color:var(--md-sys-color-on-surface-variant);border-color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-action-row{border-top-color:var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .settings-panel .mat-accordion,:host ::ng-deep .settings-panel .mat-accordion .mat-expansion-panel-spacing{background:transparent!important}:host ::ng-deep .settings-panel .mat-expansion-panel-content,:host ::ng-deep .settings-panel .mat-expansion-panel-content-wrapper,:host ::ng-deep .settings-panel .mat-expansion-panel-body{background:var(--md-sys-color-surface-container)!important;color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-expansion-panel-body{padding:8px}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-expansion-panel-body>*{color:inherit}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header{border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab{min-width:0}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab .mdc-tab__text-label{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab:hover .mdc-tab__text-label,:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab:focus .mdc-tab__text-label{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab.mdc-tab--active .mdc-tab__text-label{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination-chevron{border-color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .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(--md-sys-color-surface-container-high) 84%,var(--md-sys-color-primary) 16%)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab-indicator__content--underline{border-color:var(--md-sys-color-primary)}:host ::ng-deep .settings-panel .mat-mdc-card{background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08))}:host ::ng-deep .settings-panel .mat-mdc-card-subtitle,:host ::ng-deep .settings-panel .mat-mdc-card-content{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-card-title{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-form-field{width:100%;--mdc-filled-text-field-container-color: var(--md-sys-color-surface-container);--mdc-filled-text-field-hover-container-color: var( --md-sys-color-surface-container-high );--mdc-filled-text-field-focus-container-color: var( --md-sys-color-surface-container-high );--mdc-filled-text-field-active-indicator-color: var( --md-sys-color-outline-variant );--mdc-filled-text-field-hover-active-indicator-color: var( --md-sys-color-secondary, var(--md-sys-color-primary) );--mdc-filled-text-field-focus-active-indicator-color: var( --md-sys-color-primary );--mdc-filled-text-field-label-text-color: var( --md-sys-color-on-surface-variant );--mdc-filled-text-field-focus-label-text-color: var(--md-sys-color-primary);--mdc-filled-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-filled-text-field-caret-color: var(--md-sys-color-primary);--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var( --md-sys-color-secondary, var(--md-sys-color-primary) );--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-label-text-color: var( --md-sys-color-on-surface-variant );--mdc-outlined-text-field-focus-label-text-color: var(--md-sys-color-primary);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-caret-color: var(--md-sys-color-primary);--mat-form-field-focus-select-arrow-color: var(--md-sys-color-primary);--mat-form-field-enabled-select-arrow-color: var( --md-sys-color-on-surface-variant )}:host ::ng-deep .settings-panel .mdc-text-field--filled{background-color:var(--md-sys-color-surface-container)!important;border-radius:var(--md-sys-shape-corner-small, 8px) var(--md-sys-shape-corner-small, 8px) 0 0}:host ::ng-deep .settings-panel .mat-mdc-text-field-wrapper,:host ::ng-deep .settings-panel .mat-mdc-form-field-flex,:host ::ng-deep .settings-panel .mat-mdc-form-field-infix,:host ::ng-deep .settings-panel .mat-mdc-form-field-focus-overlay{background:var(--md-sys-color-surface-container)!important;color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-form-field-focus-overlay{opacity:0}:host ::ng-deep .settings-panel .mdc-text-field--filled .mdc-text-field__input,:host ::ng-deep .settings-panel .mdc-text-field--filled .mdc-floating-label,:host ::ng-deep .settings-panel .mat-mdc-select-value,:host ::ng-deep .settings-panel .mat-mdc-select-arrow,:host ::ng-deep .settings-panel .mat-mdc-form-field .mat-mdc-floating-label{color:var(--md-sys-color-on-surface-variant)!important}:host ::ng-deep .settings-panel .mdc-text-field--filled.mdc-text-field--focused .mdc-floating-label,:host ::ng-deep .settings-panel .mdc-text-field--filled .mdc-text-field__input{color:var(--md-sys-color-on-surface)!important}:host ::ng-deep .settings-panel .mat-mdc-form-field input,:host ::ng-deep .settings-panel .mat-mdc-form-field textarea,:host ::ng-deep .settings-panel .mat-mdc-form-field .mdc-text-field__input{color:var(--md-sys-color-on-surface)!important}:host ::ng-deep .settings-panel .mat-mdc-form-field-subscript-wrapper,:host ::ng-deep .settings-panel .mat-mdc-form-field-hint-wrapper,:host ::ng-deep .settings-panel .mat-mdc-form-field-error-wrapper,:host ::ng-deep .settings-panel .mat-mdc-hint{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-button-toggle-group{border-color:var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .settings-panel .mat-button-toggle{background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-button-toggle+.mat-button-toggle{border-left-color:var(--md-sys-color-outline-variant)}:host ::ng-deep .settings-panel .mat-button-toggle-checked{background:var(--md-sys-color-primary-container)!important;color:var(--md-sys-color-on-primary-container)!important}:host ::ng-deep .settings-panel .mat-button-toggle .mat-icon,:host ::ng-deep .settings-panel .mat-button-toggle-checked .mat-icon{color:inherit}:host ::ng-deep .settings-panel .mat-mdc-slide-toggle .mdc-label{color:var(--md-sys-color-on-surface)}:host ::ng-deep .praxis-settings-panel-pane{position:fixed!important;top:0!important;right:0!important;height:100vh!important;z-index:var(--praxis-layer-settings-panel, 1220)!important}:host ::ng-deep .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 });
737
868
  }
738
869
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SettingsPanelComponent, decorators: [{
739
870
  type: Component,
740
871
  args: [{ selector: 'praxis-settings-panel', standalone: true, imports: [
741
872
  CommonModule,
873
+ NgStyle,
742
874
  MatButtonModule,
743
875
  MatIconModule,
744
876
  PraxisIconDirective,
@@ -746,13 +878,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
746
878
  CdkTrapFocus,
747
879
  MatProgressSpinnerModule,
748
880
  MatDialogModule,
749
- ], changeDetection: ChangeDetectionStrategy.OnPush, providers: [providePraxisSettingsPanelI18n()], template: "<div\n class=\"settings-panel\"\n data-testid=\"settings-panel-root\"\n [class.expanded]=\"expanded\"\n role=\"dialog\"\n aria-modal=\"true\"\n [attr.aria-labelledby]=\"titleId\"\n cdkTrapFocus\n cdkTrapFocusAutoCapture\n>\n <header class=\"settings-panel-header\">\n <h2 class=\"settings-panel-title\" [id]=\"titleId\">\n <mat-icon *ngIf=\"titleIcon\" class=\"title-icon\" [praxisIcon]=\"titleIcon\"></mat-icon>\n <span>{{ title }}</span>\n </h2>\n <span class=\"spacer\"></span>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-toggle-expand\"\n [attr.aria-label]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n [attr.aria-expanded]=\"expanded\"\n [matTooltip]=\"expanded ? tx('Collapse panel') : tx('Expand panel')\"\n (click)=\"toggleExpand()\"\n >\n <mat-icon [praxisIcon]=\"expanded ? 'close_fullscreen' : 'open_in_full'\"></mat-icon>\n </button>\n <button\n mat-icon-button\n type=\"button\"\n data-testid=\"settings-panel-close-icon\"\n [attr.aria-label]=\"tx('Close')\"\n [matTooltip]=\"tx('Close')\"\n (click)=\"onCancel()\"\n >\n <mat-icon [praxisIcon]=\"'close'\"></mat-icon>\n </button>\n </header>\n <div\n class=\"settings-panel-status\"\n [attr.data-status]=\"statusTone\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <mat-icon\n aria-hidden=\"true\"\n [praxisIcon]=\"\n statusTone === 'dirty'\n ? 'warning'\n : statusTone === 'saved'\n ? 'check_circle'\n : statusTone === 'busy'\n ? 'autorenew'\n : 'info'\n \"\n ></mat-icon>\n <span>{{ statusMessage }}</span>\n </div>\n <div class=\"settings-panel-body\">\n <ng-template #contentHost></ng-template>\n </div>\n <footer class=\"settings-panel-footer\">\n <button mat-button type=\"button\" data-testid=\"settings-panel-reset\" (click)=\"onReset()\" [disabled]=\"isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'restart_alt'\"></mat-icon>\n <span>{{ tx('Reset') }}</span>\n </button>\n <span class=\"spacer\"></span>\n <button\n mat-button\n type=\"button\"\n data-testid=\"settings-panel-cancel\"\n (click)=\"onCancel()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'close'\"></mat-icon>\n <span>{{ tx('Cancel') }}</span>\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n data-testid=\"settings-panel-apply\"\n (click)=\"onApply()\"\n [disabled]=\"!canApply\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canApply\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'done'\"></mat-icon>\n <span>{{ tx('Apply') }}</span>\n </ng-container>\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"settings-panel-save\"\n (click)=\"onSave()\"\n [disabled]=\"!canSave\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canSave\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'save'\"></mat-icon>\n <span>{{ tx('Save & Close') }}</span>\n </ng-container>\n </button>\n </footer>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100%}.settings-panel{display:grid;grid-template-rows:auto auto 1fr auto;grid-template-areas:\"header\" \"status\" \"body\" \"footer\";height:100%;background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface);border-left:1px solid var(--md-sys-color-outline-variant);width:var(--pfx-settings-panel-width, 720px);transition:width .3s ease;overflow:hidden}.settings-panel.expanded{width:min(var(--pfx-settings-panel-width-expanded, 95vw),var(--pfx-settings-panel-max-width, 2400px))}.settings-panel-header{grid-area:header;display:flex;align-items:center;gap:var(--pfx-settings-panel-header-gap, 8px);padding:0 var(--pfx-settings-panel-header-padding-x, 16px);height:var(--pfx-settings-panel-header-height, 64px);border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-header-shadow, var(--md-sys-elevation-level1, 0 2px 6px rgba(0, 0, 0, .08)));flex-shrink:0}.settings-panel-header .spacer{flex:1}.settings-panel-status{grid-area:status;display:flex;align-items:center;gap:8px;min-height:36px;padding:6px 16px;border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface);font-size:.85rem;font-weight:500}.settings-panel-status mat-icon{width:18px;height:18px;font-size:18px}.settings-panel-status[data-status=dirty]{color:var(--md-sys-color-error);background:color-mix(in srgb,var(--md-sys-color-error-container) 30%,var(--md-sys-color-surface-container-high))}.settings-panel-status[data-status=saved]{color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 28%,var(--md-sys-color-surface-container-high))}.settings-panel-body{grid-area:body;overflow-y:auto;min-height:0;padding:var(--pfx-settings-panel-body-padding, 8px 8px 24px 8px);background:var(--md-sys-color-surface);display:flex;flex-direction:column}.settings-panel-content{display:block}.settings-panel-footer{grid-area:footer;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-footer-shadow, var(--md-sys-elevation-level1, 0 -2px 6px rgba(0, 0, 0, .08)));display:flex;align-items:center;padding:var(--pfx-settings-panel-footer-padding, 12px 16px);column-gap:var(--pfx-settings-panel-footer-gap, 12px);flex-shrink:0}.spacer{flex:1}.settings-panel-title{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-title-gap, 8px);font-weight:700;letter-spacing:.2px;margin:0}.settings-panel-title mat-icon{color:var(--md-sys-color-primary)}.settings-panel-title .title-icon{width:20px;height:20px;font-size:20px}.settings-panel-footer button+button{margin-left:var(--pfx-settings-panel-footer-gap, 12px)}.settings-panel-footer button{display:inline-flex;align-items:center}.settings-panel-footer button .mat-icon{font-size:20px;width:20px;height:20px;line-height:1;display:inline-flex;align-items:center;justify-content:center}.settings-panel-footer .mat-button-wrapper{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px)}.settings-panel-footer .mat-progress-spinner{margin-right:8px}.settings-panel-footer .mat-flat-button[color=primary]{font-weight:600}:host ::ng-deep .settings-panel .mdc-button__label{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px);line-height:1}:host ::ng-deep .settings-panel .mdc-button__label>span{display:inline-flex;align-items:center;line-height:1}:host ::ng-deep .settings-panel .mdc-button__label .mat-icon{display:inline-flex;align-items:center;justify-content:center;line-height:1}:host ::ng-deep .praxis-settings-panel-backdrop{background:var(--pfx-backdrop, var(--md-sys-color-scrim, rgba(0, 0, 0, .45)));backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%)}.settings-panel .mat-divider{background-color:var(--md-sys-color-outline-variant)!important}.settings-panel .mat-expansion-panel{background:var(--md-sys-color-surface-container);border:1px solid var(--md-sys-color-outline-variant);border-radius:var(--md-sys-shape-corner-medium, 12px);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08))}.settings-panel .mat-expansion-panel-header{background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface)}.settings-panel .mat-expansion-panel-header mat-icon,.settings-panel .mat-expansion-panel-header .mat-icon{color:var(--md-sys-color-on-surface)!important}.settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title,.settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--md-sys-color-on-surface)}.settings-panel .mat-expansion-panel-header .mat-expansion-indicator,.settings-panel .mat-expansion-panel-header .mat-expansion-indicator:after{color:var(--md-sys-color-on-surface-variant);border-color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-expansion-panel-body{padding:8px}.settings-panel .mat-mdc-tab-group .mat-mdc-tab-header{border-bottom:1px solid var(--md-sys-color-outline-variant)}.settings-panel .mat-mdc-tab-group .mdc-tab .mdc-tab__text-label{color:var(--md-sys-color-on-surface-variant)}.settings-panel .mat-mdc-tab-group .mdc-tab.mdc-tab--active .mdc-tab__text-label{color:var(--md-sys-color-on-surface)}.settings-panel .mat-mdc-tab-group .mdc-tab-indicator__content--underline{border-color:var(--md-sys-color-primary)}.settings-panel .mat-mdc-form-field{--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var(--md-sys-color-secondary, var(--md-sys-color-primary));--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary)}:host ::ng-deep .praxis-settings-panel-pane{position:fixed!important;top:0!important;right:0!important;height:100vh!important;z-index:1000}:host ::ng-deep .praxis-settings-panel-pane .settings-panel{pointer-events:auto}\n"] }]
881
+ ], changeDetection: ChangeDetectionStrategy.OnPush, 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 mat-button type=\"button\" data-testid=\"settings-panel-reset\" (click)=\"onReset()\" [disabled]=\"isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'restart_alt'\"></mat-icon>\n <span>{{ tx('Reset') }}</span>\n </button>\n <span class=\"spacer\"></span>\n <button\n mat-button\n type=\"button\"\n data-testid=\"settings-panel-cancel\"\n (click)=\"onCancel()\"\n [disabled]=\"isBusy\"\n >\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'close'\"></mat-icon>\n <span>{{ tx('Cancel') }}</span>\n </button>\n <button\n mat-stroked-button\n type=\"button\"\n data-testid=\"settings-panel-apply\"\n (click)=\"onApply()\"\n [disabled]=\"!canApply\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canApply\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'done'\"></mat-icon>\n <span>{{ tx('Apply') }}</span>\n </ng-container>\n </button>\n <button\n mat-flat-button\n color=\"primary\"\n type=\"button\"\n data-testid=\"settings-panel-save\"\n (click)=\"onSave()\"\n [disabled]=\"!canSave\"\n [matTooltip]=\"disabledReason\"\n [matTooltipDisabled]=\"canSave\"\n [attr.aria-busy]=\"isBusy\"\n >\n <mat-progress-spinner\n *ngIf=\"isBusy\"\n mode=\"indeterminate\"\n diameter=\"20\"\n ></mat-progress-spinner>\n <ng-container *ngIf=\"!isBusy\">\n <mat-icon aria-hidden=\"true\" [praxisIcon]=\"'save'\"></mat-icon>\n <span>{{ tx('Save & Close') }}</span>\n </ng-container>\n </button>\n </footer>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:block;height:100%;width:100%;min-width:0;flex:1 1 auto}.settings-panel{position:relative;display:grid;grid-template-rows:auto auto 1fr auto;grid-template-areas:\"header\" \"status\" \"body\" \"footer\";height:100%;background:var(--md-sys-color-surface-container);color:var(--md-sys-color-on-surface);border-left:1px solid var(--md-sys-color-outline-variant);width:var(--pfx-settings-panel-width, 720px);transition:width .3s ease;overflow:hidden}.settings-panel.expanded{width:min(var(--pfx-settings-panel-width-expanded, 95vw),var(--pfx-settings-panel-max-width, 2400px))}.settings-panel__resize-handle{position:absolute;inset:0 auto 0 0;width:12px;cursor:col-resize;touch-action:none;z-index:3;outline:none}.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}.settings-panel.is-resizable:hover .settings-panel__resize-handle:before,.settings-panel__resize-handle:focus-visible:before{opacity:1;background:var(--md-sys-color-outline, rgba(0, 0, 0, .24))}.settings-panel-header{grid-area:header;display:flex;align-items:center;gap:var(--pfx-settings-panel-header-gap, 8px);padding:0 var(--pfx-settings-panel-header-padding-x, 16px);height:var(--pfx-settings-panel-header-height, 64px);border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-header-shadow, var(--md-sys-elevation-level1, 0 2px 6px rgba(0, 0, 0, .08)));flex-shrink:0}.settings-panel-header .spacer{flex:1}.settings-panel-status{grid-area:status;display:flex;align-items:center;gap:8px;min-height:36px;padding:6px 16px;border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface);font-size:.85rem;font-weight:500}.settings-panel-status mat-icon{width:18px;height:18px;font-size:18px}.settings-panel-status[data-status=dirty]{color:var(--md-sys-color-error);background:color-mix(in srgb,var(--md-sys-color-error-container) 30%,var(--md-sys-color-surface-container-high))}.settings-panel-status[data-status=saved]{color:var(--md-sys-color-primary);background:color-mix(in srgb,var(--md-sys-color-primary-container) 28%,var(--md-sys-color-surface-container-high))}.settings-panel-body{grid-area:body;overflow-y:auto;min-height:0;padding:var(--pfx-settings-panel-body-padding, 8px 8px 24px 8px);background:var(--md-sys-color-surface);display:flex;flex-direction:column}.settings-panel-content{display:block}.settings-panel-footer{grid-area:footer;border-top:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-high);box-shadow:var(--pfx-settings-panel-footer-shadow, var(--md-sys-elevation-level1, 0 -2px 6px rgba(0, 0, 0, .08)));display:flex;align-items:center;padding:var(--pfx-settings-panel-footer-padding, 12px 16px);column-gap:var(--pfx-settings-panel-footer-gap, 12px);flex-shrink:0}.spacer{flex:1}.settings-panel-title{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-title-gap, 8px);font-weight:700;letter-spacing:.2px;margin:0}.settings-panel-title mat-icon{color:var(--md-sys-color-primary)}.settings-panel-title .title-icon{width:20px;height:20px;font-size:20px}.settings-panel-footer button+button{margin-left:var(--pfx-settings-panel-footer-gap, 12px)}.settings-panel-footer button{display:inline-flex;align-items:center}.settings-panel-footer button .mat-icon{font-size:20px;width:20px;height:20px;line-height:1;display:inline-flex;align-items:center;justify-content:center}.settings-panel-footer .mat-button-wrapper{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px)}.settings-panel-footer .mat-progress-spinner{margin-right:8px}.settings-panel-footer .mat-flat-button[color=primary]{font-weight:600}:host ::ng-deep .settings-panel .mdc-button__label{display:inline-flex;align-items:center;gap:var(--pfx-settings-panel-button-gap, 8px);line-height:1}:host ::ng-deep .settings-panel .mdc-button__label>span{display:inline-flex;align-items:center;line-height:1}:host ::ng-deep .settings-panel .mdc-button__label .mat-icon{display:inline-flex;align-items:center;justify-content:center;line-height:1}:host ::ng-deep .praxis-settings-panel-backdrop{background:var(--pfx-backdrop, var(--md-sys-color-scrim, rgba(0, 0, 0, .45)));backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%);-webkit-backdrop-filter:blur(var(--pfx-backdrop-blur, 6px)) saturate(110%)}:host ::ng-deep .settings-panel .mat-divider{background-color:var(--md-sys-color-outline-variant)!important}:host ::ng-deep .settings-panel .mat-expansion-panel{background:var(--md-sys-color-surface-container)!important;border:1px solid var(--md-sys-color-outline-variant);border-radius:var(--md-sys-shape-corner-medium, 12px);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08));color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel-header{background:var(--md-sys-color-surface-container)!important;color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-content{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel:not(.mat-expanded) .mat-expansion-panel-header:hover{background:color-mix(in srgb,var(--md-sys-color-surface-container-high) 84%,var(--md-sys-color-primary) 16%)}:host ::ng-deep .settings-panel .mat-expansion-panel.mat-expanded>.mat-expansion-panel-header{background:var(--md-sys-color-surface-container-high)!important}:host ::ng-deep .settings-panel .mat-expansion-panel-header mat-icon,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-icon{color:var(--md-sys-color-on-surface)!important}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title>*,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description>*{color:inherit}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-title small,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-panel-header-description small{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-indicator,:host ::ng-deep .settings-panel .mat-expansion-panel-header .mat-expansion-indicator:after{color:var(--md-sys-color-on-surface-variant);border-color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-action-row{border-top-color:var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .settings-panel .mat-accordion,:host ::ng-deep .settings-panel .mat-accordion .mat-expansion-panel-spacing{background:transparent!important}:host ::ng-deep .settings-panel .mat-expansion-panel-content,:host ::ng-deep .settings-panel .mat-expansion-panel-content-wrapper,:host ::ng-deep .settings-panel .mat-expansion-panel-body{background:var(--md-sys-color-surface-container)!important;color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-expansion-panel-body{padding:8px}:host ::ng-deep .settings-panel .mat-expansion-panel .mat-expansion-panel-body>*{color:inherit}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header{border-bottom:1px solid var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab{min-width:0}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab .mdc-tab__text-label{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab:hover .mdc-tab__text-label,:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab:focus .mdc-tab__text-label{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab.mdc-tab--active .mdc-tab__text-label{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mat-mdc-tab-header-pagination-chevron{border-color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .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(--md-sys-color-surface-container-high) 84%,var(--md-sys-color-primary) 16%)}:host ::ng-deep .settings-panel .mat-mdc-tab-group .mdc-tab-indicator__content--underline{border-color:var(--md-sys-color-primary)}:host ::ng-deep .settings-panel .mat-mdc-card{background:var(--md-sys-color-surface-container-low);border:1px solid var(--md-sys-color-outline-variant);color:var(--md-sys-color-on-surface);box-shadow:var(--md-sys-elevation-level1, 0 2px 8px rgba(0, 0, 0, .08))}:host ::ng-deep .settings-panel .mat-mdc-card-subtitle,:host ::ng-deep .settings-panel .mat-mdc-card-content{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-mdc-card-title{color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-form-field{width:100%;--mdc-filled-text-field-container-color: var(--md-sys-color-surface-container);--mdc-filled-text-field-hover-container-color: var( --md-sys-color-surface-container-high );--mdc-filled-text-field-focus-container-color: var( --md-sys-color-surface-container-high );--mdc-filled-text-field-active-indicator-color: var( --md-sys-color-outline-variant );--mdc-filled-text-field-hover-active-indicator-color: var( --md-sys-color-secondary, var(--md-sys-color-primary) );--mdc-filled-text-field-focus-active-indicator-color: var( --md-sys-color-primary );--mdc-filled-text-field-label-text-color: var( --md-sys-color-on-surface-variant );--mdc-filled-text-field-focus-label-text-color: var(--md-sys-color-primary);--mdc-filled-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-filled-text-field-caret-color: var(--md-sys-color-primary);--mdc-outlined-text-field-outline-color: var(--md-sys-color-outline-variant);--mdc-outlined-text-field-hover-outline-color: var( --md-sys-color-secondary, var(--md-sys-color-primary) );--mdc-outlined-text-field-focus-outline-color: var(--md-sys-color-primary);--mdc-outlined-text-field-label-text-color: var( --md-sys-color-on-surface-variant );--mdc-outlined-text-field-focus-label-text-color: var(--md-sys-color-primary);--mdc-outlined-text-field-input-text-color: var(--md-sys-color-on-surface);--mdc-outlined-text-field-caret-color: var(--md-sys-color-primary);--mat-form-field-focus-select-arrow-color: var(--md-sys-color-primary);--mat-form-field-enabled-select-arrow-color: var( --md-sys-color-on-surface-variant )}:host ::ng-deep .settings-panel .mdc-text-field--filled{background-color:var(--md-sys-color-surface-container)!important;border-radius:var(--md-sys-shape-corner-small, 8px) var(--md-sys-shape-corner-small, 8px) 0 0}:host ::ng-deep .settings-panel .mat-mdc-text-field-wrapper,:host ::ng-deep .settings-panel .mat-mdc-form-field-flex,:host ::ng-deep .settings-panel .mat-mdc-form-field-infix,:host ::ng-deep .settings-panel .mat-mdc-form-field-focus-overlay{background:var(--md-sys-color-surface-container)!important;color:var(--md-sys-color-on-surface)}:host ::ng-deep .settings-panel .mat-mdc-form-field-focus-overlay{opacity:0}:host ::ng-deep .settings-panel .mdc-text-field--filled .mdc-text-field__input,:host ::ng-deep .settings-panel .mdc-text-field--filled .mdc-floating-label,:host ::ng-deep .settings-panel .mat-mdc-select-value,:host ::ng-deep .settings-panel .mat-mdc-select-arrow,:host ::ng-deep .settings-panel .mat-mdc-form-field .mat-mdc-floating-label{color:var(--md-sys-color-on-surface-variant)!important}:host ::ng-deep .settings-panel .mdc-text-field--filled.mdc-text-field--focused .mdc-floating-label,:host ::ng-deep .settings-panel .mdc-text-field--filled .mdc-text-field__input{color:var(--md-sys-color-on-surface)!important}:host ::ng-deep .settings-panel .mat-mdc-form-field input,:host ::ng-deep .settings-panel .mat-mdc-form-field textarea,:host ::ng-deep .settings-panel .mat-mdc-form-field .mdc-text-field__input{color:var(--md-sys-color-on-surface)!important}:host ::ng-deep .settings-panel .mat-mdc-form-field-subscript-wrapper,:host ::ng-deep .settings-panel .mat-mdc-form-field-hint-wrapper,:host ::ng-deep .settings-panel .mat-mdc-form-field-error-wrapper,:host ::ng-deep .settings-panel .mat-mdc-hint{color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-button-toggle-group{border-color:var(--md-sys-color-outline-variant);background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .settings-panel .mat-button-toggle{background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface-variant)}:host ::ng-deep .settings-panel .mat-button-toggle+.mat-button-toggle{border-left-color:var(--md-sys-color-outline-variant)}:host ::ng-deep .settings-panel .mat-button-toggle-checked{background:var(--md-sys-color-primary-container)!important;color:var(--md-sys-color-on-primary-container)!important}:host ::ng-deep .settings-panel .mat-button-toggle .mat-icon,:host ::ng-deep .settings-panel .mat-button-toggle-checked .mat-icon{color:inherit}:host ::ng-deep .settings-panel .mat-mdc-slide-toggle .mdc-label{color:var(--md-sys-color-on-surface)}:host ::ng-deep .praxis-settings-panel-pane{position:fixed!important;top:0!important;right:0!important;height:100vh!important;z-index:var(--praxis-layer-settings-panel, 1220)!important}:host ::ng-deep .praxis-settings-panel-pane .settings-panel{pointer-events:auto}\n"] }]
750
882
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1.MatDialog }], propDecorators: { contentHost: [{
751
883
  type: ViewChild,
752
884
  args: ['contentHost', { read: ViewContainerRef, static: true }]
753
885
  }], handleKeydown: [{
754
886
  type: HostListener,
755
887
  args: ['document:keydown', ['$event']]
888
+ }], onDocumentPointerMove: [{
889
+ type: HostListener,
890
+ args: ['document:pointermove', ['$event']]
891
+ }], onDocumentPointerEnd: [{
892
+ type: HostListener,
893
+ args: ['document:pointerup', ['$event']]
894
+ }, {
895
+ type: HostListener,
896
+ args: ['document:pointercancel', ['$event']]
756
897
  }] } });
757
898
 
758
899
  class SettingsPanelRef {
@@ -761,10 +902,13 @@ class SettingsPanelRef {
761
902
  savedSubject = new Subject();
762
903
  resetSubject = new Subject();
763
904
  closedSubject = new Subject();
905
+ sizeChangedSubject = new Subject();
906
+ sizePersistor;
764
907
  applied$ = this.appliedSubject.asObservable();
765
908
  saved$ = this.savedSubject.asObservable();
766
909
  reset$ = this.resetSubject.asObservable();
767
910
  closed$ = this.closedSubject.asObservable();
911
+ sizeChanged$ = this.sizeChangedSubject.asObservable();
768
912
  constructor(overlayRef) {
769
913
  this.overlayRef = overlayRef;
770
914
  }
@@ -784,8 +928,16 @@ class SettingsPanelRef {
784
928
  reset() {
785
929
  this.resetSubject.next();
786
930
  }
931
+ bindSizePersistor(fn) {
932
+ this.sizePersistor = fn;
933
+ }
787
934
  updateSize(width) {
788
935
  this.overlayRef.updateSize({ width });
936
+ const serialized = typeof width === 'number' ? `${width}px` : width;
937
+ if (typeof serialized === 'string') {
938
+ this.sizePersistor?.(serialized);
939
+ this.sizeChangedSubject.next({ width: serialized });
940
+ }
789
941
  }
790
942
  close(reason = 'cancel') {
791
943
  if (!this.overlayRef?.hasAttached()) {
@@ -800,67 +952,570 @@ class SettingsPanelRef {
800
952
  this.savedSubject.complete();
801
953
  this.resetSubject.complete();
802
954
  this.closedSubject.complete();
955
+ this.sizeChangedSubject.complete();
803
956
  }
804
957
  }
805
958
 
806
959
  const SETTINGS_PANEL_DATA = SETTINGS_PANEL_DATA$1;
807
960
  const SETTINGS_PANEL_REF = new InjectionToken('SETTINGS_PANEL_REF');
808
961
 
809
- class SettingsPanelService {
962
+ class BaseSidePanelComponent {
963
+ cdr;
964
+ i18n;
965
+ title = '';
966
+ titleIcon;
967
+ subtitle;
968
+ widthPreset = 'default';
969
+ width;
970
+ minWidth;
971
+ maxWidth;
972
+ resizable = false;
973
+ resizeAxis = 'x';
974
+ persistSizeKey;
975
+ useHostWidth = false;
976
+ theme = null;
977
+ onClose;
978
+ onResizeWidth;
979
+ static nextId = 0;
980
+ titleId = `praxis-base-side-panel-title-${BaseSidePanelComponent.nextId++}`;
981
+ activePointerId = null;
982
+ dragStartX = 0;
983
+ dragStartWidth = 0;
984
+ contentRef;
985
+ footerRef;
986
+ contentHost;
987
+ footerHost;
988
+ constructor(cdr, i18n) {
989
+ this.cdr = cdr;
990
+ this.i18n = i18n;
991
+ }
992
+ ngOnDestroy() {
993
+ this.stopResizeSession();
994
+ }
995
+ get hostClasses() {
996
+ return this.theme?.hostClasses ?? [];
997
+ }
998
+ get hostCssVars() {
999
+ return (this.theme?.cssVars ?? {});
1000
+ }
1001
+ get panelInlineStyles() {
1002
+ return {
1003
+ width: this.useHostWidth ? null : this.width ?? null,
1004
+ minWidth: this.minWidth ?? null,
1005
+ maxWidth: this.maxWidth ?? null,
1006
+ };
1007
+ }
1008
+ get mergedHostStyles() {
1009
+ return {
1010
+ ...this.hostCssVars,
1011
+ ...this.panelInlineStyles,
1012
+ };
1013
+ }
1014
+ get closeLabel() {
1015
+ return this.i18n.t('Close', undefined, 'Close', PRAXIS_SETTINGS_PANEL_I18N_NAMESPACE);
1016
+ }
1017
+ get resizeHandleLabel() {
1018
+ return this.i18n.t('Resize panel', undefined, 'Resize panel', PRAXIS_SETTINGS_PANEL_I18N_NAMESPACE);
1019
+ }
1020
+ get hasHeader() {
1021
+ return !!(this.title || this.titleIcon || this.subtitle);
1022
+ }
1023
+ get hasFooter() {
1024
+ return !!this.footerRef;
1025
+ }
1026
+ get widthClass() {
1027
+ if (this.useHostWidth || this.width) {
1028
+ return null;
1029
+ }
1030
+ return `width-${this.widthPreset}`;
1031
+ }
1032
+ get showResizeHandle() {
1033
+ return this.resizable && this.resizeAxis === 'x';
1034
+ }
1035
+ setTitle(title) {
1036
+ this.title = title;
1037
+ this.cdr.markForCheck();
1038
+ }
1039
+ onResizeHandlePointerDown(event) {
1040
+ if (!this.showResizeHandle || event.button !== 0) {
1041
+ return;
1042
+ }
1043
+ const currentWidth = this.parseWidthPx(this.width);
1044
+ if (currentWidth === null) {
1045
+ return;
1046
+ }
1047
+ this.activePointerId = event.pointerId;
1048
+ this.dragStartX = event.clientX;
1049
+ this.dragStartWidth = currentWidth;
1050
+ const target = event.currentTarget;
1051
+ try {
1052
+ target?.setPointerCapture(event.pointerId);
1053
+ }
1054
+ catch { }
1055
+ event.preventDefault();
1056
+ }
1057
+ onResizeHandleKeydown(event) {
1058
+ if (!this.showResizeHandle) {
1059
+ return;
1060
+ }
1061
+ const step = event.shiftKey ? 48 : 24;
1062
+ switch (event.key) {
1063
+ case 'ArrowLeft':
1064
+ this.applyWidthPx(this.getCurrentWidthPx() + step);
1065
+ event.preventDefault();
1066
+ return;
1067
+ case 'ArrowRight':
1068
+ this.applyWidthPx(this.getCurrentWidthPx() - step);
1069
+ event.preventDefault();
1070
+ return;
1071
+ case 'Home': {
1072
+ const min = this.parseWidthPx(this.minWidth);
1073
+ if (min !== null) {
1074
+ this.applyWidthPx(min);
1075
+ event.preventDefault();
1076
+ }
1077
+ return;
1078
+ }
1079
+ case 'End': {
1080
+ const max = this.parseWidthPx(this.maxWidth);
1081
+ if (max !== null) {
1082
+ this.applyWidthPx(max);
1083
+ event.preventDefault();
1084
+ }
1085
+ return;
1086
+ }
1087
+ default:
1088
+ return;
1089
+ }
1090
+ }
1091
+ onDocumentPointerMove(event) {
1092
+ if (event.pointerId !== this.activePointerId) {
1093
+ return;
1094
+ }
1095
+ const deltaX = this.dragStartX - event.clientX;
1096
+ this.applyWidthPx(this.dragStartWidth + deltaX);
1097
+ }
1098
+ onDocumentPointerEnd(event) {
1099
+ if (event.pointerId !== this.activePointerId) {
1100
+ return;
1101
+ }
1102
+ this.stopResizeSession();
1103
+ }
1104
+ attachContent(component, injector, inputs) {
1105
+ this.contentRef = this.contentHost.createComponent(component, { injector });
1106
+ this.applyInputs(this.contentRef, inputs);
1107
+ this.cdr.markForCheck();
1108
+ return this.contentRef;
1109
+ }
1110
+ attachFooter(component, injector, inputs) {
1111
+ this.footerRef = this.footerHost.createComponent(component, { injector });
1112
+ this.applyInputs(this.footerRef, inputs);
1113
+ this.cdr.markForCheck();
1114
+ return this.footerRef;
1115
+ }
1116
+ clearFooter() {
1117
+ this.footerHost.clear();
1118
+ this.footerRef = undefined;
1119
+ this.cdr.markForCheck();
1120
+ }
1121
+ applyInputs(componentRef, inputs) {
1122
+ if (!inputs) {
1123
+ return;
1124
+ }
1125
+ const supportedInputs = this.resolveSupportedInputs(componentRef);
1126
+ for (const [key, value] of Object.entries(inputs)) {
1127
+ if (supportedInputs && !supportedInputs.has(key)) {
1128
+ continue;
1129
+ }
1130
+ try {
1131
+ if (typeof componentRef.setInput === 'function') {
1132
+ componentRef.setInput(key, value);
1133
+ }
1134
+ else {
1135
+ componentRef.instance[key] = value;
1136
+ }
1137
+ }
1138
+ catch { }
1139
+ }
1140
+ componentRef.changeDetectorRef.detectChanges();
1141
+ }
1142
+ resolveSupportedInputs(componentRef) {
1143
+ const componentDef = componentRef.componentType?.ɵcmp;
1144
+ const declaredInputs = componentDef?.inputs;
1145
+ if (!declaredInputs || typeof declaredInputs !== 'object') {
1146
+ return null;
1147
+ }
1148
+ return new Set(Object.keys(declaredInputs));
1149
+ }
1150
+ stopResizeSession() {
1151
+ this.activePointerId = null;
1152
+ }
1153
+ getCurrentWidthPx() {
1154
+ return this.parseWidthPx(this.width) ?? 720;
1155
+ }
1156
+ applyWidthPx(value) {
1157
+ const clampedWidth = this.clampWidthPx(value);
1158
+ const width = `${clampedWidth}px`;
1159
+ this.width = width;
1160
+ this.onResizeWidth?.(width);
1161
+ this.cdr.markForCheck();
1162
+ }
1163
+ clampWidthPx(value) {
1164
+ const viewportMax = typeof window !== 'undefined' ? window.innerWidth : value;
1165
+ const fallbackMin = 320;
1166
+ const min = this.parseWidthPx(this.minWidth) ?? fallbackMin;
1167
+ const max = Math.min(this.parseWidthPx(this.maxWidth) ?? viewportMax, viewportMax);
1168
+ return Math.min(Math.max(Math.round(value), min), Math.max(min, max));
1169
+ }
1170
+ parseWidthPx(value) {
1171
+ if (!value) {
1172
+ return null;
1173
+ }
1174
+ const normalized = value.trim().toLowerCase();
1175
+ if (!normalized.endsWith('px')) {
1176
+ return null;
1177
+ }
1178
+ const parsed = Number.parseFloat(normalized.slice(0, -2));
1179
+ return Number.isFinite(parsed) ? parsed : null;
1180
+ }
1181
+ 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 });
1182
+ 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 });
1183
+ }
1184
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: BaseSidePanelComponent, decorators: [{
1185
+ type: Component,
1186
+ args: [{ selector: 'praxis-base-side-panel', standalone: true, imports: [
1187
+ CommonModule,
1188
+ NgClass,
1189
+ NgStyle,
1190
+ MatButtonModule,
1191
+ MatIconModule,
1192
+ MatTooltipModule,
1193
+ CdkTrapFocus,
1194
+ PraxisIconDirective,
1195
+ ], changeDetection: ChangeDetectionStrategy.OnPush, 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"] }]
1196
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1$1.PraxisI18nService }], propDecorators: { contentHost: [{
1197
+ type: ViewChild,
1198
+ args: ['contentHost', { read: ViewContainerRef, static: true }]
1199
+ }], footerHost: [{
1200
+ type: ViewChild,
1201
+ args: ['footerHost', { read: ViewContainerRef, static: true }]
1202
+ }], onDocumentPointerMove: [{
1203
+ type: HostListener,
1204
+ args: ['document:pointermove', ['$event']]
1205
+ }], onDocumentPointerEnd: [{
1206
+ type: HostListener,
1207
+ args: ['document:pointerup', ['$event']]
1208
+ }, {
1209
+ type: HostListener,
1210
+ args: ['document:pointercancel', ['$event']]
1211
+ }] } });
1212
+
1213
+ class BaseSidePanelOverlayRef {
1214
+ closedSubject = new Subject();
1215
+ resultSubject = new Subject();
1216
+ sizeChangedSubject = new Subject();
1217
+ overlayRef;
1218
+ titleUpdater;
1219
+ sizePersistor;
1220
+ closed$ = this.closedSubject.asObservable();
1221
+ result$ = this.resultSubject.asObservable();
1222
+ sizeChanged$ = this.sizeChangedSubject.asObservable();
1223
+ constructor(overlayRef) {
1224
+ this.overlayRef = overlayRef;
1225
+ }
1226
+ bindTitleUpdater(fn) {
1227
+ this.titleUpdater = fn;
1228
+ }
1229
+ bindSizePersistor(fn) {
1230
+ this.sizePersistor = fn;
1231
+ }
1232
+ emitResult(result) {
1233
+ this.resultSubject.next(result);
1234
+ }
1235
+ updateTitle(title) {
1236
+ this.titleUpdater?.(title);
1237
+ }
1238
+ updateSize(size) {
1239
+ if (!this.overlayRef) {
1240
+ return;
1241
+ }
1242
+ const width = typeof size === 'string'
1243
+ ? this.resolveWidth(size)
1244
+ : size?.width;
1245
+ if (!width) {
1246
+ return;
1247
+ }
1248
+ this.overlayRef.updateSize({ width });
1249
+ this.sizePersistor?.(width);
1250
+ this.sizeChangedSubject.next({ width });
1251
+ }
1252
+ close(result) {
1253
+ if (!this.overlayRef?.hasAttached()) {
1254
+ return;
1255
+ }
1256
+ this.overlayRef.dispose();
1257
+ this.overlayRef = null;
1258
+ this.closedSubject.next(result);
1259
+ this.closedSubject.complete();
1260
+ this.resultSubject.complete();
1261
+ this.sizeChangedSubject.complete();
1262
+ }
1263
+ resolveWidth(preset) {
1264
+ switch (preset) {
1265
+ case 'narrow':
1266
+ return '420px';
1267
+ case 'wide':
1268
+ return '960px';
1269
+ case 'full':
1270
+ return '100vw';
1271
+ case 'default':
1272
+ return '720px';
1273
+ default:
1274
+ return preset;
1275
+ }
1276
+ }
1277
+ }
1278
+
1279
+ const BASE_SIDE_PANEL_DATA = new InjectionToken('BASE_SIDE_PANEL_DATA');
1280
+ const BASE_SIDE_PANEL_REF = new InjectionToken('BASE_SIDE_PANEL_REF');
1281
+
1282
+ class BaseSidePanelService {
810
1283
  overlay;
811
1284
  injector;
812
- currentRef;
813
- i18n = inject(PraxisI18nService);
1285
+ currentByScope = new Map();
814
1286
  constructor(overlay, injector) {
815
1287
  this.overlay = overlay;
816
1288
  this.injector = injector;
817
1289
  }
818
- /**
819
- * Opens a new settings panel. If another panel is already open it will be
820
- * closed before the new one is created. Future improvements may reuse the
821
- * same overlay when the provided id matches.
822
- */
823
1290
  open(config) {
824
- if (this.currentRef) {
825
- this.currentRef.close('cancel');
1291
+ return this.openHost({
1292
+ component: BaseSidePanelComponent,
1293
+ config,
1294
+ createRef: (overlayRef) => new BaseSidePanelOverlayRef(overlayRef),
1295
+ attach: (panelRef, ref, resolvedConfig) => {
1296
+ const resolvedWidth = resolvedConfig.width ??
1297
+ this.resolveWidth(resolvedConfig.widthPreset ?? 'default');
1298
+ panelRef.instance.title = resolvedConfig.title ?? '';
1299
+ panelRef.instance.titleIcon = resolvedConfig.titleIcon;
1300
+ panelRef.instance.subtitle = resolvedConfig.subtitle;
1301
+ panelRef.instance.widthPreset = resolvedConfig.widthPreset ?? 'default';
1302
+ panelRef.instance.width = resolvedWidth;
1303
+ panelRef.instance.minWidth = resolvedConfig.minWidth;
1304
+ panelRef.instance.maxWidth = resolvedConfig.maxWidth;
1305
+ panelRef.instance.resizable = resolvedConfig.resizable ?? false;
1306
+ panelRef.instance.resizeAxis = resolvedConfig.resizeAxis ?? 'x';
1307
+ panelRef.instance.persistSizeKey = resolvedConfig.persistSizeKey;
1308
+ panelRef.instance.useHostWidth = false;
1309
+ panelRef.instance.theme = resolvedConfig.theme ?? null;
1310
+ panelRef.instance.onClose = () => ref.close(this.closeResult('close'));
1311
+ panelRef.instance.onResizeWidth = (width) => ref.updateSize?.({ width });
1312
+ ref.bindTitleUpdater((title) => panelRef.instance.setTitle(title));
1313
+ const contentInjector = Injector.create({
1314
+ providers: [
1315
+ { provide: BASE_SIDE_PANEL_DATA, useValue: resolvedConfig.content.inputs },
1316
+ { provide: BASE_SIDE_PANEL_REF, useValue: ref },
1317
+ ],
1318
+ parent: this.injector,
1319
+ });
1320
+ panelRef.instance.attachContent(resolvedConfig.content.component, contentInjector, resolvedConfig.content.inputs);
1321
+ if (resolvedConfig.footer) {
1322
+ panelRef.instance.attachFooter(resolvedConfig.footer.component, this.injector, resolvedConfig.footer.inputs);
1323
+ }
1324
+ panelRef.changeDetectorRef.detectChanges();
1325
+ },
1326
+ createCloser: (ref) => (reason = 'close') => ref.close(this.closeResult(reason)),
1327
+ onBackdrop: (ref) => ref.close(this.closeResult('backdrop')),
1328
+ onEsc: (ref) => ref.close(this.closeResult('esc')),
1329
+ });
1330
+ }
1331
+ close(reason = 'close') {
1332
+ this.closeScope('default', reason);
1333
+ }
1334
+ openHost(options) {
1335
+ const scope = options.config.scope ?? 'default';
1336
+ this.closeScope(scope);
1337
+ const resolvedConfig = this.resolveOpenConfig(options.config);
1338
+ const overlayRef = this.overlay.create(this.buildOverlayConfig(resolvedConfig, options));
1339
+ const ref = options.createRef(overlayRef);
1340
+ ref.bindSizePersistor?.((width) => {
1341
+ this.persistWidth(resolvedConfig.persistSizeKey, width);
1342
+ });
1343
+ const panelRef = overlayRef.attach(new ComponentPortal(options.component));
1344
+ options.attach(panelRef, ref, resolvedConfig);
1345
+ if (options.config.closeOnBackdrop !== false) {
1346
+ overlayRef.backdropClick().subscribe(() => options.onBackdrop(ref));
1347
+ }
1348
+ if (options.config.closeOnEsc !== false) {
1349
+ overlayRef.keydownEvents().subscribe((event) => {
1350
+ if (event.key === 'Escape') {
1351
+ options.onEsc(ref);
1352
+ }
1353
+ });
826
1354
  }
827
- const overlayConfig = {
1355
+ this.trackCurrentRef(scope, ref, options.createCloser(ref));
1356
+ return ref;
1357
+ }
1358
+ closeScope(scope, reason = 'close') {
1359
+ this.currentByScope.get(scope)?.close(reason);
1360
+ this.currentByScope.delete(scope);
1361
+ }
1362
+ buildOverlayConfig(config, options) {
1363
+ return {
828
1364
  hasBackdrop: true,
829
- backdropClass: 'praxis-settings-panel-backdrop',
830
- panelClass: 'praxis-settings-panel-pane',
1365
+ backdropClass: options?.backdropClass ?? 'praxis-side-panel-backdrop',
1366
+ panelClass: options?.panelClass ?? 'praxis-side-panel-pane',
831
1367
  positionStrategy: this.overlay.position().global().top('0').right('0'),
832
- height: '100vh',
1368
+ height: options?.overlaySize?.height ?? '100vh',
1369
+ minHeight: options?.overlaySize?.minHeight,
1370
+ maxHeight: options?.overlaySize?.maxHeight,
1371
+ width: options?.overlayWidth === null
1372
+ ? options?.overlaySize?.width
1373
+ : options?.overlayWidth ??
1374
+ config.width ??
1375
+ options?.overlaySize?.width ??
1376
+ this.resolveWidth(config.widthPreset ?? 'default'),
1377
+ minWidth: options?.overlaySize?.minWidth ?? config.minWidth,
1378
+ maxWidth: options?.overlaySize?.maxWidth ?? config.maxWidth,
833
1379
  scrollStrategy: this.overlay.scrollStrategies.block(),
834
1380
  };
835
- const overlayRef = this.overlay.create(overlayConfig);
836
- const ref = new SettingsPanelRef(overlayRef);
837
- const panelPortal = new ComponentPortal(SettingsPanelComponent);
838
- const panelRef = overlayRef.attach(panelPortal);
839
- panelRef.instance.title = config.title?.trim()
840
- ? config.title
841
- : this.i18n.t(PRAXIS_SETTINGS_PANEL_DEFAULT_TITLE, undefined, PRAXIS_SETTINGS_PANEL_DEFAULT_TITLE, PRAXIS_SETTINGS_PANEL_I18N_NAMESPACE);
842
- panelRef.instance.titleIcon = config.titleIcon;
843
- panelRef.instance.expanded = config.expanded || false;
844
- const inputs = config.content.inputs;
1381
+ }
1382
+ resolveWidth(preset) {
1383
+ switch (preset) {
1384
+ case 'narrow':
1385
+ return '420px';
1386
+ case 'wide':
1387
+ return '960px';
1388
+ case 'full':
1389
+ return '100vw';
1390
+ case 'default':
1391
+ default:
1392
+ return '720px';
1393
+ }
1394
+ }
1395
+ resolveOpenConfig(config) {
1396
+ const persistedWidth = this.resolvePersistedWidth(config.persistSizeKey);
1397
+ if (!persistedWidth) {
1398
+ return config;
1399
+ }
1400
+ return {
1401
+ ...config,
1402
+ width: persistedWidth,
1403
+ };
1404
+ }
1405
+ resolvePersistedWidth(persistSizeKey) {
1406
+ if (!persistSizeKey || typeof localStorage === 'undefined') {
1407
+ return undefined;
1408
+ }
845
1409
  try {
846
- const dbg = inputs && inputs.page ? { hasPage: true, conns: Array.isArray(inputs.page.connections) ? inputs.page.connections.length : undefined } : { hasPage: false };
847
- (console.log || console.debug)('[SettingsPanel] open()', { id: config.id, title: config.title, inputs: dbg });
1410
+ const value = localStorage.getItem(this.storageKey(persistSizeKey));
1411
+ return this.isPixelWidth(value) ? value ?? undefined : undefined;
1412
+ }
1413
+ catch {
1414
+ return undefined;
1415
+ }
1416
+ }
1417
+ persistWidth(persistSizeKey, width) {
1418
+ if (!persistSizeKey || typeof localStorage === 'undefined' || !this.isPixelWidth(width)) {
1419
+ return;
1420
+ }
1421
+ try {
1422
+ localStorage.setItem(this.storageKey(persistSizeKey), width);
848
1423
  }
849
1424
  catch { }
850
- const injector = Injector.create({
851
- providers: [
852
- { provide: SETTINGS_PANEL_DATA, useValue: inputs },
853
- { provide: SETTINGS_PANEL_REF, useValue: ref },
854
- ],
855
- parent: this.injector,
856
- });
857
- panelRef.instance.attachContent(config.content.component, injector, ref, inputs);
858
- overlayRef.backdropClick().subscribe(() => ref.close('backdrop'));
859
- overlayRef.keydownEvents().subscribe((event) => {
860
- if (event.key === 'Escape') {
861
- ref.close('esc');
1425
+ }
1426
+ storageKey(persistSizeKey) {
1427
+ return `praxis.side-panel.width:${persistSizeKey}`;
1428
+ }
1429
+ isPixelWidth(value) {
1430
+ return !!value && /^\d+(\.\d+)?px$/i.test(value.trim());
1431
+ }
1432
+ closeResult(reason) {
1433
+ return { type: reason };
1434
+ }
1435
+ trackCurrentRef(scope, ref, closeCurrent) {
1436
+ this.currentByScope.set(scope, { ref, close: closeCurrent });
1437
+ ref.closed$.subscribe(() => {
1438
+ const current = this.currentByScope.get(scope);
1439
+ if (current?.ref === ref) {
1440
+ this.currentByScope.delete(scope);
862
1441
  }
863
1442
  });
1443
+ }
1444
+ 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 });
1445
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: BaseSidePanelService, providedIn: 'root' });
1446
+ }
1447
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: BaseSidePanelService, decorators: [{
1448
+ type: Injectable,
1449
+ args: [{ providedIn: 'root' }]
1450
+ }], ctorParameters: () => [{ type: i1$2.Overlay }, { type: i0.Injector }] });
1451
+
1452
+ class SettingsPanelService {
1453
+ baseSidePanelService;
1454
+ injector;
1455
+ currentRef;
1456
+ i18n = inject(PraxisI18nService);
1457
+ layerScale = inject(PraxisLayerScaleStyleService);
1458
+ constructor(baseSidePanelService, injector) {
1459
+ this.baseSidePanelService = baseSidePanelService;
1460
+ this.injector = injector;
1461
+ }
1462
+ /**
1463
+ * Opens a new settings panel. If another panel is already open it will be
1464
+ * closed before the new one is created. Future improvements may reuse the
1465
+ * same overlay when the provided id matches.
1466
+ */
1467
+ open(config) {
1468
+ this.layerScale.ensureInstalled();
1469
+ const ref = this.baseSidePanelService.openHost({
1470
+ component: SettingsPanelComponent,
1471
+ config: {
1472
+ id: config.id,
1473
+ scope: 'settings-panel',
1474
+ title: config.title,
1475
+ titleIcon: config.titleIcon,
1476
+ resizable: config.resizable ?? true,
1477
+ resizeAxis: 'x',
1478
+ persistSizeKey: config.persistSizeKey ?? `settings-panel:${config.id}`,
1479
+ minWidth: config.minWidth,
1480
+ maxWidth: config.maxWidth,
1481
+ content: {
1482
+ component: config.content.component,
1483
+ inputs: config.content.inputs,
1484
+ },
1485
+ closeOnBackdrop: true,
1486
+ closeOnEsc: true,
1487
+ },
1488
+ backdropClass: 'praxis-settings-panel-backdrop',
1489
+ panelClass: 'praxis-settings-panel-pane',
1490
+ overlayWidth: null,
1491
+ createRef: (overlayRef) => new SettingsPanelRef(overlayRef),
1492
+ attach: (panelRef, panelServiceRef, resolvedConfig) => {
1493
+ panelRef.instance.title = config.title?.trim()
1494
+ ? config.title
1495
+ : this.i18n.t(PRAXIS_SETTINGS_PANEL_DEFAULT_TITLE, undefined, PRAXIS_SETTINGS_PANEL_DEFAULT_TITLE, PRAXIS_SETTINGS_PANEL_I18N_NAMESPACE);
1496
+ panelRef.instance.titleIcon = config.titleIcon;
1497
+ panelRef.instance.width =
1498
+ resolvedConfig.width ?? '720px';
1499
+ panelRef.instance.minWidth = resolvedConfig.minWidth;
1500
+ panelRef.instance.maxWidth = resolvedConfig.maxWidth;
1501
+ panelRef.instance.resizable = resolvedConfig.resizable ?? true;
1502
+ panelRef.instance.persistSizeKey = resolvedConfig.persistSizeKey;
1503
+ panelRef.instance.expanded = config.expanded || false;
1504
+ const inputs = config.content.inputs;
1505
+ const injector = Injector.create({
1506
+ providers: [
1507
+ { provide: SETTINGS_PANEL_DATA, useValue: inputs },
1508
+ { provide: SETTINGS_PANEL_REF, useValue: panelServiceRef },
1509
+ ],
1510
+ parent: this.injector,
1511
+ });
1512
+ panelRef.instance.attachContent(config.content.component, injector, panelServiceRef, inputs);
1513
+ panelRef.changeDetectorRef.detectChanges();
1514
+ },
1515
+ createCloser: (panelServiceRef) => () => panelServiceRef.close('cancel'),
1516
+ onBackdrop: (panelServiceRef) => panelServiceRef.close('backdrop'),
1517
+ onEsc: (panelServiceRef) => panelServiceRef.close('esc'),
1518
+ });
864
1519
  ref.closed$.subscribe(() => {
865
1520
  if (this.currentRef === ref) {
866
1521
  this.currentRef = undefined;
@@ -870,35 +1525,227 @@ class SettingsPanelService {
870
1525
  return ref;
871
1526
  }
872
1527
  close(reason = 'cancel') {
873
- this.currentRef?.close(reason);
1528
+ this.baseSidePanelService.closeScope('settings-panel', reason);
874
1529
  this.currentRef = undefined;
875
1530
  }
876
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SettingsPanelService, deps: [{ token: i1$1.Overlay }, { token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
1531
+ 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 });
877
1532
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SettingsPanelService, providedIn: 'root' });
878
1533
  }
879
1534
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SettingsPanelService, decorators: [{
880
1535
  type: Injectable,
881
1536
  args: [{ providedIn: 'root' }]
882
- }], ctorParameters: () => [{ type: i1$1.Overlay }, { type: i0.Injector }] });
1537
+ }], ctorParameters: () => [{ type: BaseSidePanelService }, { type: i0.Injector }] });
883
1538
 
884
1539
  function providePraxisSettingsPanelBridge() {
885
- return {
886
- provide: SETTINGS_PANEL_BRIDGE,
887
- useFactory: (service) => ({
888
- open: (opts) => service.open({
889
- id: opts.id,
890
- title: opts.title,
891
- titleIcon: opts.titleIcon,
892
- content: {
893
- component: opts.content.component,
894
- inputs: (opts.content.inputs || undefined),
1540
+ return [
1541
+ providePraxisSettingsPanelI18n(),
1542
+ {
1543
+ provide: SETTINGS_PANEL_BRIDGE,
1544
+ useFactory: (service) => ({
1545
+ open: (opts) => service.open({
1546
+ id: opts.id,
1547
+ title: opts.title,
1548
+ titleIcon: opts.titleIcon,
1549
+ content: {
1550
+ component: opts.content.component,
1551
+ inputs: (opts.content.inputs || undefined),
1552
+ },
1553
+ }),
1554
+ }),
1555
+ deps: [SettingsPanelService],
1556
+ },
1557
+ ];
1558
+ }
1559
+
1560
+ class SurfaceDrawerComponent {
1561
+ cdr;
1562
+ i18n;
1563
+ title = '';
1564
+ titleIcon;
1565
+ subtitle;
1566
+ widthPreset = 'default';
1567
+ useHostWidth = false;
1568
+ theme = null;
1569
+ onClose;
1570
+ static nextId = 0;
1571
+ titleId = `praxis-surface-drawer-title-${SurfaceDrawerComponent.nextId++}`;
1572
+ contentRef;
1573
+ contentHost;
1574
+ constructor(cdr, i18n) {
1575
+ this.cdr = cdr;
1576
+ this.i18n = i18n;
1577
+ }
1578
+ get hostClasses() {
1579
+ return this.theme?.hostClasses ?? [];
1580
+ }
1581
+ get hostCssVars() {
1582
+ return (this.theme?.cssVars ?? {});
1583
+ }
1584
+ get closeLabel() {
1585
+ return this.i18n.t('Close', undefined, 'Close', PRAXIS_SETTINGS_PANEL_I18N_NAMESPACE);
1586
+ }
1587
+ get hasHeader() {
1588
+ return !!(this.title || this.titleIcon || this.subtitle);
1589
+ }
1590
+ get widthClass() {
1591
+ if (this.useHostWidth) {
1592
+ return null;
1593
+ }
1594
+ return `width-${this.widthPreset}`;
1595
+ }
1596
+ setTitle(title) {
1597
+ this.title = title;
1598
+ this.cdr.markForCheck();
1599
+ }
1600
+ attachContent(component, injector, inputs) {
1601
+ this.contentRef = this.contentHost.createComponent(component, { injector });
1602
+ this.applyInputs(this.contentRef, inputs);
1603
+ this.cdr.markForCheck();
1604
+ return this.contentRef;
1605
+ }
1606
+ applyInputs(componentRef, inputs) {
1607
+ if (!inputs) {
1608
+ return;
1609
+ }
1610
+ const supportedInputs = this.resolveSupportedInputs(componentRef);
1611
+ for (const [key, value] of Object.entries(inputs)) {
1612
+ if (supportedInputs && !supportedInputs.has(key)) {
1613
+ continue;
1614
+ }
1615
+ try {
1616
+ if (typeof componentRef.setInput === 'function') {
1617
+ componentRef.setInput(key, value);
1618
+ }
1619
+ else {
1620
+ componentRef.instance[key] = value;
1621
+ }
1622
+ }
1623
+ catch { }
1624
+ }
1625
+ componentRef.changeDetectorRef.detectChanges();
1626
+ }
1627
+ resolveSupportedInputs(componentRef) {
1628
+ const componentDef = componentRef.componentType?.ɵcmp;
1629
+ const declaredInputs = componentDef?.inputs;
1630
+ if (!declaredInputs || typeof declaredInputs !== 'object') {
1631
+ return null;
1632
+ }
1633
+ return new Set(Object.keys(declaredInputs));
1634
+ }
1635
+ 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 });
1636
+ 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 });
1637
+ }
1638
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: SurfaceDrawerComponent, decorators: [{
1639
+ type: Component,
1640
+ args: [{ selector: 'praxis-surface-drawer', standalone: true, imports: [
1641
+ CommonModule,
1642
+ NgClass,
1643
+ NgStyle,
1644
+ MatButtonModule,
1645
+ MatIconModule,
1646
+ MatTooltipModule,
1647
+ CdkTrapFocus,
1648
+ PraxisIconDirective,
1649
+ ], changeDetection: ChangeDetectionStrategy.OnPush, 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"] }]
1650
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1$1.PraxisI18nService }], propDecorators: { contentHost: [{
1651
+ type: ViewChild,
1652
+ args: ['contentHost', { read: ViewContainerRef, static: true }]
1653
+ }] } });
1654
+
1655
+ function providePraxisSurfaceDrawerBridge() {
1656
+ return [
1657
+ providePraxisSettingsPanelI18n(),
1658
+ {
1659
+ provide: SURFACE_DRAWER_BRIDGE,
1660
+ useFactory: (service, injector, layerScale) => ({
1661
+ open: (opts) => {
1662
+ layerScale.ensureInstalled();
1663
+ return service.openHost({
1664
+ component: SurfaceDrawerComponent,
1665
+ config: {
1666
+ id: opts.id,
1667
+ scope: 'surface-drawer',
1668
+ title: opts.title,
1669
+ titleIcon: opts.titleIcon,
1670
+ subtitle: opts.subtitle,
1671
+ widthPreset: opts.widthPreset,
1672
+ width: opts.width,
1673
+ minWidth: opts.minWidth,
1674
+ maxWidth: opts.maxWidth,
1675
+ resizable: opts.resizable,
1676
+ resizeAxis: opts.resizeAxis,
1677
+ persistSizeKey: opts.persistSizeKey,
1678
+ content: {
1679
+ component: opts.content.component,
1680
+ inputs: (opts.content.inputs || undefined),
1681
+ },
1682
+ },
1683
+ overlaySize: {
1684
+ width: opts.width,
1685
+ minWidth: opts.minWidth,
1686
+ maxWidth: opts.maxWidth,
1687
+ height: opts.height,
1688
+ minHeight: opts.minHeight,
1689
+ maxHeight: opts.maxHeight,
1690
+ },
1691
+ createRef: (overlayRef) => new BaseSidePanelOverlayRef(overlayRef),
1692
+ attach: (panelRef, ref) => {
1693
+ panelRef.instance.title = opts.title;
1694
+ panelRef.instance.titleIcon = opts.titleIcon;
1695
+ panelRef.instance.subtitle = opts.subtitle;
1696
+ panelRef.instance.widthPreset = opts.widthPreset ?? 'default';
1697
+ panelRef.instance.useHostWidth = !!(opts.width ||
1698
+ opts.minWidth ||
1699
+ opts.maxWidth);
1700
+ panelRef.instance.onClose = () => ref.close({ type: 'close' });
1701
+ ref.bindTitleUpdater((title) => panelRef.instance.setTitle(title));
1702
+ const contentInjector = Injector.create({
1703
+ providers: [
1704
+ { provide: BASE_SIDE_PANEL_DATA, useValue: opts.content.inputs },
1705
+ { provide: BASE_SIDE_PANEL_REF, useValue: ref },
1706
+ ],
1707
+ parent: injector,
1708
+ });
1709
+ panelRef.instance.attachContent(opts.content.component, contentInjector, opts.content.inputs);
1710
+ panelRef.changeDetectorRef.detectChanges();
1711
+ },
1712
+ createCloser: (ref) => (reason = 'close') => ref.close({ type: reason }),
1713
+ onBackdrop: (ref) => ref.close({ type: 'backdrop' }),
1714
+ onEsc: (ref) => ref.close({ type: 'esc' }),
1715
+ });
895
1716
  },
896
1717
  }),
897
- }),
898
- deps: [SettingsPanelService],
899
- };
1718
+ deps: [BaseSidePanelService, Injector, PraxisLayerScaleStyleService],
1719
+ },
1720
+ ];
900
1721
  }
901
1722
 
1723
+ /**
1724
+ * Neutral theme contract for the future shared drawer base.
1725
+ *
1726
+ * This contract intentionally does not expose `settings-panel` naming so the
1727
+ * runtime drawer can inherit host look-and-feel without importing authoring
1728
+ * semantics or product-specific skin.
1729
+ */
1730
+ const SIDE_PANEL_THEME_CSS_VARS = [
1731
+ '--pfx-side-panel-width',
1732
+ '--pfx-side-panel-max-width',
1733
+ '--pfx-side-panel-header-height',
1734
+ '--pfx-side-panel-header-gap',
1735
+ '--pfx-side-panel-header-padding-x',
1736
+ '--pfx-side-panel-body-padding',
1737
+ '--pfx-side-panel-footer-padding',
1738
+ '--pfx-side-panel-footer-gap',
1739
+ '--pfx-side-panel-border-radius',
1740
+ '--pfx-side-panel-elevation',
1741
+ '--pfx-side-panel-backdrop',
1742
+ '--pfx-side-panel-backdrop-blur',
1743
+ '--pfx-side-panel-motion-duration-enter',
1744
+ '--pfx-side-panel-motion-duration-exit',
1745
+ '--pfx-side-panel-motion-easing-enter',
1746
+ '--pfx-side-panel-motion-easing-exit',
1747
+ ];
1748
+
902
1749
  /**
903
1750
  * Admin helper to load, patch and persist GlobalConfig.
904
1751
  * Intended to be used by the Global Config Editor UI.
@@ -2867,7 +3714,7 @@ class GlobalConfigEditorComponent {
2867
3714
  }
2868
3715
  return true;
2869
3716
  }
2870
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: GlobalConfigEditorComponent, deps: [{ token: GlobalConfigAdminService }, { token: i2.MatSnackBar }], target: i0.ɵɵFactoryTarget.Component });
3717
+ 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 });
2871
3718
  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: `
2872
3719
  <mat-accordion multi>
2873
3720
  <mat-expansion-panel>
@@ -3124,7 +3971,7 @@ class GlobalConfigEditorComponent {
3124
3971
  </div>
3125
3972
  </mat-expansion-panel>
3126
3973
  </mat-accordion>
3127
- `, 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: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i3.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i3.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$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: 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"] }] });
3974
+ `, 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"] }] });
3128
3975
  }
3129
3976
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: GlobalConfigEditorComponent, decorators: [{
3130
3977
  type: Component,
@@ -3385,7 +4232,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
3385
4232
  </mat-expansion-panel>
3386
4233
  </mat-accordion>
3387
4234
  `, 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"] }]
3388
- }], ctorParameters: () => [{ type: GlobalConfigAdminService }, { type: i2.MatSnackBar }], propDecorators: { hostCrud: [{
4235
+ }], ctorParameters: () => [{ type: GlobalConfigAdminService }, { type: i2$1.MatSnackBar }], propDecorators: { hostCrud: [{
3389
4236
  type: ViewChild,
3390
4237
  args: ['hostCrud', { read: ViewContainerRef, static: true }]
3391
4238
  }], hostFields: [{
@@ -3463,5 +4310,4 @@ const SETTINGS_PANEL_AI_CAPABILITIES = {
3463
4310
  * Generated bundle index. Do not edit.
3464
4311
  */
3465
4312
 
3466
- export { GLOBAL_CONFIG_DYNAMIC_FORM_COMPONENT, GlobalConfigAdminService, GlobalConfigEditorComponent, SETTINGS_PANEL_AI_CAPABILITIES, SETTINGS_PANEL_DATA, SETTINGS_PANEL_REF, SettingsPanelComponent, SettingsPanelRef, SettingsPanelService, buildGlobalConfigFormConfig, openGlobalConfigEditor, providePraxisSettingsPanelBridge };
3467
- //# sourceMappingURL=praxisui-settings-panel.mjs.map
4313
+ export { BASE_SIDE_PANEL_DATA, BASE_SIDE_PANEL_REF, BaseSidePanelComponent, BaseSidePanelOverlayRef, BaseSidePanelService, 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 };