@life-cockpit/angular-ui-kit 1.2.1 → 1.3.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,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, PLATFORM_ID, signal, Injectable, input, computed, effect, ChangeDetectionStrategy, Component, model, EventEmitter, ViewChild, Output, Input, ViewEncapsulation, output, HostListener, forwardRef, ElementRef, viewChild, ChangeDetectorRef, ViewChildren, HostBinding, ViewContainerRef, Renderer2, Directive, TemplateRef, ContentChildren, ContentChild } from '@angular/core';
2
+ import { inject, PLATFORM_ID, signal, Injectable, input, computed, effect, ChangeDetectionStrategy, Component, model, EventEmitter, ViewChild, Output, Input, ViewEncapsulation, output, HostListener, forwardRef, ElementRef, viewChild, ChangeDetectorRef, ViewChildren, HostBinding, ViewContainerRef, Renderer2, Directive, TemplateRef, ContentChildren, ContentChild, SecurityContext, NgZone, ApplicationRef, EnvironmentInjector, createComponent } from '@angular/core';
3
3
  import * as i1 from '@angular/common';
4
4
  import { isPlatformBrowser, CommonModule, SlicePipe, NgStyle } from '@angular/common';
5
5
  import * as i1$4 from '@angular/platform-browser';
@@ -9,7 +9,7 @@ import * as i1$2 from '@angular/forms';
9
9
  import { NG_VALUE_ACCESSOR, NgControl, FormsModule, ReactiveFormsModule } from '@angular/forms';
10
10
  import * as i1$1 from '@angular/cdk/overlay';
11
11
  import { OverlayModule, Overlay } from '@angular/cdk/overlay';
12
- import { Subject, debounceTime, distinctUntilChanged, takeUntil } from 'rxjs';
12
+ import { Subject, debounceTime, distinctUntilChanged, takeUntil, switchMap, of } from 'rxjs';
13
13
  import * as i1$3 from '@angular/cdk/a11y';
14
14
  import { ConfigurableFocusTrapFactory, A11yModule } from '@angular/cdk/a11y';
15
15
  import { ComponentPortal } from '@angular/cdk/portal';
@@ -708,11 +708,11 @@ class ButtonComponent {
708
708
  this.buttonElement?.nativeElement.blur();
709
709
  }
710
710
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
711
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: ButtonComponent, isStandalone: true, selector: "lc-button", inputs: { variant: "variant", size: "size", disabled: "disabled", loading: "loading", isLoading: "isLoading", iconOnly: "iconOnly", fullWidth: "fullWidth", ariaLabel: "ariaLabel", type: "type" }, outputs: { clicked: "clicked", focused: "focused", blurred: "blurred" }, viewQueries: [{ propertyName: "buttonElement", first: true, predicate: ["buttonElement"], descendants: true }], ngImport: i0, template: "<button\n #buttonElement\n [type]=\"type\"\n [disabled]=\"isDisabled\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-disabled]=\"isDisabled ? 'true' : 'false'\"\n [attr.aria-busy]=\"loading ? 'true' : 'false'\"\n [ngClass]=\"{\n 'btn': true,\n 'btn-primary': variant === 'primary',\n 'btn-secondary': variant === 'secondary',\n 'btn-outline': variant === 'outline',\n 'btn-ghost': variant === 'ghost',\n 'btn-link': variant === 'link',\n 'btn-danger': variant === 'danger',\n 'btn-warning': variant === 'warning',\n 'btn-info': variant === 'info',\n 'btn-xs': size === 'xs',\n 'btn-sm': size === 'sm',\n 'btn-md': size === 'md',\n 'btn-lg': size === 'lg',\n 'btn-icon-only': iconOnly,\n 'btn-full-width': fullWidth\n }\"\n (click)=\"handleClick()\"\n (focus)=\"handleFocus()\"\n (blur)=\"handleBlur()\"\n>\n @if (loading) {\n <span class=\"loading-spinner\"></span>\n }\n @if (!loading) {\n <ng-content select=\"[slot='icon-before']\" />\n }\n <ng-content></ng-content>\n @if (!loading) {\n <ng-content select=\"[slot='icon-after']\" />\n }\n</button>\n", styles: [".btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--spacing-2);font-family:var(--font-family-sans);font-weight:var(--font-weight-medium);line-height:var(--line-height-tight);border:1px solid transparent;border-radius:var(--spacing-2);cursor:pointer;transition:all var(--animation-duration-base) var(--animation-timing-ease);white-space:nowrap;-webkit-user-select:none;user-select:none}.btn:disabled{opacity:.5;cursor:not-allowed}.btn:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.btn-primary{background-color:var(--color-primary-600);color:var(--color-neutral-white)}.btn-primary:hover:not(:disabled){background-color:var(--color-primary-700)}.btn-primary:active:not(:disabled){background-color:var(--color-primary-800)}.dark .btn-primary{background-color:#144f5b}.dark .btn-primary:hover:not(:disabled){background-color:#1a6a79}.dark .btn-primary:active:not(:disabled){background-color:#0d353d}.btn-secondary{background-color:var(--color-secondary-600);color:var(--color-neutral-white)}.btn-secondary:hover:not(:disabled){background-color:var(--color-secondary-700)}.btn-secondary:active:not(:disabled){background-color:var(--color-secondary-800)}.dark .btn-secondary{background-color:#435b69}.dark .btn-secondary:hover:not(:disabled){background-color:#5a798c}.dark .btn-secondary:active:not(:disabled){background-color:#2d3c46}.btn-outline{background-color:transparent;border-color:var(--color-neutral-300);color:var(--color-neutral-900)}.btn-outline:hover:not(:disabled){background-color:var(--color-neutral-50);border-color:var(--color-neutral-400)}.btn-outline:active:not(:disabled){background-color:var(--color-neutral-100)}.btn-ghost{background-color:transparent;color:var(--color-neutral-700)}.btn-ghost:hover:not(:disabled){background-color:var(--color-neutral-100)}.btn-ghost:active:not(:disabled){background-color:var(--color-neutral-200)}.btn-link{background-color:transparent;color:var(--color-primary-600);padding:0;border:none}.btn-link:hover:not(:disabled){color:var(--color-primary-700);text-decoration:underline}.btn-link:active:not(:disabled){color:var(--color-primary-800)}.btn-danger{background-color:var(--color-error-600);color:var(--color-neutral-white)}.btn-danger:hover:not(:disabled){background-color:var(--color-error-700)}.btn-danger:active:not(:disabled){background-color:var(--color-error-800)}.dark .btn-danger{background-color:#6b0909}.dark .btn-danger:hover:not(:disabled){background-color:#9d0e0e}.dark .btn-danger:active:not(:disabled){background-color:#6b0909;filter:brightness(.85)}.btn-warning{background-color:#e1a040;color:var(--color-neutral-white)}.btn-warning:hover:not(:disabled){background-color:#9b6b2b}.btn-warning:active:not(:disabled){background-color:#9b6b2b;filter:brightness(.85)}.dark .btn-warning{background-color:#e1a040}.dark .btn-warning:hover:not(:disabled){background-color:#f1d3a7}.dark .btn-warning:active:not(:disabled){background-color:#e1a040;filter:brightness(.85)}.btn-info{background-color:#3b6588;color:var(--color-neutral-white)}.btn-info:hover:not(:disabled){background-color:#27445c}.btn-info:active:not(:disabled){background-color:#27445c;filter:brightness(.85)}.dark .btn-info{background-color:#3b6588}.dark .btn-info:hover:not(:disabled){background-color:#b8cee0}.dark .btn-info:active:not(:disabled){background-color:#3b6588;filter:brightness(.85)}.btn-xs{padding:var(--spacing-1) var(--spacing-2);font-size:var(--font-size-xs)}.btn-sm{padding:var(--spacing-2) var(--spacing-3);font-size:var(--font-size-sm)}.btn-md{padding:var(--spacing-2-5) var(--spacing-4);font-size:var(--font-size-base)}.btn-lg{padding:var(--spacing-3) var(--spacing-6);font-size:var(--font-size-lg)}.btn-icon-only.btn-xs{padding:var(--spacing-1);width:calc(var(--spacing-1) * 2 + 1em);height:calc(var(--spacing-1) * 2 + 1em)}.btn-icon-only.btn-sm{padding:var(--spacing-2);width:calc(var(--spacing-2) * 2 + 1em);height:calc(var(--spacing-2) * 2 + 1em)}.btn-icon-only.btn-md{padding:var(--spacing-2-5);width:calc(var(--spacing-2-5) * 2 + 1em);height:calc(var(--spacing-2-5) * 2 + 1em)}.btn-icon-only.btn-lg{padding:var(--spacing-3);width:calc(var(--spacing-3) * 2 + 1.2em);height:calc(var(--spacing-3) * 2 + 1.2em)}.btn-full-width{width:100%}.loading-spinner{display:inline-block;width:1em;height:1em;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.icon-before,.icon-after{display:inline-flex;align-items:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
711
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: ButtonComponent, isStandalone: true, selector: "lc-button", inputs: { variant: "variant", size: "size", disabled: "disabled", loading: "loading", isLoading: "isLoading", iconOnly: "iconOnly", fullWidth: "fullWidth", ariaLabel: "ariaLabel", type: "type" }, outputs: { clicked: "clicked", focused: "focused", blurred: "blurred" }, viewQueries: [{ propertyName: "buttonElement", first: true, predicate: ["buttonElement"], descendants: true }], ngImport: i0, template: "<button\n #buttonElement\n [type]=\"type\"\n [disabled]=\"isDisabled\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-disabled]=\"isDisabled ? 'true' : 'false'\"\n [attr.aria-busy]=\"loading ? 'true' : 'false'\"\n [ngClass]=\"{\n 'btn': true,\n 'btn-primary': variant === 'primary',\n 'btn-secondary': variant === 'secondary',\n 'btn-outline': variant === 'outline',\n 'btn-ghost': variant === 'ghost',\n 'btn-link': variant === 'link',\n 'btn-danger': variant === 'danger',\n 'btn-warning': variant === 'warning',\n 'btn-info': variant === 'info',\n 'btn-xs': size === 'xs',\n 'btn-sm': size === 'sm',\n 'btn-md': size === 'md',\n 'btn-lg': size === 'lg',\n 'btn-icon-only': iconOnly,\n 'btn-full-width': fullWidth\n }\"\n (click)=\"handleClick()\"\n (focus)=\"handleFocus()\"\n (blur)=\"handleBlur()\"\n>\n @if (loading) {\n <span class=\"loading-spinner\"></span>\n }\n @if (!loading) {\n <ng-content select=\"[slot='icon-before']\" />\n }\n <ng-content></ng-content>\n @if (!loading) {\n <ng-content select=\"[slot='icon-after']\" />\n }\n</button>\n", styles: [".btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--spacing-2);font-family:var(--font-family-sans);font-weight:var(--font-weight-medium);line-height:var(--line-height-tight);border:1px solid transparent;border-radius:var(--spacing-2);cursor:pointer;transition:all var(--animation-duration-base) var(--animation-timing-ease);white-space:nowrap;-webkit-user-select:none;user-select:none}.btn:disabled{opacity:.5;cursor:not-allowed}.btn:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.btn-primary{background-color:var(--color-primary-600);color:var(--color-neutral-white)}.btn-primary:hover:not(:disabled){background-color:var(--color-primary-700)}.btn-primary:active:not(:disabled){background-color:var(--color-primary-800)}.dark .btn-primary{background-color:#144f5b}.dark .btn-primary:hover:not(:disabled){background-color:#1a6a79}.dark .btn-primary:active:not(:disabled){background-color:#0d353d}.btn-secondary{background-color:var(--color-secondary-600);color:var(--color-neutral-white)}.btn-secondary:hover:not(:disabled){background-color:var(--color-secondary-700)}.btn-secondary:active:not(:disabled){background-color:var(--color-secondary-800)}.dark .btn-secondary{background-color:#435b69}.dark .btn-secondary:hover:not(:disabled){background-color:#5a798c}.dark .btn-secondary:active:not(:disabled){background-color:#2d3c46}.btn-outline{background-color:transparent;border-color:var(--color-neutral-300);color:var(--color-neutral-900)}.btn-outline:hover:not(:disabled){background-color:var(--color-neutral-50);border-color:var(--color-neutral-400)}.btn-outline:active:not(:disabled){background-color:var(--color-neutral-100)}.btn-ghost{background-color:var(--lc-button-ghost-bg, transparent);color:var(--lc-button-ghost-fg, var(--color-neutral-700))}.btn-ghost:hover:not(:disabled){background-color:var(--lc-button-ghost-hover-bg, var(--color-neutral-100));color:var(--lc-button-ghost-hover-fg, var(--lc-button-ghost-fg, var(--color-neutral-900)))}.btn-ghost:active:not(:disabled){background-color:var(--lc-button-ghost-active-bg, var(--color-neutral-200))}.btn-link{background-color:transparent;color:var(--color-primary-600);padding:0;border:none}.btn-link:hover:not(:disabled){color:var(--color-primary-700);text-decoration:underline}.btn-link:active:not(:disabled){color:var(--color-primary-800)}.btn-danger{background-color:var(--color-error-600);color:var(--color-neutral-white)}.btn-danger:hover:not(:disabled){background-color:var(--color-error-700)}.btn-danger:active:not(:disabled){background-color:var(--color-error-800)}.dark .btn-danger{background-color:#6b0909}.dark .btn-danger:hover:not(:disabled){background-color:#9d0e0e}.dark .btn-danger:active:not(:disabled){background-color:#6b0909;filter:brightness(.85)}.btn-warning{background-color:#e1a040;color:var(--color-neutral-white)}.btn-warning:hover:not(:disabled){background-color:#9b6b2b}.btn-warning:active:not(:disabled){background-color:#9b6b2b;filter:brightness(.85)}.dark .btn-warning{background-color:#e1a040}.dark .btn-warning:hover:not(:disabled){background-color:#f1d3a7}.dark .btn-warning:active:not(:disabled){background-color:#e1a040;filter:brightness(.85)}.btn-info{background-color:#3b6588;color:var(--color-neutral-white)}.btn-info:hover:not(:disabled){background-color:#27445c}.btn-info:active:not(:disabled){background-color:#27445c;filter:brightness(.85)}.dark .btn-info{background-color:#3b6588}.dark .btn-info:hover:not(:disabled){background-color:#b8cee0}.dark .btn-info:active:not(:disabled){background-color:#3b6588;filter:brightness(.85)}.btn-xs{padding:var(--spacing-1) var(--spacing-2);font-size:var(--font-size-xs)}.btn-sm{padding:var(--spacing-2) var(--spacing-3);font-size:var(--font-size-sm)}.btn-md{padding:var(--spacing-2-5) var(--spacing-4);font-size:var(--font-size-base)}.btn-lg{padding:var(--spacing-3) var(--spacing-6);font-size:var(--font-size-lg)}.btn-icon-only.btn-xs{padding:var(--spacing-1);width:calc(var(--spacing-1) * 2 + 1em);height:calc(var(--spacing-1) * 2 + 1em)}.btn-icon-only.btn-sm{padding:var(--spacing-2);width:calc(var(--spacing-2) * 2 + 1em);height:calc(var(--spacing-2) * 2 + 1em)}.btn-icon-only.btn-md{padding:var(--spacing-2-5);width:calc(var(--spacing-2-5) * 2 + 1em);height:calc(var(--spacing-2-5) * 2 + 1em)}.btn-icon-only.btn-lg{padding:var(--spacing-3);width:calc(var(--spacing-3) * 2 + 1.2em);height:calc(var(--spacing-3) * 2 + 1.2em)}.btn-full-width{width:100%}.loading-spinner{display:inline-block;width:1em;height:1em;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.icon-before,.icon-after{display:inline-flex;align-items:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
712
712
  }
713
713
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ButtonComponent, decorators: [{
714
714
  type: Component,
715
- args: [{ selector: 'lc-button', standalone: true, imports: [CommonModule], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<button\n #buttonElement\n [type]=\"type\"\n [disabled]=\"isDisabled\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-disabled]=\"isDisabled ? 'true' : 'false'\"\n [attr.aria-busy]=\"loading ? 'true' : 'false'\"\n [ngClass]=\"{\n 'btn': true,\n 'btn-primary': variant === 'primary',\n 'btn-secondary': variant === 'secondary',\n 'btn-outline': variant === 'outline',\n 'btn-ghost': variant === 'ghost',\n 'btn-link': variant === 'link',\n 'btn-danger': variant === 'danger',\n 'btn-warning': variant === 'warning',\n 'btn-info': variant === 'info',\n 'btn-xs': size === 'xs',\n 'btn-sm': size === 'sm',\n 'btn-md': size === 'md',\n 'btn-lg': size === 'lg',\n 'btn-icon-only': iconOnly,\n 'btn-full-width': fullWidth\n }\"\n (click)=\"handleClick()\"\n (focus)=\"handleFocus()\"\n (blur)=\"handleBlur()\"\n>\n @if (loading) {\n <span class=\"loading-spinner\"></span>\n }\n @if (!loading) {\n <ng-content select=\"[slot='icon-before']\" />\n }\n <ng-content></ng-content>\n @if (!loading) {\n <ng-content select=\"[slot='icon-after']\" />\n }\n</button>\n", styles: [".btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--spacing-2);font-family:var(--font-family-sans);font-weight:var(--font-weight-medium);line-height:var(--line-height-tight);border:1px solid transparent;border-radius:var(--spacing-2);cursor:pointer;transition:all var(--animation-duration-base) var(--animation-timing-ease);white-space:nowrap;-webkit-user-select:none;user-select:none}.btn:disabled{opacity:.5;cursor:not-allowed}.btn:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.btn-primary{background-color:var(--color-primary-600);color:var(--color-neutral-white)}.btn-primary:hover:not(:disabled){background-color:var(--color-primary-700)}.btn-primary:active:not(:disabled){background-color:var(--color-primary-800)}.dark .btn-primary{background-color:#144f5b}.dark .btn-primary:hover:not(:disabled){background-color:#1a6a79}.dark .btn-primary:active:not(:disabled){background-color:#0d353d}.btn-secondary{background-color:var(--color-secondary-600);color:var(--color-neutral-white)}.btn-secondary:hover:not(:disabled){background-color:var(--color-secondary-700)}.btn-secondary:active:not(:disabled){background-color:var(--color-secondary-800)}.dark .btn-secondary{background-color:#435b69}.dark .btn-secondary:hover:not(:disabled){background-color:#5a798c}.dark .btn-secondary:active:not(:disabled){background-color:#2d3c46}.btn-outline{background-color:transparent;border-color:var(--color-neutral-300);color:var(--color-neutral-900)}.btn-outline:hover:not(:disabled){background-color:var(--color-neutral-50);border-color:var(--color-neutral-400)}.btn-outline:active:not(:disabled){background-color:var(--color-neutral-100)}.btn-ghost{background-color:transparent;color:var(--color-neutral-700)}.btn-ghost:hover:not(:disabled){background-color:var(--color-neutral-100)}.btn-ghost:active:not(:disabled){background-color:var(--color-neutral-200)}.btn-link{background-color:transparent;color:var(--color-primary-600);padding:0;border:none}.btn-link:hover:not(:disabled){color:var(--color-primary-700);text-decoration:underline}.btn-link:active:not(:disabled){color:var(--color-primary-800)}.btn-danger{background-color:var(--color-error-600);color:var(--color-neutral-white)}.btn-danger:hover:not(:disabled){background-color:var(--color-error-700)}.btn-danger:active:not(:disabled){background-color:var(--color-error-800)}.dark .btn-danger{background-color:#6b0909}.dark .btn-danger:hover:not(:disabled){background-color:#9d0e0e}.dark .btn-danger:active:not(:disabled){background-color:#6b0909;filter:brightness(.85)}.btn-warning{background-color:#e1a040;color:var(--color-neutral-white)}.btn-warning:hover:not(:disabled){background-color:#9b6b2b}.btn-warning:active:not(:disabled){background-color:#9b6b2b;filter:brightness(.85)}.dark .btn-warning{background-color:#e1a040}.dark .btn-warning:hover:not(:disabled){background-color:#f1d3a7}.dark .btn-warning:active:not(:disabled){background-color:#e1a040;filter:brightness(.85)}.btn-info{background-color:#3b6588;color:var(--color-neutral-white)}.btn-info:hover:not(:disabled){background-color:#27445c}.btn-info:active:not(:disabled){background-color:#27445c;filter:brightness(.85)}.dark .btn-info{background-color:#3b6588}.dark .btn-info:hover:not(:disabled){background-color:#b8cee0}.dark .btn-info:active:not(:disabled){background-color:#3b6588;filter:brightness(.85)}.btn-xs{padding:var(--spacing-1) var(--spacing-2);font-size:var(--font-size-xs)}.btn-sm{padding:var(--spacing-2) var(--spacing-3);font-size:var(--font-size-sm)}.btn-md{padding:var(--spacing-2-5) var(--spacing-4);font-size:var(--font-size-base)}.btn-lg{padding:var(--spacing-3) var(--spacing-6);font-size:var(--font-size-lg)}.btn-icon-only.btn-xs{padding:var(--spacing-1);width:calc(var(--spacing-1) * 2 + 1em);height:calc(var(--spacing-1) * 2 + 1em)}.btn-icon-only.btn-sm{padding:var(--spacing-2);width:calc(var(--spacing-2) * 2 + 1em);height:calc(var(--spacing-2) * 2 + 1em)}.btn-icon-only.btn-md{padding:var(--spacing-2-5);width:calc(var(--spacing-2-5) * 2 + 1em);height:calc(var(--spacing-2-5) * 2 + 1em)}.btn-icon-only.btn-lg{padding:var(--spacing-3);width:calc(var(--spacing-3) * 2 + 1.2em);height:calc(var(--spacing-3) * 2 + 1.2em)}.btn-full-width{width:100%}.loading-spinner{display:inline-block;width:1em;height:1em;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.icon-before,.icon-after{display:inline-flex;align-items:center}\n"] }]
715
+ args: [{ selector: 'lc-button', standalone: true, imports: [CommonModule], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<button\n #buttonElement\n [type]=\"type\"\n [disabled]=\"isDisabled\"\n [attr.aria-label]=\"ariaLabel || null\"\n [attr.aria-disabled]=\"isDisabled ? 'true' : 'false'\"\n [attr.aria-busy]=\"loading ? 'true' : 'false'\"\n [ngClass]=\"{\n 'btn': true,\n 'btn-primary': variant === 'primary',\n 'btn-secondary': variant === 'secondary',\n 'btn-outline': variant === 'outline',\n 'btn-ghost': variant === 'ghost',\n 'btn-link': variant === 'link',\n 'btn-danger': variant === 'danger',\n 'btn-warning': variant === 'warning',\n 'btn-info': variant === 'info',\n 'btn-xs': size === 'xs',\n 'btn-sm': size === 'sm',\n 'btn-md': size === 'md',\n 'btn-lg': size === 'lg',\n 'btn-icon-only': iconOnly,\n 'btn-full-width': fullWidth\n }\"\n (click)=\"handleClick()\"\n (focus)=\"handleFocus()\"\n (blur)=\"handleBlur()\"\n>\n @if (loading) {\n <span class=\"loading-spinner\"></span>\n }\n @if (!loading) {\n <ng-content select=\"[slot='icon-before']\" />\n }\n <ng-content></ng-content>\n @if (!loading) {\n <ng-content select=\"[slot='icon-after']\" />\n }\n</button>\n", styles: [".btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--spacing-2);font-family:var(--font-family-sans);font-weight:var(--font-weight-medium);line-height:var(--line-height-tight);border:1px solid transparent;border-radius:var(--spacing-2);cursor:pointer;transition:all var(--animation-duration-base) var(--animation-timing-ease);white-space:nowrap;-webkit-user-select:none;user-select:none}.btn:disabled{opacity:.5;cursor:not-allowed}.btn:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.btn-primary{background-color:var(--color-primary-600);color:var(--color-neutral-white)}.btn-primary:hover:not(:disabled){background-color:var(--color-primary-700)}.btn-primary:active:not(:disabled){background-color:var(--color-primary-800)}.dark .btn-primary{background-color:#144f5b}.dark .btn-primary:hover:not(:disabled){background-color:#1a6a79}.dark .btn-primary:active:not(:disabled){background-color:#0d353d}.btn-secondary{background-color:var(--color-secondary-600);color:var(--color-neutral-white)}.btn-secondary:hover:not(:disabled){background-color:var(--color-secondary-700)}.btn-secondary:active:not(:disabled){background-color:var(--color-secondary-800)}.dark .btn-secondary{background-color:#435b69}.dark .btn-secondary:hover:not(:disabled){background-color:#5a798c}.dark .btn-secondary:active:not(:disabled){background-color:#2d3c46}.btn-outline{background-color:transparent;border-color:var(--color-neutral-300);color:var(--color-neutral-900)}.btn-outline:hover:not(:disabled){background-color:var(--color-neutral-50);border-color:var(--color-neutral-400)}.btn-outline:active:not(:disabled){background-color:var(--color-neutral-100)}.btn-ghost{background-color:var(--lc-button-ghost-bg, transparent);color:var(--lc-button-ghost-fg, var(--color-neutral-700))}.btn-ghost:hover:not(:disabled){background-color:var(--lc-button-ghost-hover-bg, var(--color-neutral-100));color:var(--lc-button-ghost-hover-fg, var(--lc-button-ghost-fg, var(--color-neutral-900)))}.btn-ghost:active:not(:disabled){background-color:var(--lc-button-ghost-active-bg, var(--color-neutral-200))}.btn-link{background-color:transparent;color:var(--color-primary-600);padding:0;border:none}.btn-link:hover:not(:disabled){color:var(--color-primary-700);text-decoration:underline}.btn-link:active:not(:disabled){color:var(--color-primary-800)}.btn-danger{background-color:var(--color-error-600);color:var(--color-neutral-white)}.btn-danger:hover:not(:disabled){background-color:var(--color-error-700)}.btn-danger:active:not(:disabled){background-color:var(--color-error-800)}.dark .btn-danger{background-color:#6b0909}.dark .btn-danger:hover:not(:disabled){background-color:#9d0e0e}.dark .btn-danger:active:not(:disabled){background-color:#6b0909;filter:brightness(.85)}.btn-warning{background-color:#e1a040;color:var(--color-neutral-white)}.btn-warning:hover:not(:disabled){background-color:#9b6b2b}.btn-warning:active:not(:disabled){background-color:#9b6b2b;filter:brightness(.85)}.dark .btn-warning{background-color:#e1a040}.dark .btn-warning:hover:not(:disabled){background-color:#f1d3a7}.dark .btn-warning:active:not(:disabled){background-color:#e1a040;filter:brightness(.85)}.btn-info{background-color:#3b6588;color:var(--color-neutral-white)}.btn-info:hover:not(:disabled){background-color:#27445c}.btn-info:active:not(:disabled){background-color:#27445c;filter:brightness(.85)}.dark .btn-info{background-color:#3b6588}.dark .btn-info:hover:not(:disabled){background-color:#b8cee0}.dark .btn-info:active:not(:disabled){background-color:#3b6588;filter:brightness(.85)}.btn-xs{padding:var(--spacing-1) var(--spacing-2);font-size:var(--font-size-xs)}.btn-sm{padding:var(--spacing-2) var(--spacing-3);font-size:var(--font-size-sm)}.btn-md{padding:var(--spacing-2-5) var(--spacing-4);font-size:var(--font-size-base)}.btn-lg{padding:var(--spacing-3) var(--spacing-6);font-size:var(--font-size-lg)}.btn-icon-only.btn-xs{padding:var(--spacing-1);width:calc(var(--spacing-1) * 2 + 1em);height:calc(var(--spacing-1) * 2 + 1em)}.btn-icon-only.btn-sm{padding:var(--spacing-2);width:calc(var(--spacing-2) * 2 + 1em);height:calc(var(--spacing-2) * 2 + 1em)}.btn-icon-only.btn-md{padding:var(--spacing-2-5);width:calc(var(--spacing-2-5) * 2 + 1em);height:calc(var(--spacing-2-5) * 2 + 1em)}.btn-icon-only.btn-lg{padding:var(--spacing-3);width:calc(var(--spacing-3) * 2 + 1.2em);height:calc(var(--spacing-3) * 2 + 1.2em)}.btn-full-width{width:100%}.loading-spinner{display:inline-block;width:1em;height:1em;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.icon-before,.icon-after{display:inline-flex;align-items:center}\n"] }]
716
716
  }], propDecorators: { variant: [{
717
717
  type: Input
718
718
  }], size: [{
@@ -5841,7 +5841,7 @@ class HeaderComponent {
5841
5841
  }
5842
5842
  }
5843
5843
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: HeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5844
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: HeaderComponent, isStandalone: true, selector: "lc-header", inputs: { theme: "theme", logo: "logo", title: "title", subtitle: "subtitle", userEmail: "userEmail", userName: "userName", showHamburger: "showHamburger", showThemeButton: "showThemeButton", menuSize: "menuSize", showProfileMenuItem: "showProfileMenuItem" }, outputs: { hamburgerClick: "hamburgerClick", themeToggleClick: "themeToggleClick", logoutClick: "logoutClick", profileClick: "profileClick" }, host: { classAttribute: "lc-header-host" }, ngImport: i0, template: "<header class=\"lc-header\" [class.lc-header--dark]=\"theme === 'dark'\" [class.lc-header--light]=\"theme === 'light'\">\n <!-- Hamburger menu icon (mobile only) -->\n @if (showHamburger) {\n <lc-button\n variant=\"ghost\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [ariaLabel]=\"'Toggle sidebar menu'\"\n (clicked)=\"onHamburgerClick()\"\n class=\"lc-header__hamburger\"\n >\n <lc-icon name=\"bars-3\" size=\"sm\" />\n </lc-button>\n }\n\n <!-- Logo (clickable, navigates to home) -->\n <div class=\"lc-header__brand\">\n @if (logo || title) {\n <a routerLink=\"/\" class=\"lc-header__logo\" aria-label=\"Go to home\">\n <lc-logo [variant]=\"logo ? 'full' : 'emblem'\" size=\"md\" [clickable]=\"false\" [colorMode]=\"theme === 'dark' ? 'dark' : theme === 'light' ? 'light' : 'auto'\" />\n </a>\n }\n \n @if (title) {\n <div class=\"lc-header__title-group\">\n <h1 class=\"lc-header__title\">{{ title }}</h1>\n @if (subtitle) {\n <span class=\"lc-header__subtitle\">{{ subtitle }}</span>\n }\n </div>\n }\n </div>\n\n <div class=\"lc-header__spacer\"></div>\n\n <!-- Theme toggle button -->\n @if (showThemeButton) {\n <lc-button\n variant=\"ghost\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [ariaLabel]=\"themeService.isDark() ? 'Switch to light mode' : 'Switch to dark mode'\"\n (clicked)=\"onThemeButtonClick()\"\n class=\"lc-header__theme-toggle\"\n >\n <lc-icon [name]=\"themeService.isDark() ? 'sun' : 'moon'\" size=\"sm\" />\n </lc-button>\n }\n\n <!-- User profile menu -->\n <lc-menu\n [items]=\"menuItems()\"\n [isOpen]=\"isDropdownOpen()\"\n position=\"bottom-right\"\n minWidth=\"220px\"\n [size]=\"menuSize\"\n (itemClick)=\"onMenuItemClick($event)\"\n (closed)=\"closeDropdown()\"\n >\n <!-- Menu header with avatar, name and email -->\n @if (userName || userEmail) {\n <div header class=\"lc-header__menu-header\">\n <lc-avatar [name]=\"userName\" size=\"md\" />\n <div class=\"lc-header__menu-user-info\">\n @if (userName) {\n <div class=\"lc-header__menu-user-name\">{{ userName }}</div>\n }\n @if (userEmail) {\n <div class=\"lc-header__menu-user-email\">{{ userEmail }}</div>\n }\n </div>\n </div>\n }\n\n <button\n trigger\n class=\"lc-header__profile-trigger\"\n type=\"button\"\n aria-label=\"Open user menu\"\n [attr.aria-expanded]=\"isDropdownOpen()\"\n (click)=\"toggleDropdown()\"\n >\n <lc-icon name=\"user\" size=\"sm\" />\n <lc-icon name=\"chevron-down\" size=\"xs\" />\n </button>\n </lc-menu>\n</header>\n", styles: [".lc-header{--lc-header-bg: var(--color-neutral-0, #ffffff);--lc-header-fg: var(--color-neutral-900, #111827);--lc-header-fg-secondary: var(--color-neutral-600, #4b5563);--lc-header-border: var(--color-neutral-200, #e5e7eb);--lc-header-hover-bg: var(--color-neutral-100, #f3f4f6);--lc-header-trigger-border: var(--color-neutral-200, #e5e7eb);--lc-header-trigger-fg: var(--color-neutral-700, #374151);display:flex;align-items:center;gap:var(--spacing-4);padding:var(--spacing-3) var(--spacing-6);background-color:var(--lc-header-bg);border-bottom:1px solid var(--lc-header-border);height:64px;position:sticky;top:0;z-index:1000}@media(max-width:768px){.lc-header{padding:var(--spacing-3) var(--spacing-4)}}.lc-header--dark{--lc-header-bg: #18181b;--lc-header-fg: #f5f5f5;--lc-header-fg-secondary: #a1a1aa;--lc-header-border: #27272a;--lc-header-hover-bg: #27272a;--lc-header-trigger-border: #52525b;--lc-header-trigger-fg: #e4e4e7}.lc-header--light{--lc-header-bg: var(--color-neutral-0, #ffffff);--lc-header-fg: var(--color-neutral-900, #111827);--lc-header-fg-secondary: var(--color-neutral-600, #4b5563);--lc-header-border: var(--color-neutral-200, #e5e7eb);--lc-header-hover-bg: var(--color-neutral-100, #f3f4f6);--lc-header-trigger-border: var(--color-neutral-200, #e5e7eb);--lc-header-trigger-fg: var(--color-neutral-700, #374151)}html.dark .lc-header:not(.lc-header--light),:root.dark .lc-header:not(.lc-header--light),.dark .lc-header:not(.lc-header--light){--lc-header-bg: #18181b;--lc-header-fg: #f5f5f5;--lc-header-fg-secondary: #a1a1aa;--lc-header-border: #27272a;--lc-header-hover-bg: #27272a;--lc-header-trigger-border: #52525b;--lc-header-trigger-fg: #e4e4e7}.lc-header__hamburger{display:flex;align-items:center;justify-content:center;padding:var(--spacing-2);background:transparent;border:none;cursor:pointer;color:var(--lc-header-trigger-fg);border-radius:var(--radius-md);transition:background-color .2s ease,color .2s ease}.lc-header__hamburger:hover{background-color:var(--lc-header-hover-bg);color:var(--lc-header-fg)}.lc-header__hamburger:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-header__hamburger .lc-icon{width:24px;height:24px}.lc-header__brand{display:flex;align-items:center;gap:var(--spacing-3)}.lc-header__logo{display:flex;align-items:center;text-decoration:none;color:var(--lc-header-fg);flex-shrink:0}.lc-header__logo:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:4px;border-radius:var(--radius-sm)}.lc-header__logo-img{height:32px;width:auto}.lc-header__title-group{display:flex;flex-direction:column;gap:.125rem}.lc-header__title{font-size:1.25rem;font-weight:700;color:var(--lc-header-fg);margin:0;line-height:1.2;transition:color .2s ease}@media(max-width:640px){.lc-header__title{font-size:1.125rem}}.lc-header__subtitle{font-size:.75rem;font-weight:500;color:var(--lc-header-fg-secondary);text-transform:uppercase;letter-spacing:.05em;transition:color .2s ease}.lc-header__spacer{flex:1}.lc-header__theme-toggle{margin-right:var(--spacing-2);color:var(--lc-header-trigger-fg)}.lc-header--dark .lc-header__theme-toggle lc-icon,.lc-header--dark .lc-header__theme-toggle .lc-icon,.lc-header--dark .lc-header__theme-toggle svg,.lc-header--dark .lc-header__profile-trigger lc-icon,.lc-header--dark .lc-header__profile-trigger .lc-icon,.lc-header--dark .lc-header__profile-trigger svg,html.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle lc-icon,html.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle .lc-icon,html.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle svg,html.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger lc-icon,html.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger .lc-icon,html.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger svg,:root.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle lc-icon,:root.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle .lc-icon,:root.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle svg,:root.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger lc-icon,:root.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger .lc-icon,:root.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger svg,.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle lc-icon,.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle .lc-icon,.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle svg,.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger lc-icon,.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger .lc-icon,.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger svg{color:var(--lc-header-trigger-fg)}.lc-header--dark lc-button,html.dark .lc-header:not(.lc-header--light) lc-button,:root.dark .lc-header:not(.lc-header--light) lc-button,.dark .lc-header:not(.lc-header--light) lc-button{--lc-button-ghost-fg: var(--lc-header-trigger-fg);color:var(--lc-header-trigger-fg)}.lc-header__profile-trigger{display:flex;align-items:center;gap:var(--spacing-2);padding:var(--spacing-2) var(--spacing-3);background:transparent;border:1px solid var(--lc-header-trigger-border);border-radius:var(--radius-full);cursor:pointer;color:var(--lc-header-trigger-fg);transition:background-color .2s ease,border-color .2s ease}.lc-header__profile-trigger:hover{background-color:var(--lc-header-hover-bg);border-color:var(--lc-header-fg-secondary)}.lc-header__profile-trigger:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-menu__header{padding:var(--spacing-3) var(--spacing-4);font-size:var(--font-size-sm);color:var(--color-text-secondary, #6b7280);border-bottom:1px solid var(--color-divider, #e5e7eb);margin-bottom:var(--spacing-1);word-break:break-word}.lc-header__menu-header{display:flex;align-items:center;gap:var(--spacing-3);padding:var(--spacing-4);border-bottom:1px solid var(--color-divider, #e5e7eb);margin-bottom:var(--spacing-1)}.lc-header__menu-user-info{flex:1;min-width:0}.lc-header__menu-user-name{font-size:var(--font-size-sm);font-weight:600;color:var(--color-text, #111827);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.4}.lc-header__menu-user-email{font-size:var(--font-size-xs);color:var(--color-text-secondary, #6b7280);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.4}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: ButtonComponent, selector: "lc-button", inputs: ["variant", "size", "disabled", "loading", "isLoading", "iconOnly", "fullWidth", "ariaLabel", "type"], outputs: ["clicked", "focused", "blurred"] }, { kind: "component", type: LogoComponent, selector: "lc-logo", inputs: ["variant", "size", "alt", "clickable", "colorMode"] }, { kind: "component", type: IconComponent, selector: "lc-icon", inputs: ["name", "variant", "size", "color", "ariaLabel", "decorative"] }, { kind: "component", type: AvatarComponent, selector: "lc-avatar", inputs: ["src", "alt", "name", "size", "status"] }, { kind: "component", type: MenuComponent, selector: "lc-menu", inputs: ["items", "isOpen", "position", "size", "minWidth"], outputs: ["itemClick", "closed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
5844
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: HeaderComponent, isStandalone: true, selector: "lc-header", inputs: { theme: "theme", logo: "logo", title: "title", subtitle: "subtitle", userEmail: "userEmail", userName: "userName", showHamburger: "showHamburger", showThemeButton: "showThemeButton", menuSize: "menuSize", showProfileMenuItem: "showProfileMenuItem" }, outputs: { hamburgerClick: "hamburgerClick", themeToggleClick: "themeToggleClick", logoutClick: "logoutClick", profileClick: "profileClick" }, host: { classAttribute: "lc-header-host" }, ngImport: i0, template: "<header class=\"lc-header\" [class.lc-header--dark]=\"theme === 'dark'\" [class.lc-header--light]=\"theme === 'light'\">\n <!-- Hamburger menu icon (mobile only) -->\n @if (showHamburger) {\n <lc-button\n variant=\"ghost\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [ariaLabel]=\"'Toggle sidebar menu'\"\n (clicked)=\"onHamburgerClick()\"\n class=\"lc-header__hamburger\"\n >\n <lc-icon name=\"bars-3\" size=\"sm\" />\n </lc-button>\n }\n\n <!-- Logo (clickable, navigates to home) -->\n <div class=\"lc-header__brand\">\n @if (logo || title) {\n <a routerLink=\"/\" class=\"lc-header__logo\" aria-label=\"Go to home\">\n <lc-logo [variant]=\"logo ? 'full' : 'emblem'\" size=\"md\" [clickable]=\"false\" [colorMode]=\"theme === 'dark' ? 'dark' : theme === 'light' ? 'light' : 'auto'\" />\n </a>\n }\n \n @if (title) {\n <div class=\"lc-header__title-group\">\n <h1 class=\"lc-header__title\">{{ title }}</h1>\n @if (subtitle) {\n <span class=\"lc-header__subtitle\">{{ subtitle }}</span>\n }\n </div>\n }\n </div>\n\n <div class=\"lc-header__spacer\"></div>\n\n <!-- Theme toggle button -->\n @if (showThemeButton) {\n <lc-button\n variant=\"ghost\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [ariaLabel]=\"themeService.isDark() ? 'Switch to light mode' : 'Switch to dark mode'\"\n (clicked)=\"onThemeButtonClick()\"\n class=\"lc-header__theme-toggle\"\n >\n <lc-icon [name]=\"themeService.isDark() ? 'sun' : 'moon'\" size=\"sm\" />\n </lc-button>\n }\n\n <!-- User profile menu -->\n <lc-menu\n [items]=\"menuItems()\"\n [isOpen]=\"isDropdownOpen()\"\n position=\"bottom-right\"\n minWidth=\"220px\"\n [size]=\"menuSize\"\n (itemClick)=\"onMenuItemClick($event)\"\n (closed)=\"closeDropdown()\"\n >\n <!-- Menu header with avatar, name and email -->\n @if (userName || userEmail) {\n <div header class=\"lc-header__menu-header\">\n <lc-avatar [name]=\"userName\" size=\"md\" />\n <div class=\"lc-header__menu-user-info\">\n @if (userName) {\n <div class=\"lc-header__menu-user-name\">{{ userName }}</div>\n }\n @if (userEmail) {\n <div class=\"lc-header__menu-user-email\">{{ userEmail }}</div>\n }\n </div>\n </div>\n }\n\n <button\n trigger\n class=\"lc-header__profile-trigger\"\n type=\"button\"\n aria-label=\"Open user menu\"\n [attr.aria-expanded]=\"isDropdownOpen()\"\n (click)=\"toggleDropdown()\"\n >\n <lc-icon name=\"user\" size=\"sm\" />\n <lc-icon name=\"chevron-down\" size=\"xs\" />\n </button>\n </lc-menu>\n</header>\n", styles: [".lc-header{--lc-header-bg: var(--color-neutral-0, #ffffff);--lc-header-fg: var(--color-neutral-900, #111827);--lc-header-fg-secondary: var(--color-neutral-600, #4b5563);--lc-header-border: var(--color-neutral-200, #e5e7eb);--lc-header-hover-bg: var(--color-neutral-100, #f3f4f6);--lc-header-trigger-border: var(--color-neutral-200, #e5e7eb);--lc-header-trigger-fg: var(--color-neutral-700, #374151);display:flex;align-items:center;gap:var(--spacing-4);padding:var(--spacing-3) var(--spacing-6);background-color:var(--lc-header-bg);border-bottom:1px solid var(--lc-header-border);height:64px;position:sticky;top:0;z-index:1000}@media(max-width:768px){.lc-header{padding:var(--spacing-3) var(--spacing-4)}}.lc-header--dark{--lc-header-bg: #18181b;--lc-header-fg: #f5f5f5;--lc-header-fg-secondary: #a1a1aa;--lc-header-border: #27272a;--lc-header-hover-bg: #27272a;--lc-header-trigger-border: #52525b;--lc-header-trigger-fg: #e4e4e7}.lc-header--light{--lc-header-bg: var(--color-neutral-0, #ffffff);--lc-header-fg: var(--color-neutral-900, #111827);--lc-header-fg-secondary: var(--color-neutral-600, #4b5563);--lc-header-border: var(--color-neutral-200, #e5e7eb);--lc-header-hover-bg: var(--color-neutral-100, #f3f4f6);--lc-header-trigger-border: var(--color-neutral-200, #e5e7eb);--lc-header-trigger-fg: var(--color-neutral-700, #374151)}html.dark .lc-header:not(.lc-header--light),:root.dark .lc-header:not(.lc-header--light),.dark .lc-header:not(.lc-header--light){--lc-header-bg: #18181b;--lc-header-fg: #f5f5f5;--lc-header-fg-secondary: #a1a1aa;--lc-header-border: #27272a;--lc-header-hover-bg: #27272a;--lc-header-trigger-border: #52525b;--lc-header-trigger-fg: #e4e4e7}.lc-header__hamburger{display:flex;align-items:center;justify-content:center;padding:var(--spacing-2);background:transparent;border:none;cursor:pointer;color:var(--lc-header-trigger-fg);border-radius:var(--radius-md);transition:background-color .2s ease,color .2s ease}.lc-header__hamburger:hover{background-color:var(--lc-header-hover-bg);color:var(--lc-header-fg)}.lc-header__hamburger:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-header__hamburger .lc-icon{width:24px;height:24px}.lc-header__brand{display:flex;align-items:center;gap:var(--spacing-3)}.lc-header__logo{display:flex;align-items:center;text-decoration:none;color:var(--lc-header-fg);flex-shrink:0}.lc-header__logo:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:4px;border-radius:var(--radius-sm)}.lc-header__logo-img{height:32px;width:auto}.lc-header__title-group{display:flex;flex-direction:column;gap:.125rem}.lc-header__title{font-size:1.25rem;font-weight:700;color:var(--lc-header-fg);margin:0;line-height:1.2;transition:color .2s ease}@media(max-width:640px){.lc-header__title{font-size:1.125rem}}.lc-header__subtitle{font-size:.75rem;font-weight:500;color:var(--lc-header-fg-secondary);text-transform:uppercase;letter-spacing:.05em;transition:color .2s ease}.lc-header__spacer{flex:1}.lc-header__theme-toggle{margin-right:var(--spacing-2);color:var(--lc-header-trigger-fg)}.lc-header--dark .lc-header__theme-toggle lc-icon,.lc-header--dark .lc-header__theme-toggle .lc-icon,.lc-header--dark .lc-header__theme-toggle svg,.lc-header--dark .lc-header__hamburger lc-icon,.lc-header--dark .lc-header__hamburger .lc-icon,.lc-header--dark .lc-header__hamburger svg,.lc-header--dark .lc-header__profile-trigger lc-icon,.lc-header--dark .lc-header__profile-trigger .lc-icon,.lc-header--dark .lc-header__profile-trigger svg,html.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle lc-icon,html.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle .lc-icon,html.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle svg,html.dark .lc-header:not(.lc-header--light) .lc-header__hamburger lc-icon,html.dark .lc-header:not(.lc-header--light) .lc-header__hamburger .lc-icon,html.dark .lc-header:not(.lc-header--light) .lc-header__hamburger svg,html.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger lc-icon,html.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger .lc-icon,html.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger svg,:root.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle lc-icon,:root.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle .lc-icon,:root.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle svg,:root.dark .lc-header:not(.lc-header--light) .lc-header__hamburger lc-icon,:root.dark .lc-header:not(.lc-header--light) .lc-header__hamburger .lc-icon,:root.dark .lc-header:not(.lc-header--light) .lc-header__hamburger svg,:root.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger lc-icon,:root.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger .lc-icon,:root.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger svg,.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle lc-icon,.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle .lc-icon,.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle svg,.dark .lc-header:not(.lc-header--light) .lc-header__hamburger lc-icon,.dark .lc-header:not(.lc-header--light) .lc-header__hamburger .lc-icon,.dark .lc-header:not(.lc-header--light) .lc-header__hamburger svg,.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger lc-icon,.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger .lc-icon,.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger svg{color:var(--lc-header-trigger-fg)}.lc-header--dark lc-button,html.dark .lc-header:not(.lc-header--light) lc-button,:root.dark .lc-header:not(.lc-header--light) lc-button,.dark .lc-header:not(.lc-header--light) lc-button{--lc-button-ghost-fg: var(--lc-header-trigger-fg);--lc-button-ghost-hover-bg: var(--lc-header-hover-bg);--lc-button-ghost-hover-fg: var(--lc-header-fg);--lc-button-ghost-active-bg: var(--lc-header-hover-bg)}.lc-header__profile-trigger{display:flex;align-items:center;gap:var(--spacing-2);padding:var(--spacing-2) var(--spacing-3);background:transparent;border:1px solid var(--lc-header-trigger-border);border-radius:var(--radius-full);cursor:pointer;color:var(--lc-header-trigger-fg);transition:background-color .2s ease,border-color .2s ease}.lc-header__profile-trigger:hover{background-color:var(--lc-header-hover-bg);border-color:var(--lc-header-fg-secondary)}.lc-header__profile-trigger:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-menu__header{padding:var(--spacing-3) var(--spacing-4);font-size:var(--font-size-sm);color:var(--color-text-secondary, #6b7280);border-bottom:1px solid var(--color-divider, #e5e7eb);margin-bottom:var(--spacing-1);word-break:break-word}.lc-header__menu-header{display:flex;align-items:center;gap:var(--spacing-3);padding:var(--spacing-4);border-bottom:1px solid var(--color-divider, #e5e7eb);margin-bottom:var(--spacing-1)}.lc-header__menu-user-info{flex:1;min-width:0}.lc-header__menu-user-name{font-size:var(--font-size-sm);font-weight:600;color:var(--color-text, #111827);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.4}.lc-header__menu-user-email{font-size:var(--font-size-xs);color:var(--color-text-secondary, #6b7280);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.4}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: ButtonComponent, selector: "lc-button", inputs: ["variant", "size", "disabled", "loading", "isLoading", "iconOnly", "fullWidth", "ariaLabel", "type"], outputs: ["clicked", "focused", "blurred"] }, { kind: "component", type: LogoComponent, selector: "lc-logo", inputs: ["variant", "size", "alt", "clickable", "colorMode"] }, { kind: "component", type: IconComponent, selector: "lc-icon", inputs: ["name", "variant", "size", "color", "ariaLabel", "decorative"] }, { kind: "component", type: AvatarComponent, selector: "lc-avatar", inputs: ["src", "alt", "name", "size", "status"] }, { kind: "component", type: MenuComponent, selector: "lc-menu", inputs: ["items", "isOpen", "position", "size", "minWidth"], outputs: ["itemClick", "closed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
5845
5845
  }
5846
5846
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: HeaderComponent, decorators: [{
5847
5847
  type: Component,
@@ -5855,7 +5855,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
5855
5855
  MenuComponent,
5856
5856
  ], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
5857
5857
  class: 'lc-header-host',
5858
- }, template: "<header class=\"lc-header\" [class.lc-header--dark]=\"theme === 'dark'\" [class.lc-header--light]=\"theme === 'light'\">\n <!-- Hamburger menu icon (mobile only) -->\n @if (showHamburger) {\n <lc-button\n variant=\"ghost\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [ariaLabel]=\"'Toggle sidebar menu'\"\n (clicked)=\"onHamburgerClick()\"\n class=\"lc-header__hamburger\"\n >\n <lc-icon name=\"bars-3\" size=\"sm\" />\n </lc-button>\n }\n\n <!-- Logo (clickable, navigates to home) -->\n <div class=\"lc-header__brand\">\n @if (logo || title) {\n <a routerLink=\"/\" class=\"lc-header__logo\" aria-label=\"Go to home\">\n <lc-logo [variant]=\"logo ? 'full' : 'emblem'\" size=\"md\" [clickable]=\"false\" [colorMode]=\"theme === 'dark' ? 'dark' : theme === 'light' ? 'light' : 'auto'\" />\n </a>\n }\n \n @if (title) {\n <div class=\"lc-header__title-group\">\n <h1 class=\"lc-header__title\">{{ title }}</h1>\n @if (subtitle) {\n <span class=\"lc-header__subtitle\">{{ subtitle }}</span>\n }\n </div>\n }\n </div>\n\n <div class=\"lc-header__spacer\"></div>\n\n <!-- Theme toggle button -->\n @if (showThemeButton) {\n <lc-button\n variant=\"ghost\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [ariaLabel]=\"themeService.isDark() ? 'Switch to light mode' : 'Switch to dark mode'\"\n (clicked)=\"onThemeButtonClick()\"\n class=\"lc-header__theme-toggle\"\n >\n <lc-icon [name]=\"themeService.isDark() ? 'sun' : 'moon'\" size=\"sm\" />\n </lc-button>\n }\n\n <!-- User profile menu -->\n <lc-menu\n [items]=\"menuItems()\"\n [isOpen]=\"isDropdownOpen()\"\n position=\"bottom-right\"\n minWidth=\"220px\"\n [size]=\"menuSize\"\n (itemClick)=\"onMenuItemClick($event)\"\n (closed)=\"closeDropdown()\"\n >\n <!-- Menu header with avatar, name and email -->\n @if (userName || userEmail) {\n <div header class=\"lc-header__menu-header\">\n <lc-avatar [name]=\"userName\" size=\"md\" />\n <div class=\"lc-header__menu-user-info\">\n @if (userName) {\n <div class=\"lc-header__menu-user-name\">{{ userName }}</div>\n }\n @if (userEmail) {\n <div class=\"lc-header__menu-user-email\">{{ userEmail }}</div>\n }\n </div>\n </div>\n }\n\n <button\n trigger\n class=\"lc-header__profile-trigger\"\n type=\"button\"\n aria-label=\"Open user menu\"\n [attr.aria-expanded]=\"isDropdownOpen()\"\n (click)=\"toggleDropdown()\"\n >\n <lc-icon name=\"user\" size=\"sm\" />\n <lc-icon name=\"chevron-down\" size=\"xs\" />\n </button>\n </lc-menu>\n</header>\n", styles: [".lc-header{--lc-header-bg: var(--color-neutral-0, #ffffff);--lc-header-fg: var(--color-neutral-900, #111827);--lc-header-fg-secondary: var(--color-neutral-600, #4b5563);--lc-header-border: var(--color-neutral-200, #e5e7eb);--lc-header-hover-bg: var(--color-neutral-100, #f3f4f6);--lc-header-trigger-border: var(--color-neutral-200, #e5e7eb);--lc-header-trigger-fg: var(--color-neutral-700, #374151);display:flex;align-items:center;gap:var(--spacing-4);padding:var(--spacing-3) var(--spacing-6);background-color:var(--lc-header-bg);border-bottom:1px solid var(--lc-header-border);height:64px;position:sticky;top:0;z-index:1000}@media(max-width:768px){.lc-header{padding:var(--spacing-3) var(--spacing-4)}}.lc-header--dark{--lc-header-bg: #18181b;--lc-header-fg: #f5f5f5;--lc-header-fg-secondary: #a1a1aa;--lc-header-border: #27272a;--lc-header-hover-bg: #27272a;--lc-header-trigger-border: #52525b;--lc-header-trigger-fg: #e4e4e7}.lc-header--light{--lc-header-bg: var(--color-neutral-0, #ffffff);--lc-header-fg: var(--color-neutral-900, #111827);--lc-header-fg-secondary: var(--color-neutral-600, #4b5563);--lc-header-border: var(--color-neutral-200, #e5e7eb);--lc-header-hover-bg: var(--color-neutral-100, #f3f4f6);--lc-header-trigger-border: var(--color-neutral-200, #e5e7eb);--lc-header-trigger-fg: var(--color-neutral-700, #374151)}html.dark .lc-header:not(.lc-header--light),:root.dark .lc-header:not(.lc-header--light),.dark .lc-header:not(.lc-header--light){--lc-header-bg: #18181b;--lc-header-fg: #f5f5f5;--lc-header-fg-secondary: #a1a1aa;--lc-header-border: #27272a;--lc-header-hover-bg: #27272a;--lc-header-trigger-border: #52525b;--lc-header-trigger-fg: #e4e4e7}.lc-header__hamburger{display:flex;align-items:center;justify-content:center;padding:var(--spacing-2);background:transparent;border:none;cursor:pointer;color:var(--lc-header-trigger-fg);border-radius:var(--radius-md);transition:background-color .2s ease,color .2s ease}.lc-header__hamburger:hover{background-color:var(--lc-header-hover-bg);color:var(--lc-header-fg)}.lc-header__hamburger:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-header__hamburger .lc-icon{width:24px;height:24px}.lc-header__brand{display:flex;align-items:center;gap:var(--spacing-3)}.lc-header__logo{display:flex;align-items:center;text-decoration:none;color:var(--lc-header-fg);flex-shrink:0}.lc-header__logo:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:4px;border-radius:var(--radius-sm)}.lc-header__logo-img{height:32px;width:auto}.lc-header__title-group{display:flex;flex-direction:column;gap:.125rem}.lc-header__title{font-size:1.25rem;font-weight:700;color:var(--lc-header-fg);margin:0;line-height:1.2;transition:color .2s ease}@media(max-width:640px){.lc-header__title{font-size:1.125rem}}.lc-header__subtitle{font-size:.75rem;font-weight:500;color:var(--lc-header-fg-secondary);text-transform:uppercase;letter-spacing:.05em;transition:color .2s ease}.lc-header__spacer{flex:1}.lc-header__theme-toggle{margin-right:var(--spacing-2);color:var(--lc-header-trigger-fg)}.lc-header--dark .lc-header__theme-toggle lc-icon,.lc-header--dark .lc-header__theme-toggle .lc-icon,.lc-header--dark .lc-header__theme-toggle svg,.lc-header--dark .lc-header__profile-trigger lc-icon,.lc-header--dark .lc-header__profile-trigger .lc-icon,.lc-header--dark .lc-header__profile-trigger svg,html.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle lc-icon,html.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle .lc-icon,html.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle svg,html.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger lc-icon,html.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger .lc-icon,html.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger svg,:root.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle lc-icon,:root.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle .lc-icon,:root.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle svg,:root.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger lc-icon,:root.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger .lc-icon,:root.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger svg,.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle lc-icon,.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle .lc-icon,.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle svg,.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger lc-icon,.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger .lc-icon,.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger svg{color:var(--lc-header-trigger-fg)}.lc-header--dark lc-button,html.dark .lc-header:not(.lc-header--light) lc-button,:root.dark .lc-header:not(.lc-header--light) lc-button,.dark .lc-header:not(.lc-header--light) lc-button{--lc-button-ghost-fg: var(--lc-header-trigger-fg);color:var(--lc-header-trigger-fg)}.lc-header__profile-trigger{display:flex;align-items:center;gap:var(--spacing-2);padding:var(--spacing-2) var(--spacing-3);background:transparent;border:1px solid var(--lc-header-trigger-border);border-radius:var(--radius-full);cursor:pointer;color:var(--lc-header-trigger-fg);transition:background-color .2s ease,border-color .2s ease}.lc-header__profile-trigger:hover{background-color:var(--lc-header-hover-bg);border-color:var(--lc-header-fg-secondary)}.lc-header__profile-trigger:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-menu__header{padding:var(--spacing-3) var(--spacing-4);font-size:var(--font-size-sm);color:var(--color-text-secondary, #6b7280);border-bottom:1px solid var(--color-divider, #e5e7eb);margin-bottom:var(--spacing-1);word-break:break-word}.lc-header__menu-header{display:flex;align-items:center;gap:var(--spacing-3);padding:var(--spacing-4);border-bottom:1px solid var(--color-divider, #e5e7eb);margin-bottom:var(--spacing-1)}.lc-header__menu-user-info{flex:1;min-width:0}.lc-header__menu-user-name{font-size:var(--font-size-sm);font-weight:600;color:var(--color-text, #111827);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.4}.lc-header__menu-user-email{font-size:var(--font-size-xs);color:var(--color-text-secondary, #6b7280);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.4}\n"] }]
5858
+ }, template: "<header class=\"lc-header\" [class.lc-header--dark]=\"theme === 'dark'\" [class.lc-header--light]=\"theme === 'light'\">\n <!-- Hamburger menu icon (mobile only) -->\n @if (showHamburger) {\n <lc-button\n variant=\"ghost\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [ariaLabel]=\"'Toggle sidebar menu'\"\n (clicked)=\"onHamburgerClick()\"\n class=\"lc-header__hamburger\"\n >\n <lc-icon name=\"bars-3\" size=\"sm\" />\n </lc-button>\n }\n\n <!-- Logo (clickable, navigates to home) -->\n <div class=\"lc-header__brand\">\n @if (logo || title) {\n <a routerLink=\"/\" class=\"lc-header__logo\" aria-label=\"Go to home\">\n <lc-logo [variant]=\"logo ? 'full' : 'emblem'\" size=\"md\" [clickable]=\"false\" [colorMode]=\"theme === 'dark' ? 'dark' : theme === 'light' ? 'light' : 'auto'\" />\n </a>\n }\n \n @if (title) {\n <div class=\"lc-header__title-group\">\n <h1 class=\"lc-header__title\">{{ title }}</h1>\n @if (subtitle) {\n <span class=\"lc-header__subtitle\">{{ subtitle }}</span>\n }\n </div>\n }\n </div>\n\n <div class=\"lc-header__spacer\"></div>\n\n <!-- Theme toggle button -->\n @if (showThemeButton) {\n <lc-button\n variant=\"ghost\"\n size=\"sm\"\n [iconOnly]=\"true\"\n [ariaLabel]=\"themeService.isDark() ? 'Switch to light mode' : 'Switch to dark mode'\"\n (clicked)=\"onThemeButtonClick()\"\n class=\"lc-header__theme-toggle\"\n >\n <lc-icon [name]=\"themeService.isDark() ? 'sun' : 'moon'\" size=\"sm\" />\n </lc-button>\n }\n\n <!-- User profile menu -->\n <lc-menu\n [items]=\"menuItems()\"\n [isOpen]=\"isDropdownOpen()\"\n position=\"bottom-right\"\n minWidth=\"220px\"\n [size]=\"menuSize\"\n (itemClick)=\"onMenuItemClick($event)\"\n (closed)=\"closeDropdown()\"\n >\n <!-- Menu header with avatar, name and email -->\n @if (userName || userEmail) {\n <div header class=\"lc-header__menu-header\">\n <lc-avatar [name]=\"userName\" size=\"md\" />\n <div class=\"lc-header__menu-user-info\">\n @if (userName) {\n <div class=\"lc-header__menu-user-name\">{{ userName }}</div>\n }\n @if (userEmail) {\n <div class=\"lc-header__menu-user-email\">{{ userEmail }}</div>\n }\n </div>\n </div>\n }\n\n <button\n trigger\n class=\"lc-header__profile-trigger\"\n type=\"button\"\n aria-label=\"Open user menu\"\n [attr.aria-expanded]=\"isDropdownOpen()\"\n (click)=\"toggleDropdown()\"\n >\n <lc-icon name=\"user\" size=\"sm\" />\n <lc-icon name=\"chevron-down\" size=\"xs\" />\n </button>\n </lc-menu>\n</header>\n", styles: [".lc-header{--lc-header-bg: var(--color-neutral-0, #ffffff);--lc-header-fg: var(--color-neutral-900, #111827);--lc-header-fg-secondary: var(--color-neutral-600, #4b5563);--lc-header-border: var(--color-neutral-200, #e5e7eb);--lc-header-hover-bg: var(--color-neutral-100, #f3f4f6);--lc-header-trigger-border: var(--color-neutral-200, #e5e7eb);--lc-header-trigger-fg: var(--color-neutral-700, #374151);display:flex;align-items:center;gap:var(--spacing-4);padding:var(--spacing-3) var(--spacing-6);background-color:var(--lc-header-bg);border-bottom:1px solid var(--lc-header-border);height:64px;position:sticky;top:0;z-index:1000}@media(max-width:768px){.lc-header{padding:var(--spacing-3) var(--spacing-4)}}.lc-header--dark{--lc-header-bg: #18181b;--lc-header-fg: #f5f5f5;--lc-header-fg-secondary: #a1a1aa;--lc-header-border: #27272a;--lc-header-hover-bg: #27272a;--lc-header-trigger-border: #52525b;--lc-header-trigger-fg: #e4e4e7}.lc-header--light{--lc-header-bg: var(--color-neutral-0, #ffffff);--lc-header-fg: var(--color-neutral-900, #111827);--lc-header-fg-secondary: var(--color-neutral-600, #4b5563);--lc-header-border: var(--color-neutral-200, #e5e7eb);--lc-header-hover-bg: var(--color-neutral-100, #f3f4f6);--lc-header-trigger-border: var(--color-neutral-200, #e5e7eb);--lc-header-trigger-fg: var(--color-neutral-700, #374151)}html.dark .lc-header:not(.lc-header--light),:root.dark .lc-header:not(.lc-header--light),.dark .lc-header:not(.lc-header--light){--lc-header-bg: #18181b;--lc-header-fg: #f5f5f5;--lc-header-fg-secondary: #a1a1aa;--lc-header-border: #27272a;--lc-header-hover-bg: #27272a;--lc-header-trigger-border: #52525b;--lc-header-trigger-fg: #e4e4e7}.lc-header__hamburger{display:flex;align-items:center;justify-content:center;padding:var(--spacing-2);background:transparent;border:none;cursor:pointer;color:var(--lc-header-trigger-fg);border-radius:var(--radius-md);transition:background-color .2s ease,color .2s ease}.lc-header__hamburger:hover{background-color:var(--lc-header-hover-bg);color:var(--lc-header-fg)}.lc-header__hamburger:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-header__hamburger .lc-icon{width:24px;height:24px}.lc-header__brand{display:flex;align-items:center;gap:var(--spacing-3)}.lc-header__logo{display:flex;align-items:center;text-decoration:none;color:var(--lc-header-fg);flex-shrink:0}.lc-header__logo:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:4px;border-radius:var(--radius-sm)}.lc-header__logo-img{height:32px;width:auto}.lc-header__title-group{display:flex;flex-direction:column;gap:.125rem}.lc-header__title{font-size:1.25rem;font-weight:700;color:var(--lc-header-fg);margin:0;line-height:1.2;transition:color .2s ease}@media(max-width:640px){.lc-header__title{font-size:1.125rem}}.lc-header__subtitle{font-size:.75rem;font-weight:500;color:var(--lc-header-fg-secondary);text-transform:uppercase;letter-spacing:.05em;transition:color .2s ease}.lc-header__spacer{flex:1}.lc-header__theme-toggle{margin-right:var(--spacing-2);color:var(--lc-header-trigger-fg)}.lc-header--dark .lc-header__theme-toggle lc-icon,.lc-header--dark .lc-header__theme-toggle .lc-icon,.lc-header--dark .lc-header__theme-toggle svg,.lc-header--dark .lc-header__hamburger lc-icon,.lc-header--dark .lc-header__hamburger .lc-icon,.lc-header--dark .lc-header__hamburger svg,.lc-header--dark .lc-header__profile-trigger lc-icon,.lc-header--dark .lc-header__profile-trigger .lc-icon,.lc-header--dark .lc-header__profile-trigger svg,html.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle lc-icon,html.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle .lc-icon,html.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle svg,html.dark .lc-header:not(.lc-header--light) .lc-header__hamburger lc-icon,html.dark .lc-header:not(.lc-header--light) .lc-header__hamburger .lc-icon,html.dark .lc-header:not(.lc-header--light) .lc-header__hamburger svg,html.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger lc-icon,html.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger .lc-icon,html.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger svg,:root.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle lc-icon,:root.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle .lc-icon,:root.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle svg,:root.dark .lc-header:not(.lc-header--light) .lc-header__hamburger lc-icon,:root.dark .lc-header:not(.lc-header--light) .lc-header__hamburger .lc-icon,:root.dark .lc-header:not(.lc-header--light) .lc-header__hamburger svg,:root.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger lc-icon,:root.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger .lc-icon,:root.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger svg,.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle lc-icon,.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle .lc-icon,.dark .lc-header:not(.lc-header--light) .lc-header__theme-toggle svg,.dark .lc-header:not(.lc-header--light) .lc-header__hamburger lc-icon,.dark .lc-header:not(.lc-header--light) .lc-header__hamburger .lc-icon,.dark .lc-header:not(.lc-header--light) .lc-header__hamburger svg,.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger lc-icon,.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger .lc-icon,.dark .lc-header:not(.lc-header--light) .lc-header__profile-trigger svg{color:var(--lc-header-trigger-fg)}.lc-header--dark lc-button,html.dark .lc-header:not(.lc-header--light) lc-button,:root.dark .lc-header:not(.lc-header--light) lc-button,.dark .lc-header:not(.lc-header--light) lc-button{--lc-button-ghost-fg: var(--lc-header-trigger-fg);--lc-button-ghost-hover-bg: var(--lc-header-hover-bg);--lc-button-ghost-hover-fg: var(--lc-header-fg);--lc-button-ghost-active-bg: var(--lc-header-hover-bg)}.lc-header__profile-trigger{display:flex;align-items:center;gap:var(--spacing-2);padding:var(--spacing-2) var(--spacing-3);background:transparent;border:1px solid var(--lc-header-trigger-border);border-radius:var(--radius-full);cursor:pointer;color:var(--lc-header-trigger-fg);transition:background-color .2s ease,border-color .2s ease}.lc-header__profile-trigger:hover{background-color:var(--lc-header-hover-bg);border-color:var(--lc-header-fg-secondary)}.lc-header__profile-trigger:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-menu__header{padding:var(--spacing-3) var(--spacing-4);font-size:var(--font-size-sm);color:var(--color-text-secondary, #6b7280);border-bottom:1px solid var(--color-divider, #e5e7eb);margin-bottom:var(--spacing-1);word-break:break-word}.lc-header__menu-header{display:flex;align-items:center;gap:var(--spacing-3);padding:var(--spacing-4);border-bottom:1px solid var(--color-divider, #e5e7eb);margin-bottom:var(--spacing-1)}.lc-header__menu-user-info{flex:1;min-width:0}.lc-header__menu-user-name{font-size:var(--font-size-sm);font-weight:600;color:var(--color-text, #111827);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.4}.lc-header__menu-user-email{font-size:var(--font-size-xs);color:var(--color-text-secondary, #6b7280);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.4}\n"] }]
5859
5859
  }], propDecorators: { theme: [{
5860
5860
  type: Input
5861
5861
  }], logo: [{
@@ -12848,6 +12848,1089 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
12848
12848
  args: ['richArea']
12849
12849
  }] } });
12850
12850
 
12851
+ /**
12852
+ * Markdown renderer component.
12853
+ *
12854
+ * Renders GitHub-Flavored Markdown (GFM) to sanitized HTML with
12855
+ * optional syntax highlighting via `<lc-code-block>`.
12856
+ *
12857
+ * @example
12858
+ * ```html
12859
+ * <lc-markdown [content]="'# Hello World'" />
12860
+ * <lc-markdown [src]="'/docs/readme.md'" />
12861
+ * ```
12862
+ */
12863
+ class MarkdownComponent {
12864
+ sanitizer = inject(DomSanitizer);
12865
+ http = inject(HttpClient);
12866
+ httpSub;
12867
+ /** URL or path to load markdown from */
12868
+ src = input(...(ngDevMode ? [undefined, { debugName: "src" }] : /* istanbul ignore next */ []));
12869
+ /** Raw markdown string */
12870
+ content = input(...(ngDevMode ? [undefined, { debugName: "content" }] : /* istanbul ignore next */ []));
12871
+ /** Display variant */
12872
+ variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
12873
+ /** Target for links */
12874
+ linkTarget = input('_self', ...(ngDevMode ? [{ debugName: "linkTarget" }] : /* istanbul ignore next */ []));
12875
+ /** Whether to sanitize HTML output */
12876
+ sanitize = input(true, ...(ngDevMode ? [{ debugName: "sanitize" }] : /* istanbul ignore next */ []));
12877
+ /** Whether to use code-block for fenced code */
12878
+ syntaxHighlight = input(true, ...(ngDevMode ? [{ debugName: "syntaxHighlight" }] : /* istanbul ignore next */ []));
12879
+ /** Whether to show anchor links on headings */
12880
+ showHeadingAnchors = input(false, ...(ngDevMode ? [{ debugName: "showHeadingAnchors" }] : /* istanbul ignore next */ []));
12881
+ /** Base URL for resolving relative links/images */
12882
+ baseUrl = input(...(ngDevMode ? [undefined, { debugName: "baseUrl" }] : /* istanbul ignore next */ []));
12883
+ /** Emitted when a link is clicked */
12884
+ linkClick = output();
12885
+ /** Emitted after rendering with heading TOC */
12886
+ rendered = output();
12887
+ /** Internal resolved markdown source */
12888
+ resolvedMarkdown = signal('', ...(ngDevMode ? [{ debugName: "resolvedMarkdown" }] : /* istanbul ignore next */ []));
12889
+ /** Parsed result (computed once from resolvedMarkdown) */
12890
+ parsed = computed(() => {
12891
+ const md = this.resolvedMarkdown();
12892
+ if (!md)
12893
+ return { html: '', blocks: [], headings: [] };
12894
+ return this.parseMarkdown(md);
12895
+ }, ...(ngDevMode ? [{ debugName: "parsed" }] : /* istanbul ignore next */ []));
12896
+ /** Computed render parts (HTML chunks + code blocks interleaved) */
12897
+ renderParts = computed(() => {
12898
+ const { html, blocks } = this.parsed();
12899
+ if (!html)
12900
+ return [];
12901
+ if (this.syntaxHighlight() && blocks.length > 0) {
12902
+ return this.splitIntoParts(html, blocks);
12903
+ }
12904
+ // No code blocks — single HTML part
12905
+ const sanitized = this.sanitize()
12906
+ ? this.sanitizer.sanitize(SecurityContext.HTML, html) || ''
12907
+ : html;
12908
+ return [{
12909
+ type: 'html',
12910
+ index: 0,
12911
+ safeHtml: this.sanitizer.bypassSecurityTrustHtml(sanitized),
12912
+ }];
12913
+ }, ...(ngDevMode ? [{ debugName: "renderParts" }] : /* istanbul ignore next */ []));
12914
+ containerClasses = computed(() => {
12915
+ return `lc-markdown lc-markdown--${this.variant()}`;
12916
+ }, ...(ngDevMode ? [{ debugName: "containerClasses" }] : /* istanbul ignore next */ []));
12917
+ headings = computed(() => this.parsed().headings, ...(ngDevMode ? [{ debugName: "headings" }] : /* istanbul ignore next */ []));
12918
+ constructor() {
12919
+ // Load content from src when src changes
12920
+ effect(() => {
12921
+ const src = this.src();
12922
+ if (src) {
12923
+ this.loadFromUrl(src);
12924
+ }
12925
+ });
12926
+ // Use raw content when provided
12927
+ effect(() => {
12928
+ const content = this.content();
12929
+ if (content !== undefined) {
12930
+ this.resolvedMarkdown.set(content);
12931
+ }
12932
+ });
12933
+ // Emit rendered event when headings change
12934
+ effect(() => {
12935
+ const h = this.headings();
12936
+ if (h.length > 0) {
12937
+ this.rendered.emit({ headings: h });
12938
+ }
12939
+ });
12940
+ }
12941
+ ngOnDestroy() {
12942
+ this.httpSub?.unsubscribe();
12943
+ }
12944
+ onLinkClick(event) {
12945
+ const target = event.target;
12946
+ const anchor = target.closest('a');
12947
+ if (anchor) {
12948
+ this.linkClick.emit({ href: anchor.href, event });
12949
+ }
12950
+ }
12951
+ loadFromUrl(url) {
12952
+ this.httpSub?.unsubscribe();
12953
+ this.httpSub = this.http.get(url, { responseType: 'text' }).subscribe({
12954
+ next: (text) => this.resolvedMarkdown.set(text),
12955
+ error: () => this.resolvedMarkdown.set(`*Failed to load ${url}*`),
12956
+ });
12957
+ }
12958
+ splitIntoParts(html, blocks) {
12959
+ const parts = [];
12960
+ // The placeholders survive HTML escaping as &lt;!--CODE_BLOCK_N--&gt;
12961
+ const regex = /&lt;!--CODE_BLOCK_(\d+)--&gt;/g;
12962
+ let lastIndex = 0;
12963
+ let partIndex = 0;
12964
+ let match;
12965
+ while ((match = regex.exec(html)) !== null) {
12966
+ const before = html.slice(lastIndex, match.index);
12967
+ if (before.trim()) {
12968
+ const sanitized = this.sanitize()
12969
+ ? this.sanitizer.sanitize(SecurityContext.HTML, before) || ''
12970
+ : before;
12971
+ parts.push({
12972
+ type: 'html',
12973
+ index: partIndex++,
12974
+ safeHtml: this.sanitizer.bypassSecurityTrustHtml(sanitized),
12975
+ });
12976
+ }
12977
+ const blockIdx = parseInt(match[1], 10);
12978
+ parts.push({
12979
+ type: 'code',
12980
+ index: partIndex++,
12981
+ code: blocks[blockIdx].code,
12982
+ lang: blocks[blockIdx].lang,
12983
+ });
12984
+ lastIndex = match.index + match[0].length;
12985
+ }
12986
+ const remaining = html.slice(lastIndex);
12987
+ if (remaining.trim()) {
12988
+ const sanitized = this.sanitize()
12989
+ ? this.sanitizer.sanitize(SecurityContext.HTML, remaining) || ''
12990
+ : remaining;
12991
+ parts.push({
12992
+ type: 'html',
12993
+ index: partIndex++,
12994
+ safeHtml: this.sanitizer.bypassSecurityTrustHtml(sanitized),
12995
+ });
12996
+ }
12997
+ return parts;
12998
+ }
12999
+ parseMarkdown(md) {
13000
+ const blocks = [];
13001
+ const headings = [];
13002
+ const baseUrl = this.baseUrl();
13003
+ const linkTarget = this.linkTarget();
13004
+ const showAnchors = this.showHeadingAnchors();
13005
+ // 1. Extract fenced code blocks → placeholders
13006
+ let processed = md.replace(/```(\w*)\n([\s\S]*?)```/g, (_match, lang, code) => {
13007
+ const idx = blocks.length;
13008
+ blocks.push({ lang: lang || 'text', code: code.trimEnd() });
13009
+ return `<!--CODE_BLOCK_${idx}-->`;
13010
+ });
13011
+ // 2. Escape HTML entities
13012
+ processed = processed
13013
+ .replace(/&/g, '&amp;')
13014
+ .replace(/</g, '&lt;')
13015
+ .replace(/>/g, '&gt;');
13016
+ // 3. Headings (# to ######)
13017
+ processed = processed.replace(/^(#{1,6})\s+(.+)$/gm, (_match, hashes, text) => {
13018
+ const level = hashes.length;
13019
+ const id = text
13020
+ .toLowerCase()
13021
+ .replace(/[^\w\s-]/g, '')
13022
+ .replace(/\s+/g, '-');
13023
+ headings.push({ level, text, id });
13024
+ const anchor = showAnchors
13025
+ ? `<a href="#${id}" class="lc-markdown__anchor" aria-hidden="true">#</a>`
13026
+ : '';
13027
+ return `<h${level} id="${id}">${anchor}${text}</h${level}>`;
13028
+ });
13029
+ // 4. Horizontal rules
13030
+ processed = processed.replace(/^---+$/gm, '<hr>');
13031
+ // 5. Blockquotes
13032
+ processed = processed.replace(/^(?:&gt;)\s?(.*)$/gm, '<blockquote>$1</blockquote>');
13033
+ // Merge consecutive blockquotes
13034
+ processed = processed.replace(/<\/blockquote>\n<blockquote>/g, '\n');
13035
+ // 6. Tables (GFM)
13036
+ processed = this.parseTables(processed);
13037
+ // 7. Task lists
13038
+ processed = processed.replace(/^[-*]\s+\[x\]\s+(.*)$/gm, '<li class="lc-markdown__task"><input type="checkbox" checked disabled> $1</li>');
13039
+ processed = processed.replace(/^[-*]\s+\[ \]\s+(.*)$/gm, '<li class="lc-markdown__task"><input type="checkbox" disabled> $1</li>');
13040
+ // 8. Unordered lists
13041
+ processed = processed.replace(/^[-*]\s+(.*)$/gm, '<li>$1</li>');
13042
+ processed = processed.replace(/(<li>[\s\S]*?<\/li>)/g, (match) => {
13043
+ if (!match.includes('lc-markdown__task')) {
13044
+ return match;
13045
+ }
13046
+ return match;
13047
+ });
13048
+ // Wrap consecutive <li> in <ul>
13049
+ processed = processed.replace(/((?:<li[^>]*>.*<\/li>\n?)+)/g, '<ul>$1</ul>');
13050
+ // 9. Ordered lists
13051
+ processed = processed.replace(/^\d+\.\s+(.*)$/gm, '<li>$1</li>');
13052
+ // 10. Inline code
13053
+ processed = processed.replace(/`([^`]+)`/g, '<code>$1</code>');
13054
+ // 11. Bold + italic combos
13055
+ processed = processed.replace(/\*\*\*(.+?)\*\*\*/g, '<strong><em>$1</em></strong>');
13056
+ processed = processed.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
13057
+ processed = processed.replace(/\*(.+?)\*/g, '<em>$1</em>');
13058
+ // 12. Strikethrough (GFM)
13059
+ processed = processed.replace(/~~(.+?)~~/g, '<del>$1</del>');
13060
+ // 13. Images
13061
+ processed = processed.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (_match, alt, src) => {
13062
+ const resolvedSrc = baseUrl && !src.startsWith('http') ? `${baseUrl}/${src}` : src;
13063
+ return `<img src="${resolvedSrc}" alt="${alt}" class="lc-markdown__img">`;
13064
+ });
13065
+ // 14. Links
13066
+ processed = processed.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, text, href) => {
13067
+ const resolvedHref = baseUrl && !href.startsWith('http') && !href.startsWith('#')
13068
+ ? `${baseUrl}/${href}`
13069
+ : href;
13070
+ return `<a href="${resolvedHref}" target="${linkTarget}" rel="noopener">${text}</a>`;
13071
+ });
13072
+ // 15. Paragraphs — wrap standalone lines
13073
+ processed = processed
13074
+ .split('\n\n')
13075
+ .map((block) => {
13076
+ const trimmed = block.trim();
13077
+ if (!trimmed)
13078
+ return '';
13079
+ if (trimmed.startsWith('<h') ||
13080
+ trimmed.startsWith('<ul') ||
13081
+ trimmed.startsWith('<ol') ||
13082
+ trimmed.startsWith('<blockquote') ||
13083
+ trimmed.startsWith('<hr') ||
13084
+ trimmed.startsWith('<table') ||
13085
+ trimmed.startsWith('<!--CODE_BLOCK')) {
13086
+ return trimmed;
13087
+ }
13088
+ return `<p>${trimmed.replace(/\n/g, '<br>')}</p>`;
13089
+ })
13090
+ .join('\n');
13091
+ // 16. Restore code block placeholders
13092
+ if (this.syntaxHighlight()) {
13093
+ // Leave placeholders — the template will render <lc-code-block> for each
13094
+ // No replacement needed here
13095
+ }
13096
+ else {
13097
+ processed = processed.replace(/&lt;!--CODE_BLOCK_(\d+)--&gt;/g, (_match, idx) => {
13098
+ const block = blocks[parseInt(idx, 10)];
13099
+ return `<pre><code class="language-${block.lang}">${this.escapeHtml(block.code)}</code></pre>`;
13100
+ });
13101
+ }
13102
+ return { html: processed, blocks, headings };
13103
+ }
13104
+ parseTables(text) {
13105
+ return text.replace(/^(\|.+\|)\n(\|[-| :]+\|)\n((?:\|.+\|\n?)*)/gm, (_match, header, _separator, body) => {
13106
+ const headers = header
13107
+ .split('|')
13108
+ .filter((c) => c.trim())
13109
+ .map((c) => `<th>${c.trim()}</th>`)
13110
+ .join('');
13111
+ const rows = body
13112
+ .trim()
13113
+ .split('\n')
13114
+ .map((row) => {
13115
+ const cells = row
13116
+ .split('|')
13117
+ .filter((c) => c.trim())
13118
+ .map((c) => `<td>${c.trim()}</td>`)
13119
+ .join('');
13120
+ return `<tr>${cells}</tr>`;
13121
+ })
13122
+ .join('');
13123
+ return `<table class="lc-markdown__table"><thead><tr>${headers}</tr></thead><tbody>${rows}</tbody></table>`;
13124
+ });
13125
+ }
13126
+ escapeHtml(text) {
13127
+ return text
13128
+ .replace(/&/g, '&amp;')
13129
+ .replace(/</g, '&lt;')
13130
+ .replace(/>/g, '&gt;');
13131
+ }
13132
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: MarkdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13133
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: MarkdownComponent, isStandalone: true, selector: "lc-markdown", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, content: { classPropertyName: "content", publicName: "content", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, linkTarget: { classPropertyName: "linkTarget", publicName: "linkTarget", isSignal: true, isRequired: false, transformFunction: null }, sanitize: { classPropertyName: "sanitize", publicName: "sanitize", isSignal: true, isRequired: false, transformFunction: null }, syntaxHighlight: { classPropertyName: "syntaxHighlight", publicName: "syntaxHighlight", isSignal: true, isRequired: false, transformFunction: null }, showHeadingAnchors: { classPropertyName: "showHeadingAnchors", publicName: "showHeadingAnchors", isSignal: true, isRequired: false, transformFunction: null }, baseUrl: { classPropertyName: "baseUrl", publicName: "baseUrl", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { linkClick: "linkClick", rendered: "rendered" }, ngImport: i0, template: "<div [class]=\"containerClasses()\" (click)=\"onLinkClick($event)\">\n @for (part of renderParts(); track part.index) {\n @if (part.type === 'html') {\n <div [innerHTML]=\"part.safeHtml\"></div>\n } @else {\n <lc-code-block\n [code]=\"part.code!\"\n [language]=\"$any(part.lang)\"\n [showLineNumbers]=\"true\"\n [showCopy]=\"true\"\n />\n }\n }\n</div>\n", styles: [".lc-markdown{color:var(--color-text, #111827);font-family:var(--font-family-sans);font-size:var(--font-size-base, 1rem);line-height:var(--line-height-relaxed, 1.75)}.lc-markdown h1,.lc-markdown h2,.lc-markdown h3,.lc-markdown h4,.lc-markdown h5,.lc-markdown h6{color:var(--color-text, #111827);font-weight:var(--font-weight-semibold, 600);line-height:var(--line-height-tight, 1.25);margin:1.5em 0 .5em}.lc-markdown h1:first-child,.lc-markdown h2:first-child,.lc-markdown h3:first-child,.lc-markdown h4:first-child,.lc-markdown h5:first-child,.lc-markdown h6:first-child{margin-top:0}.lc-markdown h1{font-size:2rem}.lc-markdown h2{font-size:1.5rem}.lc-markdown h3{font-size:1.25rem}.lc-markdown h4{font-size:1.125rem}.lc-markdown h5{font-size:1rem}.lc-markdown h6{font-size:.875rem}.lc-markdown p{margin:0 0 1em}.lc-markdown a{color:var(--color-primary-600, #2563eb);text-decoration:none}.lc-markdown a:hover{text-decoration:underline}.lc-markdown strong{font-weight:var(--font-weight-semibold, 600)}.lc-markdown code{background-color:var(--color-neutral-100, #f3f4f6);color:var(--color-neutral-800, #1f2937);padding:.125em .375em;border-radius:var(--radius-sm, .25rem);font-family:var(--font-family-mono, monospace);font-size:.875em}.lc-markdown pre{margin:1em 0;overflow-x:auto}.lc-markdown pre code{background:none;padding:0;border-radius:0}.lc-markdown blockquote{margin:1em 0;padding:.5em 1em;border-left:4px solid var(--color-primary-300, #93c5fd);background-color:var(--color-neutral-50, #f9fafb);color:var(--color-text-secondary, #4b5563)}.lc-markdown blockquote p{margin:0}.lc-markdown ul,.lc-markdown ol{margin:0 0 1em;padding-left:1.5em}.lc-markdown li{margin:.25em 0}.lc-markdown hr{border:none;border-top:1px solid var(--color-divider, #e5e7eb);margin:1.5em 0}.lc-markdown img{max-width:100%;height:auto;border-radius:var(--radius-md, .375rem)}.lc-markdown del{text-decoration:line-through;color:var(--color-text-secondary, #6b7280)}.lc-markdown__table{width:100%;border-collapse:collapse;margin:1em 0;font-size:var(--font-size-sm, .875rem)}.lc-markdown__table th,.lc-markdown__table td{padding:.5rem .75rem;border:1px solid var(--color-divider, #e5e7eb);text-align:left}.lc-markdown__table th{background-color:var(--color-neutral-50, #f9fafb);font-weight:var(--font-weight-semibold, 600)}.lc-markdown__table tr:hover td{background-color:var(--color-neutral-50, #f9fafb)}.lc-markdown__task{list-style:none;margin-left:-1.5em}.lc-markdown__task input[type=checkbox]{margin-right:.5em;vertical-align:middle}.lc-markdown__anchor{color:var(--color-neutral-400, #9ca3af);text-decoration:none;margin-right:.25em;opacity:0;transition:opacity .15s ease}h1:hover .lc-markdown__anchor,h2:hover .lc-markdown__anchor,h3:hover .lc-markdown__anchor,h4:hover .lc-markdown__anchor,h5:hover .lc-markdown__anchor,h6:hover .lc-markdown__anchor{opacity:1}.lc-markdown lc-code-block{display:block;margin:1em 0}.lc-markdown--compact{font-size:var(--font-size-sm, .875rem);line-height:var(--line-height-normal, 1.5)}.lc-markdown--compact h1{font-size:1.5rem}.lc-markdown--compact h2{font-size:1.25rem}.lc-markdown--compact h3{font-size:1.125rem}.lc-markdown--compact h4{font-size:1rem}.lc-markdown--compact h5,.lc-markdown--compact h6{font-size:.875rem}.lc-markdown--compact h1,.lc-markdown--compact h2,.lc-markdown--compact h3,.lc-markdown--compact h4,.lc-markdown--compact h5,.lc-markdown--compact h6{margin:1em 0 .25em}.lc-markdown--compact p{margin:0 0 .5em}.lc-markdown--compact blockquote{margin:.5em 0;padding:.25em .75em}.lc-markdown--compact ul,.lc-markdown--compact ol{margin:0 0 .5em}\n"], dependencies: [{ kind: "component", type: CodeBlockComponent, selector: "lc-code-block", inputs: ["code", "language", "filename", "showLineNumbers", "showCopy", "showHeader"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
13134
+ }
13135
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: MarkdownComponent, decorators: [{
13136
+ type: Component,
13137
+ args: [{ selector: 'lc-markdown', standalone: true, imports: [CodeBlockComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [class]=\"containerClasses()\" (click)=\"onLinkClick($event)\">\n @for (part of renderParts(); track part.index) {\n @if (part.type === 'html') {\n <div [innerHTML]=\"part.safeHtml\"></div>\n } @else {\n <lc-code-block\n [code]=\"part.code!\"\n [language]=\"$any(part.lang)\"\n [showLineNumbers]=\"true\"\n [showCopy]=\"true\"\n />\n }\n }\n</div>\n", styles: [".lc-markdown{color:var(--color-text, #111827);font-family:var(--font-family-sans);font-size:var(--font-size-base, 1rem);line-height:var(--line-height-relaxed, 1.75)}.lc-markdown h1,.lc-markdown h2,.lc-markdown h3,.lc-markdown h4,.lc-markdown h5,.lc-markdown h6{color:var(--color-text, #111827);font-weight:var(--font-weight-semibold, 600);line-height:var(--line-height-tight, 1.25);margin:1.5em 0 .5em}.lc-markdown h1:first-child,.lc-markdown h2:first-child,.lc-markdown h3:first-child,.lc-markdown h4:first-child,.lc-markdown h5:first-child,.lc-markdown h6:first-child{margin-top:0}.lc-markdown h1{font-size:2rem}.lc-markdown h2{font-size:1.5rem}.lc-markdown h3{font-size:1.25rem}.lc-markdown h4{font-size:1.125rem}.lc-markdown h5{font-size:1rem}.lc-markdown h6{font-size:.875rem}.lc-markdown p{margin:0 0 1em}.lc-markdown a{color:var(--color-primary-600, #2563eb);text-decoration:none}.lc-markdown a:hover{text-decoration:underline}.lc-markdown strong{font-weight:var(--font-weight-semibold, 600)}.lc-markdown code{background-color:var(--color-neutral-100, #f3f4f6);color:var(--color-neutral-800, #1f2937);padding:.125em .375em;border-radius:var(--radius-sm, .25rem);font-family:var(--font-family-mono, monospace);font-size:.875em}.lc-markdown pre{margin:1em 0;overflow-x:auto}.lc-markdown pre code{background:none;padding:0;border-radius:0}.lc-markdown blockquote{margin:1em 0;padding:.5em 1em;border-left:4px solid var(--color-primary-300, #93c5fd);background-color:var(--color-neutral-50, #f9fafb);color:var(--color-text-secondary, #4b5563)}.lc-markdown blockquote p{margin:0}.lc-markdown ul,.lc-markdown ol{margin:0 0 1em;padding-left:1.5em}.lc-markdown li{margin:.25em 0}.lc-markdown hr{border:none;border-top:1px solid var(--color-divider, #e5e7eb);margin:1.5em 0}.lc-markdown img{max-width:100%;height:auto;border-radius:var(--radius-md, .375rem)}.lc-markdown del{text-decoration:line-through;color:var(--color-text-secondary, #6b7280)}.lc-markdown__table{width:100%;border-collapse:collapse;margin:1em 0;font-size:var(--font-size-sm, .875rem)}.lc-markdown__table th,.lc-markdown__table td{padding:.5rem .75rem;border:1px solid var(--color-divider, #e5e7eb);text-align:left}.lc-markdown__table th{background-color:var(--color-neutral-50, #f9fafb);font-weight:var(--font-weight-semibold, 600)}.lc-markdown__table tr:hover td{background-color:var(--color-neutral-50, #f9fafb)}.lc-markdown__task{list-style:none;margin-left:-1.5em}.lc-markdown__task input[type=checkbox]{margin-right:.5em;vertical-align:middle}.lc-markdown__anchor{color:var(--color-neutral-400, #9ca3af);text-decoration:none;margin-right:.25em;opacity:0;transition:opacity .15s ease}h1:hover .lc-markdown__anchor,h2:hover .lc-markdown__anchor,h3:hover .lc-markdown__anchor,h4:hover .lc-markdown__anchor,h5:hover .lc-markdown__anchor,h6:hover .lc-markdown__anchor{opacity:1}.lc-markdown lc-code-block{display:block;margin:1em 0}.lc-markdown--compact{font-size:var(--font-size-sm, .875rem);line-height:var(--line-height-normal, 1.5)}.lc-markdown--compact h1{font-size:1.5rem}.lc-markdown--compact h2{font-size:1.25rem}.lc-markdown--compact h3{font-size:1.125rem}.lc-markdown--compact h4{font-size:1rem}.lc-markdown--compact h5,.lc-markdown--compact h6{font-size:.875rem}.lc-markdown--compact h1,.lc-markdown--compact h2,.lc-markdown--compact h3,.lc-markdown--compact h4,.lc-markdown--compact h5,.lc-markdown--compact h6{margin:1em 0 .25em}.lc-markdown--compact p{margin:0 0 .5em}.lc-markdown--compact blockquote{margin:.5em 0;padding:.25em .75em}.lc-markdown--compact ul,.lc-markdown--compact ol{margin:0 0 .5em}\n"] }]
13138
+ }], ctorParameters: () => [], propDecorators: { src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], content: [{ type: i0.Input, args: [{ isSignal: true, alias: "content", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], linkTarget: [{ type: i0.Input, args: [{ isSignal: true, alias: "linkTarget", required: false }] }], sanitize: [{ type: i0.Input, args: [{ isSignal: true, alias: "sanitize", required: false }] }], syntaxHighlight: [{ type: i0.Input, args: [{ isSignal: true, alias: "syntaxHighlight", required: false }] }], showHeadingAnchors: [{ type: i0.Input, args: [{ isSignal: true, alias: "showHeadingAnchors", required: false }] }], baseUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "baseUrl", required: false }] }], linkClick: [{ type: i0.Output, args: ["linkClick"] }], rendered: [{ type: i0.Output, args: ["rendered"] }] } });
13139
+
13140
+ /**
13141
+ * Streaming log / terminal viewer component.
13142
+ *
13143
+ * Supports controlled (lines input) and streaming (stream$ observable) modes,
13144
+ * virtualized rendering for large buffers, ANSI color parsing, auto-scroll,
13145
+ * and filtering.
13146
+ *
13147
+ * @example
13148
+ * ```html
13149
+ * <lc-log-viewer [stream$]="logs$" autoScroll height="600px" variant="terminal" />
13150
+ * ```
13151
+ */
13152
+ class LogViewerComponent {
13153
+ scrollContainer;
13154
+ ngZone = inject(NgZone);
13155
+ streamSub;
13156
+ scrollListener;
13157
+ /** Controlled mode: array of log lines */
13158
+ lines = input([], ...(ngDevMode ? [{ debugName: "lines" }] : /* istanbul ignore next */ []));
13159
+ /** Streaming mode: observable of log lines */
13160
+ stream$ = input(...(ngDevMode ? [undefined, { debugName: "stream$" }] : /* istanbul ignore next */ []));
13161
+ /** Maximum lines to keep in buffer */
13162
+ maxLines = input(10_000, ...(ngDevMode ? [{ debugName: "maxLines" }] : /* istanbul ignore next */ []));
13163
+ /** Auto-scroll to bottom on new lines */
13164
+ autoScroll = input(true, ...(ngDevMode ? [{ debugName: "autoScroll" }] : /* istanbul ignore next */ []));
13165
+ /** Show timestamps column */
13166
+ showTimestamps = input(true, ...(ngDevMode ? [{ debugName: "showTimestamps" }] : /* istanbul ignore next */ []));
13167
+ /** Show line numbers */
13168
+ showLineNumbers = input(false, ...(ngDevMode ? [{ debugName: "showLineNumbers" }] : /* istanbul ignore next */ []));
13169
+ /** Parse ANSI color codes */
13170
+ ansiColors = input(true, ...(ngDevMode ? [{ debugName: "ansiColors" }] : /* istanbul ignore next */ []));
13171
+ /** Filter by log levels */
13172
+ levelFilter = input(...(ngDevMode ? [undefined, { debugName: "levelFilter" }] : /* istanbul ignore next */ []));
13173
+ /** Search query to highlight */
13174
+ searchQuery = input(...(ngDevMode ? [undefined, { debugName: "searchQuery" }] : /* istanbul ignore next */ []));
13175
+ /** Container height */
13176
+ height = input('400px', ...(ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []));
13177
+ /** Visual variant */
13178
+ variant = input('log', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
13179
+ /** Emitted when a line is clicked */
13180
+ lineClick = output();
13181
+ /** Emitted on copy all */
13182
+ copyAll = output();
13183
+ /** Emitted when scroll state changes */
13184
+ scrollStateChange = output();
13185
+ /** Internal buffer of all lines */
13186
+ buffer = signal([], ...(ngDevMode ? [{ debugName: "buffer" }] : /* istanbul ignore next */ []));
13187
+ /** Whether stream is paused */
13188
+ paused = signal(false, ...(ngDevMode ? [{ debugName: "paused" }] : /* istanbul ignore next */ []));
13189
+ /** Whether user is at bottom of scroll */
13190
+ atBottom = signal(true, ...(ngDevMode ? [{ debugName: "atBottom" }] : /* istanbul ignore next */ []));
13191
+ /** Internal search input */
13192
+ internalSearch = signal('', ...(ngDevMode ? [{ debugName: "internalSearch" }] : /* istanbul ignore next */ []));
13193
+ /** Internal level filter */
13194
+ internalLevelFilter = signal(new Set(), ...(ngDevMode ? [{ debugName: "internalLevelFilter" }] : /* istanbul ignore next */ []));
13195
+ /** Show search bar */
13196
+ showSearch = signal(false, ...(ngDevMode ? [{ debugName: "showSearch" }] : /* istanbul ignore next */ []));
13197
+ /** Effective search query */
13198
+ effectiveSearch = computed(() => {
13199
+ return this.searchQuery() || this.internalSearch();
13200
+ }, ...(ngDevMode ? [{ debugName: "effectiveSearch" }] : /* istanbul ignore next */ []));
13201
+ /** Filtered lines for display */
13202
+ filteredLines = computed(() => {
13203
+ let lines = this.buffer();
13204
+ const levels = this.levelFilter();
13205
+ const internalFilter = this.internalLevelFilter();
13206
+ // Apply level filter
13207
+ if (levels?.length) {
13208
+ lines = lines.filter((l) => l.level && levels.includes(l.level));
13209
+ }
13210
+ else if (internalFilter.size > 0) {
13211
+ lines = lines.filter((l) => l.level && internalFilter.has(l.level));
13212
+ }
13213
+ return lines;
13214
+ }, ...(ngDevMode ? [{ debugName: "filteredLines" }] : /* istanbul ignore next */ []));
13215
+ /** Visible window of lines (virtualized) */
13216
+ scrollTop = signal(0, ...(ngDevMode ? [{ debugName: "scrollTop" }] : /* istanbul ignore next */ []));
13217
+ LINE_HEIGHT = 22;
13218
+ visibleRange = computed(() => {
13219
+ const containerHeight = parseInt(this.height(), 10) || 400;
13220
+ const toolbarHeight = 40;
13221
+ const viewHeight = containerHeight - toolbarHeight;
13222
+ const total = this.filteredLines().length;
13223
+ const start = Math.floor(this.scrollTop() / this.LINE_HEIGHT);
13224
+ const visible = Math.ceil(viewHeight / this.LINE_HEIGHT) + 2;
13225
+ return {
13226
+ start: Math.max(0, start - 1),
13227
+ end: Math.min(total, start + visible + 1),
13228
+ total,
13229
+ totalHeight: total * this.LINE_HEIGHT,
13230
+ };
13231
+ }, ...(ngDevMode ? [{ debugName: "visibleRange" }] : /* istanbul ignore next */ []));
13232
+ visibleLines = computed(() => {
13233
+ const range = this.visibleRange();
13234
+ return this.filteredLines().slice(range.start, range.end).map((line, i) => ({
13235
+ ...line,
13236
+ _index: range.start + i,
13237
+ }));
13238
+ }, ...(ngDevMode ? [{ debugName: "visibleLines" }] : /* istanbul ignore next */ []));
13239
+ containerClasses = computed(() => {
13240
+ return `lc-log-viewer lc-log-viewer--${this.variant()}`;
13241
+ }, ...(ngDevMode ? [{ debugName: "containerClasses" }] : /* istanbul ignore next */ []));
13242
+ /** Line counts per level for toolbar */
13243
+ levelCounts = computed(() => {
13244
+ const buf = this.buffer();
13245
+ const counts = { debug: 0, info: 0, warn: 0, error: 0 };
13246
+ for (const line of buf) {
13247
+ if (line.level && line.level in counts)
13248
+ counts[line.level]++;
13249
+ }
13250
+ return counts;
13251
+ }, ...(ngDevMode ? [{ debugName: "levelCounts" }] : /* istanbul ignore next */ []));
13252
+ constructor() {
13253
+ // Sync controlled lines to buffer
13254
+ effect(() => {
13255
+ const input = this.lines();
13256
+ if (input.length > 0) {
13257
+ this.buffer.set(input.slice(-this.maxLines()));
13258
+ }
13259
+ });
13260
+ // Subscribe to stream$
13261
+ effect(() => {
13262
+ const stream = this.stream$();
13263
+ this.streamSub?.unsubscribe();
13264
+ if (stream) {
13265
+ this.streamSub = stream.subscribe((data) => {
13266
+ if (this.paused())
13267
+ return;
13268
+ const newLines = Array.isArray(data) ? data : [data];
13269
+ this.buffer.update((buf) => {
13270
+ const combined = [...buf, ...newLines];
13271
+ return combined.length > this.maxLines()
13272
+ ? combined.slice(-this.maxLines())
13273
+ : combined;
13274
+ });
13275
+ });
13276
+ }
13277
+ });
13278
+ }
13279
+ ngAfterViewInit() {
13280
+ if (this.scrollContainer) {
13281
+ this.ngZone.runOutsideAngular(() => {
13282
+ this.scrollListener = () => this.onScroll();
13283
+ this.scrollContainer.nativeElement.addEventListener('scroll', this.scrollListener, { passive: true });
13284
+ });
13285
+ }
13286
+ }
13287
+ ngOnDestroy() {
13288
+ this.streamSub?.unsubscribe();
13289
+ if (this.scrollContainer && this.scrollListener) {
13290
+ this.scrollContainer.nativeElement.removeEventListener('scroll', this.scrollListener);
13291
+ }
13292
+ }
13293
+ onScroll() {
13294
+ const el = this.scrollContainer?.nativeElement;
13295
+ if (!el)
13296
+ return;
13297
+ this.scrollTop.set(el.scrollTop);
13298
+ const wasAtBottom = this.atBottom();
13299
+ const isAtBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 30;
13300
+ if (wasAtBottom !== isAtBottom) {
13301
+ this.ngZone.run(() => {
13302
+ this.atBottom.set(isAtBottom);
13303
+ this.scrollStateChange.emit({ atBottom: isAtBottom });
13304
+ });
13305
+ }
13306
+ else {
13307
+ this.scrollTop.set(el.scrollTop);
13308
+ }
13309
+ }
13310
+ scrollToBottom() {
13311
+ const el = this.scrollContainer?.nativeElement;
13312
+ if (el) {
13313
+ el.scrollTop = el.scrollHeight;
13314
+ this.atBottom.set(true);
13315
+ }
13316
+ }
13317
+ togglePause() {
13318
+ this.paused.update((v) => !v);
13319
+ }
13320
+ clearBuffer() {
13321
+ this.buffer.set([]);
13322
+ }
13323
+ async onCopyAll() {
13324
+ const text = this.filteredLines()
13325
+ .map((l) => {
13326
+ const ts = l.timestamp ? `[${l.timestamp.toISOString()}] ` : '';
13327
+ const lvl = l.level ? `[${l.level.toUpperCase()}] ` : '';
13328
+ return `${ts}${lvl}${l.text}`;
13329
+ })
13330
+ .join('\n');
13331
+ await navigator.clipboard.writeText(text);
13332
+ this.copyAll.emit(text);
13333
+ }
13334
+ toggleSearch() {
13335
+ this.showSearch.update((v) => !v);
13336
+ if (!this.showSearch()) {
13337
+ this.internalSearch.set('');
13338
+ }
13339
+ }
13340
+ toggleLevelFilter(level) {
13341
+ this.internalLevelFilter.update((set) => {
13342
+ const newSet = new Set(set);
13343
+ if (newSet.has(level)) {
13344
+ newSet.delete(level);
13345
+ }
13346
+ else {
13347
+ newSet.add(level);
13348
+ }
13349
+ return newSet;
13350
+ });
13351
+ }
13352
+ onLineClick(line) {
13353
+ this.lineClick.emit(line);
13354
+ }
13355
+ onKeydown(event) {
13356
+ if (event.key === '/' && !this.showSearch()) {
13357
+ event.preventDefault();
13358
+ this.toggleSearch();
13359
+ }
13360
+ if (event.key === 'g' && !event.shiftKey && !this.showSearch()) {
13361
+ this.scrollContainer?.nativeElement.scrollTo(0, 0);
13362
+ }
13363
+ if (event.key === 'G' && !this.showSearch()) {
13364
+ this.scrollToBottom();
13365
+ }
13366
+ }
13367
+ formatTimestamp(date) {
13368
+ if (!date)
13369
+ return '';
13370
+ return date.toLocaleTimeString('en-US', { hour12: false });
13371
+ }
13372
+ parseAnsi(text) {
13373
+ if (!this.ansiColors())
13374
+ return this.escapeHtml(text);
13375
+ const escaped = this.escapeHtml(text);
13376
+ // Replace ANSI codes with spans
13377
+ return escaped
13378
+ .replace(/\x1b\[0m/g, '</span>')
13379
+ .replace(/\x1b\[1m/g, '<span class="ansi-bold">')
13380
+ .replace(/\x1b\[3m/g, '<span class="ansi-italic">')
13381
+ .replace(/\x1b\[30m/g, '<span class="ansi-black">')
13382
+ .replace(/\x1b\[31m/g, '<span class="ansi-red">')
13383
+ .replace(/\x1b\[32m/g, '<span class="ansi-green">')
13384
+ .replace(/\x1b\[33m/g, '<span class="ansi-yellow">')
13385
+ .replace(/\x1b\[34m/g, '<span class="ansi-blue">')
13386
+ .replace(/\x1b\[35m/g, '<span class="ansi-magenta">')
13387
+ .replace(/\x1b\[36m/g, '<span class="ansi-cyan">')
13388
+ .replace(/\x1b\[37m/g, '<span class="ansi-white">')
13389
+ .replace(/\x1b\[90m/g, '<span class="ansi-gray">')
13390
+ .replace(/\x1b\[91m/g, '<span class="ansi-bright-red">')
13391
+ .replace(/\x1b\[92m/g, '<span class="ansi-bright-green">')
13392
+ .replace(/\x1b\[93m/g, '<span class="ansi-bright-yellow">')
13393
+ .replace(/\x1b\[94m/g, '<span class="ansi-bright-blue">')
13394
+ .replace(/\x1b\[95m/g, '<span class="ansi-bright-magenta">')
13395
+ .replace(/\x1b\[96m/g, '<span class="ansi-bright-cyan">')
13396
+ .replace(/\x1b\[97m/g, '<span class="ansi-bright-white">')
13397
+ .replace(/\x1b\[\d+m/g, ''); // strip unrecognized codes
13398
+ }
13399
+ highlightSearch(html) {
13400
+ const query = this.effectiveSearch();
13401
+ if (!query)
13402
+ return html;
13403
+ const escaped = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
13404
+ return html.replace(new RegExp(`(${escaped})`, 'gi'), '<mark class="lc-log-viewer__match">$1</mark>');
13405
+ }
13406
+ escapeHtml(text) {
13407
+ return text
13408
+ .replace(/&/g, '&amp;')
13409
+ .replace(/</g, '&lt;')
13410
+ .replace(/>/g, '&gt;');
13411
+ }
13412
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: LogViewerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13413
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: LogViewerComponent, isStandalone: true, selector: "lc-log-viewer", inputs: { lines: { classPropertyName: "lines", publicName: "lines", isSignal: true, isRequired: false, transformFunction: null }, stream$: { classPropertyName: "stream$", publicName: "stream$", isSignal: true, isRequired: false, transformFunction: null }, maxLines: { classPropertyName: "maxLines", publicName: "maxLines", isSignal: true, isRequired: false, transformFunction: null }, autoScroll: { classPropertyName: "autoScroll", publicName: "autoScroll", isSignal: true, isRequired: false, transformFunction: null }, showTimestamps: { classPropertyName: "showTimestamps", publicName: "showTimestamps", isSignal: true, isRequired: false, transformFunction: null }, showLineNumbers: { classPropertyName: "showLineNumbers", publicName: "showLineNumbers", isSignal: true, isRequired: false, transformFunction: null }, ansiColors: { classPropertyName: "ansiColors", publicName: "ansiColors", isSignal: true, isRequired: false, transformFunction: null }, levelFilter: { classPropertyName: "levelFilter", publicName: "levelFilter", isSignal: true, isRequired: false, transformFunction: null }, searchQuery: { classPropertyName: "searchQuery", publicName: "searchQuery", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { lineClick: "lineClick", copyAll: "copyAll", scrollStateChange: "scrollStateChange" }, viewQueries: [{ propertyName: "scrollContainer", first: true, predicate: ["scrollContainer"], descendants: true }], ngImport: i0, template: "<div\n [class]=\"containerClasses()\"\n [style.height]=\"height()\"\n tabindex=\"0\"\n (keydown)=\"onKeydown($event)\"\n>\n <!-- Toolbar -->\n <div class=\"lc-log-viewer__toolbar\">\n <div class=\"lc-log-viewer__toolbar-left\">\n @if (stream$()) {\n <button\n type=\"button\"\n class=\"lc-log-viewer__tool-btn\"\n [class.lc-log-viewer__tool-btn--active]=\"paused()\"\n (click)=\"togglePause()\"\n [title]=\"paused() ? 'Resume' : 'Pause'\"\n >\n <lc-icon [name]=\"paused() ? 'play' : 'pause'\" size=\"xs\" />\n </button>\n }\n <button\n type=\"button\"\n class=\"lc-log-viewer__tool-btn\"\n (click)=\"clearBuffer()\"\n title=\"Clear\"\n >\n <lc-icon name=\"trash\" size=\"xs\" />\n </button>\n <button\n type=\"button\"\n class=\"lc-log-viewer__tool-btn\"\n (click)=\"onCopyAll()\"\n title=\"Copy all\"\n >\n <lc-icon name=\"clipboard-document\" size=\"xs\" />\n </button>\n <button\n type=\"button\"\n class=\"lc-log-viewer__tool-btn\"\n [class.lc-log-viewer__tool-btn--active]=\"showSearch()\"\n (click)=\"toggleSearch()\"\n title=\"Search (/)\"\n >\n <lc-icon name=\"magnifying-glass\" size=\"xs\" />\n </button>\n </div>\n\n <div class=\"lc-log-viewer__toolbar-right\">\n <!-- Level filter pills -->\n @if (levelCounts().error > 0) {\n <button\n type=\"button\"\n class=\"lc-log-viewer__level-pill lc-log-viewer__level-pill--error\"\n [class.lc-log-viewer__level-pill--active]=\"internalLevelFilter().has('error')\"\n (click)=\"toggleLevelFilter('error')\"\n >\n {{ levelCounts().error }} errors\n </button>\n }\n @if (levelCounts().warn > 0) {\n <button\n type=\"button\"\n class=\"lc-log-viewer__level-pill lc-log-viewer__level-pill--warn\"\n [class.lc-log-viewer__level-pill--active]=\"internalLevelFilter().has('warn')\"\n (click)=\"toggleLevelFilter('warn')\"\n >\n {{ levelCounts().warn }} warnings\n </button>\n }\n <span class=\"lc-log-viewer__line-count\">{{ filteredLines().length }} lines</span>\n </div>\n </div>\n\n <!-- Search bar -->\n @if (showSearch()) {\n <div class=\"lc-log-viewer__search\">\n <lc-icon name=\"magnifying-glass\" size=\"xs\" />\n <input\n type=\"text\"\n class=\"lc-log-viewer__search-input\"\n placeholder=\"Search logs...\"\n [value]=\"internalSearch()\"\n (input)=\"internalSearch.set($any($event.target).value)\"\n autofocus\n />\n </div>\n }\n\n <!-- Log content (virtualized) -->\n <div\n #scrollContainer\n class=\"lc-log-viewer__content\"\n >\n <div\n class=\"lc-log-viewer__spacer\"\n [style.height.px]=\"visibleRange().totalHeight\"\n >\n <div\n class=\"lc-log-viewer__window\"\n [style.transform]=\"'translateY(' + (visibleRange().start * LINE_HEIGHT) + 'px)'\"\n >\n @for (line of visibleLines(); track line._index) {\n <div\n class=\"lc-log-viewer__line\"\n [class.lc-log-viewer__line--debug]=\"line.level === 'debug'\"\n [class.lc-log-viewer__line--info]=\"line.level === 'info'\"\n [class.lc-log-viewer__line--warn]=\"line.level === 'warn'\"\n [class.lc-log-viewer__line--error]=\"line.level === 'error'\"\n (click)=\"onLineClick(line)\"\n >\n @if (showLineNumbers()) {\n <span class=\"lc-log-viewer__line-num\">{{ line._index + 1 }}</span>\n }\n @if (showTimestamps() && line.timestamp) {\n <span class=\"lc-log-viewer__timestamp\">{{ formatTimestamp(line.timestamp) }}</span>\n }\n @if (line.level) {\n <span class=\"lc-log-viewer__level\">{{ line.level.toUpperCase().padEnd(5) }}</span>\n }\n @if (line.source) {\n <span class=\"lc-log-viewer__source\">[{{ line.source }}]</span>\n }\n <span\n class=\"lc-log-viewer__text\"\n [innerHTML]=\"highlightSearch(parseAnsi(line.text))\"\n ></span>\n </div>\n }\n </div>\n </div>\n </div>\n\n <!-- Jump to bottom button -->\n @if (!atBottom()) {\n <button\n type=\"button\"\n class=\"lc-log-viewer__jump-bottom\"\n (click)=\"scrollToBottom()\"\n >\n <lc-icon name=\"arrow-down\" size=\"xs\" />\n Jump to bottom\n </button>\n }\n</div>\n", styles: [".lc-log-viewer{display:flex;flex-direction:column;border:1px solid var(--color-divider, #e5e7eb);border-radius:var(--radius-md, .375rem);overflow:hidden;font-family:var(--font-family-mono, \"Menlo\", \"Monaco\", \"Courier New\", monospace);font-size:.8125rem;line-height:1}.lc-log-viewer:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-log-viewer--log{background-color:var(--color-background, #ffffff);color:var(--color-text, #111827)}.lc-log-viewer--terminal{background-color:#1e1e2e;color:#cdd6f4;border-color:#313244}.lc-log-viewer__toolbar{display:flex;align-items:center;justify-content:space-between;padding:.375rem .5rem;border-bottom:1px solid var(--color-divider, #e5e7eb);gap:.5rem;flex-shrink:0}.lc-log-viewer--terminal .lc-log-viewer__toolbar{border-bottom-color:#313244;background-color:#181825}.lc-log-viewer__toolbar-left,.lc-log-viewer__toolbar-right{display:flex;align-items:center;gap:.25rem}.lc-log-viewer__tool-btn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:none;background:transparent;border-radius:var(--radius-sm, .25rem);cursor:pointer;color:inherit;opacity:.7;transition:opacity .15s,background-color .15s}.lc-log-viewer__tool-btn:hover{opacity:1;background-color:#80808026}.lc-log-viewer__tool-btn--active{opacity:1;background-color:#80808033}.lc-log-viewer__level-pill{font-size:.6875rem;padding:.125rem .5rem;border-radius:9999px;border:none;cursor:pointer;font-family:var(--font-family-sans);transition:opacity .15s;opacity:.6}.lc-log-viewer__level-pill--active{opacity:1}.lc-log-viewer__level-pill--error{background-color:#fecaca;color:#991b1b}.lc-log-viewer--terminal .lc-log-viewer__level-pill--error{background-color:#45202a;color:#f38ba8}.lc-log-viewer__level-pill--warn{background-color:#fef3c7;color:#92400e}.lc-log-viewer--terminal .lc-log-viewer__level-pill--warn{background-color:#3d3520;color:#f9e2af}.lc-log-viewer__line-count{font-size:.6875rem;opacity:.5;font-family:var(--font-family-sans);padding:0 .25rem}.lc-log-viewer__search{display:flex;align-items:center;gap:.375rem;padding:.25rem .5rem;border-bottom:1px solid var(--color-divider, #e5e7eb);flex-shrink:0}.lc-log-viewer--terminal .lc-log-viewer__search{border-bottom-color:#313244;background-color:#181825}.lc-log-viewer__search-input{flex:1;background:transparent;border:none;outline:none;color:inherit;font-family:inherit;font-size:inherit}.lc-log-viewer__search-input::placeholder{opacity:.4}.lc-log-viewer__content{flex:1;overflow-y:auto;overflow-x:auto;position:relative;min-height:0}.lc-log-viewer__spacer{position:relative;width:100%}.lc-log-viewer__window{position:absolute;top:0;left:0;right:0}.lc-log-viewer__line{display:flex;align-items:baseline;gap:.625rem;padding:1px .75rem;height:22px;white-space:nowrap;cursor:pointer;transition:background-color .1s}.lc-log-viewer__line:hover{background-color:#80808014}.lc-log-viewer__line--debug{opacity:.6}.lc-log-viewer--log .lc-log-viewer__line--warn{background-color:#fffbeb}.lc-log-viewer--terminal .lc-log-viewer__line--warn{background-color:#f9e2af0d}.lc-log-viewer--log .lc-log-viewer__line--error{background-color:#fef2f2}.lc-log-viewer--terminal .lc-log-viewer__line--error{background-color:#f38ba814}.lc-log-viewer__line-num{color:inherit;opacity:.3;min-width:3ch;text-align:right;-webkit-user-select:none;user-select:none}.lc-log-viewer__timestamp{color:inherit;opacity:.5;min-width:12ch}.lc-log-viewer__level{min-width:5ch;font-weight:600}.lc-log-viewer__line--debug .lc-log-viewer__level{color:#9ca3af}.lc-log-viewer__line--info .lc-log-viewer__level{color:#3b82f6}.lc-log-viewer__line--warn .lc-log-viewer__level{color:#f59e0b}.lc-log-viewer__line--error .lc-log-viewer__level{color:#ef4444}.lc-log-viewer__line--debug .lc-log-viewer--terminal .lc-log-viewer__level{color:#6c7086}.lc-log-viewer__line--info .lc-log-viewer--terminal .lc-log-viewer__level{color:#89b4fa}.lc-log-viewer__line--warn .lc-log-viewer--terminal .lc-log-viewer__level{color:#f9e2af}.lc-log-viewer__line--error .lc-log-viewer--terminal .lc-log-viewer__level{color:#f38ba8}.lc-log-viewer__source{color:inherit;opacity:.5}.lc-log-viewer__text{flex:1;min-width:0}.lc-log-viewer__match{background-color:#fbbf2480;border-radius:2px;padding:0 1px}.lc-log-viewer--terminal .lc-log-viewer__match{background-color:#f9e2af40}.lc-log-viewer__jump-bottom{position:absolute;bottom:1rem;left:50%;transform:translate(-50%);display:inline-flex;align-items:center;gap:.375rem;padding:.375rem .75rem;border:1px solid var(--color-divider, #e5e7eb);border-radius:9999px;background-color:var(--color-background, #ffffff);color:var(--color-text, #111827);font-size:.75rem;font-family:var(--font-family-sans);cursor:pointer;box-shadow:0 2px 8px #00000026;z-index:10;transition:background-color .15s}.lc-log-viewer__jump-bottom:hover{background-color:var(--color-neutral-50, #f9fafb)}.lc-log-viewer--terminal .lc-log-viewer__jump-bottom{background-color:#313244;border-color:#45475a;color:#cdd6f4}.lc-log-viewer--terminal .lc-log-viewer__jump-bottom:hover{background-color:#45475a}.ansi-bold{font-weight:700}.ansi-italic{font-style:italic}.ansi-black{color:#1e1e2e}.ansi-red{color:#f38ba8}.ansi-green{color:#a6e3a1}.ansi-yellow{color:#f9e2af}.ansi-blue{color:#89b4fa}.ansi-magenta{color:#cba6f7}.ansi-cyan{color:#94e2d5}.ansi-white{color:#cdd6f4}.ansi-gray{color:#6c7086}.ansi-bright-red{color:#eba0ac}.ansi-bright-green{color:#a6e3a1}.ansi-bright-yellow{color:#f9e2af}.ansi-bright-blue{color:#89dceb}.ansi-bright-magenta{color:#f5c2e7}.ansi-bright-cyan{color:#94e2d5}.ansi-bright-white{color:#bac2de}.lc-log-viewer--log .ansi-black{color:#111827}.lc-log-viewer--log .ansi-red{color:#dc2626}.lc-log-viewer--log .ansi-green{color:#16a34a}.lc-log-viewer--log .ansi-yellow{color:#ca8a04}.lc-log-viewer--log .ansi-blue{color:#2563eb}.lc-log-viewer--log .ansi-magenta{color:#9333ea}.lc-log-viewer--log .ansi-cyan{color:#0891b2}.lc-log-viewer--log .ansi-white{color:#6b7280}.lc-log-viewer--log .ansi-gray{color:#9ca3af}.lc-log-viewer--terminal .lc-log-viewer__content::-webkit-scrollbar{width:8px;height:8px}.lc-log-viewer--terminal .lc-log-viewer__content::-webkit-scrollbar-track{background:#181825}.lc-log-viewer--terminal .lc-log-viewer__content::-webkit-scrollbar-thumb{background:#45475a;border-radius:4px}.lc-log-viewer--terminal .lc-log-viewer__content::-webkit-scrollbar-thumb:hover{background:#585b70}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IconComponent, selector: "lc-icon", inputs: ["name", "variant", "size", "color", "ariaLabel", "decorative"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
13414
+ }
13415
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: LogViewerComponent, decorators: [{
13416
+ type: Component,
13417
+ args: [{ selector: 'lc-log-viewer', standalone: true, imports: [CommonModule, IconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n [class]=\"containerClasses()\"\n [style.height]=\"height()\"\n tabindex=\"0\"\n (keydown)=\"onKeydown($event)\"\n>\n <!-- Toolbar -->\n <div class=\"lc-log-viewer__toolbar\">\n <div class=\"lc-log-viewer__toolbar-left\">\n @if (stream$()) {\n <button\n type=\"button\"\n class=\"lc-log-viewer__tool-btn\"\n [class.lc-log-viewer__tool-btn--active]=\"paused()\"\n (click)=\"togglePause()\"\n [title]=\"paused() ? 'Resume' : 'Pause'\"\n >\n <lc-icon [name]=\"paused() ? 'play' : 'pause'\" size=\"xs\" />\n </button>\n }\n <button\n type=\"button\"\n class=\"lc-log-viewer__tool-btn\"\n (click)=\"clearBuffer()\"\n title=\"Clear\"\n >\n <lc-icon name=\"trash\" size=\"xs\" />\n </button>\n <button\n type=\"button\"\n class=\"lc-log-viewer__tool-btn\"\n (click)=\"onCopyAll()\"\n title=\"Copy all\"\n >\n <lc-icon name=\"clipboard-document\" size=\"xs\" />\n </button>\n <button\n type=\"button\"\n class=\"lc-log-viewer__tool-btn\"\n [class.lc-log-viewer__tool-btn--active]=\"showSearch()\"\n (click)=\"toggleSearch()\"\n title=\"Search (/)\"\n >\n <lc-icon name=\"magnifying-glass\" size=\"xs\" />\n </button>\n </div>\n\n <div class=\"lc-log-viewer__toolbar-right\">\n <!-- Level filter pills -->\n @if (levelCounts().error > 0) {\n <button\n type=\"button\"\n class=\"lc-log-viewer__level-pill lc-log-viewer__level-pill--error\"\n [class.lc-log-viewer__level-pill--active]=\"internalLevelFilter().has('error')\"\n (click)=\"toggleLevelFilter('error')\"\n >\n {{ levelCounts().error }} errors\n </button>\n }\n @if (levelCounts().warn > 0) {\n <button\n type=\"button\"\n class=\"lc-log-viewer__level-pill lc-log-viewer__level-pill--warn\"\n [class.lc-log-viewer__level-pill--active]=\"internalLevelFilter().has('warn')\"\n (click)=\"toggleLevelFilter('warn')\"\n >\n {{ levelCounts().warn }} warnings\n </button>\n }\n <span class=\"lc-log-viewer__line-count\">{{ filteredLines().length }} lines</span>\n </div>\n </div>\n\n <!-- Search bar -->\n @if (showSearch()) {\n <div class=\"lc-log-viewer__search\">\n <lc-icon name=\"magnifying-glass\" size=\"xs\" />\n <input\n type=\"text\"\n class=\"lc-log-viewer__search-input\"\n placeholder=\"Search logs...\"\n [value]=\"internalSearch()\"\n (input)=\"internalSearch.set($any($event.target).value)\"\n autofocus\n />\n </div>\n }\n\n <!-- Log content (virtualized) -->\n <div\n #scrollContainer\n class=\"lc-log-viewer__content\"\n >\n <div\n class=\"lc-log-viewer__spacer\"\n [style.height.px]=\"visibleRange().totalHeight\"\n >\n <div\n class=\"lc-log-viewer__window\"\n [style.transform]=\"'translateY(' + (visibleRange().start * LINE_HEIGHT) + 'px)'\"\n >\n @for (line of visibleLines(); track line._index) {\n <div\n class=\"lc-log-viewer__line\"\n [class.lc-log-viewer__line--debug]=\"line.level === 'debug'\"\n [class.lc-log-viewer__line--info]=\"line.level === 'info'\"\n [class.lc-log-viewer__line--warn]=\"line.level === 'warn'\"\n [class.lc-log-viewer__line--error]=\"line.level === 'error'\"\n (click)=\"onLineClick(line)\"\n >\n @if (showLineNumbers()) {\n <span class=\"lc-log-viewer__line-num\">{{ line._index + 1 }}</span>\n }\n @if (showTimestamps() && line.timestamp) {\n <span class=\"lc-log-viewer__timestamp\">{{ formatTimestamp(line.timestamp) }}</span>\n }\n @if (line.level) {\n <span class=\"lc-log-viewer__level\">{{ line.level.toUpperCase().padEnd(5) }}</span>\n }\n @if (line.source) {\n <span class=\"lc-log-viewer__source\">[{{ line.source }}]</span>\n }\n <span\n class=\"lc-log-viewer__text\"\n [innerHTML]=\"highlightSearch(parseAnsi(line.text))\"\n ></span>\n </div>\n }\n </div>\n </div>\n </div>\n\n <!-- Jump to bottom button -->\n @if (!atBottom()) {\n <button\n type=\"button\"\n class=\"lc-log-viewer__jump-bottom\"\n (click)=\"scrollToBottom()\"\n >\n <lc-icon name=\"arrow-down\" size=\"xs\" />\n Jump to bottom\n </button>\n }\n</div>\n", styles: [".lc-log-viewer{display:flex;flex-direction:column;border:1px solid var(--color-divider, #e5e7eb);border-radius:var(--radius-md, .375rem);overflow:hidden;font-family:var(--font-family-mono, \"Menlo\", \"Monaco\", \"Courier New\", monospace);font-size:.8125rem;line-height:1}.lc-log-viewer:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-log-viewer--log{background-color:var(--color-background, #ffffff);color:var(--color-text, #111827)}.lc-log-viewer--terminal{background-color:#1e1e2e;color:#cdd6f4;border-color:#313244}.lc-log-viewer__toolbar{display:flex;align-items:center;justify-content:space-between;padding:.375rem .5rem;border-bottom:1px solid var(--color-divider, #e5e7eb);gap:.5rem;flex-shrink:0}.lc-log-viewer--terminal .lc-log-viewer__toolbar{border-bottom-color:#313244;background-color:#181825}.lc-log-viewer__toolbar-left,.lc-log-viewer__toolbar-right{display:flex;align-items:center;gap:.25rem}.lc-log-viewer__tool-btn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:none;background:transparent;border-radius:var(--radius-sm, .25rem);cursor:pointer;color:inherit;opacity:.7;transition:opacity .15s,background-color .15s}.lc-log-viewer__tool-btn:hover{opacity:1;background-color:#80808026}.lc-log-viewer__tool-btn--active{opacity:1;background-color:#80808033}.lc-log-viewer__level-pill{font-size:.6875rem;padding:.125rem .5rem;border-radius:9999px;border:none;cursor:pointer;font-family:var(--font-family-sans);transition:opacity .15s;opacity:.6}.lc-log-viewer__level-pill--active{opacity:1}.lc-log-viewer__level-pill--error{background-color:#fecaca;color:#991b1b}.lc-log-viewer--terminal .lc-log-viewer__level-pill--error{background-color:#45202a;color:#f38ba8}.lc-log-viewer__level-pill--warn{background-color:#fef3c7;color:#92400e}.lc-log-viewer--terminal .lc-log-viewer__level-pill--warn{background-color:#3d3520;color:#f9e2af}.lc-log-viewer__line-count{font-size:.6875rem;opacity:.5;font-family:var(--font-family-sans);padding:0 .25rem}.lc-log-viewer__search{display:flex;align-items:center;gap:.375rem;padding:.25rem .5rem;border-bottom:1px solid var(--color-divider, #e5e7eb);flex-shrink:0}.lc-log-viewer--terminal .lc-log-viewer__search{border-bottom-color:#313244;background-color:#181825}.lc-log-viewer__search-input{flex:1;background:transparent;border:none;outline:none;color:inherit;font-family:inherit;font-size:inherit}.lc-log-viewer__search-input::placeholder{opacity:.4}.lc-log-viewer__content{flex:1;overflow-y:auto;overflow-x:auto;position:relative;min-height:0}.lc-log-viewer__spacer{position:relative;width:100%}.lc-log-viewer__window{position:absolute;top:0;left:0;right:0}.lc-log-viewer__line{display:flex;align-items:baseline;gap:.625rem;padding:1px .75rem;height:22px;white-space:nowrap;cursor:pointer;transition:background-color .1s}.lc-log-viewer__line:hover{background-color:#80808014}.lc-log-viewer__line--debug{opacity:.6}.lc-log-viewer--log .lc-log-viewer__line--warn{background-color:#fffbeb}.lc-log-viewer--terminal .lc-log-viewer__line--warn{background-color:#f9e2af0d}.lc-log-viewer--log .lc-log-viewer__line--error{background-color:#fef2f2}.lc-log-viewer--terminal .lc-log-viewer__line--error{background-color:#f38ba814}.lc-log-viewer__line-num{color:inherit;opacity:.3;min-width:3ch;text-align:right;-webkit-user-select:none;user-select:none}.lc-log-viewer__timestamp{color:inherit;opacity:.5;min-width:12ch}.lc-log-viewer__level{min-width:5ch;font-weight:600}.lc-log-viewer__line--debug .lc-log-viewer__level{color:#9ca3af}.lc-log-viewer__line--info .lc-log-viewer__level{color:#3b82f6}.lc-log-viewer__line--warn .lc-log-viewer__level{color:#f59e0b}.lc-log-viewer__line--error .lc-log-viewer__level{color:#ef4444}.lc-log-viewer__line--debug .lc-log-viewer--terminal .lc-log-viewer__level{color:#6c7086}.lc-log-viewer__line--info .lc-log-viewer--terminal .lc-log-viewer__level{color:#89b4fa}.lc-log-viewer__line--warn .lc-log-viewer--terminal .lc-log-viewer__level{color:#f9e2af}.lc-log-viewer__line--error .lc-log-viewer--terminal .lc-log-viewer__level{color:#f38ba8}.lc-log-viewer__source{color:inherit;opacity:.5}.lc-log-viewer__text{flex:1;min-width:0}.lc-log-viewer__match{background-color:#fbbf2480;border-radius:2px;padding:0 1px}.lc-log-viewer--terminal .lc-log-viewer__match{background-color:#f9e2af40}.lc-log-viewer__jump-bottom{position:absolute;bottom:1rem;left:50%;transform:translate(-50%);display:inline-flex;align-items:center;gap:.375rem;padding:.375rem .75rem;border:1px solid var(--color-divider, #e5e7eb);border-radius:9999px;background-color:var(--color-background, #ffffff);color:var(--color-text, #111827);font-size:.75rem;font-family:var(--font-family-sans);cursor:pointer;box-shadow:0 2px 8px #00000026;z-index:10;transition:background-color .15s}.lc-log-viewer__jump-bottom:hover{background-color:var(--color-neutral-50, #f9fafb)}.lc-log-viewer--terminal .lc-log-viewer__jump-bottom{background-color:#313244;border-color:#45475a;color:#cdd6f4}.lc-log-viewer--terminal .lc-log-viewer__jump-bottom:hover{background-color:#45475a}.ansi-bold{font-weight:700}.ansi-italic{font-style:italic}.ansi-black{color:#1e1e2e}.ansi-red{color:#f38ba8}.ansi-green{color:#a6e3a1}.ansi-yellow{color:#f9e2af}.ansi-blue{color:#89b4fa}.ansi-magenta{color:#cba6f7}.ansi-cyan{color:#94e2d5}.ansi-white{color:#cdd6f4}.ansi-gray{color:#6c7086}.ansi-bright-red{color:#eba0ac}.ansi-bright-green{color:#a6e3a1}.ansi-bright-yellow{color:#f9e2af}.ansi-bright-blue{color:#89dceb}.ansi-bright-magenta{color:#f5c2e7}.ansi-bright-cyan{color:#94e2d5}.ansi-bright-white{color:#bac2de}.lc-log-viewer--log .ansi-black{color:#111827}.lc-log-viewer--log .ansi-red{color:#dc2626}.lc-log-viewer--log .ansi-green{color:#16a34a}.lc-log-viewer--log .ansi-yellow{color:#ca8a04}.lc-log-viewer--log .ansi-blue{color:#2563eb}.lc-log-viewer--log .ansi-magenta{color:#9333ea}.lc-log-viewer--log .ansi-cyan{color:#0891b2}.lc-log-viewer--log .ansi-white{color:#6b7280}.lc-log-viewer--log .ansi-gray{color:#9ca3af}.lc-log-viewer--terminal .lc-log-viewer__content::-webkit-scrollbar{width:8px;height:8px}.lc-log-viewer--terminal .lc-log-viewer__content::-webkit-scrollbar-track{background:#181825}.lc-log-viewer--terminal .lc-log-viewer__content::-webkit-scrollbar-thumb{background:#45475a;border-radius:4px}.lc-log-viewer--terminal .lc-log-viewer__content::-webkit-scrollbar-thumb:hover{background:#585b70}\n"] }]
13418
+ }], ctorParameters: () => [], propDecorators: { scrollContainer: [{
13419
+ type: ViewChild,
13420
+ args: ['scrollContainer']
13421
+ }], lines: [{ type: i0.Input, args: [{ isSignal: true, alias: "lines", required: false }] }], stream$: [{ type: i0.Input, args: [{ isSignal: true, alias: "stream$", required: false }] }], maxLines: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLines", required: false }] }], autoScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoScroll", required: false }] }], showTimestamps: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTimestamps", required: false }] }], showLineNumbers: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLineNumbers", required: false }] }], ansiColors: [{ type: i0.Input, args: [{ isSignal: true, alias: "ansiColors", required: false }] }], levelFilter: [{ type: i0.Input, args: [{ isSignal: true, alias: "levelFilter", required: false }] }], searchQuery: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchQuery", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], lineClick: [{ type: i0.Output, args: ["lineClick"] }], copyAll: [{ type: i0.Output, args: ["copyAll"] }], scrollStateChange: [{ type: i0.Output, args: ["scrollStateChange"] }] } });
13422
+
13423
+ /**
13424
+ * Confirm dialog component for confirming user actions.
13425
+ *
13426
+ * Builds on `<lc-modal>` to provide a standardized confirmation pattern
13427
+ * with optional destructive variant and text-matching confirmation.
13428
+ *
13429
+ * @example
13430
+ * ```html
13431
+ * <lc-confirm-dialog
13432
+ * [open]="showConfirm()"
13433
+ * variant="destructive"
13434
+ * title="Delete project?"
13435
+ * message="This cannot be undone."
13436
+ * (confirmed)="onDelete()"
13437
+ * (cancelled)="showConfirm.set(false)"
13438
+ * />
13439
+ * ```
13440
+ */
13441
+ class ConfirmDialogComponent {
13442
+ confirmInput;
13443
+ /** Whether the dialog is open */
13444
+ open = input(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
13445
+ /** Dialog variant */
13446
+ variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
13447
+ /** Dialog title */
13448
+ title = input.required(...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
13449
+ /** Dialog message */
13450
+ message = input.required(...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
13451
+ /** Confirm button label */
13452
+ confirmLabel = input('Confirm', ...(ngDevMode ? [{ debugName: "confirmLabel" }] : /* istanbul ignore next */ []));
13453
+ /** Cancel button label */
13454
+ cancelLabel = input('Cancel', ...(ngDevMode ? [{ debugName: "cancelLabel" }] : /* istanbul ignore next */ []));
13455
+ /** Icon name (auto-set by variant if not provided) */
13456
+ icon = input(...(ngDevMode ? [undefined, { debugName: "icon" }] : /* istanbul ignore next */ []));
13457
+ /** Require text match to enable confirm */
13458
+ requireText = input(...(ngDevMode ? [undefined, { debugName: "requireText" }] : /* istanbul ignore next */ []));
13459
+ /** Emitted when user confirms */
13460
+ confirmed = output();
13461
+ /** Emitted when user cancels */
13462
+ cancelled = output();
13463
+ /** Internal text input value */
13464
+ inputValue = signal('', ...(ngDevMode ? [{ debugName: "inputValue" }] : /* istanbul ignore next */ []));
13465
+ /** Resolved icon name */
13466
+ resolvedIcon = computed(() => {
13467
+ const icon = this.icon();
13468
+ if (icon)
13469
+ return icon;
13470
+ switch (this.variant()) {
13471
+ case 'destructive':
13472
+ return 'exclamation-triangle';
13473
+ case 'warning':
13474
+ return 'exclamation-circle';
13475
+ default:
13476
+ return 'question-mark-circle';
13477
+ }
13478
+ }, ...(ngDevMode ? [{ debugName: "resolvedIcon" }] : /* istanbul ignore next */ []));
13479
+ /** Resolved icon color */
13480
+ iconColor = computed(() => {
13481
+ switch (this.variant()) {
13482
+ case 'destructive':
13483
+ return 'var(--color-error-default, #ef4444)';
13484
+ case 'warning':
13485
+ return 'var(--color-warning-default, #f59e0b)';
13486
+ default:
13487
+ return 'var(--color-primary-600, #2563eb)';
13488
+ }
13489
+ }, ...(ngDevMode ? [{ debugName: "iconColor" }] : /* istanbul ignore next */ []));
13490
+ /** Confirm button variant */
13491
+ confirmButtonVariant = computed(() => {
13492
+ switch (this.variant()) {
13493
+ case 'destructive':
13494
+ return 'danger';
13495
+ case 'warning':
13496
+ return 'warning';
13497
+ default:
13498
+ return 'primary';
13499
+ }
13500
+ }, ...(ngDevMode ? [{ debugName: "confirmButtonVariant" }] : /* istanbul ignore next */ []));
13501
+ /** Whether confirm is allowed (text match check) */
13502
+ confirmAllowed = computed(() => {
13503
+ const req = this.requireText();
13504
+ if (!req)
13505
+ return true;
13506
+ return this.inputValue() === req.expected;
13507
+ }, ...(ngDevMode ? [{ debugName: "confirmAllowed" }] : /* istanbul ignore next */ []));
13508
+ constructor() {
13509
+ // Reset input when dialog opens
13510
+ effect(() => {
13511
+ if (this.open()) {
13512
+ this.inputValue.set('');
13513
+ }
13514
+ });
13515
+ }
13516
+ onConfirm() {
13517
+ if (!this.confirmAllowed())
13518
+ return;
13519
+ this.confirmed.emit();
13520
+ }
13521
+ onCancel() {
13522
+ this.cancelled.emit();
13523
+ }
13524
+ onModalClose() {
13525
+ this.cancelled.emit();
13526
+ }
13527
+ onInputChange(value) {
13528
+ this.inputValue.set(String(value));
13529
+ }
13530
+ onKeydown(event) {
13531
+ if (event.key === 'Enter' && this.confirmAllowed()) {
13532
+ event.preventDefault();
13533
+ this.onConfirm();
13534
+ }
13535
+ }
13536
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ConfirmDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13537
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: ConfirmDialogComponent, isStandalone: true, selector: "lc-confirm-dialog", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null }, confirmLabel: { classPropertyName: "confirmLabel", publicName: "confirmLabel", isSignal: true, isRequired: false, transformFunction: null }, cancelLabel: { classPropertyName: "cancelLabel", publicName: "cancelLabel", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, requireText: { classPropertyName: "requireText", publicName: "requireText", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { confirmed: "confirmed", cancelled: "cancelled" }, viewQueries: [{ propertyName: "confirmInput", first: true, predicate: ["confirmInput"], descendants: true }], ngImport: i0, template: "<lc-modal\n [open]=\"open()\"\n size=\"sm\"\n [showCloseButton]=\"true\"\n (openChange)=\"$event ? null : onModalClose()\"\n (keydown)=\"onKeydown($event)\"\n>\n <div slot=\"header\">\n <div class=\"lc-confirm__header\">\n <lc-icon\n [name]=\"resolvedIcon()\"\n size=\"md\"\n [color]=\"iconColor()\"\n />\n <h2>{{ title() }}</h2>\n </div>\n </div>\n\n <div slot=\"body\">\n <p class=\"lc-confirm__message\">{{ message() }}</p>\n\n @if (requireText(); as req) {\n <div class=\"lc-confirm__require-text\">\n <label class=\"lc-confirm__require-label\">{{ req.prompt }}</label>\n <lc-input\n #confirmInput\n [placeholder]=\"req.expected\"\n (valueChange)=\"onInputChange($event)\"\n />\n </div>\n }\n </div>\n\n <div slot=\"footer\" class=\"lc-confirm__footer\">\n <lc-button\n variant=\"ghost\"\n (clicked)=\"onCancel()\"\n >\n {{ cancelLabel() }}\n </lc-button>\n <lc-button\n [variant]=\"confirmButtonVariant()\"\n [disabled]=\"!confirmAllowed()\"\n (clicked)=\"onConfirm()\"\n >\n {{ confirmLabel() }}\n </lc-button>\n </div>\n</lc-modal>\n", styles: [".lc-confirm__header{display:flex;align-items:center;gap:.5rem}.lc-confirm__header h2{margin:0;font-size:var(--font-size-lg, 1.125rem);font-weight:var(--font-weight-semibold, 600);color:var(--color-text, #111827)}.lc-confirm__message{margin:0;color:var(--color-text-secondary, #4b5563);font-size:var(--font-size-sm, .875rem);line-height:1.6}.lc-confirm__require-text{margin-top:1rem}.lc-confirm__require-label{display:block;font-size:var(--font-size-sm, .875rem);font-weight:500;color:var(--color-text, #111827);margin-bottom:.375rem}.lc-confirm__footer{display:flex;gap:.5rem;justify-content:flex-end}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: IconComponent, selector: "lc-icon", inputs: ["name", "variant", "size", "color", "ariaLabel", "decorative"] }, { kind: "component", type: ButtonComponent, selector: "lc-button", inputs: ["variant", "size", "disabled", "loading", "isLoading", "iconOnly", "fullWidth", "ariaLabel", "type"], outputs: ["clicked", "focused", "blurred"] }, { kind: "component", type: ModalComponent, selector: "lc-modal", inputs: ["open", "size", "closeOnBackdropClick", "closeOnEscape", "showCloseButton", "ariaLabel", "ariaLabelledBy", "ariaDescribedBy"], outputs: ["modalOpened", "modalClosed", "openChange", "backdropClicked"] }, { kind: "component", type: InputComponent, selector: "lc-input", inputs: ["label", "placeholder", "type", "size", "disabled", "readonly", "required", "error", "helperText", "iconBefore", "iconAfter", "maxLength", "showCharCount", "ariaLabel"], outputs: ["valueChange", "focused", "blurred", "enterPressed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
13538
+ }
13539
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ConfirmDialogComponent, decorators: [{
13540
+ type: Component,
13541
+ args: [{ selector: 'lc-confirm-dialog', standalone: true, imports: [CommonModule, FormsModule, IconComponent, ButtonComponent, ModalComponent, InputComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<lc-modal\n [open]=\"open()\"\n size=\"sm\"\n [showCloseButton]=\"true\"\n (openChange)=\"$event ? null : onModalClose()\"\n (keydown)=\"onKeydown($event)\"\n>\n <div slot=\"header\">\n <div class=\"lc-confirm__header\">\n <lc-icon\n [name]=\"resolvedIcon()\"\n size=\"md\"\n [color]=\"iconColor()\"\n />\n <h2>{{ title() }}</h2>\n </div>\n </div>\n\n <div slot=\"body\">\n <p class=\"lc-confirm__message\">{{ message() }}</p>\n\n @if (requireText(); as req) {\n <div class=\"lc-confirm__require-text\">\n <label class=\"lc-confirm__require-label\">{{ req.prompt }}</label>\n <lc-input\n #confirmInput\n [placeholder]=\"req.expected\"\n (valueChange)=\"onInputChange($event)\"\n />\n </div>\n }\n </div>\n\n <div slot=\"footer\" class=\"lc-confirm__footer\">\n <lc-button\n variant=\"ghost\"\n (clicked)=\"onCancel()\"\n >\n {{ cancelLabel() }}\n </lc-button>\n <lc-button\n [variant]=\"confirmButtonVariant()\"\n [disabled]=\"!confirmAllowed()\"\n (clicked)=\"onConfirm()\"\n >\n {{ confirmLabel() }}\n </lc-button>\n </div>\n</lc-modal>\n", styles: [".lc-confirm__header{display:flex;align-items:center;gap:.5rem}.lc-confirm__header h2{margin:0;font-size:var(--font-size-lg, 1.125rem);font-weight:var(--font-weight-semibold, 600);color:var(--color-text, #111827)}.lc-confirm__message{margin:0;color:var(--color-text-secondary, #4b5563);font-size:var(--font-size-sm, .875rem);line-height:1.6}.lc-confirm__require-text{margin-top:1rem}.lc-confirm__require-label{display:block;font-size:var(--font-size-sm, .875rem);font-weight:500;color:var(--color-text, #111827);margin-bottom:.375rem}.lc-confirm__footer{display:flex;gap:.5rem;justify-content:flex-end}\n"] }]
13542
+ }], ctorParameters: () => [], propDecorators: { confirmInput: [{
13543
+ type: ViewChild,
13544
+ args: ['confirmInput']
13545
+ }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: true }] }], confirmLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmLabel", required: false }] }], cancelLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "cancelLabel", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], requireText: [{ type: i0.Input, args: [{ isSignal: true, alias: "requireText", required: false }] }], confirmed: [{ type: i0.Output, args: ["confirmed"] }], cancelled: [{ type: i0.Output, args: ["cancelled"] }] } });
13546
+
13547
+ /**
13548
+ * Imperative confirm dialog service.
13549
+ *
13550
+ * @example
13551
+ * ```ts
13552
+ * const ok = await this.confirmService.confirm({
13553
+ * title: 'Delete item?',
13554
+ * message: 'This cannot be undone.',
13555
+ * });
13556
+ * if (ok) { ... }
13557
+ * ```
13558
+ */
13559
+ class ConfirmService {
13560
+ appRef = inject(ApplicationRef);
13561
+ injector = inject(EnvironmentInjector);
13562
+ /**
13563
+ * Show a confirmation dialog and return a promise that resolves
13564
+ * to true (confirmed) or false (cancelled).
13565
+ */
13566
+ confirm(opts) {
13567
+ return new Promise((resolve) => {
13568
+ // Create a host element
13569
+ const hostEl = document.createElement('div');
13570
+ document.body.appendChild(hostEl);
13571
+ const componentRef = createComponent(ConfirmDialogComponent, {
13572
+ hostElement: hostEl,
13573
+ environmentInjector: this.injector,
13574
+ });
13575
+ // Set inputs
13576
+ componentRef.setInput('open', true);
13577
+ componentRef.setInput('title', opts.title);
13578
+ componentRef.setInput('message', opts.message);
13579
+ if (opts.confirmLabel)
13580
+ componentRef.setInput('confirmLabel', opts.confirmLabel);
13581
+ if (opts.cancelLabel)
13582
+ componentRef.setInput('cancelLabel', opts.cancelLabel);
13583
+ if (opts.variant)
13584
+ componentRef.setInput('variant', opts.variant);
13585
+ if (opts.icon)
13586
+ componentRef.setInput('icon', opts.icon);
13587
+ if (opts.requireText)
13588
+ componentRef.setInput('requireText', opts.requireText);
13589
+ const cleanup = () => {
13590
+ this.appRef.detachView(componentRef.hostView);
13591
+ componentRef.destroy();
13592
+ hostEl.remove();
13593
+ };
13594
+ componentRef.instance.confirmed.subscribe(() => {
13595
+ cleanup();
13596
+ resolve(true);
13597
+ });
13598
+ componentRef.instance.cancelled.subscribe(() => {
13599
+ cleanup();
13600
+ resolve(false);
13601
+ });
13602
+ this.appRef.attachView(componentRef.hostView);
13603
+ });
13604
+ }
13605
+ /**
13606
+ * Show a destructive confirmation dialog.
13607
+ * Shorthand for `confirm({ ...opts, variant: 'destructive' })`.
13608
+ */
13609
+ destructive(opts) {
13610
+ return this.confirm({ ...opts, variant: 'destructive' });
13611
+ }
13612
+ /**
13613
+ * Show a warning confirmation dialog.
13614
+ * Shorthand for `confirm({ ...opts, variant: 'warning' })`.
13615
+ */
13616
+ warning(opts) {
13617
+ return this.confirm({ ...opts, variant: 'warning' });
13618
+ }
13619
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ConfirmService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
13620
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ConfirmService, providedIn: 'root' });
13621
+ }
13622
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ConfirmService, decorators: [{
13623
+ type: Injectable,
13624
+ args: [{ providedIn: 'root' }]
13625
+ }] });
13626
+
13627
+ /**
13628
+ * Combobox / async autocomplete component.
13629
+ *
13630
+ * Supports free-text input with sync/async option suggestions,
13631
+ * single/multiple selection, create new entries, and keyboard navigation.
13632
+ *
13633
+ * @example
13634
+ * ```html
13635
+ * <lc-combobox
13636
+ * label="Assign to user"
13637
+ * [loadOptions]="searchUsers"
13638
+ * [(value)]="assignee"
13639
+ * allowCreate
13640
+ * />
13641
+ * ```
13642
+ */
13643
+ class ComboboxComponent {
13644
+ inputEl;
13645
+ elRef = inject(ElementRef);
13646
+ querySubject = new Subject();
13647
+ asyncSub;
13648
+ /** Sync options */
13649
+ options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
13650
+ /** Async option loader */
13651
+ loadOptions = input(...(ngDevMode ? [undefined, { debugName: "loadOptions" }] : /* istanbul ignore next */ []));
13652
+ /** Debounce for async queries */
13653
+ debounceMs = input(250, ...(ngDevMode ? [{ debugName: "debounceMs" }] : /* istanbul ignore next */ []));
13654
+ /** Minimum characters before triggering search */
13655
+ minChars = input(1, ...(ngDevMode ? [{ debugName: "minChars" }] : /* istanbul ignore next */ []));
13656
+ /** Placeholder text */
13657
+ placeholder = input('Search…', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
13658
+ /** Label */
13659
+ label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
13660
+ /** Helper text */
13661
+ helperText = input(...(ngDevMode ? [undefined, { debugName: "helperText" }] : /* istanbul ignore next */ []));
13662
+ /** Error message */
13663
+ error = input(...(ngDevMode ? [undefined, { debugName: "error" }] : /* istanbul ignore next */ []));
13664
+ /** Disabled state */
13665
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
13666
+ /** Allow multiple selections */
13667
+ multiple = input(false, ...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
13668
+ /** Allow creating new entries */
13669
+ allowCreate = input(false, ...(ngDevMode ? [{ debugName: "allowCreate" }] : /* istanbul ignore next */ []));
13670
+ /** Show loading spinner (controlled) */
13671
+ loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
13672
+ /** Empty state message */
13673
+ emptyMessage = input('No results', ...(ngDevMode ? [{ debugName: "emptyMessage" }] : /* istanbul ignore next */ []));
13674
+ /** Size */
13675
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
13676
+ /** Value change event */
13677
+ valueChange = output();
13678
+ /** Query change event */
13679
+ queryChange = output();
13680
+ /** Option selected event */
13681
+ optionSelected = output();
13682
+ /** Created event (allowCreate) */
13683
+ created = output();
13684
+ /** Internal state */
13685
+ query = signal('', ...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
13686
+ isOpen = signal(false, ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
13687
+ highlightedIndex = signal(-1, ...(ngDevMode ? [{ debugName: "highlightedIndex" }] : /* istanbul ignore next */ []));
13688
+ asyncOptions = signal([], ...(ngDevMode ? [{ debugName: "asyncOptions" }] : /* istanbul ignore next */ []));
13689
+ isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []));
13690
+ /** Selected values */
13691
+ selectedSingle = signal(null, ...(ngDevMode ? [{ debugName: "selectedSingle" }] : /* istanbul ignore next */ []));
13692
+ selectedMultiple = signal([], ...(ngDevMode ? [{ debugName: "selectedMultiple" }] : /* istanbul ignore next */ []));
13693
+ /** Resolved visible options */
13694
+ visibleOptions = computed(() => {
13695
+ const q = this.query().toLowerCase();
13696
+ const loader = this.loadOptions();
13697
+ let opts;
13698
+ if (loader) {
13699
+ opts = this.asyncOptions();
13700
+ }
13701
+ else {
13702
+ opts = this.options();
13703
+ if (q.length >= this.minChars()) {
13704
+ opts = opts.filter((o) => o.label.toLowerCase().includes(q) ||
13705
+ o.value.toLowerCase().includes(q) ||
13706
+ (o.description && o.description.toLowerCase().includes(q)));
13707
+ }
13708
+ }
13709
+ // Remove already selected in multiple mode
13710
+ if (this.multiple()) {
13711
+ const selected = new Set(this.selectedMultiple().map((s) => s.value));
13712
+ opts = opts.filter((o) => !selected.has(o.value));
13713
+ }
13714
+ return opts;
13715
+ }, ...(ngDevMode ? [{ debugName: "visibleOptions" }] : /* istanbul ignore next */ []));
13716
+ /** Grouped options */
13717
+ groupedOptions = computed(() => {
13718
+ const opts = this.visibleOptions();
13719
+ const groups = new Map();
13720
+ for (const opt of opts) {
13721
+ const group = opt.group || '';
13722
+ if (!groups.has(group))
13723
+ groups.set(group, []);
13724
+ groups.get(group).push(opt);
13725
+ }
13726
+ return Array.from(groups.entries()).map(([label, items]) => ({
13727
+ label,
13728
+ items,
13729
+ }));
13730
+ }, ...(ngDevMode ? [{ debugName: "groupedOptions" }] : /* istanbul ignore next */ []));
13731
+ /** Flat list for keyboard nav */
13732
+ flatOptions = computed(() => {
13733
+ return this.groupedOptions().flatMap((g) => g.items);
13734
+ }, ...(ngDevMode ? [{ debugName: "flatOptions" }] : /* istanbul ignore next */ []));
13735
+ /** Whether to show "Create" option */
13736
+ showCreateOption = computed(() => {
13737
+ if (!this.allowCreate())
13738
+ return false;
13739
+ const q = this.query().trim();
13740
+ if (!q)
13741
+ return false;
13742
+ const exact = this.visibleOptions().some((o) => o.label.toLowerCase() === q.toLowerCase());
13743
+ return !exact;
13744
+ }, ...(ngDevMode ? [{ debugName: "showCreateOption" }] : /* istanbul ignore next */ []));
13745
+ /** Container classes */
13746
+ containerClasses = computed(() => {
13747
+ const classes = [`lc-combobox`, `lc-combobox--${this.size()}`];
13748
+ if (this.disabled())
13749
+ classes.push('lc-combobox--disabled');
13750
+ if (this.error())
13751
+ classes.push('lc-combobox--error');
13752
+ if (this.isOpen())
13753
+ classes.push('lc-combobox--open');
13754
+ return classes.join(' ');
13755
+ }, ...(ngDevMode ? [{ debugName: "containerClasses" }] : /* istanbul ignore next */ []));
13756
+ // ControlValueAccessor
13757
+ onChange = () => { };
13758
+ onTouched = () => { };
13759
+ constructor() {
13760
+ // Async loading pipe
13761
+ this.asyncSub = this.querySubject
13762
+ .pipe(debounceTime(this.debounceMs()), switchMap((q) => {
13763
+ const loader = this.loadOptions();
13764
+ if (!loader || q.length < this.minChars()) {
13765
+ return of([]);
13766
+ }
13767
+ this.isLoading.set(true);
13768
+ return loader(q);
13769
+ }))
13770
+ .subscribe((results) => {
13771
+ this.asyncOptions.set(results);
13772
+ this.isLoading.set(false);
13773
+ });
13774
+ }
13775
+ ngOnDestroy() {
13776
+ this.asyncSub?.unsubscribe();
13777
+ this.querySubject.complete();
13778
+ }
13779
+ writeValue(value) {
13780
+ if (this.multiple()) {
13781
+ this.selectedMultiple.set(Array.isArray(value) ? value : []);
13782
+ }
13783
+ else {
13784
+ this.selectedSingle.set(value && !Array.isArray(value) ? value : null);
13785
+ if (value && !Array.isArray(value)) {
13786
+ this.query.set(value.label);
13787
+ }
13788
+ }
13789
+ }
13790
+ registerOnChange(fn) {
13791
+ this.onChange = fn;
13792
+ }
13793
+ registerOnTouched(fn) {
13794
+ this.onTouched = fn;
13795
+ }
13796
+ setDisabledState(isDisabled) {
13797
+ // handled by input
13798
+ }
13799
+ onInputChange(event) {
13800
+ const value = event.target.value;
13801
+ this.query.set(value);
13802
+ this.queryChange.emit(value);
13803
+ this.highlightedIndex.set(-1);
13804
+ if (this.loadOptions()) {
13805
+ this.querySubject.next(value);
13806
+ }
13807
+ if (value.length >= this.minChars()) {
13808
+ this.isOpen.set(true);
13809
+ }
13810
+ }
13811
+ onInputFocus() {
13812
+ if (this.query().length >= this.minChars() || this.options().length > 0) {
13813
+ this.isOpen.set(true);
13814
+ }
13815
+ }
13816
+ onInputBlur() {
13817
+ this.onTouched();
13818
+ // Delay to allow click on dropdown
13819
+ setTimeout(() => this.isOpen.set(false), 200);
13820
+ }
13821
+ onKeydown(event) {
13822
+ const flat = this.flatOptions();
13823
+ const total = flat.length + (this.showCreateOption() ? 1 : 0);
13824
+ switch (event.key) {
13825
+ case 'ArrowDown':
13826
+ event.preventDefault();
13827
+ this.isOpen.set(true);
13828
+ this.highlightedIndex.update((i) => (i + 1) % total);
13829
+ break;
13830
+ case 'ArrowUp':
13831
+ event.preventDefault();
13832
+ this.highlightedIndex.update((i) => (i - 1 + total) % total);
13833
+ break;
13834
+ case 'Enter':
13835
+ event.preventDefault();
13836
+ if (this.highlightedIndex() >= 0) {
13837
+ if (this.highlightedIndex() === flat.length && this.showCreateOption()) {
13838
+ this.onCreateNew();
13839
+ }
13840
+ else {
13841
+ const opt = flat[this.highlightedIndex()];
13842
+ if (opt && !opt.disabled) {
13843
+ this.selectOption(opt);
13844
+ }
13845
+ }
13846
+ }
13847
+ else if (this.showCreateOption()) {
13848
+ this.onCreateNew();
13849
+ }
13850
+ break;
13851
+ case 'Escape':
13852
+ this.isOpen.set(false);
13853
+ break;
13854
+ case 'Tab':
13855
+ if (this.highlightedIndex() >= 0 && this.isOpen()) {
13856
+ const opt = flat[this.highlightedIndex()];
13857
+ if (opt && !opt.disabled) {
13858
+ this.selectOption(opt);
13859
+ }
13860
+ }
13861
+ this.isOpen.set(false);
13862
+ break;
13863
+ case 'Backspace':
13864
+ if (this.multiple() && !this.query() && this.selectedMultiple().length > 0) {
13865
+ this.removeSelected(this.selectedMultiple()[this.selectedMultiple().length - 1]);
13866
+ }
13867
+ break;
13868
+ }
13869
+ }
13870
+ selectOption(option) {
13871
+ if (this.multiple()) {
13872
+ this.selectedMultiple.update((sel) => [...sel, option]);
13873
+ this.query.set('');
13874
+ const value = this.selectedMultiple();
13875
+ this.onChange(value);
13876
+ this.valueChange.emit(value);
13877
+ }
13878
+ else {
13879
+ this.selectedSingle.set(option);
13880
+ this.query.set(option.label);
13881
+ this.onChange(option);
13882
+ this.valueChange.emit(option);
13883
+ }
13884
+ this.optionSelected.emit(option);
13885
+ this.isOpen.set(false);
13886
+ this.highlightedIndex.set(-1);
13887
+ }
13888
+ removeSelected(option) {
13889
+ this.selectedMultiple.update((sel) => sel.filter((s) => s.value !== option.value));
13890
+ const value = this.selectedMultiple();
13891
+ this.onChange(value);
13892
+ this.valueChange.emit(value);
13893
+ this.inputEl?.nativeElement.focus();
13894
+ }
13895
+ onCreateNew() {
13896
+ const q = this.query().trim();
13897
+ if (!q)
13898
+ return;
13899
+ const newOption = { value: q, label: q };
13900
+ this.created.emit(q);
13901
+ this.selectOption(newOption);
13902
+ }
13903
+ onDocumentClick(event) {
13904
+ if (!this.elRef.nativeElement.contains(event.target)) {
13905
+ this.isOpen.set(false);
13906
+ }
13907
+ }
13908
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ComboboxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13909
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: ComboboxComponent, isStandalone: true, selector: "lc-combobox", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, loadOptions: { classPropertyName: "loadOptions", publicName: "loadOptions", isSignal: true, isRequired: false, transformFunction: null }, debounceMs: { classPropertyName: "debounceMs", publicName: "debounceMs", isSignal: true, isRequired: false, transformFunction: null }, minChars: { classPropertyName: "minChars", publicName: "minChars", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, helperText: { classPropertyName: "helperText", publicName: "helperText", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, allowCreate: { classPropertyName: "allowCreate", publicName: "allowCreate", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange", queryChange: "queryChange", optionSelected: "optionSelected", created: "created" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, providers: [
13910
+ {
13911
+ provide: NG_VALUE_ACCESSOR,
13912
+ useExisting: forwardRef(() => ComboboxComponent),
13913
+ multi: true,
13914
+ },
13915
+ ], viewQueries: [{ propertyName: "inputEl", first: true, predicate: ["inputEl"], descendants: true }], ngImport: i0, template: "<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"lc-combobox__label\">{{ label() }}</label>\n }\n\n <div class=\"lc-combobox__input-wrapper\">\n <!-- Selected chips (multiple mode) -->\n @if (multiple()) {\n @for (sel of selectedMultiple(); track sel.value) {\n <lc-chip\n [removable]=\"!disabled()\"\n size=\"sm\"\n (remove)=\"removeSelected(sel)\"\n >{{ sel.label }}</lc-chip>\n }\n }\n\n <input\n #inputEl\n type=\"text\"\n class=\"lc-combobox__input\"\n [placeholder]=\"multiple() && selectedMultiple().length > 0 ? '' : placeholder()\"\n [value]=\"query()\"\n [disabled]=\"disabled()\"\n role=\"combobox\"\n [attr.aria-expanded]=\"isOpen()\"\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n autocomplete=\"off\"\n (input)=\"onInputChange($event)\"\n (focus)=\"onInputFocus()\"\n (blur)=\"onInputBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n\n @if (isLoading() || loading()) {\n <lc-spinner size=\"sm\" class=\"lc-combobox__spinner\" />\n } @else if (!multiple() && selectedSingle()) {\n <button\n type=\"button\"\n class=\"lc-combobox__clear\"\n (click)=\"selectOption({value: '', label: ''}); query.set('')\"\n tabindex=\"-1\"\n aria-label=\"Clear\"\n >\n <lc-icon name=\"x-mark\" size=\"xs\" />\n </button>\n }\n </div>\n\n <!-- Dropdown -->\n @if (isOpen()) {\n <div class=\"lc-combobox__dropdown\" role=\"listbox\">\n @for (group of groupedOptions(); track group.label) {\n @if (group.label) {\n <div class=\"lc-combobox__group-header\">{{ group.label }}</div>\n }\n @for (option of group.items; track option.value; let i = $index) {\n <div\n class=\"lc-combobox__option\"\n [class.lc-combobox__option--highlighted]=\"flatOptions().indexOf(option) === highlightedIndex()\"\n [class.lc-combobox__option--disabled]=\"option.disabled\"\n role=\"option\"\n [attr.aria-selected]=\"flatOptions().indexOf(option) === highlightedIndex()\"\n [attr.aria-disabled]=\"option.disabled\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"!option.disabled && selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(flatOptions().indexOf(option))\"\n >\n @if (option.icon) {\n <lc-icon [name]=\"option.icon\" size=\"sm\" class=\"lc-combobox__option-icon\" />\n }\n <div class=\"lc-combobox__option-content\">\n <span class=\"lc-combobox__option-label\">{{ option.label }}</span>\n @if (option.description) {\n <span class=\"lc-combobox__option-desc\">{{ option.description }}</span>\n }\n </div>\n </div>\n }\n }\n\n @if (showCreateOption()) {\n <div\n class=\"lc-combobox__option lc-combobox__option--create\"\n [class.lc-combobox__option--highlighted]=\"highlightedIndex() === flatOptions().length\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"onCreateNew()\"\n (mouseenter)=\"highlightedIndex.set(flatOptions().length)\"\n >\n <lc-icon name=\"plus\" size=\"sm\" />\n <span>Create \"{{ query() }}\"</span>\n </div>\n }\n\n @if (visibleOptions().length === 0 && !showCreateOption() && !isLoading() && !loading()) {\n <div class=\"lc-combobox__empty\">{{ emptyMessage() }}</div>\n }\n </div>\n }\n\n @if (helperText() && !error()) {\n <span class=\"lc-combobox__helper\">{{ helperText() }}</span>\n }\n @if (error()) {\n <span class=\"lc-combobox__error\">{{ error() }}</span>\n }\n</div>\n", styles: [".lc-combobox{display:flex;flex-direction:column;gap:.25rem;position:relative;font-family:var(--font-family-sans)}.lc-combobox__label{font-size:var(--font-size-sm, .875rem);font-weight:500;color:var(--color-text, #111827);margin-bottom:.125rem}.lc-combobox__input-wrapper{display:flex;flex-wrap:wrap;align-items:center;gap:.25rem;border:1px solid var(--color-neutral-300, #d1d5db);border-radius:var(--radius-md, .375rem);background-color:var(--color-background, #ffffff);padding:.25rem .5rem;transition:border-color .15s,box-shadow .15s;cursor:text}.lc-combobox__input-wrapper:focus-within{border-color:var(--color-primary-500, #3b82f6);box-shadow:0 0 0 3px #3b82f626}.lc-combobox--error .lc-combobox__input-wrapper{border-color:var(--color-error-default, #ef4444)}.lc-combobox--error .lc-combobox__input-wrapper:focus-within{box-shadow:0 0 0 3px #ef444426}.lc-combobox--disabled .lc-combobox__input-wrapper{opacity:.5;cursor:not-allowed;background-color:var(--color-neutral-50, #f9fafb)}.lc-combobox__input{flex:1;min-width:80px;border:none;outline:none;background:transparent;color:var(--color-text, #111827);font-family:inherit}.lc-combobox__input::placeholder{color:var(--color-neutral-400, #9ca3af)}.lc-combobox__input:disabled{cursor:not-allowed}.lc-combobox--xs .lc-combobox__input{font-size:var(--font-size-xs, .75rem);height:1.5rem}.lc-combobox--sm .lc-combobox__input{font-size:var(--font-size-sm, .875rem);height:1.75rem}.lc-combobox--md .lc-combobox__input{font-size:var(--font-size-base, 1rem);height:2rem}.lc-combobox--lg .lc-combobox__input{font-size:var(--font-size-lg, 1.125rem);height:2.25rem}.lc-combobox__spinner{flex-shrink:0}.lc-combobox__clear{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border:none;background:transparent;cursor:pointer;color:var(--color-neutral-400, #9ca3af);border-radius:var(--radius-sm, .25rem);flex-shrink:0}.lc-combobox__clear:hover{color:var(--color-text, #111827);background-color:var(--color-neutral-100, #f3f4f6)}.lc-combobox__dropdown{position:absolute;top:100%;left:0;right:0;margin-top:.25rem;max-height:240px;overflow-y:auto;background-color:var(--color-background, #ffffff);border:1px solid var(--color-neutral-200, #e5e7eb);border-radius:var(--radius-md, .375rem);box-shadow:var(--elevation-lg, 0 10px 15px -3px rgba(0, 0, 0, .1));z-index:50}.lc-combobox__group-header{padding:.375rem .75rem;font-size:var(--font-size-xs, .75rem);font-weight:600;color:var(--color-neutral-500, #6b7280);text-transform:uppercase;letter-spacing:.05em;margin-top:.25rem}.lc-combobox__group-header:first-child{margin-top:0}.lc-combobox__option{display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;cursor:pointer;transition:background-color .1s}.lc-combobox__option--highlighted{background-color:var(--color-neutral-100, #f3f4f6)}.lc-combobox__option--disabled{opacity:.5;cursor:not-allowed}.lc-combobox__option--create{color:var(--color-primary-600, #2563eb);font-weight:500;border-top:1px solid var(--color-neutral-100, #f3f4f6)}.lc-combobox__option-icon{flex-shrink:0;color:var(--color-neutral-500, #6b7280)}.lc-combobox__option-content{display:flex;flex-direction:column;min-width:0}.lc-combobox__option-label{font-size:var(--font-size-sm, .875rem);color:var(--color-text, #111827);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-combobox__option-desc{font-size:var(--font-size-xs, .75rem);color:var(--color-text-secondary, #6b7280);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-combobox__empty{padding:.75rem;text-align:center;color:var(--color-neutral-400, #9ca3af);font-size:var(--font-size-sm, .875rem)}.lc-combobox__helper{font-size:var(--font-size-xs, .75rem);color:var(--color-text-secondary, #6b7280)}.lc-combobox__error{font-size:var(--font-size-xs, .75rem);color:var(--color-error-default, #ef4444)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: IconComponent, selector: "lc-icon", inputs: ["name", "variant", "size", "color", "ariaLabel", "decorative"] }, { kind: "component", type: SpinnerComponent, selector: "lc-spinner", inputs: ["size", "message"] }, { kind: "component", type: ChipComponent, selector: "lc-chip", inputs: ["variant", "size", "icon", "removable", "disabled"], outputs: ["remove"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
13916
+ }
13917
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ComboboxComponent, decorators: [{
13918
+ type: Component,
13919
+ args: [{ selector: 'lc-combobox', standalone: true, imports: [CommonModule, IconComponent, SpinnerComponent, ChipComponent], changeDetection: ChangeDetectionStrategy.OnPush, providers: [
13920
+ {
13921
+ provide: NG_VALUE_ACCESSOR,
13922
+ useExisting: forwardRef(() => ComboboxComponent),
13923
+ multi: true,
13924
+ },
13925
+ ], template: "<div [class]=\"containerClasses()\">\n @if (label()) {\n <label class=\"lc-combobox__label\">{{ label() }}</label>\n }\n\n <div class=\"lc-combobox__input-wrapper\">\n <!-- Selected chips (multiple mode) -->\n @if (multiple()) {\n @for (sel of selectedMultiple(); track sel.value) {\n <lc-chip\n [removable]=\"!disabled()\"\n size=\"sm\"\n (remove)=\"removeSelected(sel)\"\n >{{ sel.label }}</lc-chip>\n }\n }\n\n <input\n #inputEl\n type=\"text\"\n class=\"lc-combobox__input\"\n [placeholder]=\"multiple() && selectedMultiple().length > 0 ? '' : placeholder()\"\n [value]=\"query()\"\n [disabled]=\"disabled()\"\n role=\"combobox\"\n [attr.aria-expanded]=\"isOpen()\"\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n autocomplete=\"off\"\n (input)=\"onInputChange($event)\"\n (focus)=\"onInputFocus()\"\n (blur)=\"onInputBlur()\"\n (keydown)=\"onKeydown($event)\"\n />\n\n @if (isLoading() || loading()) {\n <lc-spinner size=\"sm\" class=\"lc-combobox__spinner\" />\n } @else if (!multiple() && selectedSingle()) {\n <button\n type=\"button\"\n class=\"lc-combobox__clear\"\n (click)=\"selectOption({value: '', label: ''}); query.set('')\"\n tabindex=\"-1\"\n aria-label=\"Clear\"\n >\n <lc-icon name=\"x-mark\" size=\"xs\" />\n </button>\n }\n </div>\n\n <!-- Dropdown -->\n @if (isOpen()) {\n <div class=\"lc-combobox__dropdown\" role=\"listbox\">\n @for (group of groupedOptions(); track group.label) {\n @if (group.label) {\n <div class=\"lc-combobox__group-header\">{{ group.label }}</div>\n }\n @for (option of group.items; track option.value; let i = $index) {\n <div\n class=\"lc-combobox__option\"\n [class.lc-combobox__option--highlighted]=\"flatOptions().indexOf(option) === highlightedIndex()\"\n [class.lc-combobox__option--disabled]=\"option.disabled\"\n role=\"option\"\n [attr.aria-selected]=\"flatOptions().indexOf(option) === highlightedIndex()\"\n [attr.aria-disabled]=\"option.disabled\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"!option.disabled && selectOption(option)\"\n (mouseenter)=\"highlightedIndex.set(flatOptions().indexOf(option))\"\n >\n @if (option.icon) {\n <lc-icon [name]=\"option.icon\" size=\"sm\" class=\"lc-combobox__option-icon\" />\n }\n <div class=\"lc-combobox__option-content\">\n <span class=\"lc-combobox__option-label\">{{ option.label }}</span>\n @if (option.description) {\n <span class=\"lc-combobox__option-desc\">{{ option.description }}</span>\n }\n </div>\n </div>\n }\n }\n\n @if (showCreateOption()) {\n <div\n class=\"lc-combobox__option lc-combobox__option--create\"\n [class.lc-combobox__option--highlighted]=\"highlightedIndex() === flatOptions().length\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"onCreateNew()\"\n (mouseenter)=\"highlightedIndex.set(flatOptions().length)\"\n >\n <lc-icon name=\"plus\" size=\"sm\" />\n <span>Create \"{{ query() }}\"</span>\n </div>\n }\n\n @if (visibleOptions().length === 0 && !showCreateOption() && !isLoading() && !loading()) {\n <div class=\"lc-combobox__empty\">{{ emptyMessage() }}</div>\n }\n </div>\n }\n\n @if (helperText() && !error()) {\n <span class=\"lc-combobox__helper\">{{ helperText() }}</span>\n }\n @if (error()) {\n <span class=\"lc-combobox__error\">{{ error() }}</span>\n }\n</div>\n", styles: [".lc-combobox{display:flex;flex-direction:column;gap:.25rem;position:relative;font-family:var(--font-family-sans)}.lc-combobox__label{font-size:var(--font-size-sm, .875rem);font-weight:500;color:var(--color-text, #111827);margin-bottom:.125rem}.lc-combobox__input-wrapper{display:flex;flex-wrap:wrap;align-items:center;gap:.25rem;border:1px solid var(--color-neutral-300, #d1d5db);border-radius:var(--radius-md, .375rem);background-color:var(--color-background, #ffffff);padding:.25rem .5rem;transition:border-color .15s,box-shadow .15s;cursor:text}.lc-combobox__input-wrapper:focus-within{border-color:var(--color-primary-500, #3b82f6);box-shadow:0 0 0 3px #3b82f626}.lc-combobox--error .lc-combobox__input-wrapper{border-color:var(--color-error-default, #ef4444)}.lc-combobox--error .lc-combobox__input-wrapper:focus-within{box-shadow:0 0 0 3px #ef444426}.lc-combobox--disabled .lc-combobox__input-wrapper{opacity:.5;cursor:not-allowed;background-color:var(--color-neutral-50, #f9fafb)}.lc-combobox__input{flex:1;min-width:80px;border:none;outline:none;background:transparent;color:var(--color-text, #111827);font-family:inherit}.lc-combobox__input::placeholder{color:var(--color-neutral-400, #9ca3af)}.lc-combobox__input:disabled{cursor:not-allowed}.lc-combobox--xs .lc-combobox__input{font-size:var(--font-size-xs, .75rem);height:1.5rem}.lc-combobox--sm .lc-combobox__input{font-size:var(--font-size-sm, .875rem);height:1.75rem}.lc-combobox--md .lc-combobox__input{font-size:var(--font-size-base, 1rem);height:2rem}.lc-combobox--lg .lc-combobox__input{font-size:var(--font-size-lg, 1.125rem);height:2.25rem}.lc-combobox__spinner{flex-shrink:0}.lc-combobox__clear{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border:none;background:transparent;cursor:pointer;color:var(--color-neutral-400, #9ca3af);border-radius:var(--radius-sm, .25rem);flex-shrink:0}.lc-combobox__clear:hover{color:var(--color-text, #111827);background-color:var(--color-neutral-100, #f3f4f6)}.lc-combobox__dropdown{position:absolute;top:100%;left:0;right:0;margin-top:.25rem;max-height:240px;overflow-y:auto;background-color:var(--color-background, #ffffff);border:1px solid var(--color-neutral-200, #e5e7eb);border-radius:var(--radius-md, .375rem);box-shadow:var(--elevation-lg, 0 10px 15px -3px rgba(0, 0, 0, .1));z-index:50}.lc-combobox__group-header{padding:.375rem .75rem;font-size:var(--font-size-xs, .75rem);font-weight:600;color:var(--color-neutral-500, #6b7280);text-transform:uppercase;letter-spacing:.05em;margin-top:.25rem}.lc-combobox__group-header:first-child{margin-top:0}.lc-combobox__option{display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;cursor:pointer;transition:background-color .1s}.lc-combobox__option--highlighted{background-color:var(--color-neutral-100, #f3f4f6)}.lc-combobox__option--disabled{opacity:.5;cursor:not-allowed}.lc-combobox__option--create{color:var(--color-primary-600, #2563eb);font-weight:500;border-top:1px solid var(--color-neutral-100, #f3f4f6)}.lc-combobox__option-icon{flex-shrink:0;color:var(--color-neutral-500, #6b7280)}.lc-combobox__option-content{display:flex;flex-direction:column;min-width:0}.lc-combobox__option-label{font-size:var(--font-size-sm, .875rem);color:var(--color-text, #111827);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-combobox__option-desc{font-size:var(--font-size-xs, .75rem);color:var(--color-text-secondary, #6b7280);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lc-combobox__empty{padding:.75rem;text-align:center;color:var(--color-neutral-400, #9ca3af);font-size:var(--font-size-sm, .875rem)}.lc-combobox__helper{font-size:var(--font-size-xs, .75rem);color:var(--color-text-secondary, #6b7280)}.lc-combobox__error{font-size:var(--font-size-xs, .75rem);color:var(--color-error-default, #ef4444)}\n"] }]
13926
+ }], ctorParameters: () => [], propDecorators: { inputEl: [{
13927
+ type: ViewChild,
13928
+ args: ['inputEl']
13929
+ }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], loadOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadOptions", required: false }] }], debounceMs: [{ type: i0.Input, args: [{ isSignal: true, alias: "debounceMs", required: false }] }], minChars: [{ type: i0.Input, args: [{ isSignal: true, alias: "minChars", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], helperText: [{ type: i0.Input, args: [{ isSignal: true, alias: "helperText", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], allowCreate: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowCreate", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], queryChange: [{ type: i0.Output, args: ["queryChange"] }], optionSelected: [{ type: i0.Output, args: ["optionSelected"] }], created: [{ type: i0.Output, args: ["created"] }], onDocumentClick: [{
13930
+ type: HostListener,
13931
+ args: ['document:click', ['$event']]
13932
+ }] } });
13933
+
12851
13934
  /**
12852
13935
  * Life-Cockpit Design System (UI Kit)
12853
13936
  *
@@ -12870,5 +13953,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
12870
13953
  * Generated bundle index. Do not edit.
12871
13954
  */
12872
13955
 
12873
- export { AccordionComponent, AlertComponent, AnimationDurationFast, AnimationEasingEaseIn, AnimationEasingEaseInOut, AnimationEasingEaseOut, AreaChartComponent, AvatarComponent, AvatarGroupComponent, BadgeComponent, BarChartComponent, BorderRadius2xl, BorderRadiusFull, BorderRadiusLg, BorderRadiusMd, BorderRadiusNone, BorderRadiusSm, BorderRadiusXl, BreadcrumbsComponent, ButtonComponent, CalendarComponent, CalloutComponent, CardComponent, ChatComponent, CheckboxComponent, ChipComponent, CodeBlockComponent, ColorAccentOrange, ColorAccentPurple, ColorAccentRed, ColorAccentRust, ColorAccentViolet, ColorErrorDark, ColorErrorDefault, ColorErrorLight, ColorInfoDark, ColorInfoDefault, ColorInfoLight, ColorNeutral100, ColorNeutral200, ColorNeutral300, ColorNeutral400, ColorNeutral50, ColorNeutral500, ColorNeutral600, ColorNeutral700, ColorNeutral800, ColorNeutral900, ColorPickerComponent, ColorPrimary100, ColorPrimary200, ColorPrimary300, ColorPrimary400, ColorPrimary50, ColorPrimary500, ColorPrimary600, ColorPrimary700, ColorPrimary800, ColorPrimary900, ColorSecondary100, ColorSecondary200, ColorSecondary300, ColorSecondary400, ColorSecondary50, ColorSecondary500, ColorSecondary600, ColorSecondary700, ColorSecondary800, ColorSecondary900, ColorSuccessDark, ColorSuccessDefault, ColorSuccessLight, ColorWarningDark, ColorWarningDefault, ColorWarningLight, ContainerComponent, DateRangePickerComponent, DatepickerComponent, DependencyViewerComponent, DiffViewerComponent, DividerComponent, DocumentViewerComponent, DonutChartComponent, DrawerComponent, Elevation1, Elevation2, Elevation3, Elevation4, EmailInputComponent, EmptyStateComponent, ErrorDisplayComponent, FieldGroupComponent, FileUploadComponent, FilterBarComponent, FooterComponent, FunnelChartComponent, GalleryComponent, GanttChartComponent, GaugeComponent, HeaderComponent, HeatmapComponent, HeroComponent, IconComponent, InputComponent, KanbanBoardComponent, LineChartComponent, ListComponent, ListItemTemplateDirective, LogoComponent, MenuComponent, ModalComponent, NotificationCenterComponent, NumberInputComponent, PaginationComponent, PasswordInputComponent, PieChartComponent, PopoverComponent, ProgressBarComponent, ProgressRingComponent, RadarChartComponent, RadioComponent, RatingComponent, RichTextEditorComponent, ScatterPlotComponent, SearchInputComponent, SectionComponent, SelectComponent, SidenavComponent, SizeInteractiveLgFontSize, SizeInteractiveLgHeight, SizeInteractiveLgPadding, SizeInteractiveMdFontSize, SizeInteractiveMdHeight, SizeInteractiveMdPadding, SizeInteractiveSmFontSize, SizeInteractiveSmHeight, SizeInteractiveSmPadding, SizeInteractiveXsFontSize, SizeInteractiveXsHeight, SizeInteractiveXsPadding, SizeMinTouchHeight, SizeMinTouchWidth, SkeletonComponent, SliderComponent, SpacerComponent, Spacing0, Spacing05, Spacing1, Spacing10, Spacing11, Spacing12, Spacing14, Spacing15, Spacing16, Spacing2, Spacing25, Spacing3, Spacing35, Spacing4, Spacing5, Spacing6, Spacing7, Spacing8, Spacing9, SparklineComponent, SpinnerComponent, StackComponent, StackedBarChartComponent, StatTrendComponent, StepperComponent, SwitchComponent, TabComponent, TableCellDirective, TableComponent, TabsComponent, TagInputComponent, TextareaComponent, ThemeService, TimelineComponent, ToastComponent, ToastService, ToggleGroupComponent, TooltipContentComponent, TooltipDirective, TypographyComponent, TypographyFontFamilyBase, TypographyFontFamilyMono, TypographyFontSize2xl, TypographyFontSize3xl, TypographyFontSize4xl, TypographyFontSize5xl, TypographyFontSize6xl, TypographyFontSizeBase, TypographyFontSizeLg, TypographyFontSizeSm, TypographyFontSizeXl, TypographyFontSizeXs, TypographyFontWeightBold, TypographyFontWeightMedium, TypographyFontWeightNormal, TypographyFontWeightSemibold, TypographyLineHeightNormal, TypographyLineHeightRelaxed, TypographyLineHeightTight, VerificationCodeInputComponent, WaterfallChartComponent };
13956
+ export { AccordionComponent, AlertComponent, AnimationDurationFast, AnimationEasingEaseIn, AnimationEasingEaseInOut, AnimationEasingEaseOut, AreaChartComponent, AvatarComponent, AvatarGroupComponent, BadgeComponent, BarChartComponent, BorderRadius2xl, BorderRadiusFull, BorderRadiusLg, BorderRadiusMd, BorderRadiusNone, BorderRadiusSm, BorderRadiusXl, BreadcrumbsComponent, ButtonComponent, CalendarComponent, CalloutComponent, CardComponent, ChatComponent, CheckboxComponent, ChipComponent, CodeBlockComponent, ColorAccentOrange, ColorAccentPurple, ColorAccentRed, ColorAccentRust, ColorAccentViolet, ColorErrorDark, ColorErrorDefault, ColorErrorLight, ColorInfoDark, ColorInfoDefault, ColorInfoLight, ColorNeutral100, ColorNeutral200, ColorNeutral300, ColorNeutral400, ColorNeutral50, ColorNeutral500, ColorNeutral600, ColorNeutral700, ColorNeutral800, ColorNeutral900, ColorPickerComponent, ColorPrimary100, ColorPrimary200, ColorPrimary300, ColorPrimary400, ColorPrimary50, ColorPrimary500, ColorPrimary600, ColorPrimary700, ColorPrimary800, ColorPrimary900, ColorSecondary100, ColorSecondary200, ColorSecondary300, ColorSecondary400, ColorSecondary50, ColorSecondary500, ColorSecondary600, ColorSecondary700, ColorSecondary800, ColorSecondary900, ColorSuccessDark, ColorSuccessDefault, ColorSuccessLight, ColorWarningDark, ColorWarningDefault, ColorWarningLight, ComboboxComponent, ConfirmDialogComponent, ConfirmService, ContainerComponent, DateRangePickerComponent, DatepickerComponent, DependencyViewerComponent, DiffViewerComponent, DividerComponent, DocumentViewerComponent, DonutChartComponent, DrawerComponent, Elevation1, Elevation2, Elevation3, Elevation4, EmailInputComponent, EmptyStateComponent, ErrorDisplayComponent, FieldGroupComponent, FileUploadComponent, FilterBarComponent, FooterComponent, FunnelChartComponent, GalleryComponent, GanttChartComponent, GaugeComponent, HeaderComponent, HeatmapComponent, HeroComponent, IconComponent, InputComponent, KanbanBoardComponent, LineChartComponent, ListComponent, ListItemTemplateDirective, LogViewerComponent, LogoComponent, MarkdownComponent, MenuComponent, ModalComponent, NotificationCenterComponent, NumberInputComponent, PaginationComponent, PasswordInputComponent, PieChartComponent, PopoverComponent, ProgressBarComponent, ProgressRingComponent, RadarChartComponent, RadioComponent, RatingComponent, RichTextEditorComponent, ScatterPlotComponent, SearchInputComponent, SectionComponent, SelectComponent, SidenavComponent, SizeInteractiveLgFontSize, SizeInteractiveLgHeight, SizeInteractiveLgPadding, SizeInteractiveMdFontSize, SizeInteractiveMdHeight, SizeInteractiveMdPadding, SizeInteractiveSmFontSize, SizeInteractiveSmHeight, SizeInteractiveSmPadding, SizeInteractiveXsFontSize, SizeInteractiveXsHeight, SizeInteractiveXsPadding, SizeMinTouchHeight, SizeMinTouchWidth, SkeletonComponent, SliderComponent, SpacerComponent, Spacing0, Spacing05, Spacing1, Spacing10, Spacing11, Spacing12, Spacing14, Spacing15, Spacing16, Spacing2, Spacing25, Spacing3, Spacing35, Spacing4, Spacing5, Spacing6, Spacing7, Spacing8, Spacing9, SparklineComponent, SpinnerComponent, StackComponent, StackedBarChartComponent, StatTrendComponent, StepperComponent, SwitchComponent, TabComponent, TableCellDirective, TableComponent, TabsComponent, TagInputComponent, TextareaComponent, ThemeService, TimelineComponent, ToastComponent, ToastService, ToggleGroupComponent, TooltipContentComponent, TooltipDirective, TypographyComponent, TypographyFontFamilyBase, TypographyFontFamilyMono, TypographyFontSize2xl, TypographyFontSize3xl, TypographyFontSize4xl, TypographyFontSize5xl, TypographyFontSize6xl, TypographyFontSizeBase, TypographyFontSizeLg, TypographyFontSizeSm, TypographyFontSizeXl, TypographyFontSizeXs, TypographyFontWeightBold, TypographyFontWeightMedium, TypographyFontWeightNormal, TypographyFontWeightSemibold, TypographyLineHeightNormal, TypographyLineHeightRelaxed, TypographyLineHeightTight, VerificationCodeInputComponent, WaterfallChartComponent };
12874
13957
  //# sourceMappingURL=life-cockpit-angular-ui-kit.mjs.map