@life-cockpit/angular-ui-kit 2.12.0 → 2.13.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, computed, Injectable, input, effect, isDevMode, ChangeDetectionStrategy, Component, TemplateRef, Directive, model, contentChild, contentChildren, untracked, output, ViewChild, ViewEncapsulation, HostListener, forwardRef, InjectionToken, ElementRef, viewChild, ChangeDetectorRef, EventEmitter, Output, Input, ViewChildren, HostBinding, ViewContainerRef, Renderer2, ContentChildren, ContentChild, linkedSignal, DestroyRef, afterNextRender, SecurityContext, NgZone, ApplicationRef, EnvironmentInjector, createComponent } from '@angular/core';
2
+ import { inject, PLATFORM_ID, signal, computed, Injectable, input, effect, isDevMode, ChangeDetectionStrategy, Component, TemplateRef, Directive, model, contentChild, contentChildren, untracked, output, ViewChild, ViewEncapsulation, HostListener, forwardRef, InjectionToken, ElementRef, viewChild, ChangeDetectorRef, EventEmitter, Output, Input, ViewChildren, HostBinding, DestroyRef, ViewContainerRef, Renderer2, ContentChildren, ContentChild, linkedSignal, afterNextRender, SecurityContext, NgZone, ApplicationRef, EnvironmentInjector, createComponent } from '@angular/core';
3
3
  import { isPlatformBrowser, NgTemplateOutlet, NgClass, CommonModule, NgStyle, SlicePipe } from '@angular/common';
4
4
  import * as i1$4 from '@angular/platform-browser';
5
5
  import { DomSanitizer } from '@angular/platform-browser';
@@ -5155,6 +5155,244 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
5155
5155
  args: ['attr.tabindex']
5156
5156
  }] } });
5157
5157
 
5158
+ /**
5159
+ * Resizable two-pane layout container.
5160
+ *
5161
+ * Projects two panes (`slot="start"`, `slot="end"`) side by side with a
5162
+ * draggable separator in between. The start pane has a fixed, user-adjustable
5163
+ * width; the end pane fills the remaining space.
5164
+ *
5165
+ * Features:
5166
+ * - Drag the separator with mouse / touch (pointer capture), bounded by
5167
+ * `minSize` / `maxSize` (px or percentage of the container)
5168
+ * - Double-click the separator to restore `initialSize`
5169
+ * - Keyboard resizing: focusable separator, ←/→ adjust by `step`,
5170
+ * Home/End jump to min/max (`role="separator"`, `aria-valuenow`)
5171
+ * - Optional persistence: with `storageKey`, the width survives a reload
5172
+ * - Below the `stackBelow` viewport breakpoint the panes stack vertically
5173
+ * and the resizer is disabled
5174
+ *
5175
+ * @example
5176
+ * ```html
5177
+ * <lc-split-pane
5178
+ * [initialSize]="360"
5179
+ * [minSize]="280"
5180
+ * [maxSize]="'55%'"
5181
+ * storageKey="sidebar-width"
5182
+ * (sizeChange)="onWidthChange($event)"
5183
+ * >
5184
+ * <div slot="start">…</div>
5185
+ * <div slot="end">…</div>
5186
+ * </lc-split-pane>
5187
+ * ```
5188
+ */
5189
+ class SplitPaneComponent {
5190
+ /** Initial width of the start pane in px. Restored on separator double-click. */
5191
+ initialSize = input(320, ...(ngDevMode ? [{ debugName: "initialSize" }] : /* istanbul ignore next */ []));
5192
+ /** Minimum width of the start pane in px. */
5193
+ minSize = input(0, ...(ngDevMode ? [{ debugName: "minSize" }] : /* istanbul ignore next */ []));
5194
+ /**
5195
+ * Maximum width of the start pane: a number (px) or a percentage string
5196
+ * such as `'55%'` (relative to the container width). `null` = no limit
5197
+ * beyond the container itself.
5198
+ */
5199
+ maxSize = input(null, ...(ngDevMode ? [{ debugName: "maxSize" }] : /* istanbul ignore next */ []));
5200
+ /**
5201
+ * When set, the current width is persisted to localStorage under this key
5202
+ * and restored on the next init.
5203
+ */
5204
+ storageKey = input(...(ngDevMode ? [undefined, { debugName: "storageKey" }] : /* istanbul ignore next */ []));
5205
+ /** Step in px for keyboard resizing (←/→ on the focused separator). */
5206
+ step = input(16, ...(ngDevMode ? [{ debugName: "step" }] : /* istanbul ignore next */ []));
5207
+ /**
5208
+ * Viewport width in px below which the panes stack vertically and the
5209
+ * resizer is disabled. `0` disables stacking.
5210
+ */
5211
+ stackBelow = input(0, ...(ngDevMode ? [{ debugName: "stackBelow" }] : /* istanbul ignore next */ []));
5212
+ /** Accessible label for the separator. */
5213
+ separatorLabel = input('Resize panel', ...(ngDevMode ? [{ debugName: "separatorLabel" }] : /* istanbul ignore next */ []));
5214
+ /** Emitted whenever the start-pane width changes (drag, keyboard, reset). */
5215
+ sizeChange = output();
5216
+ host = inject(ElementRef);
5217
+ destroyRef = inject(DestroyRef);
5218
+ /** Requested start-pane width; clamped to min/max for rendering. */
5219
+ size = signal(0, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
5220
+ /** Container width, tracked so percentage `maxSize` stays responsive. */
5221
+ containerWidth = signal(0, ...(ngDevMode ? [{ debugName: "containerWidth" }] : /* istanbul ignore next */ []));
5222
+ /** Whether the panes are stacked (viewport below `stackBelow`). */
5223
+ stacked = signal(false, ...(ngDevMode ? [{ debugName: "stacked" }] : /* istanbul ignore next */ []));
5224
+ dragPointerId = null;
5225
+ dragStartX = 0;
5226
+ dragStartSize = 0;
5227
+ /** Clamped width actually applied to the start pane. */
5228
+ effectiveSize = computed(() => this.clamp(this.size()), ...(ngDevMode ? [{ debugName: "effectiveSize" }] : /* istanbul ignore next */ []));
5229
+ /** Resolved max in px for aria-valuemax; null when unbounded. */
5230
+ ariaMax = computed(() => {
5231
+ const max = this.resolveMaxPx();
5232
+ return Number.isFinite(max) && max < Number.MAX_SAFE_INTEGER
5233
+ ? Math.round(max)
5234
+ : null;
5235
+ }, ...(ngDevMode ? [{ debugName: "ariaMax" }] : /* istanbul ignore next */ []));
5236
+ constructor() {
5237
+ // Track the stacking breakpoint reactively; re-subscribes when the
5238
+ // `stackBelow` input changes.
5239
+ effect((onCleanup) => {
5240
+ const breakpoint = this.stackBelow();
5241
+ if (!breakpoint ||
5242
+ typeof window === 'undefined' ||
5243
+ typeof window.matchMedia !== 'function') {
5244
+ this.stacked.set(false);
5245
+ return;
5246
+ }
5247
+ const query = window.matchMedia(`(max-width: ${breakpoint - 0.02}px)`);
5248
+ this.stacked.set(query.matches);
5249
+ const onChange = (event) => this.stacked.set(event.matches);
5250
+ query.addEventListener('change', onChange);
5251
+ onCleanup(() => query.removeEventListener('change', onChange));
5252
+ });
5253
+ }
5254
+ ngOnInit() {
5255
+ this.size.set(this.restoreSize() ?? this.initialSize());
5256
+ this.observeContainer();
5257
+ }
5258
+ /** Restore the initial width (also wired to separator double-click). */
5259
+ reset() {
5260
+ this.applySize(this.initialSize());
5261
+ this.persist();
5262
+ }
5263
+ onPointerDown(event) {
5264
+ const separator = event.currentTarget;
5265
+ try {
5266
+ separator.setPointerCapture?.(event.pointerId);
5267
+ }
5268
+ catch {
5269
+ // Synthetic events carry no active pointer — dragging still works as
5270
+ // long as the pointer stays over the separator.
5271
+ }
5272
+ this.dragPointerId = event.pointerId;
5273
+ this.dragStartX = event.clientX;
5274
+ this.dragStartSize = this.effectiveSize();
5275
+ event.preventDefault();
5276
+ }
5277
+ onPointerMove(event) {
5278
+ if (this.dragPointerId !== event.pointerId)
5279
+ return;
5280
+ this.applySize(this.dragStartSize + (event.clientX - this.dragStartX));
5281
+ }
5282
+ onPointerUp(event) {
5283
+ if (this.dragPointerId !== event.pointerId)
5284
+ return;
5285
+ this.dragPointerId = null;
5286
+ try {
5287
+ event.currentTarget.releasePointerCapture?.(event.pointerId);
5288
+ }
5289
+ catch {
5290
+ // No capture to release — nothing to do.
5291
+ }
5292
+ this.persist();
5293
+ }
5294
+ onKeydown(event) {
5295
+ let next;
5296
+ switch (event.key) {
5297
+ case 'ArrowLeft':
5298
+ next = this.effectiveSize() - this.step();
5299
+ break;
5300
+ case 'ArrowRight':
5301
+ next = this.effectiveSize() + this.step();
5302
+ break;
5303
+ case 'Home':
5304
+ next = this.minSize();
5305
+ break;
5306
+ case 'End':
5307
+ next = this.resolveMaxPx();
5308
+ break;
5309
+ default:
5310
+ return;
5311
+ }
5312
+ event.preventDefault();
5313
+ this.applySize(next);
5314
+ this.persist();
5315
+ }
5316
+ // ── internals ──────────────────────────────────────────────────────────────
5317
+ applySize(px) {
5318
+ const clamped = this.clamp(px);
5319
+ const previous = this.effectiveSize();
5320
+ // Always store the clamped value so deltas can't accumulate off-limits.
5321
+ this.size.set(clamped);
5322
+ if (clamped !== previous) {
5323
+ this.sizeChange.emit(clamped);
5324
+ }
5325
+ }
5326
+ clamp(px) {
5327
+ const min = this.minSize();
5328
+ const max = Math.max(min, this.resolveMaxPx());
5329
+ return Math.min(Math.max(px, min), max);
5330
+ }
5331
+ resolveMaxPx() {
5332
+ const raw = this.maxSize();
5333
+ const width = this.containerWidth();
5334
+ if (raw == null) {
5335
+ return width > 0 ? width : Number.MAX_SAFE_INTEGER;
5336
+ }
5337
+ if (typeof raw === 'number')
5338
+ return raw;
5339
+ const trimmed = raw.trim();
5340
+ const value = parseFloat(trimmed);
5341
+ if (Number.isNaN(value))
5342
+ return Number.MAX_SAFE_INTEGER;
5343
+ if (trimmed.endsWith('%')) {
5344
+ return width > 0 ? (width * value) / 100 : Number.MAX_SAFE_INTEGER;
5345
+ }
5346
+ return value;
5347
+ }
5348
+ observeContainer() {
5349
+ const element = this.host.nativeElement;
5350
+ this.containerWidth.set(element.clientWidth || 0);
5351
+ if (typeof ResizeObserver === 'undefined')
5352
+ return;
5353
+ const observer = new ResizeObserver((entries) => {
5354
+ const width = entries[0]?.contentRect.width ?? 0;
5355
+ this.containerWidth.set(width);
5356
+ });
5357
+ observer.observe(element);
5358
+ this.destroyRef.onDestroy(() => observer.disconnect());
5359
+ }
5360
+ storageId() {
5361
+ const key = this.storageKey();
5362
+ return key ? `lc-split-pane:${key}` : null;
5363
+ }
5364
+ restoreSize() {
5365
+ const id = this.storageId();
5366
+ if (!id)
5367
+ return null;
5368
+ try {
5369
+ const raw = globalThis.localStorage?.getItem(id);
5370
+ const parsed = raw == null ? NaN : parseFloat(raw);
5371
+ return Number.isFinite(parsed) ? parsed : null;
5372
+ }
5373
+ catch {
5374
+ return null;
5375
+ }
5376
+ }
5377
+ persist() {
5378
+ const id = this.storageId();
5379
+ if (!id)
5380
+ return;
5381
+ try {
5382
+ globalThis.localStorage?.setItem(id, String(this.effectiveSize()));
5383
+ }
5384
+ catch {
5385
+ // Storage unavailable (SSR, private mode) — width simply won't persist.
5386
+ }
5387
+ }
5388
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: SplitPaneComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5389
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: SplitPaneComponent, isStandalone: true, selector: "lc-split-pane", inputs: { initialSize: { classPropertyName: "initialSize", publicName: "initialSize", isSignal: true, isRequired: false, transformFunction: null }, minSize: { classPropertyName: "minSize", publicName: "minSize", isSignal: true, isRequired: false, transformFunction: null }, maxSize: { classPropertyName: "maxSize", publicName: "maxSize", isSignal: true, isRequired: false, transformFunction: null }, storageKey: { classPropertyName: "storageKey", publicName: "storageKey", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, stackBelow: { classPropertyName: "stackBelow", publicName: "stackBelow", isSignal: true, isRequired: false, transformFunction: null }, separatorLabel: { classPropertyName: "separatorLabel", publicName: "separatorLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sizeChange: "sizeChange" }, ngImport: i0, template: "<div class=\"lc-split-pane\" [class.lc-split-pane--stacked]=\"stacked()\">\n <div\n class=\"lc-split-pane__pane lc-split-pane__pane--start\"\n [style.flex-basis.px]=\"stacked() ? null : effectiveSize()\"\n >\n <ng-content select=\"[slot=start]\" />\n </div>\n\n @if (!stacked()) {\n <div\n class=\"lc-split-pane__separator\"\n role=\"separator\"\n tabindex=\"0\"\n aria-orientation=\"vertical\"\n [attr.aria-label]=\"separatorLabel()\"\n [attr.aria-valuenow]=\"effectiveSize()\"\n [attr.aria-valuemin]=\"minSize()\"\n [attr.aria-valuemax]=\"ariaMax()\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n (dblclick)=\"reset()\"\n (keydown)=\"onKeydown($event)\"\n ></div>\n }\n\n <div class=\"lc-split-pane__pane lc-split-pane__pane--end\">\n <ng-content select=\"[slot=end]\" />\n </div>\n</div>\n", styles: [":host{display:block;height:100%;min-height:0}.lc-split-pane{display:flex;align-items:stretch;width:100%;height:100%;min-height:0}.lc-split-pane__pane{min-width:0;min-height:0;overflow:hidden}.lc-split-pane__pane--start{flex:0 0 auto}.lc-split-pane__pane--end{flex:1 1 auto}.lc-split-pane__separator{flex:0 0 auto;width:8px;cursor:col-resize;touch-action:none;position:relative;background:transparent;outline:none}.lc-split-pane__separator:before{content:\"\";position:absolute;inset-block:0;left:50%;width:1px;transform:translate(-50%);background:var(--color-border);transition:background-color .15s ease,width .15s ease}.lc-split-pane__separator:hover:before,.lc-split-pane__separator:active:before{width:3px;background:var(--color-primary-500)}.lc-split-pane__separator:focus-visible:before{width:3px;background:var(--color-primary-500)}.lc-split-pane--stacked{flex-direction:column}.lc-split-pane--stacked .lc-split-pane__pane--start{flex:0 0 auto}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
5390
+ }
5391
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: SplitPaneComponent, decorators: [{
5392
+ type: Component,
5393
+ args: [{ selector: 'lc-split-pane', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"lc-split-pane\" [class.lc-split-pane--stacked]=\"stacked()\">\n <div\n class=\"lc-split-pane__pane lc-split-pane__pane--start\"\n [style.flex-basis.px]=\"stacked() ? null : effectiveSize()\"\n >\n <ng-content select=\"[slot=start]\" />\n </div>\n\n @if (!stacked()) {\n <div\n class=\"lc-split-pane__separator\"\n role=\"separator\"\n tabindex=\"0\"\n aria-orientation=\"vertical\"\n [attr.aria-label]=\"separatorLabel()\"\n [attr.aria-valuenow]=\"effectiveSize()\"\n [attr.aria-valuemin]=\"minSize()\"\n [attr.aria-valuemax]=\"ariaMax()\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerUp($event)\"\n (dblclick)=\"reset()\"\n (keydown)=\"onKeydown($event)\"\n ></div>\n }\n\n <div class=\"lc-split-pane__pane lc-split-pane__pane--end\">\n <ng-content select=\"[slot=end]\" />\n </div>\n</div>\n", styles: [":host{display:block;height:100%;min-height:0}.lc-split-pane{display:flex;align-items:stretch;width:100%;height:100%;min-height:0}.lc-split-pane__pane{min-width:0;min-height:0;overflow:hidden}.lc-split-pane__pane--start{flex:0 0 auto}.lc-split-pane__pane--end{flex:1 1 auto}.lc-split-pane__separator{flex:0 0 auto;width:8px;cursor:col-resize;touch-action:none;position:relative;background:transparent;outline:none}.lc-split-pane__separator:before{content:\"\";position:absolute;inset-block:0;left:50%;width:1px;transform:translate(-50%);background:var(--color-border);transition:background-color .15s ease,width .15s ease}.lc-split-pane__separator:hover:before,.lc-split-pane__separator:active:before{width:3px;background:var(--color-primary-500)}.lc-split-pane__separator:focus-visible:before{width:3px;background:var(--color-primary-500)}.lc-split-pane--stacked{flex-direction:column}.lc-split-pane--stacked .lc-split-pane__pane--start{flex:0 0 auto}\n"] }]
5394
+ }], ctorParameters: () => [], propDecorators: { initialSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialSize", required: false }] }], minSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "minSize", required: false }] }], maxSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSize", required: false }] }], storageKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "storageKey", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], stackBelow: [{ type: i0.Input, args: [{ isSignal: true, alias: "stackBelow", required: false }] }], separatorLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "separatorLabel", required: false }] }], sizeChange: [{ type: i0.Output, args: ["sizeChange"] }] } });
5395
+
5158
5396
  /**
5159
5397
  * Stack component for flexbox-based layout composition.
5160
5398
  *
@@ -6143,11 +6381,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
6143
6381
  * - Ellipsis collapse for long paths with maxItems
6144
6382
  * - Multiple size variants (sm, md, lg)
6145
6383
  * - Router link support for navigation items
6384
+ * - Items without `url` render as buttons and emit `itemClick` (for in-memory
6385
+ * state navigation instead of routing)
6146
6386
  * - Accessible with ARIA breadcrumb landmark
6147
6387
  *
6148
6388
  * @example
6149
6389
  * ```html
6150
6390
  * <lc-breadcrumbs [items]="[{label: 'Home', url: '/'}, {label: 'Settings'}]" />
6391
+ *
6392
+ * <!-- state-based navigation without routes -->
6393
+ * <lc-breadcrumbs
6394
+ * [items]="[{label: 'Root', id: 'root'}, {label: 'Child', id: 'c1'}]"
6395
+ * (itemClick)="activate($event)"
6396
+ * />
6151
6397
  * ```
6152
6398
  */
6153
6399
  class BreadcrumbsComponent {
@@ -6156,6 +6402,11 @@ class BreadcrumbsComponent {
6156
6402
  maxItems = input(0, ...(ngDevMode ? [{ debugName: "maxItems" }] : /* istanbul ignore next */ []));
6157
6403
  size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
6158
6404
  ariaLabel = input('Breadcrumbs', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
6405
+ /**
6406
+ * Emitted when an item without `url` is activated. Items with `url` keep
6407
+ * their link navigation and do not emit.
6408
+ */
6409
+ itemClick = output();
6159
6410
  // Computed CSS classes
6160
6411
  breadcrumbClasses = computed(() => {
6161
6412
  const classes = ['lc-breadcrumbs'];
@@ -6189,13 +6440,17 @@ class BreadcrumbsComponent {
6189
6440
  isEllipsis(item) {
6190
6441
  return item.label === '...';
6191
6442
  }
6443
+ // Handle activation of an item without url
6444
+ onItemClick(item) {
6445
+ this.itemClick.emit(item);
6446
+ }
6192
6447
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: BreadcrumbsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
6193
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: BreadcrumbsComponent, isStandalone: true, selector: "lc-breadcrumbs", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, separator: { classPropertyName: "separator", publicName: "separator", isSignal: true, isRequired: false, transformFunction: null }, maxItems: { classPropertyName: "maxItems", publicName: "maxItems", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (items().length > 0) {\n <nav [attr.aria-label]=\"ariaLabel()\">\n <ol [ngClass]=\"breadcrumbClasses()\">\n @for (item of visibleItems(); track item; let i = $index) {\n <li class=\"lc-breadcrumbs__item\">\n @if (isLastItem(i)) {\n <span class=\"lc-breadcrumbs__current\" aria-current=\"page\">\n {{ item.label }}\n </span>\n } @else if (isEllipsis(item)) {\n <span class=\"lc-breadcrumbs__ellipsis\" aria-hidden=\"true\">\n {{ item.label }}\n </span>\n } @else {\n <a\n [routerLink]=\"item.url\"\n class=\"lc-breadcrumbs__link\"\n >\n {{ item.label }}\n </a>\n }\n \n @if (!isLastItem(i)) {\n <span class=\"lc-breadcrumbs__separator\" aria-hidden=\"true\">\n {{ separator() }}\n </span>\n }\n </li>\n }\n </ol>\n \n <!-- Support for additional content projection -->\n <ng-content></ng-content>\n </nav>\n}\n", styles: [".lc-breadcrumbs{display:flex;flex-wrap:wrap;align-items:center;list-style:none;padding:0;margin:0}.lc-breadcrumbs--sm{font-size:var(--font-size-xs);gap:var(--lc-density-gap-xs, var(--spacing-1))}.lc-breadcrumbs--sm .lc-breadcrumbs__separator{margin:0 var(--spacing-1)}.lc-breadcrumbs--md{font-size:var(--font-size-sm);gap:var(--lc-density-gap-sm, var(--spacing-2))}.lc-breadcrumbs--md .lc-breadcrumbs__separator{margin:0 var(--spacing-2)}.lc-breadcrumbs--lg{font-size:var(--font-size-base);gap:var(--spacing-3)}.lc-breadcrumbs--lg .lc-breadcrumbs__separator{margin:0 var(--spacing-3)}.lc-breadcrumbs__item{display:flex;align-items:center;list-style:none}.lc-breadcrumbs__link{color:var(--color-primary-600);text-decoration:none;transition:all var(--transition-normal);border-radius:var(--border-radius-sm);padding:var(--spacing-1) var(--spacing-2);margin:calc(var(--spacing-1) * -1) calc(var(--spacing-2) * -1)}.lc-breadcrumbs__link:hover{color:var(--color-primary-700);background-color:var(--color-primary-50);text-decoration:underline}.lc-breadcrumbs__link:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-breadcrumbs__link:active{color:var(--color-primary-800)}.lc-breadcrumbs__current{color:var(--color-text-primary);font-weight:var(--font-weight-medium);padding:var(--spacing-1) var(--spacing-2);margin:calc(var(--spacing-1) * -1) calc(var(--spacing-2) * -1)}.lc-breadcrumbs__separator{color:var(--color-text-tertiary);-webkit-user-select:none;user-select:none;pointer-events:none}.lc-breadcrumbs__ellipsis{color:var(--color-text-tertiary);-webkit-user-select:none;user-select:none;pointer-events:none;padding:var(--spacing-1) var(--spacing-2);margin:calc(var(--spacing-1) * -1) calc(var(--spacing-2) * -1)}@media(max-width:768px){.lc-breadcrumbs--md{font-size:var(--font-size-xs);gap:var(--spacing-1)}.lc-breadcrumbs--md .lc-breadcrumbs__separator{margin:0 var(--spacing-1)}.lc-breadcrumbs--lg{font-size:var(--font-size-sm);gap:var(--spacing-2)}.lc-breadcrumbs--lg .lc-breadcrumbs__separator{margin:0 var(--spacing-2)}.lc-breadcrumbs__link,.lc-breadcrumbs__current,.lc-breadcrumbs__ellipsis{padding:var(--spacing-0\\.5) var(--spacing-1);margin:calc(var(--spacing-0\\.5) * -1) calc(var(--spacing-1) * -1)}}.lc-breadcrumbs__link,.lc-breadcrumbs__current{max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@media(max-width:768px){.lc-breadcrumbs__link,.lc-breadcrumbs__current{max-width:150px}}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$3.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6448
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: BreadcrumbsComponent, isStandalone: true, selector: "lc-breadcrumbs", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, separator: { classPropertyName: "separator", publicName: "separator", isSignal: true, isRequired: false, transformFunction: null }, maxItems: { classPropertyName: "maxItems", publicName: "maxItems", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemClick: "itemClick" }, ngImport: i0, template: "@if (items().length > 0) {\n <nav [attr.aria-label]=\"ariaLabel()\">\n <ol [ngClass]=\"breadcrumbClasses()\">\n @for (item of visibleItems(); track item; let i = $index) {\n <li class=\"lc-breadcrumbs__item\">\n @if (isLastItem(i)) {\n <span class=\"lc-breadcrumbs__current\" aria-current=\"page\">\n {{ item.label }}\n </span>\n } @else if (isEllipsis(item)) {\n <span class=\"lc-breadcrumbs__ellipsis\" aria-hidden=\"true\">\n {{ item.label }}\n </span>\n } @else if (item.url) {\n <a\n [routerLink]=\"item.url\"\n class=\"lc-breadcrumbs__link\"\n >\n {{ item.label }}\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"lc-breadcrumbs__link lc-breadcrumbs__link--button\"\n (click)=\"onItemClick(item)\"\n >\n {{ item.label }}\n </button>\n }\n \n @if (!isLastItem(i)) {\n <span class=\"lc-breadcrumbs__separator\" aria-hidden=\"true\">\n {{ separator() }}\n </span>\n }\n </li>\n }\n </ol>\n \n <!-- Support for additional content projection -->\n <ng-content></ng-content>\n </nav>\n}\n", styles: [".lc-breadcrumbs{display:flex;flex-wrap:wrap;align-items:center;list-style:none;padding:0;margin:0}.lc-breadcrumbs--sm{font-size:var(--font-size-xs);gap:var(--lc-density-gap-xs, var(--spacing-1))}.lc-breadcrumbs--sm .lc-breadcrumbs__separator{margin:0 var(--spacing-1)}.lc-breadcrumbs--md{font-size:var(--font-size-sm);gap:var(--lc-density-gap-sm, var(--spacing-2))}.lc-breadcrumbs--md .lc-breadcrumbs__separator{margin:0 var(--spacing-2)}.lc-breadcrumbs--lg{font-size:var(--font-size-base);gap:var(--spacing-3)}.lc-breadcrumbs--lg .lc-breadcrumbs__separator{margin:0 var(--spacing-3)}.lc-breadcrumbs__item{display:flex;align-items:center;list-style:none}.lc-breadcrumbs__link{color:var(--color-primary-600);text-decoration:none;transition:all var(--transition-normal);border-radius:var(--border-radius-sm);padding:var(--spacing-1) var(--spacing-2);margin:calc(var(--spacing-1) * -1) calc(var(--spacing-2) * -1)}.lc-breadcrumbs__link:hover{color:var(--color-primary-700);background-color:var(--color-primary-50);text-decoration:underline}.lc-breadcrumbs__link:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-breadcrumbs__link:active{color:var(--color-primary-800)}.lc-breadcrumbs__link--button{appearance:none;background:none;border:none;font:inherit;cursor:pointer}.lc-breadcrumbs__current{color:var(--color-text-primary);font-weight:var(--font-weight-medium);padding:var(--spacing-1) var(--spacing-2);margin:calc(var(--spacing-1) * -1) calc(var(--spacing-2) * -1)}.lc-breadcrumbs__separator{color:var(--color-text-tertiary);-webkit-user-select:none;user-select:none;pointer-events:none}.lc-breadcrumbs__ellipsis{color:var(--color-text-tertiary);-webkit-user-select:none;user-select:none;pointer-events:none;padding:var(--spacing-1) var(--spacing-2);margin:calc(var(--spacing-1) * -1) calc(var(--spacing-2) * -1)}@media(max-width:768px){.lc-breadcrumbs--md{font-size:var(--font-size-xs);gap:var(--spacing-1)}.lc-breadcrumbs--md .lc-breadcrumbs__separator{margin:0 var(--spacing-1)}.lc-breadcrumbs--lg{font-size:var(--font-size-sm);gap:var(--spacing-2)}.lc-breadcrumbs--lg .lc-breadcrumbs__separator{margin:0 var(--spacing-2)}.lc-breadcrumbs__link,.lc-breadcrumbs__current,.lc-breadcrumbs__ellipsis{padding:var(--spacing-0\\.5) var(--spacing-1);margin:calc(var(--spacing-0\\.5) * -1) calc(var(--spacing-1) * -1)}}.lc-breadcrumbs__link,.lc-breadcrumbs__current{max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@media(max-width:768px){.lc-breadcrumbs__link,.lc-breadcrumbs__current{max-width:150px}}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1$3.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
6194
6449
  }
6195
6450
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: BreadcrumbsComponent, decorators: [{
6196
6451
  type: Component,
6197
- args: [{ selector: 'lc-breadcrumbs', imports: [NgClass, RouterModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (items().length > 0) {\n <nav [attr.aria-label]=\"ariaLabel()\">\n <ol [ngClass]=\"breadcrumbClasses()\">\n @for (item of visibleItems(); track item; let i = $index) {\n <li class=\"lc-breadcrumbs__item\">\n @if (isLastItem(i)) {\n <span class=\"lc-breadcrumbs__current\" aria-current=\"page\">\n {{ item.label }}\n </span>\n } @else if (isEllipsis(item)) {\n <span class=\"lc-breadcrumbs__ellipsis\" aria-hidden=\"true\">\n {{ item.label }}\n </span>\n } @else {\n <a\n [routerLink]=\"item.url\"\n class=\"lc-breadcrumbs__link\"\n >\n {{ item.label }}\n </a>\n }\n \n @if (!isLastItem(i)) {\n <span class=\"lc-breadcrumbs__separator\" aria-hidden=\"true\">\n {{ separator() }}\n </span>\n }\n </li>\n }\n </ol>\n \n <!-- Support for additional content projection -->\n <ng-content></ng-content>\n </nav>\n}\n", styles: [".lc-breadcrumbs{display:flex;flex-wrap:wrap;align-items:center;list-style:none;padding:0;margin:0}.lc-breadcrumbs--sm{font-size:var(--font-size-xs);gap:var(--lc-density-gap-xs, var(--spacing-1))}.lc-breadcrumbs--sm .lc-breadcrumbs__separator{margin:0 var(--spacing-1)}.lc-breadcrumbs--md{font-size:var(--font-size-sm);gap:var(--lc-density-gap-sm, var(--spacing-2))}.lc-breadcrumbs--md .lc-breadcrumbs__separator{margin:0 var(--spacing-2)}.lc-breadcrumbs--lg{font-size:var(--font-size-base);gap:var(--spacing-3)}.lc-breadcrumbs--lg .lc-breadcrumbs__separator{margin:0 var(--spacing-3)}.lc-breadcrumbs__item{display:flex;align-items:center;list-style:none}.lc-breadcrumbs__link{color:var(--color-primary-600);text-decoration:none;transition:all var(--transition-normal);border-radius:var(--border-radius-sm);padding:var(--spacing-1) var(--spacing-2);margin:calc(var(--spacing-1) * -1) calc(var(--spacing-2) * -1)}.lc-breadcrumbs__link:hover{color:var(--color-primary-700);background-color:var(--color-primary-50);text-decoration:underline}.lc-breadcrumbs__link:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-breadcrumbs__link:active{color:var(--color-primary-800)}.lc-breadcrumbs__current{color:var(--color-text-primary);font-weight:var(--font-weight-medium);padding:var(--spacing-1) var(--spacing-2);margin:calc(var(--spacing-1) * -1) calc(var(--spacing-2) * -1)}.lc-breadcrumbs__separator{color:var(--color-text-tertiary);-webkit-user-select:none;user-select:none;pointer-events:none}.lc-breadcrumbs__ellipsis{color:var(--color-text-tertiary);-webkit-user-select:none;user-select:none;pointer-events:none;padding:var(--spacing-1) var(--spacing-2);margin:calc(var(--spacing-1) * -1) calc(var(--spacing-2) * -1)}@media(max-width:768px){.lc-breadcrumbs--md{font-size:var(--font-size-xs);gap:var(--spacing-1)}.lc-breadcrumbs--md .lc-breadcrumbs__separator{margin:0 var(--spacing-1)}.lc-breadcrumbs--lg{font-size:var(--font-size-sm);gap:var(--spacing-2)}.lc-breadcrumbs--lg .lc-breadcrumbs__separator{margin:0 var(--spacing-2)}.lc-breadcrumbs__link,.lc-breadcrumbs__current,.lc-breadcrumbs__ellipsis{padding:var(--spacing-0\\.5) var(--spacing-1);margin:calc(var(--spacing-0\\.5) * -1) calc(var(--spacing-1) * -1)}}.lc-breadcrumbs__link,.lc-breadcrumbs__current{max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@media(max-width:768px){.lc-breadcrumbs__link,.lc-breadcrumbs__current{max-width:150px}}\n"] }]
6198
- }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], separator: [{ type: i0.Input, args: [{ isSignal: true, alias: "separator", required: false }] }], maxItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxItems", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
6452
+ args: [{ selector: 'lc-breadcrumbs', imports: [NgClass, RouterModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (items().length > 0) {\n <nav [attr.aria-label]=\"ariaLabel()\">\n <ol [ngClass]=\"breadcrumbClasses()\">\n @for (item of visibleItems(); track item; let i = $index) {\n <li class=\"lc-breadcrumbs__item\">\n @if (isLastItem(i)) {\n <span class=\"lc-breadcrumbs__current\" aria-current=\"page\">\n {{ item.label }}\n </span>\n } @else if (isEllipsis(item)) {\n <span class=\"lc-breadcrumbs__ellipsis\" aria-hidden=\"true\">\n {{ item.label }}\n </span>\n } @else if (item.url) {\n <a\n [routerLink]=\"item.url\"\n class=\"lc-breadcrumbs__link\"\n >\n {{ item.label }}\n </a>\n } @else {\n <button\n type=\"button\"\n class=\"lc-breadcrumbs__link lc-breadcrumbs__link--button\"\n (click)=\"onItemClick(item)\"\n >\n {{ item.label }}\n </button>\n }\n \n @if (!isLastItem(i)) {\n <span class=\"lc-breadcrumbs__separator\" aria-hidden=\"true\">\n {{ separator() }}\n </span>\n }\n </li>\n }\n </ol>\n \n <!-- Support for additional content projection -->\n <ng-content></ng-content>\n </nav>\n}\n", styles: [".lc-breadcrumbs{display:flex;flex-wrap:wrap;align-items:center;list-style:none;padding:0;margin:0}.lc-breadcrumbs--sm{font-size:var(--font-size-xs);gap:var(--lc-density-gap-xs, var(--spacing-1))}.lc-breadcrumbs--sm .lc-breadcrumbs__separator{margin:0 var(--spacing-1)}.lc-breadcrumbs--md{font-size:var(--font-size-sm);gap:var(--lc-density-gap-sm, var(--spacing-2))}.lc-breadcrumbs--md .lc-breadcrumbs__separator{margin:0 var(--spacing-2)}.lc-breadcrumbs--lg{font-size:var(--font-size-base);gap:var(--spacing-3)}.lc-breadcrumbs--lg .lc-breadcrumbs__separator{margin:0 var(--spacing-3)}.lc-breadcrumbs__item{display:flex;align-items:center;list-style:none}.lc-breadcrumbs__link{color:var(--color-primary-600);text-decoration:none;transition:all var(--transition-normal);border-radius:var(--border-radius-sm);padding:var(--spacing-1) var(--spacing-2);margin:calc(var(--spacing-1) * -1) calc(var(--spacing-2) * -1)}.lc-breadcrumbs__link:hover{color:var(--color-primary-700);background-color:var(--color-primary-50);text-decoration:underline}.lc-breadcrumbs__link:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-breadcrumbs__link:active{color:var(--color-primary-800)}.lc-breadcrumbs__link--button{appearance:none;background:none;border:none;font:inherit;cursor:pointer}.lc-breadcrumbs__current{color:var(--color-text-primary);font-weight:var(--font-weight-medium);padding:var(--spacing-1) var(--spacing-2);margin:calc(var(--spacing-1) * -1) calc(var(--spacing-2) * -1)}.lc-breadcrumbs__separator{color:var(--color-text-tertiary);-webkit-user-select:none;user-select:none;pointer-events:none}.lc-breadcrumbs__ellipsis{color:var(--color-text-tertiary);-webkit-user-select:none;user-select:none;pointer-events:none;padding:var(--spacing-1) var(--spacing-2);margin:calc(var(--spacing-1) * -1) calc(var(--spacing-2) * -1)}@media(max-width:768px){.lc-breadcrumbs--md{font-size:var(--font-size-xs);gap:var(--spacing-1)}.lc-breadcrumbs--md .lc-breadcrumbs__separator{margin:0 var(--spacing-1)}.lc-breadcrumbs--lg{font-size:var(--font-size-sm);gap:var(--spacing-2)}.lc-breadcrumbs--lg .lc-breadcrumbs__separator{margin:0 var(--spacing-2)}.lc-breadcrumbs__link,.lc-breadcrumbs__current,.lc-breadcrumbs__ellipsis{padding:var(--spacing-0\\.5) var(--spacing-1);margin:calc(var(--spacing-0\\.5) * -1) calc(var(--spacing-1) * -1)}}.lc-breadcrumbs__link,.lc-breadcrumbs__current{max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}@media(max-width:768px){.lc-breadcrumbs__link,.lc-breadcrumbs__current{max-width:150px}}\n"] }]
6453
+ }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], separator: [{ type: i0.Input, args: [{ isSignal: true, alias: "separator", required: false }] }], maxItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxItems", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], itemClick: [{ type: i0.Output, args: ["itemClick"] }] } });
6199
6454
 
6200
6455
  /**
6201
6456
  * Avatar component for circular user representation.
@@ -7212,12 +7467,18 @@ class ToggleGroupComponent {
7212
7467
  }
7213
7468
  return classes.join(' ');
7214
7469
  }
7470
+ /** Resolve the dot tone for an option (null when no dot is shown) */
7471
+ dotTone(option) {
7472
+ if (!option.dot)
7473
+ return null;
7474
+ return option.dot === true ? 'warning' : option.dot;
7475
+ }
7215
7476
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ToggleGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7216
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: ToggleGroupComponent, isStandalone: true, selector: "lc-toggle-group", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selected: "selectedChange", selectionChange: "selectionChange" }, ngImport: i0, template: "<div class=\"lc-toggle-group\" [ngClass]=\"sizeClass()\">\n @for (option of options(); track option.value) {\n <button\n type=\"button\"\n [class]=\"getButtonClasses(option)\"\n [disabled]=\"option.disabled ?? false\"\n [attr.aria-pressed]=\"isActive(option)\"\n (click)=\"select(option)\"\n >\n {{ option.label }}\n </button>\n }\n</div>\n", styles: [".lc-toggle-group{display:inline-flex;border-radius:var(--border-radius-md);overflow:hidden;border:1px solid var(--color-border);background-color:var(--color-surface-2)}.lc-toggle-group__btn{border:none;background:transparent;cursor:pointer;font-weight:var(--typography-font-weight-medium, 500);color:var(--color-text-secondary);transition:background-color .15s ease,color .15s ease;white-space:nowrap}.lc-toggle-group__btn:hover:not(:disabled):not(.lc-toggle-group__btn--active){background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-toggle-group__btn--active{background-color:var(--color-primary-fill);color:var(--color-on-primary)}.lc-toggle-group__btn--disabled{opacity:.5;cursor:not-allowed}.lc-toggle-group__btn+.lc-toggle-group__btn{border-left:1px solid var(--color-border)}.lc-toggle-group--sm .lc-toggle-group__btn{padding:.25rem .5rem;font-size:var(--typography-font-size-xs, .75rem)}.lc-toggle-group--md .lc-toggle-group__btn{padding:.375rem .75rem;font-size:var(--typography-font-size-sm, .875rem)}.lc-toggle-group--lg .lc-toggle-group__btn{padding:.5rem 1rem;font-size:var(--typography-font-size-base, 1rem)}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
7477
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: ToggleGroupComponent, isStandalone: true, selector: "lc-toggle-group", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selected: "selectedChange", selectionChange: "selectionChange" }, ngImport: i0, template: "<div class=\"lc-toggle-group\" [ngClass]=\"sizeClass()\">\n @for (option of options(); track option.value) {\n <button\n type=\"button\"\n [class]=\"getButtonClasses(option)\"\n [disabled]=\"option.disabled ?? false\"\n [attr.aria-pressed]=\"isActive(option)\"\n (click)=\"select(option)\"\n >\n {{ option.label }}\n @if (dotTone(option); as tone) {\n <span\n class=\"lc-toggle-group__dot lc-toggle-group__dot--{{ tone }}\"\n aria-hidden=\"true\"\n ></span>\n }\n </button>\n }\n</div>\n", styles: [".lc-toggle-group{display:inline-flex;border-radius:var(--border-radius-md);overflow:hidden;border:1px solid var(--color-border);background-color:var(--color-surface-2)}.lc-toggle-group__btn{display:inline-flex;align-items:center;gap:.375rem;border:none;background:transparent;cursor:pointer;font-weight:var(--typography-font-weight-medium, 500);color:var(--color-text-secondary);transition:background-color .15s ease,color .15s ease;white-space:nowrap}.lc-toggle-group__btn:hover:not(:disabled):not(.lc-toggle-group__btn--active){background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-toggle-group__btn--active{background-color:var(--color-primary-fill);color:var(--color-on-primary)}.lc-toggle-group__btn--disabled{opacity:.5;cursor:not-allowed}.lc-toggle-group__btn+.lc-toggle-group__btn{border-left:1px solid var(--color-border)}.lc-toggle-group__dot{flex:0 0 auto;width:.5em;height:.5em;border-radius:50%}.lc-toggle-group__dot--warning{background-color:var(--color-warning)}.lc-toggle-group__dot--error{background-color:var(--color-error)}.lc-toggle-group__dot--success{background-color:var(--color-success)}.lc-toggle-group--sm .lc-toggle-group__btn{padding:.25rem .5rem;font-size:var(--typography-font-size-xs, .75rem)}.lc-toggle-group--md .lc-toggle-group__btn{padding:.375rem .75rem;font-size:var(--typography-font-size-sm, .875rem)}.lc-toggle-group--lg .lc-toggle-group__btn{padding:.5rem 1rem;font-size:var(--typography-font-size-base, 1rem)}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
7217
7478
  }
7218
7479
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ToggleGroupComponent, decorators: [{
7219
7480
  type: Component,
7220
- args: [{ selector: 'lc-toggle-group', standalone: true, imports: [NgClass], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"lc-toggle-group\" [ngClass]=\"sizeClass()\">\n @for (option of options(); track option.value) {\n <button\n type=\"button\"\n [class]=\"getButtonClasses(option)\"\n [disabled]=\"option.disabled ?? false\"\n [attr.aria-pressed]=\"isActive(option)\"\n (click)=\"select(option)\"\n >\n {{ option.label }}\n </button>\n }\n</div>\n", styles: [".lc-toggle-group{display:inline-flex;border-radius:var(--border-radius-md);overflow:hidden;border:1px solid var(--color-border);background-color:var(--color-surface-2)}.lc-toggle-group__btn{border:none;background:transparent;cursor:pointer;font-weight:var(--typography-font-weight-medium, 500);color:var(--color-text-secondary);transition:background-color .15s ease,color .15s ease;white-space:nowrap}.lc-toggle-group__btn:hover:not(:disabled):not(.lc-toggle-group__btn--active){background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-toggle-group__btn--active{background-color:var(--color-primary-fill);color:var(--color-on-primary)}.lc-toggle-group__btn--disabled{opacity:.5;cursor:not-allowed}.lc-toggle-group__btn+.lc-toggle-group__btn{border-left:1px solid var(--color-border)}.lc-toggle-group--sm .lc-toggle-group__btn{padding:.25rem .5rem;font-size:var(--typography-font-size-xs, .75rem)}.lc-toggle-group--md .lc-toggle-group__btn{padding:.375rem .75rem;font-size:var(--typography-font-size-sm, .875rem)}.lc-toggle-group--lg .lc-toggle-group__btn{padding:.5rem 1rem;font-size:var(--typography-font-size-base, 1rem)}\n"] }]
7481
+ args: [{ selector: 'lc-toggle-group', standalone: true, imports: [NgClass], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"lc-toggle-group\" [ngClass]=\"sizeClass()\">\n @for (option of options(); track option.value) {\n <button\n type=\"button\"\n [class]=\"getButtonClasses(option)\"\n [disabled]=\"option.disabled ?? false\"\n [attr.aria-pressed]=\"isActive(option)\"\n (click)=\"select(option)\"\n >\n {{ option.label }}\n @if (dotTone(option); as tone) {\n <span\n class=\"lc-toggle-group__dot lc-toggle-group__dot--{{ tone }}\"\n aria-hidden=\"true\"\n ></span>\n }\n </button>\n }\n</div>\n", styles: [".lc-toggle-group{display:inline-flex;border-radius:var(--border-radius-md);overflow:hidden;border:1px solid var(--color-border);background-color:var(--color-surface-2)}.lc-toggle-group__btn{display:inline-flex;align-items:center;gap:.375rem;border:none;background:transparent;cursor:pointer;font-weight:var(--typography-font-weight-medium, 500);color:var(--color-text-secondary);transition:background-color .15s ease,color .15s ease;white-space:nowrap}.lc-toggle-group__btn:hover:not(:disabled):not(.lc-toggle-group__btn--active){background-color:var(--color-surface-hover);color:var(--color-text-primary)}.lc-toggle-group__btn--active{background-color:var(--color-primary-fill);color:var(--color-on-primary)}.lc-toggle-group__btn--disabled{opacity:.5;cursor:not-allowed}.lc-toggle-group__btn+.lc-toggle-group__btn{border-left:1px solid var(--color-border)}.lc-toggle-group__dot{flex:0 0 auto;width:.5em;height:.5em;border-radius:50%}.lc-toggle-group__dot--warning{background-color:var(--color-warning)}.lc-toggle-group__dot--error{background-color:var(--color-error)}.lc-toggle-group__dot--success{background-color:var(--color-success)}.lc-toggle-group--sm .lc-toggle-group__btn{padding:.25rem .5rem;font-size:var(--typography-font-size-xs, .75rem)}.lc-toggle-group--md .lc-toggle-group__btn{padding:.375rem .75rem;font-size:var(--typography-font-size-sm, .875rem)}.lc-toggle-group--lg .lc-toggle-group__btn{padding:.5rem 1rem;font-size:var(--typography-font-size-base, 1rem)}\n"] }]
7221
7482
  }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }, { type: i0.Output, args: ["selectedChange"] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }] } });
7222
7483
 
7223
7484
  /**
@@ -7344,6 +7605,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
7344
7605
  * - Multiple size options (sm, md, lg)
7345
7606
  * - Optional leading icon
7346
7607
  * - Removable with close button and remove event
7608
+ * - Clickable variant that renders as a button and emits `chipClick`
7347
7609
  * - Disabled state support
7348
7610
  *
7349
7611
  * @example
@@ -7351,6 +7613,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
7351
7613
  * <lc-chip variant="primary" [removable]="true" (remove)="onRemove()">
7352
7614
  * Tag Name
7353
7615
  * </lc-chip>
7616
+ *
7617
+ * <lc-chip [clickable]="true" (chipClick)="onNavigate()">
7618
+ * Linked item
7619
+ * </lc-chip>
7354
7620
  * ```
7355
7621
  */
7356
7622
  class ChipComponent {
@@ -7362,10 +7628,17 @@ class ChipComponent {
7362
7628
  icon = input(...(ngDevMode ? [undefined, { debugName: "icon" }] : /* istanbul ignore next */ []));
7363
7629
  /** Whether the chip can be removed */
7364
7630
  removable = input(false, ...(ngDevMode ? [{ debugName: "removable" }] : /* istanbul ignore next */ []));
7631
+ /**
7632
+ * Whether the chip acts as a button (e.g. for navigation). Renders the chip
7633
+ * as a `<button type="button">` with hover / active / focus-visible states.
7634
+ */
7635
+ clickable = input(false, ...(ngDevMode ? [{ debugName: "clickable" }] : /* istanbul ignore next */ []));
7365
7636
  /** Whether the chip is disabled */
7366
7637
  disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
7367
7638
  /** Emitted when the chip is removed */
7368
7639
  remove = output();
7640
+ /** Emitted when a clickable chip is activated (click, Enter or Space). */
7641
+ chipClick = output();
7369
7642
  /**
7370
7643
  * Computed CSS classes for the chip
7371
7644
  */
@@ -7376,11 +7649,22 @@ class ChipComponent {
7376
7649
  if (this.removable()) {
7377
7650
  classes.push('lc-chip--removable');
7378
7651
  }
7652
+ if (this.clickable()) {
7653
+ classes.push('lc-chip--clickable');
7654
+ }
7379
7655
  if (this.disabled()) {
7380
7656
  classes.push('lc-chip--disabled');
7381
7657
  }
7382
7658
  return classes.join(' ');
7383
7659
  }, ...(ngDevMode ? [{ debugName: "chipClasses" }] : /* istanbul ignore next */ []));
7660
+ /**
7661
+ * Handle activation of a clickable chip
7662
+ */
7663
+ onChipClick() {
7664
+ if (!this.disabled()) {
7665
+ this.chipClick.emit();
7666
+ }
7667
+ }
7384
7668
  /**
7385
7669
  * Handle remove button click
7386
7670
  */
@@ -7390,6 +7674,17 @@ class ChipComponent {
7390
7674
  this.remove.emit();
7391
7675
  }
7392
7676
  }
7677
+ /**
7678
+ * Keyboard activation of the delete affordance inside a clickable chip
7679
+ * (a span with role="button", since nesting native buttons is invalid).
7680
+ */
7681
+ onDeleteKeydown(event) {
7682
+ if (event.key === 'Enter' || event.key === ' ') {
7683
+ event.preventDefault();
7684
+ event.stopPropagation();
7685
+ this.onRemove(event);
7686
+ }
7687
+ }
7393
7688
  /**
7394
7689
  * Handle keyboard navigation
7395
7690
  */
@@ -7403,12 +7698,12 @@ class ChipComponent {
7403
7698
  }
7404
7699
  }
7405
7700
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ChipComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
7406
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: ChipComponent, isStandalone: true, selector: "lc-chip", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, removable: { classPropertyName: "removable", publicName: "removable", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { remove: "remove" }, ngImport: i0, template: "<span \n [class]=\"chipClasses()\"\n [attr.role]=\"removable() ? 'button' : null\"\n [attr.tabindex]=\"removable() ? (disabled() ? '-1' : '0') : null\"\n [attr.aria-disabled]=\"disabled() ? 'true' : null\"\n (keydown)=\"onKeydown($event)\"\n>\n <span class=\"lc-chip__content\">\n @if (icon()) {\n <lc-icon [name]=\"icon()!\" class=\"lc-chip__icon\" size=\"sm\" />\n }\n <span class=\"lc-chip__label\">\n <ng-content></ng-content>\n </span>\n @if (removable() && !disabled()) {\n <button\n type=\"button\"\n class=\"lc-chip__delete\"\n aria-label=\"Remove chip\"\n (click)=\"onRemove($event)\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n </svg>\n </button>\n }\n </span>\n</span>\n", styles: [".lc-chip{display:inline-flex;align-items:center;border-radius:var(--border-radius-full, 9999px);font-weight:var(--typography-font-weight-medium, 500);line-height:1;white-space:nowrap;transition:background-color .15s ease,color .15s ease,opacity .15s ease;cursor:default;-webkit-user-select:none;user-select:none}.lc-chip__content{display:inline-flex;align-items:center;gap:.375rem}.lc-chip__label{display:inline-flex;align-items:center}.lc-chip__delete{display:inline-flex;align-items:center;justify-content:center;padding:0;margin:0;background:none;border:none;cursor:pointer;color:currentColor;opacity:.6;transition:opacity .15s ease;flex-shrink:0}.lc-chip__delete:hover{opacity:1}.lc-chip__delete:focus{outline:2px solid currentColor;outline-offset:2px;border-radius:50%}.lc-chip__delete svg{width:1em;height:1em}.lc-chip--sm{padding:.25rem .625rem;font-size:var(--typography-font-size-sm, .875rem);height:1.5rem}.lc-chip--sm .lc-chip__content{gap:.25rem}.lc-chip--md{padding:.375rem .75rem;font-size:var(--typography-font-size-sm, .875rem);height:2rem}.lc-chip--lg{padding:.5rem 1rem;font-size:var(--typography-font-size-base, 1rem);height:2.5rem}.lc-chip--lg .lc-chip__content{gap:.5rem}.lc-chip{border:1px solid transparent}.lc-chip--default{background-color:var(--color-surface-hover);color:var(--color-text-secondary);border-color:var(--color-border)}.lc-chip--primary{background-color:#20849729;color:var(--color-primary-500);border-color:var(--color-border-strong)}.lc-chip--success{background-color:#8ea4752e;color:var(--color-success);border-color:#8ea47566}.lc-chip--warning{background-color:#e1a0402e;color:var(--color-warning);border-color:#e1a04066}.lc-chip--error{background-color:#9d0e0e2e;color:var(--color-error);border-color:#9d0e0e66}.lc-chip--info{background-color:#3b658833;color:var(--color-info);border-color:#3b65886b}.lc-chip--removable{cursor:pointer}.lc-chip--removable:hover:not(.lc-chip--removable--disabled){opacity:.9}.lc-chip--removable:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-chip--disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.lc-chip__icon{display:inline-flex;align-items:center;flex-shrink:0}\n"], dependencies: [{ kind: "component", type: IconComponent, selector: "lc-icon", inputs: ["name", "variant", "size", "color", "ariaLabel", "decorative", "strict"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
7701
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: ChipComponent, isStandalone: true, selector: "lc-chip", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, removable: { classPropertyName: "removable", publicName: "removable", isSignal: true, isRequired: false, transformFunction: null }, clickable: { classPropertyName: "clickable", publicName: "clickable", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { remove: "remove", chipClick: "chipClick" }, ngImport: i0, template: "<!--\n The chip renders as a real <button> when clickable, otherwise as a <span>\n (optionally with role=\"button\" for the legacy removable keyboard behavior).\n The inner content is shared via template so both shells stay identical.\n-->\n<ng-template #chipContent>\n <span class=\"lc-chip__content\">\n @if (icon()) {\n <lc-icon [name]=\"icon()!\" class=\"lc-chip__icon\" size=\"sm\" />\n }\n <span class=\"lc-chip__label\">\n <ng-content></ng-content>\n </span>\n @if (removable() && !disabled()) {\n @if (clickable()) {\n <!--\n Inside a clickable chip the shell is already a <button>; a nested\n native button would be invalid HTML, so the delete affordance is a\n focusable span with button semantics.\n -->\n <span\n class=\"lc-chip__delete\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"Remove chip\"\n (click)=\"onRemove($event)\"\n (keydown)=\"onDeleteKeydown($event)\"\n >\n <ng-container [ngTemplateOutlet]=\"deleteIcon\" />\n </span>\n } @else {\n <button\n type=\"button\"\n class=\"lc-chip__delete\"\n aria-label=\"Remove chip\"\n (click)=\"onRemove($event)\"\n >\n <ng-container [ngTemplateOutlet]=\"deleteIcon\" />\n </button>\n }\n }\n </span>\n</ng-template>\n\n<ng-template #deleteIcon>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n </svg>\n</ng-template>\n\n@if (clickable()) {\n <button\n type=\"button\"\n [class]=\"chipClasses()\"\n [disabled]=\"disabled()\"\n [attr.aria-disabled]=\"disabled() ? 'true' : null\"\n (click)=\"onChipClick()\"\n >\n <ng-container [ngTemplateOutlet]=\"chipContent\" />\n </button>\n} @else {\n <span\n [class]=\"chipClasses()\"\n [attr.role]=\"removable() ? 'button' : null\"\n [attr.tabindex]=\"removable() ? (disabled() ? '-1' : '0') : null\"\n [attr.aria-disabled]=\"disabled() ? 'true' : null\"\n (keydown)=\"onKeydown($event)\"\n >\n <ng-container [ngTemplateOutlet]=\"chipContent\" />\n </span>\n}\n", styles: [".lc-chip{display:inline-flex;align-items:center;border-radius:var(--border-radius-full, 9999px);font-weight:var(--typography-font-weight-medium, 500);line-height:1;white-space:nowrap;transition:background-color .15s ease,color .15s ease,opacity .15s ease;cursor:default;-webkit-user-select:none;user-select:none}.lc-chip__content{display:inline-flex;align-items:center;gap:.375rem}.lc-chip__label{display:inline-flex;align-items:center}.lc-chip__delete{display:inline-flex;align-items:center;justify-content:center;padding:0;margin:0;background:none;border:none;cursor:pointer;color:currentColor;opacity:.6;transition:opacity .15s ease;flex-shrink:0}.lc-chip__delete:hover{opacity:1}.lc-chip__delete:focus{outline:2px solid currentColor;outline-offset:2px;border-radius:50%}.lc-chip__delete svg{width:1em;height:1em}.lc-chip--sm{padding:.25rem .625rem;font-size:var(--typography-font-size-sm, .875rem);height:1.5rem}.lc-chip--sm .lc-chip__content{gap:.25rem}.lc-chip--md{padding:.375rem .75rem;font-size:var(--typography-font-size-sm, .875rem);height:2rem}.lc-chip--lg{padding:.5rem 1rem;font-size:var(--typography-font-size-base, 1rem);height:2.5rem}.lc-chip--lg .lc-chip__content{gap:.5rem}.lc-chip{border:1px solid transparent}.lc-chip--default{background-color:var(--color-surface-hover);color:var(--color-text-secondary);border-color:var(--color-border)}.lc-chip--primary{background-color:#20849729;color:var(--color-primary-500);border-color:var(--color-border-strong)}.lc-chip--success{background-color:#8ea4752e;color:var(--color-success);border-color:#8ea47566}.lc-chip--warning{background-color:#e1a0402e;color:var(--color-warning);border-color:#e1a04066}.lc-chip--error{background-color:#9d0e0e2e;color:var(--color-error);border-color:#9d0e0e66}.lc-chip--info{background-color:#3b658833;color:var(--color-info);border-color:#3b65886b}.lc-chip--removable{cursor:pointer}.lc-chip--removable:hover:not(.lc-chip--removable--disabled){opacity:.9}.lc-chip--removable:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-chip--clickable{font:inherit;font-weight:var(--typography-font-weight-medium, 500);text-align:inherit;appearance:none;cursor:pointer}.lc-chip--clickable:hover:not(:disabled){filter:brightness(1.06);border-color:var(--color-border-strong, var(--color-border))}.lc-chip--clickable:active:not(:disabled){filter:brightness(.96)}.lc-chip--clickable:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-chip--disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.lc-chip__icon{display:inline-flex;align-items:center;flex-shrink:0}\n"], dependencies: [{ kind: "component", type: IconComponent, selector: "lc-icon", inputs: ["name", "variant", "size", "color", "ariaLabel", "decorative", "strict"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
7407
7702
  }
7408
7703
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ChipComponent, decorators: [{
7409
7704
  type: Component,
7410
- args: [{ selector: 'lc-chip', standalone: true, imports: [IconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<span \n [class]=\"chipClasses()\"\n [attr.role]=\"removable() ? 'button' : null\"\n [attr.tabindex]=\"removable() ? (disabled() ? '-1' : '0') : null\"\n [attr.aria-disabled]=\"disabled() ? 'true' : null\"\n (keydown)=\"onKeydown($event)\"\n>\n <span class=\"lc-chip__content\">\n @if (icon()) {\n <lc-icon [name]=\"icon()!\" class=\"lc-chip__icon\" size=\"sm\" />\n }\n <span class=\"lc-chip__label\">\n <ng-content></ng-content>\n </span>\n @if (removable() && !disabled()) {\n <button\n type=\"button\"\n class=\"lc-chip__delete\"\n aria-label=\"Remove chip\"\n (click)=\"onRemove($event)\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n </svg>\n </button>\n }\n </span>\n</span>\n", styles: [".lc-chip{display:inline-flex;align-items:center;border-radius:var(--border-radius-full, 9999px);font-weight:var(--typography-font-weight-medium, 500);line-height:1;white-space:nowrap;transition:background-color .15s ease,color .15s ease,opacity .15s ease;cursor:default;-webkit-user-select:none;user-select:none}.lc-chip__content{display:inline-flex;align-items:center;gap:.375rem}.lc-chip__label{display:inline-flex;align-items:center}.lc-chip__delete{display:inline-flex;align-items:center;justify-content:center;padding:0;margin:0;background:none;border:none;cursor:pointer;color:currentColor;opacity:.6;transition:opacity .15s ease;flex-shrink:0}.lc-chip__delete:hover{opacity:1}.lc-chip__delete:focus{outline:2px solid currentColor;outline-offset:2px;border-radius:50%}.lc-chip__delete svg{width:1em;height:1em}.lc-chip--sm{padding:.25rem .625rem;font-size:var(--typography-font-size-sm, .875rem);height:1.5rem}.lc-chip--sm .lc-chip__content{gap:.25rem}.lc-chip--md{padding:.375rem .75rem;font-size:var(--typography-font-size-sm, .875rem);height:2rem}.lc-chip--lg{padding:.5rem 1rem;font-size:var(--typography-font-size-base, 1rem);height:2.5rem}.lc-chip--lg .lc-chip__content{gap:.5rem}.lc-chip{border:1px solid transparent}.lc-chip--default{background-color:var(--color-surface-hover);color:var(--color-text-secondary);border-color:var(--color-border)}.lc-chip--primary{background-color:#20849729;color:var(--color-primary-500);border-color:var(--color-border-strong)}.lc-chip--success{background-color:#8ea4752e;color:var(--color-success);border-color:#8ea47566}.lc-chip--warning{background-color:#e1a0402e;color:var(--color-warning);border-color:#e1a04066}.lc-chip--error{background-color:#9d0e0e2e;color:var(--color-error);border-color:#9d0e0e66}.lc-chip--info{background-color:#3b658833;color:var(--color-info);border-color:#3b65886b}.lc-chip--removable{cursor:pointer}.lc-chip--removable:hover:not(.lc-chip--removable--disabled){opacity:.9}.lc-chip--removable:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-chip--disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.lc-chip__icon{display:inline-flex;align-items:center;flex-shrink:0}\n"] }]
7411
- }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], removable: [{ type: i0.Input, args: [{ isSignal: true, alias: "removable", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], remove: [{ type: i0.Output, args: ["remove"] }] } });
7705
+ args: [{ selector: 'lc-chip', standalone: true, imports: [IconComponent, NgTemplateOutlet], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!--\n The chip renders as a real <button> when clickable, otherwise as a <span>\n (optionally with role=\"button\" for the legacy removable keyboard behavior).\n The inner content is shared via template so both shells stay identical.\n-->\n<ng-template #chipContent>\n <span class=\"lc-chip__content\">\n @if (icon()) {\n <lc-icon [name]=\"icon()!\" class=\"lc-chip__icon\" size=\"sm\" />\n }\n <span class=\"lc-chip__label\">\n <ng-content></ng-content>\n </span>\n @if (removable() && !disabled()) {\n @if (clickable()) {\n <!--\n Inside a clickable chip the shell is already a <button>; a nested\n native button would be invalid HTML, so the delete affordance is a\n focusable span with button semantics.\n -->\n <span\n class=\"lc-chip__delete\"\n role=\"button\"\n tabindex=\"0\"\n aria-label=\"Remove chip\"\n (click)=\"onRemove($event)\"\n (keydown)=\"onDeleteKeydown($event)\"\n >\n <ng-container [ngTemplateOutlet]=\"deleteIcon\" />\n </span>\n } @else {\n <button\n type=\"button\"\n class=\"lc-chip__delete\"\n aria-label=\"Remove chip\"\n (click)=\"onRemove($event)\"\n >\n <ng-container [ngTemplateOutlet]=\"deleteIcon\" />\n </button>\n }\n }\n </span>\n</ng-template>\n\n<ng-template #deleteIcon>\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n >\n <line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\"></line>\n <line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\"></line>\n </svg>\n</ng-template>\n\n@if (clickable()) {\n <button\n type=\"button\"\n [class]=\"chipClasses()\"\n [disabled]=\"disabled()\"\n [attr.aria-disabled]=\"disabled() ? 'true' : null\"\n (click)=\"onChipClick()\"\n >\n <ng-container [ngTemplateOutlet]=\"chipContent\" />\n </button>\n} @else {\n <span\n [class]=\"chipClasses()\"\n [attr.role]=\"removable() ? 'button' : null\"\n [attr.tabindex]=\"removable() ? (disabled() ? '-1' : '0') : null\"\n [attr.aria-disabled]=\"disabled() ? 'true' : null\"\n (keydown)=\"onKeydown($event)\"\n >\n <ng-container [ngTemplateOutlet]=\"chipContent\" />\n </span>\n}\n", styles: [".lc-chip{display:inline-flex;align-items:center;border-radius:var(--border-radius-full, 9999px);font-weight:var(--typography-font-weight-medium, 500);line-height:1;white-space:nowrap;transition:background-color .15s ease,color .15s ease,opacity .15s ease;cursor:default;-webkit-user-select:none;user-select:none}.lc-chip__content{display:inline-flex;align-items:center;gap:.375rem}.lc-chip__label{display:inline-flex;align-items:center}.lc-chip__delete{display:inline-flex;align-items:center;justify-content:center;padding:0;margin:0;background:none;border:none;cursor:pointer;color:currentColor;opacity:.6;transition:opacity .15s ease;flex-shrink:0}.lc-chip__delete:hover{opacity:1}.lc-chip__delete:focus{outline:2px solid currentColor;outline-offset:2px;border-radius:50%}.lc-chip__delete svg{width:1em;height:1em}.lc-chip--sm{padding:.25rem .625rem;font-size:var(--typography-font-size-sm, .875rem);height:1.5rem}.lc-chip--sm .lc-chip__content{gap:.25rem}.lc-chip--md{padding:.375rem .75rem;font-size:var(--typography-font-size-sm, .875rem);height:2rem}.lc-chip--lg{padding:.5rem 1rem;font-size:var(--typography-font-size-base, 1rem);height:2.5rem}.lc-chip--lg .lc-chip__content{gap:.5rem}.lc-chip{border:1px solid transparent}.lc-chip--default{background-color:var(--color-surface-hover);color:var(--color-text-secondary);border-color:var(--color-border)}.lc-chip--primary{background-color:#20849729;color:var(--color-primary-500);border-color:var(--color-border-strong)}.lc-chip--success{background-color:#8ea4752e;color:var(--color-success);border-color:#8ea47566}.lc-chip--warning{background-color:#e1a0402e;color:var(--color-warning);border-color:#e1a04066}.lc-chip--error{background-color:#9d0e0e2e;color:var(--color-error);border-color:#9d0e0e66}.lc-chip--info{background-color:#3b658833;color:var(--color-info);border-color:#3b65886b}.lc-chip--removable{cursor:pointer}.lc-chip--removable:hover:not(.lc-chip--removable--disabled){opacity:.9}.lc-chip--removable:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-chip--clickable{font:inherit;font-weight:var(--typography-font-weight-medium, 500);text-align:inherit;appearance:none;cursor:pointer}.lc-chip--clickable:hover:not(:disabled){filter:brightness(1.06);border-color:var(--color-border-strong, var(--color-border))}.lc-chip--clickable:active:not(:disabled){filter:brightness(.96)}.lc-chip--clickable:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:2px}.lc-chip--disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.lc-chip__icon{display:inline-flex;align-items:center;flex-shrink:0}\n"] }]
7706
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], removable: [{ type: i0.Input, args: [{ isSignal: true, alias: "removable", required: false }] }], clickable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clickable", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], remove: [{ type: i0.Output, args: ["remove"] }], chipClick: [{ type: i0.Output, args: ["chipClick"] }] } });
7412
7707
 
7413
7708
  /**
7414
7709
  * Directive for defining custom cell templates in the Table component.
@@ -12053,11 +12348,11 @@ class MarkdownComponent {
12053
12348
  return this.mermaidApiPromise;
12054
12349
  }
12055
12350
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: MarkdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
12056
- 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 }, highlightChanges: { classPropertyName: "highlightChanges", publicName: "highlightChanges", isSignal: true, isRequired: false, transformFunction: null }, previousContent: { classPropertyName: "previousContent", publicName: "previousContent", isSignal: true, isRequired: false, transformFunction: null }, changeHighlightFadeMs: { classPropertyName: "changeHighlightFadeMs", publicName: "changeHighlightFadeMs", isSignal: true, isRequired: false, transformFunction: null }, scrollToFirstChange: { classPropertyName: "scrollToFirstChange", publicName: "scrollToFirstChange", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { linkClick: "linkClick", rendered: "rendered", changesHighlighted: "changesHighlighted" }, ngImport: i0, template: "<div [class]=\"containerClasses()\" (click)=\"onLinkClick($event)\">\n @if (highlightChanges()) {\n <div class=\"lc-markdown__sr-only\" role=\"status\" aria-live=\"polite\">{{ changeSummary() }}</div>\n }\n @for (part of renderParts(); track part.index) {\n @if (part.type === 'html') {\n <div [innerHTML]=\"part.safeHtml\"></div>\n } @else if (part.type === 'code') {\n <lc-code-block\n [code]=\"part.code!\"\n [language]=\"part.lang ?? 'text'\"\n [showLineNumbers]=\"true\"\n [showCopy]=\"true\"\n />\n } @else {\n @if (mermaidSvgFor(part.index); as svg) {\n <div class=\"lc-markdown__mermaid\" [innerHTML]=\"svg\"></div>\n } @else if (mermaidErrorFor(part.index); as errorMessage) {\n <div class=\"lc-markdown__mermaid-fallback\">\n <p class=\"lc-markdown__mermaid-error\">{{ errorMessage }}</p>\n <lc-code-block\n [code]=\"part.code!\"\n [language]=\"'text'\"\n [showLineNumbers]=\"true\"\n [showCopy]=\"true\"\n />\n </div>\n } @else {\n <div class=\"lc-markdown__mermaid-loading\">Rendering Mermaid diagram...</div>\n }\n }\n }\n</div>\n", styles: [".lc-markdown{color:var(--color-text-primary);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-primary);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-surface-hover);color:var(--color-text-primary);padding:.125em .375em;border-radius:var(--border-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-surface-2);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 ul{list-style:disc}.lc-markdown ol{list-style:decimal}.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(--border-radius-md, .375rem)}.lc-markdown del{text-decoration:line-through;color:var(--color-text-secondary, #6b7280)}.lc-markdown em{font-style:italic}.lc-markdown__table-wrap{margin:1em 0;overflow-x:auto;-webkit-overflow-scrolling:touch}.lc-markdown__table{width:100%;border-collapse:collapse;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-surface-2);font-weight:var(--font-weight-semibold, 600)}.lc-markdown__table tr:hover td{background-color:var(--color-surface-2)}.lc-markdown__cell--left{text-align:left}.lc-markdown__cell--center{text-align:center}.lc-markdown__cell--right{text-align:right}.lc-markdown__task-item{list-style:none;display:flex;align-items:flex-start;gap:.5em}.lc-markdown__checkbox{flex:0 0 auto;box-sizing:border-box;width:1.05em;height:1.05em;margin-top:.18em;border:1.5px solid var(--color-border-strong, var(--color-border));border-radius:3px;background-color:var(--color-surface);position:relative}.lc-markdown__checkbox--checked{background-color:var(--color-primary);border-color:var(--color-primary)}.lc-markdown__checkbox--checked:after{content:\"\";position:absolute;left:.32em;top:.12em;width:.28em;height:.55em;border:solid var(--color-on-primary, #fff);border-width:0 2px 2px 0;transform:rotate(45deg)}.lc-markdown__task-label{flex:1 1 auto;min-width:0}.lc-markdown__anchor{color:var(--color-text-tertiary);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 lc-code-block code{background:none;color:inherit;padding:0;border-radius:0;font-size:inherit}.lc-markdown__mermaid{margin:1em 0;padding:.75rem;border:1px solid var(--color-divider, #e5e7eb);border-radius:var(--border-radius-md, .375rem);background:var(--color-surface, #ffffff);overflow-x:auto}.lc-markdown__mermaid svg{display:block;margin:0 auto;max-width:100%;height:auto}.lc-markdown__mermaid-loading{margin:1em 0;padding:.75rem;border:1px dashed var(--color-divider, #e5e7eb);border-radius:var(--border-radius-md, .375rem);color:var(--color-text-secondary, #6b7280);font-size:var(--font-size-sm, .875rem)}.lc-markdown__mermaid-fallback{margin:1em 0}.lc-markdown__mermaid-error{margin:0 0 .5rem;color:var(--color-error);font-size:var(--font-size-sm, .875rem)}.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}.lc-markdown--chat{font-size:inherit;line-height:1.4}.lc-markdown--chat p,.lc-markdown--chat ul,.lc-markdown--chat ol,.lc-markdown--chat pre,.lc-markdown--chat blockquote,.lc-markdown--chat table{margin:0 0 6px}.lc-markdown--chat h1,.lc-markdown--chat h2,.lc-markdown--chat h3,.lc-markdown--chat h4,.lc-markdown--chat h5,.lc-markdown--chat h6{margin:8px 0 4px;line-height:1.25}.lc-markdown--chat h1{font-size:1.25rem}.lc-markdown--chat h2{font-size:1.125rem}.lc-markdown--chat h3{font-size:1rem}.lc-markdown--chat h4,.lc-markdown--chat h5,.lc-markdown--chat h6{font-size:.9375rem}.lc-markdown--chat li{margin:0;line-height:1.4}.lc-markdown--chat ul,.lc-markdown--chat ol{padding-left:1.25em}.lc-markdown--chat blockquote{padding:.25em .75em}.lc-markdown--chat lc-code-block{margin:6px 0}.lc-markdown--chat .lc-markdown__mermaid,.lc-markdown--chat .lc-markdown__mermaid-loading,.lc-markdown--chat .lc-markdown__mermaid-fallback{margin:6px 0}.lc-markdown--chat>*:first-child{margin-top:0}.lc-markdown--chat>*:last-child{margin-bottom:0}.lc-markdown__block--changed{position:relative;padding:.4rem .875rem;border-radius:0 6px 6px 0;background-color:color-mix(in srgb,var(--color-primary) 13%,var(--color-surface));transition:background-color .42s ease,padding .42s ease}.lc-markdown__block--changed:before{content:\"\";position:absolute;inset-block:0;left:0;width:3px;border-radius:0 3px 3px 0;background-color:var(--color-primary);transition:opacity .42s ease}.lc-markdown--faded .lc-markdown__block--changed{padding:0;background-color:transparent}.lc-markdown--faded .lc-markdown__block--changed:before{opacity:0}.lc-markdown__sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"], dependencies: [{ kind: "component", type: CodeBlockComponent, selector: "lc-code-block", inputs: ["code", "language", "filename", "showLineNumbers", "showCopy", "showHeader"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
12351
+ 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 }, highlightChanges: { classPropertyName: "highlightChanges", publicName: "highlightChanges", isSignal: true, isRequired: false, transformFunction: null }, previousContent: { classPropertyName: "previousContent", publicName: "previousContent", isSignal: true, isRequired: false, transformFunction: null }, changeHighlightFadeMs: { classPropertyName: "changeHighlightFadeMs", publicName: "changeHighlightFadeMs", isSignal: true, isRequired: false, transformFunction: null }, scrollToFirstChange: { classPropertyName: "scrollToFirstChange", publicName: "scrollToFirstChange", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { linkClick: "linkClick", rendered: "rendered", changesHighlighted: "changesHighlighted" }, ngImport: i0, template: "<div [class]=\"containerClasses()\" (click)=\"onLinkClick($event)\">\n @if (highlightChanges()) {\n <div class=\"lc-markdown__sr-only\" role=\"status\" aria-live=\"polite\">{{ changeSummary() }}</div>\n }\n @for (part of renderParts(); track part.index) {\n @if (part.type === 'html') {\n <div [innerHTML]=\"part.safeHtml\"></div>\n } @else if (part.type === 'code') {\n <lc-code-block\n [code]=\"part.code!\"\n [language]=\"part.lang ?? 'text'\"\n [showLineNumbers]=\"true\"\n [showCopy]=\"true\"\n />\n } @else {\n @if (mermaidSvgFor(part.index); as svg) {\n <div class=\"lc-markdown__mermaid\" [innerHTML]=\"svg\"></div>\n } @else if (mermaidErrorFor(part.index); as errorMessage) {\n <div class=\"lc-markdown__mermaid-fallback\">\n <p class=\"lc-markdown__mermaid-error\">{{ errorMessage }}</p>\n <lc-code-block\n [code]=\"part.code!\"\n [language]=\"'text'\"\n [showLineNumbers]=\"true\"\n [showCopy]=\"true\"\n />\n </div>\n } @else {\n <div class=\"lc-markdown__mermaid-loading\">Rendering Mermaid diagram...</div>\n }\n }\n }\n</div>\n", styles: [".lc-markdown{color:var(--color-text-primary);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-primary);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-surface-hover);color:var(--color-text-primary);padding:.125em .375em;border-radius:var(--border-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-surface-2);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 ul{list-style:disc}.lc-markdown ol{list-style:decimal}.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(--border-radius-md, .375rem)}.lc-markdown del{text-decoration:line-through;color:var(--color-text-secondary, #6b7280)}.lc-markdown em{font-style:italic}.lc-markdown__table-wrap{margin:1em 0;overflow-x:auto;-webkit-overflow-scrolling:touch}.lc-markdown__table{width:100%;border-collapse:collapse;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-surface-2);font-weight:var(--font-weight-semibold, 600)}.lc-markdown__table tr:hover td{background-color:var(--color-surface-2)}.lc-markdown__cell--left{text-align:left}.lc-markdown__cell--center{text-align:center}.lc-markdown__cell--right{text-align:right}.lc-markdown__task-item{list-style:none;display:flex;align-items:flex-start;gap:.5em}.lc-markdown__checkbox{flex:0 0 auto;box-sizing:border-box;width:1.05em;height:1.05em;margin-top:.18em;border:1.5px solid var(--color-border-strong, var(--color-border));border-radius:3px;background-color:var(--color-surface);position:relative}.lc-markdown__checkbox--checked{background-color:var(--color-primary);border-color:var(--color-primary)}.lc-markdown__checkbox--checked:after{content:\"\";position:absolute;left:.32em;top:.12em;width:.28em;height:.55em;border:solid var(--color-on-primary, #fff);border-width:0 2px 2px 0;transform:rotate(45deg)}.lc-markdown__task-label{flex:1 1 auto;min-width:0}.lc-markdown__anchor{color:var(--color-text-tertiary);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 lc-code-block code{background:none;color:inherit;padding:0;border-radius:0;font-size:inherit}.lc-markdown__mermaid{margin:1em 0;padding:.75rem;border:1px solid var(--color-divider, #e5e7eb);border-radius:var(--border-radius-md, .375rem);background:var(--color-surface, #ffffff);overflow-x:auto}.lc-markdown__mermaid svg{display:block;margin:0 auto;max-width:100%;height:auto}.lc-markdown__mermaid-loading{margin:1em 0;padding:.75rem;border:1px dashed var(--color-divider, #e5e7eb);border-radius:var(--border-radius-md, .375rem);color:var(--color-text-secondary, #6b7280);font-size:var(--font-size-sm, .875rem)}.lc-markdown__mermaid-fallback{margin:1em 0}.lc-markdown__mermaid-error{margin:0 0 .5rem;color:var(--color-error);font-size:var(--font-size-sm, .875rem)}.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}.lc-markdown--chat{font-size:inherit;line-height:1.4}.lc-markdown--chat p,.lc-markdown--chat ul,.lc-markdown--chat ol,.lc-markdown--chat pre,.lc-markdown--chat blockquote,.lc-markdown--chat table{margin:0 0 6px}.lc-markdown--chat h1,.lc-markdown--chat h2,.lc-markdown--chat h3,.lc-markdown--chat h4,.lc-markdown--chat h5,.lc-markdown--chat h6{margin:8px 0 4px;line-height:1.25}.lc-markdown--chat h1{font-size:1.25rem}.lc-markdown--chat h2{font-size:1.125rem}.lc-markdown--chat h3{font-size:1rem}.lc-markdown--chat h4,.lc-markdown--chat h5,.lc-markdown--chat h6{font-size:.9375rem}.lc-markdown--chat li{margin:0;line-height:1.4}.lc-markdown--chat ul,.lc-markdown--chat ol{padding-left:1.25em}.lc-markdown--chat blockquote{padding:.25em .75em}.lc-markdown--chat lc-code-block{margin:6px 0}.lc-markdown--chat hr{margin:8px 0}.lc-markdown--chat .lc-markdown__mermaid,.lc-markdown--chat .lc-markdown__mermaid-loading,.lc-markdown--chat .lc-markdown__mermaid-fallback{margin:6px 0}.lc-markdown--chat>*:first-child{margin-top:0}.lc-markdown--chat>*:last-child{margin-bottom:0}.lc-markdown__block--changed{position:relative;padding:.4rem .875rem;border-radius:0 6px 6px 0;background-color:color-mix(in srgb,var(--color-primary) 13%,var(--color-surface));transition:background-color .42s ease,padding .42s ease}.lc-markdown__block--changed:before{content:\"\";position:absolute;inset-block:0;left:0;width:3px;border-radius:0 3px 3px 0;background-color:var(--color-primary);transition:opacity .42s ease}.lc-markdown--faded .lc-markdown__block--changed{padding:0;background-color:transparent}.lc-markdown--faded .lc-markdown__block--changed:before{opacity:0}.lc-markdown__sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"], dependencies: [{ kind: "component", type: CodeBlockComponent, selector: "lc-code-block", inputs: ["code", "language", "filename", "showLineNumbers", "showCopy", "showHeader"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
12057
12352
  }
12058
12353
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: MarkdownComponent, decorators: [{
12059
12354
  type: Component,
12060
- args: [{ selector: 'lc-markdown', standalone: true, imports: [CodeBlockComponent], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div [class]=\"containerClasses()\" (click)=\"onLinkClick($event)\">\n @if (highlightChanges()) {\n <div class=\"lc-markdown__sr-only\" role=\"status\" aria-live=\"polite\">{{ changeSummary() }}</div>\n }\n @for (part of renderParts(); track part.index) {\n @if (part.type === 'html') {\n <div [innerHTML]=\"part.safeHtml\"></div>\n } @else if (part.type === 'code') {\n <lc-code-block\n [code]=\"part.code!\"\n [language]=\"part.lang ?? 'text'\"\n [showLineNumbers]=\"true\"\n [showCopy]=\"true\"\n />\n } @else {\n @if (mermaidSvgFor(part.index); as svg) {\n <div class=\"lc-markdown__mermaid\" [innerHTML]=\"svg\"></div>\n } @else if (mermaidErrorFor(part.index); as errorMessage) {\n <div class=\"lc-markdown__mermaid-fallback\">\n <p class=\"lc-markdown__mermaid-error\">{{ errorMessage }}</p>\n <lc-code-block\n [code]=\"part.code!\"\n [language]=\"'text'\"\n [showLineNumbers]=\"true\"\n [showCopy]=\"true\"\n />\n </div>\n } @else {\n <div class=\"lc-markdown__mermaid-loading\">Rendering Mermaid diagram...</div>\n }\n }\n }\n</div>\n", styles: [".lc-markdown{color:var(--color-text-primary);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-primary);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-surface-hover);color:var(--color-text-primary);padding:.125em .375em;border-radius:var(--border-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-surface-2);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 ul{list-style:disc}.lc-markdown ol{list-style:decimal}.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(--border-radius-md, .375rem)}.lc-markdown del{text-decoration:line-through;color:var(--color-text-secondary, #6b7280)}.lc-markdown em{font-style:italic}.lc-markdown__table-wrap{margin:1em 0;overflow-x:auto;-webkit-overflow-scrolling:touch}.lc-markdown__table{width:100%;border-collapse:collapse;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-surface-2);font-weight:var(--font-weight-semibold, 600)}.lc-markdown__table tr:hover td{background-color:var(--color-surface-2)}.lc-markdown__cell--left{text-align:left}.lc-markdown__cell--center{text-align:center}.lc-markdown__cell--right{text-align:right}.lc-markdown__task-item{list-style:none;display:flex;align-items:flex-start;gap:.5em}.lc-markdown__checkbox{flex:0 0 auto;box-sizing:border-box;width:1.05em;height:1.05em;margin-top:.18em;border:1.5px solid var(--color-border-strong, var(--color-border));border-radius:3px;background-color:var(--color-surface);position:relative}.lc-markdown__checkbox--checked{background-color:var(--color-primary);border-color:var(--color-primary)}.lc-markdown__checkbox--checked:after{content:\"\";position:absolute;left:.32em;top:.12em;width:.28em;height:.55em;border:solid var(--color-on-primary, #fff);border-width:0 2px 2px 0;transform:rotate(45deg)}.lc-markdown__task-label{flex:1 1 auto;min-width:0}.lc-markdown__anchor{color:var(--color-text-tertiary);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 lc-code-block code{background:none;color:inherit;padding:0;border-radius:0;font-size:inherit}.lc-markdown__mermaid{margin:1em 0;padding:.75rem;border:1px solid var(--color-divider, #e5e7eb);border-radius:var(--border-radius-md, .375rem);background:var(--color-surface, #ffffff);overflow-x:auto}.lc-markdown__mermaid svg{display:block;margin:0 auto;max-width:100%;height:auto}.lc-markdown__mermaid-loading{margin:1em 0;padding:.75rem;border:1px dashed var(--color-divider, #e5e7eb);border-radius:var(--border-radius-md, .375rem);color:var(--color-text-secondary, #6b7280);font-size:var(--font-size-sm, .875rem)}.lc-markdown__mermaid-fallback{margin:1em 0}.lc-markdown__mermaid-error{margin:0 0 .5rem;color:var(--color-error);font-size:var(--font-size-sm, .875rem)}.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}.lc-markdown--chat{font-size:inherit;line-height:1.4}.lc-markdown--chat p,.lc-markdown--chat ul,.lc-markdown--chat ol,.lc-markdown--chat pre,.lc-markdown--chat blockquote,.lc-markdown--chat table{margin:0 0 6px}.lc-markdown--chat h1,.lc-markdown--chat h2,.lc-markdown--chat h3,.lc-markdown--chat h4,.lc-markdown--chat h5,.lc-markdown--chat h6{margin:8px 0 4px;line-height:1.25}.lc-markdown--chat h1{font-size:1.25rem}.lc-markdown--chat h2{font-size:1.125rem}.lc-markdown--chat h3{font-size:1rem}.lc-markdown--chat h4,.lc-markdown--chat h5,.lc-markdown--chat h6{font-size:.9375rem}.lc-markdown--chat li{margin:0;line-height:1.4}.lc-markdown--chat ul,.lc-markdown--chat ol{padding-left:1.25em}.lc-markdown--chat blockquote{padding:.25em .75em}.lc-markdown--chat lc-code-block{margin:6px 0}.lc-markdown--chat .lc-markdown__mermaid,.lc-markdown--chat .lc-markdown__mermaid-loading,.lc-markdown--chat .lc-markdown__mermaid-fallback{margin:6px 0}.lc-markdown--chat>*:first-child{margin-top:0}.lc-markdown--chat>*:last-child{margin-bottom:0}.lc-markdown__block--changed{position:relative;padding:.4rem .875rem;border-radius:0 6px 6px 0;background-color:color-mix(in srgb,var(--color-primary) 13%,var(--color-surface));transition:background-color .42s ease,padding .42s ease}.lc-markdown__block--changed:before{content:\"\";position:absolute;inset-block:0;left:0;width:3px;border-radius:0 3px 3px 0;background-color:var(--color-primary);transition:opacity .42s ease}.lc-markdown--faded .lc-markdown__block--changed{padding:0;background-color:transparent}.lc-markdown--faded .lc-markdown__block--changed:before{opacity:0}.lc-markdown__sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"] }]
12355
+ args: [{ selector: 'lc-markdown', standalone: true, imports: [CodeBlockComponent], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: "<div [class]=\"containerClasses()\" (click)=\"onLinkClick($event)\">\n @if (highlightChanges()) {\n <div class=\"lc-markdown__sr-only\" role=\"status\" aria-live=\"polite\">{{ changeSummary() }}</div>\n }\n @for (part of renderParts(); track part.index) {\n @if (part.type === 'html') {\n <div [innerHTML]=\"part.safeHtml\"></div>\n } @else if (part.type === 'code') {\n <lc-code-block\n [code]=\"part.code!\"\n [language]=\"part.lang ?? 'text'\"\n [showLineNumbers]=\"true\"\n [showCopy]=\"true\"\n />\n } @else {\n @if (mermaidSvgFor(part.index); as svg) {\n <div class=\"lc-markdown__mermaid\" [innerHTML]=\"svg\"></div>\n } @else if (mermaidErrorFor(part.index); as errorMessage) {\n <div class=\"lc-markdown__mermaid-fallback\">\n <p class=\"lc-markdown__mermaid-error\">{{ errorMessage }}</p>\n <lc-code-block\n [code]=\"part.code!\"\n [language]=\"'text'\"\n [showLineNumbers]=\"true\"\n [showCopy]=\"true\"\n />\n </div>\n } @else {\n <div class=\"lc-markdown__mermaid-loading\">Rendering Mermaid diagram...</div>\n }\n }\n }\n</div>\n", styles: [".lc-markdown{color:var(--color-text-primary);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-primary);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-surface-hover);color:var(--color-text-primary);padding:.125em .375em;border-radius:var(--border-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-surface-2);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 ul{list-style:disc}.lc-markdown ol{list-style:decimal}.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(--border-radius-md, .375rem)}.lc-markdown del{text-decoration:line-through;color:var(--color-text-secondary, #6b7280)}.lc-markdown em{font-style:italic}.lc-markdown__table-wrap{margin:1em 0;overflow-x:auto;-webkit-overflow-scrolling:touch}.lc-markdown__table{width:100%;border-collapse:collapse;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-surface-2);font-weight:var(--font-weight-semibold, 600)}.lc-markdown__table tr:hover td{background-color:var(--color-surface-2)}.lc-markdown__cell--left{text-align:left}.lc-markdown__cell--center{text-align:center}.lc-markdown__cell--right{text-align:right}.lc-markdown__task-item{list-style:none;display:flex;align-items:flex-start;gap:.5em}.lc-markdown__checkbox{flex:0 0 auto;box-sizing:border-box;width:1.05em;height:1.05em;margin-top:.18em;border:1.5px solid var(--color-border-strong, var(--color-border));border-radius:3px;background-color:var(--color-surface);position:relative}.lc-markdown__checkbox--checked{background-color:var(--color-primary);border-color:var(--color-primary)}.lc-markdown__checkbox--checked:after{content:\"\";position:absolute;left:.32em;top:.12em;width:.28em;height:.55em;border:solid var(--color-on-primary, #fff);border-width:0 2px 2px 0;transform:rotate(45deg)}.lc-markdown__task-label{flex:1 1 auto;min-width:0}.lc-markdown__anchor{color:var(--color-text-tertiary);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 lc-code-block code{background:none;color:inherit;padding:0;border-radius:0;font-size:inherit}.lc-markdown__mermaid{margin:1em 0;padding:.75rem;border:1px solid var(--color-divider, #e5e7eb);border-radius:var(--border-radius-md, .375rem);background:var(--color-surface, #ffffff);overflow-x:auto}.lc-markdown__mermaid svg{display:block;margin:0 auto;max-width:100%;height:auto}.lc-markdown__mermaid-loading{margin:1em 0;padding:.75rem;border:1px dashed var(--color-divider, #e5e7eb);border-radius:var(--border-radius-md, .375rem);color:var(--color-text-secondary, #6b7280);font-size:var(--font-size-sm, .875rem)}.lc-markdown__mermaid-fallback{margin:1em 0}.lc-markdown__mermaid-error{margin:0 0 .5rem;color:var(--color-error);font-size:var(--font-size-sm, .875rem)}.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}.lc-markdown--chat{font-size:inherit;line-height:1.4}.lc-markdown--chat p,.lc-markdown--chat ul,.lc-markdown--chat ol,.lc-markdown--chat pre,.lc-markdown--chat blockquote,.lc-markdown--chat table{margin:0 0 6px}.lc-markdown--chat h1,.lc-markdown--chat h2,.lc-markdown--chat h3,.lc-markdown--chat h4,.lc-markdown--chat h5,.lc-markdown--chat h6{margin:8px 0 4px;line-height:1.25}.lc-markdown--chat h1{font-size:1.25rem}.lc-markdown--chat h2{font-size:1.125rem}.lc-markdown--chat h3{font-size:1rem}.lc-markdown--chat h4,.lc-markdown--chat h5,.lc-markdown--chat h6{font-size:.9375rem}.lc-markdown--chat li{margin:0;line-height:1.4}.lc-markdown--chat ul,.lc-markdown--chat ol{padding-left:1.25em}.lc-markdown--chat blockquote{padding:.25em .75em}.lc-markdown--chat lc-code-block{margin:6px 0}.lc-markdown--chat hr{margin:8px 0}.lc-markdown--chat .lc-markdown__mermaid,.lc-markdown--chat .lc-markdown__mermaid-loading,.lc-markdown--chat .lc-markdown__mermaid-fallback{margin:6px 0}.lc-markdown--chat>*:first-child{margin-top:0}.lc-markdown--chat>*:last-child{margin-bottom:0}.lc-markdown__block--changed{position:relative;padding:.4rem .875rem;border-radius:0 6px 6px 0;background-color:color-mix(in srgb,var(--color-primary) 13%,var(--color-surface));transition:background-color .42s ease,padding .42s ease}.lc-markdown__block--changed:before{content:\"\";position:absolute;inset-block:0;left:0;width:3px;border-radius:0 3px 3px 0;background-color:var(--color-primary);transition:opacity .42s ease}.lc-markdown--faded .lc-markdown__block--changed{padding:0;background-color:transparent}.lc-markdown--faded .lc-markdown__block--changed:before{opacity:0}.lc-markdown__sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"] }]
12061
12356
  }], 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 }] }], highlightChanges: [{ type: i0.Input, args: [{ isSignal: true, alias: "highlightChanges", required: false }] }], previousContent: [{ type: i0.Input, args: [{ isSignal: true, alias: "previousContent", required: false }] }], changeHighlightFadeMs: [{ type: i0.Input, args: [{ isSignal: true, alias: "changeHighlightFadeMs", required: false }] }], scrollToFirstChange: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollToFirstChange", required: false }] }], linkClick: [{ type: i0.Output, args: ["linkClick"] }], rendered: [{ type: i0.Output, args: ["rendered"] }], changesHighlighted: [{ type: i0.Output, args: ["changesHighlighted"] }] } });
12062
12357
 
12063
12358
  /**
@@ -13273,13 +13568,16 @@ function directGeometry(source, target, dir) {
13273
13568
  const p3 = { x: target.x, y: target.y + target.height / 2 };
13274
13569
  const mx = (p0.x + p3.x) / 2;
13275
13570
  const points = [p0, { x: mx, y: p0.y }, { x: mx, y: p3.y }, p3];
13276
- return { path: cubicPath(points), labelX: mx, labelY: (p0.y + p3.y) / 2 - 8, points };
13571
+ // labelY is the curve midpoint, not a baseline offset: the template centres the
13572
+ // text on it via `dominant-baseline`. Nudging it up instead (as this did) pushes
13573
+ // the label out of a 24px gutter and into the node above it.
13574
+ return { path: cubicPath(points), labelX: mx, labelY: (p0.y + p3.y) / 2, points };
13277
13575
  }
13278
13576
  const p0 = { x: source.x + source.width / 2, y: source.y + source.height };
13279
13577
  const p3 = { x: target.x + target.width / 2, y: target.y };
13280
13578
  const my = (p0.y + p3.y) / 2;
13281
13579
  const points = [p0, { x: p0.x, y: my }, { x: p3.x, y: my }, p3];
13282
- return { path: cubicPath(points), labelX: (p0.x + p3.x) / 2, labelY: my - 8, points };
13580
+ return { path: cubicPath(points), labelX: (p0.x + p3.x) / 2, labelY: my, points };
13283
13581
  }
13284
13582
  /**
13285
13583
  * Leaves from one side of both boxes and bows clear of everything in `obstacles`,
@@ -13805,11 +14103,11 @@ class DependencyViewerComponent {
13805
14103
  return typeColor ? readableInk(typeColor) : STATUS_COLORS[node.status].text;
13806
14104
  }
13807
14105
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DependencyViewerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13808
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: DependencyViewerComponent, isStandalone: true, selector: "lc-dependency-viewer", inputs: { root: { classPropertyName: "root", publicName: "root", isSignal: true, isRequired: true, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, showEdgeLabels: { classPropertyName: "showEdgeLabels", publicName: "showEdgeLabels", isSignal: true, isRequired: false, transformFunction: null }, edgeWidth: { classPropertyName: "edgeWidth", publicName: "edgeWidth", isSignal: true, isRequired: false, transformFunction: null }, anchorNodeId: { classPropertyName: "anchorNodeId", publicName: "anchorNodeId", isSignal: true, isRequired: false, transformFunction: null }, typeColors: { classPropertyName: "typeColors", publicName: "typeColors", isSignal: true, isRequired: false, transformFunction: null }, hiddenRelations: { classPropertyName: "hiddenRelations", publicName: "hiddenRelations", isSignal: true, isRequired: false, transformFunction: null }, hiddenTypes: { classPropertyName: "hiddenTypes", publicName: "hiddenTypes", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { nodeSelect: "nodeSelect", nodeExpand: "nodeExpand" }, host: { listeners: { "document:mousemove": "onMouseMove($event)", "document:mouseup": "onMouseUp()" }, properties: { "class": "\"dep-viewer\"", "style.height": "height()" } }, ngImport: i0, template: "<!-- Toolbar -->\n@if (showToolbar()) {\n <div class=\"dep-viewer__toolbar\">\n <button class=\"dep-viewer__btn\" (click)=\"zoomIn()\" title=\"Zoom in\">+</button>\n <span class=\"dep-viewer__zoom\">{{ zoom() }}%</span>\n <button class=\"dep-viewer__btn\" (click)=\"zoomOut()\" title=\"Zoom out\">\u2212</button>\n <button class=\"dep-viewer__btn dep-viewer__btn--reset\" (click)=\"resetZoom()\" title=\"Reset\">\u27F2</button>\n <span class=\"dep-viewer__direction-label\">{{ direction() === 'horizontal' ? '\u2192' : '\u2193' }}</span>\n </div>\n}\n\n<!-- Canvas -->\n<div\n class=\"dep-viewer__canvas\"\n (mousedown)=\"onMouseDown($event)\"\n (wheel)=\"onWheel($event)\"\n (click)=\"deselectNode()\"\n>\n <svg\n [attr.width]=\"svgWidth()\"\n [attr.height]=\"svgHeight()\"\n [attr.viewBox]=\"viewBox()\"\n [style.transform]=\"transform()\"\n class=\"dep-viewer__svg\"\n >\n <!-- Arrow markers for cross-reference edges -->\n <defs>\n <marker id=\"arrow-default\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-neutral-400)\"/></marker>\n <marker id=\"arrow-blocks\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-error-default)\"/></marker>\n <marker id=\"arrow-references\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-info-default, #3b82f6)\"/></marker>\n <marker id=\"arrow-requires\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-warning-default)\"/></marker>\n <marker id=\"arrow-extends\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-primary-400)\"/></marker>\n <marker id=\"arrow-implements\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-success-default)\"/></marker>\n <marker id=\"arrow-uses\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-neutral-300)\"/></marker>\n <marker id=\"arrow-depends\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-neutral-400)\"/></marker>\n </defs>\n\n <!-- Edges -->\n @for (edge of layout().edges; track edge.id) {\n <path\n [attr.d]=\"edge.path\"\n [attr.stroke]=\"edge.color\"\n [attr.stroke-width]=\"edgeWidth()\"\n [attr.stroke-dasharray]=\"edge.dashed ? '6 3' : 'none'\"\n [attr.marker-end]=\"edge.isCrossRef ? 'url(#arrow-' + edge.marker + ')' : ''\"\n fill=\"none\"\n class=\"dep-viewer__edge\"\n [class.dep-viewer__edge--cross-ref]=\"edge.isCrossRef\"\n />\n @if (showEdgeLabels() && edge.isCrossRef && edge.label) {\n <text\n [attr.x]=\"edge.labelX\"\n [attr.y]=\"edge.labelY\"\n class=\"dep-viewer__edge-label\"\n [attr.fill]=\"edge.color\"\n text-anchor=\"middle\"\n >{{ edge.label }}</text>\n }\n }\n\n <!-- Nodes -->\n @for (node of layout().nodes; track node.id) {\n <g\n class=\"dep-viewer__node\"\n [class.dep-viewer__node--selected]=\"selectedNodeId() === node.id\"\n (click)=\"selectNode(node.id, $event)\"\n (dblclick)=\"expandNode(node.id, $event)\"\n >\n <!-- Opaque backdrop. The status fills are translucent tints (in the dark\n theme --color-success-50 & co. resolve to rgba(\u2026, 0.18)), so without\n something solid beneath them the edges routed under a node show\n straight through it. This keeps the tint compositing against the\n viewer's own surface instead of against whatever passes behind. -->\n <rect\n [attr.x]=\"node.x\"\n [attr.y]=\"node.y\"\n [attr.width]=\"node.width\"\n [attr.height]=\"node.height\"\n rx=\"6\" ry=\"6\"\n class=\"dep-viewer__node-backdrop\"\n />\n <rect\n class=\"dep-viewer__node-fill\"\n [attr.x]=\"node.x\"\n [attr.y]=\"node.y\"\n [attr.width]=\"node.width\"\n [attr.height]=\"node.height\"\n rx=\"6\" ry=\"6\"\n [attr.fill]=\"getNodeBg(node)\"\n [attr.stroke]=\"selectedNodeId() === node.id ? 'var(--color-primary-500)' : getNodeBorder(node)\"\n stroke-width=\"1.5\"\n />\n <text\n [attr.x]=\"node.x + node.width / 2\"\n [attr.y]=\"node.y + node.height / 2 + 4\"\n text-anchor=\"middle\"\n [attr.fill]=\"getNodeText(node)\"\n class=\"dep-viewer__node-label\"\n >{{ node.label }}</text>\n\n <!-- \"+N\" marker: this node has more neighbours than are shown -->\n @if (node.moreCount) {\n <g class=\"dep-viewer__more\">\n <rect\n [attr.x]=\"node.x + node.width - 26\"\n [attr.y]=\"node.y - 8\"\n width=\"32\" height=\"16\" rx=\"8\" ry=\"8\"\n />\n <text\n [attr.x]=\"node.x + node.width - 10\"\n [attr.y]=\"node.y + 3\"\n text-anchor=\"middle\"\n >+{{ node.moreCount }}</text>\n </g>\n }\n\n <!-- Collapse/expand toggle -->\n @if (hasChildren(node.id)) {\n <g\n class=\"dep-viewer__toggle\"\n (click)=\"toggleCollapse(node.id, $event)\"\n >\n @if (direction() === 'horizontal') {\n <circle\n [attr.cx]=\"node.x + node.width + 10\"\n [attr.cy]=\"node.y + node.height / 2\"\n r=\"8\"\n fill=\"var(--color-neutral-100)\" stroke=\"var(--color-neutral-300)\" stroke-width=\"1\"\n />\n <text\n [attr.x]=\"node.x + node.width + 10\"\n [attr.y]=\"node.y + node.height / 2 + 4\"\n text-anchor=\"middle\"\n class=\"dep-viewer__toggle-text\"\n >{{ isCollapsed(node.id) ? '+' : '\u2212' }}</text>\n } @else {\n <circle\n [attr.cx]=\"node.x + node.width / 2\"\n [attr.cy]=\"node.y + node.height + 10\"\n r=\"8\"\n fill=\"var(--color-neutral-100)\" stroke=\"var(--color-neutral-300)\" stroke-width=\"1\"\n />\n <text\n [attr.x]=\"node.x + node.width / 2\"\n [attr.y]=\"node.y + node.height + 14\"\n text-anchor=\"middle\"\n class=\"dep-viewer__toggle-text\"\n >{{ isCollapsed(node.id) ? '+' : '\u2212' }}</text>\n }\n </g>\n }\n </g>\n }\n </svg>\n</div>\n\n<!-- Legend -->\n@if (legendItems().length || typeLegendItems().length) {\n <div class=\"dep-viewer__legend\">\n @for (item of legendItems(); track item.label) {\n <span class=\"dep-viewer__legend-item\">\n <svg width=\"24\" height=\"10\">\n <line\n x1=\"0\" y1=\"5\" x2=\"24\" y2=\"5\"\n [attr.stroke]=\"item.color\"\n stroke-width=\"2\"\n [attr.stroke-dasharray]=\"item.dashed ? '4 2' : 'none'\"\n />\n </svg>\n <span class=\"dep-viewer__legend-text\">{{ item.label }}</span>\n </span>\n }\n @for (item of typeLegendItems(); track item.type) {\n <span class=\"dep-viewer__legend-item\">\n <svg width=\"10\" height=\"10\">\n <rect width=\"10\" height=\"10\" rx=\"2\" ry=\"2\" [attr.fill]=\"item.color\" />\n </svg>\n <span class=\"dep-viewer__legend-text\">{{ item.type }}</span>\n </span>\n }\n </div>\n}\n\n<!-- Detail panel -->\n@if (selectedNode(); as node) {\n <div class=\"dep-viewer__detail\">\n <div class=\"dep-viewer__detail-header\">\n <span class=\"dep-viewer__detail-status dep-viewer__detail-status--{{ node.status }}\">\u25CF</span>\n <strong>{{ node.label }}</strong>\n @if (node.type) {\n <span class=\"dep-viewer__detail-type\">{{ node.type }}</span>\n }\n </div>\n @if (node.description) {\n <p class=\"dep-viewer__detail-desc\">{{ node.description }}</p>\n }\n <div class=\"dep-viewer__detail-meta\">\n <span>Depth: {{ node.depth }}</span>\n <span>Children: {{ node.outgoingCount }}</span>\n <span>Depends on: {{ node.incomingCount }}</span>\n </div>\n @if (selectedDependsOn().length) {\n <div class=\"dep-viewer__detail-deps\">\n <strong>Dependencies:</strong>\n <ul>\n @for (dep of selectedDependsOn(); track dep.id) {\n <li>\n <span class=\"dep-viewer__dep-relation\">{{\n dep.relationLabel || getRelationLabel(dep.relation || 'depends')\n }}</span>\n \u2192 {{ dep.id }}\n </li>\n }\n </ul>\n </div>\n }\n </div>\n}\n", styles: [":host{display:block;position:relative;overflow:hidden;border:1px solid var(--color-border);border-radius:8px;background:var(--color-surface)}.dep-viewer__toolbar{display:flex;align-items:center;gap:4px;padding:8px 12px;border-bottom:1px solid var(--color-border);background:var(--color-surface-2);-webkit-user-select:none;user-select:none}.dep-viewer__btn{width:28px;height:28px;display:flex;align-items:center;justify-content:center;border:1px solid var(--color-border);border-radius:4px;background:var(--color-surface);color:var(--color-text-secondary);cursor:pointer;font-size:14px;line-height:1}.dep-viewer__btn:hover{background:var(--color-surface-hover)}.dep-viewer__btn--reset{margin-left:4px}.dep-viewer__zoom{font-size:12px;min-width:40px;text-align:center;color:var(--color-text-secondary)}.dep-viewer__direction-label{margin-left:8px;font-size:14px;color:var(--color-text-tertiary)}.dep-viewer__canvas{cursor:grab;overflow:hidden;width:100%;height:100%}.dep-viewer__canvas:active{cursor:grabbing}.dep-viewer__svg{transform-origin:0 0;transition:transform .1s ease-out}.dep-viewer__edge{pointer-events:none;transition:opacity .15s}.dep-viewer__edge--cross-ref{opacity:.8}.dep-viewer__edge-label{font-size:9px;font-weight:500;pointer-events:none;-webkit-user-select:none;user-select:none}.dep-viewer__node{cursor:pointer;transition:filter .15s}.dep-viewer__node:hover{filter:brightness(.96)}.dep-viewer__node--selected rect{stroke-width:2.5}.dep-viewer__node-backdrop{fill:var(--color-surface)}.dep-viewer__node-label{font-size:12px;font-weight:500;pointer-events:none;-webkit-user-select:none;user-select:none}.dep-viewer__toggle{cursor:pointer}.dep-viewer__toggle:hover circle{fill:var(--color-surface-hover)}.dep-viewer__more{pointer-events:none}.dep-viewer__more rect{fill:var(--color-surface-2);stroke:var(--color-border-strong);stroke-width:1}.dep-viewer__more text{font-size:9px;font-weight:600;fill:var(--color-text-secondary);-webkit-user-select:none;user-select:none}.dep-viewer__toggle-text{font-size:11px;font-weight:600;fill:var(--color-text-secondary);pointer-events:none;-webkit-user-select:none;user-select:none}.dep-viewer__legend{display:flex;flex-wrap:wrap;gap:12px;padding:6px 12px;border-top:1px solid var(--color-border);background:var(--color-surface-2)}.dep-viewer__legend-item{display:flex;align-items:center;gap:4px}.dep-viewer__legend-text{font-size:11px;color:var(--color-text-secondary)}.dep-viewer__detail{position:absolute;bottom:12px;right:12px;width:240px;padding:12px;background:var(--color-surface-2);border:1px solid var(--color-border);border-radius:8px;box-shadow:var(--elevation-1);font-size:12px;z-index:10}.dep-viewer__detail-header{display:flex;align-items:center;gap:6px;margin-bottom:4px;font-size:13px}.dep-viewer__detail-type{margin-left:auto;padding:1px 6px;border-radius:3px;background:var(--color-surface-hover);color:var(--color-text-secondary);font-size:10px;font-weight:500}.dep-viewer__detail-status{font-size:10px}.dep-viewer__detail-status--default{color:var(--color-text-tertiary)}.dep-viewer__detail-status--active{color:var(--color-primary-400)}.dep-viewer__detail-status--success{color:var(--color-success-default)}.dep-viewer__detail-status--warning{color:var(--color-warning-default)}.dep-viewer__detail-status--error{color:var(--color-error-default)}.dep-viewer__detail-status--muted{color:var(--color-text-disabled)}.dep-viewer__detail-desc{color:var(--color-text-secondary);margin:0 0 6px;line-height:1.4}.dep-viewer__detail-meta{display:flex;flex-wrap:wrap;gap:8px;color:var(--color-text-tertiary);font-size:11px}.dep-viewer__detail-deps{margin-top:8px;padding-top:8px;border-top:1px solid var(--color-border)}.dep-viewer__detail-deps ul{list-style:none;margin:4px 0 0;padding:0}.dep-viewer__detail-deps li{padding:2px 0;color:var(--color-text-secondary)}.dep-viewer__dep-relation{display:inline-block;padding:1px 4px;background:var(--color-surface-hover);border-radius:3px;font-size:10px;font-weight:500;color:var(--color-text-secondary)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14106
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: DependencyViewerComponent, isStandalone: true, selector: "lc-dependency-viewer", inputs: { root: { classPropertyName: "root", publicName: "root", isSignal: true, isRequired: true, transformFunction: null }, direction: { classPropertyName: "direction", publicName: "direction", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, showEdgeLabels: { classPropertyName: "showEdgeLabels", publicName: "showEdgeLabels", isSignal: true, isRequired: false, transformFunction: null }, edgeWidth: { classPropertyName: "edgeWidth", publicName: "edgeWidth", isSignal: true, isRequired: false, transformFunction: null }, anchorNodeId: { classPropertyName: "anchorNodeId", publicName: "anchorNodeId", isSignal: true, isRequired: false, transformFunction: null }, typeColors: { classPropertyName: "typeColors", publicName: "typeColors", isSignal: true, isRequired: false, transformFunction: null }, hiddenRelations: { classPropertyName: "hiddenRelations", publicName: "hiddenRelations", isSignal: true, isRequired: false, transformFunction: null }, hiddenTypes: { classPropertyName: "hiddenTypes", publicName: "hiddenTypes", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { nodeSelect: "nodeSelect", nodeExpand: "nodeExpand" }, host: { listeners: { "document:mousemove": "onMouseMove($event)", "document:mouseup": "onMouseUp()" }, properties: { "class": "\"dep-viewer\"", "style.height": "height()" } }, ngImport: i0, template: "<!-- Toolbar -->\n@if (showToolbar()) {\n <div class=\"dep-viewer__toolbar\">\n <button class=\"dep-viewer__btn\" (click)=\"zoomIn()\" title=\"Zoom in\">+</button>\n <span class=\"dep-viewer__zoom\">{{ zoom() }}%</span>\n <button class=\"dep-viewer__btn\" (click)=\"zoomOut()\" title=\"Zoom out\">\u2212</button>\n <button class=\"dep-viewer__btn dep-viewer__btn--reset\" (click)=\"resetZoom()\" title=\"Reset\">\u27F2</button>\n <span class=\"dep-viewer__direction-label\">{{ direction() === 'horizontal' ? '\u2192' : '\u2193' }}</span>\n </div>\n}\n\n<!-- Canvas -->\n<div\n class=\"dep-viewer__canvas\"\n (mousedown)=\"onMouseDown($event)\"\n (wheel)=\"onWheel($event)\"\n (click)=\"deselectNode()\"\n>\n <svg\n [attr.width]=\"svgWidth()\"\n [attr.height]=\"svgHeight()\"\n [attr.viewBox]=\"viewBox()\"\n [style.transform]=\"transform()\"\n class=\"dep-viewer__svg\"\n >\n <!-- Arrow markers for cross-reference edges -->\n <defs>\n <marker id=\"arrow-default\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-neutral-400)\"/></marker>\n <marker id=\"arrow-blocks\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-error-default)\"/></marker>\n <marker id=\"arrow-references\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-info-default, #3b82f6)\"/></marker>\n <marker id=\"arrow-requires\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-warning-default)\"/></marker>\n <marker id=\"arrow-extends\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-primary-400)\"/></marker>\n <marker id=\"arrow-implements\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-success-default)\"/></marker>\n <marker id=\"arrow-uses\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-neutral-300)\"/></marker>\n <marker id=\"arrow-depends\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-neutral-400)\"/></marker>\n </defs>\n\n <!-- Edges. Their labels are deliberately NOT drawn here \u2014 see below. -->\n @for (edge of layout().edges; track edge.id) {\n <path\n [attr.d]=\"edge.path\"\n [attr.stroke]=\"edge.color\"\n [attr.stroke-width]=\"edgeWidth()\"\n [attr.stroke-dasharray]=\"edge.dashed ? '6 3' : 'none'\"\n [attr.marker-end]=\"edge.isCrossRef ? 'url(#arrow-' + edge.marker + ')' : ''\"\n fill=\"none\"\n class=\"dep-viewer__edge\"\n [class.dep-viewer__edge--cross-ref]=\"edge.isCrossRef\"\n />\n }\n\n <!-- Nodes -->\n @for (node of layout().nodes; track node.id) {\n <g\n class=\"dep-viewer__node\"\n [class.dep-viewer__node--selected]=\"selectedNodeId() === node.id\"\n (click)=\"selectNode(node.id, $event)\"\n (dblclick)=\"expandNode(node.id, $event)\"\n >\n <!-- Opaque backdrop. The status fills are translucent tints (in the dark\n theme --color-success-50 & co. resolve to rgba(\u2026, 0.18)), so without\n something solid beneath them the edges routed under a node show\n straight through it. This keeps the tint compositing against the\n viewer's own surface instead of against whatever passes behind. -->\n <rect\n [attr.x]=\"node.x\"\n [attr.y]=\"node.y\"\n [attr.width]=\"node.width\"\n [attr.height]=\"node.height\"\n rx=\"6\" ry=\"6\"\n class=\"dep-viewer__node-backdrop\"\n />\n <rect\n class=\"dep-viewer__node-fill\"\n [attr.x]=\"node.x\"\n [attr.y]=\"node.y\"\n [attr.width]=\"node.width\"\n [attr.height]=\"node.height\"\n rx=\"6\" ry=\"6\"\n [attr.fill]=\"getNodeBg(node)\"\n [attr.stroke]=\"selectedNodeId() === node.id ? 'var(--color-primary-500)' : getNodeBorder(node)\"\n stroke-width=\"1.5\"\n />\n <text\n [attr.x]=\"node.x + node.width / 2\"\n [attr.y]=\"node.y + node.height / 2 + 4\"\n text-anchor=\"middle\"\n [attr.fill]=\"getNodeText(node)\"\n class=\"dep-viewer__node-label\"\n >{{ node.label }}</text>\n\n <!-- \"+N\" marker: this node has more neighbours than are shown -->\n @if (node.moreCount) {\n <g class=\"dep-viewer__more\">\n <rect\n [attr.x]=\"node.x + node.width - 26\"\n [attr.y]=\"node.y - 8\"\n width=\"32\" height=\"16\" rx=\"8\" ry=\"8\"\n />\n <text\n [attr.x]=\"node.x + node.width - 10\"\n [attr.y]=\"node.y + 3\"\n text-anchor=\"middle\"\n >+{{ node.moreCount }}</text>\n </g>\n }\n\n <!-- Collapse/expand toggle -->\n @if (hasChildren(node.id)) {\n <g\n class=\"dep-viewer__toggle\"\n (click)=\"toggleCollapse(node.id, $event)\"\n >\n @if (direction() === 'horizontal') {\n <circle\n [attr.cx]=\"node.x + node.width + 10\"\n [attr.cy]=\"node.y + node.height / 2\"\n r=\"8\"\n fill=\"var(--color-neutral-100)\" stroke=\"var(--color-neutral-300)\" stroke-width=\"1\"\n />\n <text\n [attr.x]=\"node.x + node.width + 10\"\n [attr.y]=\"node.y + node.height / 2 + 4\"\n text-anchor=\"middle\"\n class=\"dep-viewer__toggle-text\"\n >{{ isCollapsed(node.id) ? '+' : '\u2212' }}</text>\n } @else {\n <circle\n [attr.cx]=\"node.x + node.width / 2\"\n [attr.cy]=\"node.y + node.height + 10\"\n r=\"8\"\n fill=\"var(--color-neutral-100)\" stroke=\"var(--color-neutral-300)\" stroke-width=\"1\"\n />\n <text\n [attr.x]=\"node.x + node.width / 2\"\n [attr.y]=\"node.y + node.height + 14\"\n text-anchor=\"middle\"\n class=\"dep-viewer__toggle-text\"\n >{{ isCollapsed(node.id) ? '+' : '\u2212' }}</text>\n }\n </g>\n }\n </g>\n }\n\n <!-- Edge labels last: SVG paints in document order, so anything drawn before\n the nodes is covered by them. A label sits at the midpoint of its edge,\n which frequently lands on a node \u2014 it has to come after. The halo (see\n the stylesheet) keeps it legible over whatever it lands on. -->\n @if (showEdgeLabels()) {\n @for (edge of layout().edges; track edge.id) {\n @if (edge.isCrossRef && edge.label) {\n <text\n [attr.x]=\"edge.labelX\"\n [attr.y]=\"edge.labelY\"\n class=\"dep-viewer__edge-label\"\n [attr.fill]=\"edge.color\"\n text-anchor=\"middle\"\n >{{ edge.label }}</text>\n }\n }\n }\n </svg>\n</div>\n\n<!-- Legend -->\n@if (legendItems().length || typeLegendItems().length) {\n <div class=\"dep-viewer__legend\">\n @for (item of legendItems(); track item.label) {\n <span class=\"dep-viewer__legend-item\">\n <svg width=\"24\" height=\"10\">\n <line\n x1=\"0\" y1=\"5\" x2=\"24\" y2=\"5\"\n [attr.stroke]=\"item.color\"\n stroke-width=\"2\"\n [attr.stroke-dasharray]=\"item.dashed ? '4 2' : 'none'\"\n />\n </svg>\n <span class=\"dep-viewer__legend-text\">{{ item.label }}</span>\n </span>\n }\n @for (item of typeLegendItems(); track item.type) {\n <span class=\"dep-viewer__legend-item\">\n <svg width=\"10\" height=\"10\">\n <rect width=\"10\" height=\"10\" rx=\"2\" ry=\"2\" [attr.fill]=\"item.color\" />\n </svg>\n <span class=\"dep-viewer__legend-text\">{{ item.type }}</span>\n </span>\n }\n </div>\n}\n\n<!-- Detail panel -->\n@if (selectedNode(); as node) {\n <div class=\"dep-viewer__detail\">\n <div class=\"dep-viewer__detail-header\">\n <span class=\"dep-viewer__detail-status dep-viewer__detail-status--{{ node.status }}\">\u25CF</span>\n <strong>{{ node.label }}</strong>\n @if (node.type) {\n <span class=\"dep-viewer__detail-type\">{{ node.type }}</span>\n }\n </div>\n @if (node.description) {\n <p class=\"dep-viewer__detail-desc\">{{ node.description }}</p>\n }\n <div class=\"dep-viewer__detail-meta\">\n <span>Depth: {{ node.depth }}</span>\n <span>Children: {{ node.outgoingCount }}</span>\n <span>Depends on: {{ node.incomingCount }}</span>\n </div>\n @if (selectedDependsOn().length) {\n <div class=\"dep-viewer__detail-deps\">\n <strong>Dependencies:</strong>\n <ul>\n @for (dep of selectedDependsOn(); track dep.id) {\n <li>\n <span class=\"dep-viewer__dep-relation\">{{\n dep.relationLabel || getRelationLabel(dep.relation || 'depends')\n }}</span>\n \u2192 {{ dep.id }}\n </li>\n }\n </ul>\n </div>\n }\n </div>\n}\n", styles: [":host{display:block;position:relative;overflow:hidden;border:1px solid var(--color-border);border-radius:8px;background:var(--color-surface)}.dep-viewer__toolbar{display:flex;align-items:center;gap:4px;padding:8px 12px;border-bottom:1px solid var(--color-border);background:var(--color-surface-2);-webkit-user-select:none;user-select:none}.dep-viewer__btn{width:28px;height:28px;display:flex;align-items:center;justify-content:center;border:1px solid var(--color-border);border-radius:4px;background:var(--color-surface);color:var(--color-text-secondary);cursor:pointer;font-size:14px;line-height:1}.dep-viewer__btn:hover{background:var(--color-surface-hover)}.dep-viewer__btn--reset{margin-left:4px}.dep-viewer__zoom{font-size:12px;min-width:40px;text-align:center;color:var(--color-text-secondary)}.dep-viewer__direction-label{margin-left:8px;font-size:14px;color:var(--color-text-tertiary)}.dep-viewer__canvas{cursor:grab;overflow:hidden;width:100%;height:100%}.dep-viewer__canvas:active{cursor:grabbing}.dep-viewer__svg{transform-origin:0 0;transition:transform .1s ease-out}.dep-viewer__edge{pointer-events:none;transition:opacity .15s}.dep-viewer__edge--cross-ref{opacity:.8}.dep-viewer__edge-label{font-size:9px;font-weight:500;pointer-events:none;-webkit-user-select:none;user-select:none;dominant-baseline:central;paint-order:stroke;stroke:var(--color-surface);stroke-width:3px;stroke-linejoin:round}.dep-viewer__node{cursor:pointer;transition:filter .15s}.dep-viewer__node:hover{filter:brightness(.96)}.dep-viewer__node--selected rect{stroke-width:2.5}.dep-viewer__node-backdrop{fill:var(--color-surface)}.dep-viewer__node-label{font-size:12px;font-weight:500;pointer-events:none;-webkit-user-select:none;user-select:none}.dep-viewer__toggle{cursor:pointer}.dep-viewer__toggle:hover circle{fill:var(--color-surface-hover)}.dep-viewer__more{pointer-events:none}.dep-viewer__more rect{fill:var(--color-surface-2);stroke:var(--color-border-strong);stroke-width:1}.dep-viewer__more text{font-size:9px;font-weight:600;fill:var(--color-text-secondary);-webkit-user-select:none;user-select:none}.dep-viewer__toggle-text{font-size:11px;font-weight:600;fill:var(--color-text-secondary);pointer-events:none;-webkit-user-select:none;user-select:none}.dep-viewer__legend{display:flex;flex-wrap:wrap;gap:12px;padding:6px 12px;border-top:1px solid var(--color-border);background:var(--color-surface-2)}.dep-viewer__legend-item{display:flex;align-items:center;gap:4px}.dep-viewer__legend-text{font-size:11px;color:var(--color-text-secondary)}.dep-viewer__detail{position:absolute;bottom:12px;right:12px;width:240px;padding:12px;background:var(--color-surface-2);border:1px solid var(--color-border);border-radius:8px;box-shadow:var(--elevation-1);font-size:12px;z-index:10}.dep-viewer__detail-header{display:flex;align-items:center;gap:6px;margin-bottom:4px;font-size:13px}.dep-viewer__detail-type{margin-left:auto;padding:1px 6px;border-radius:3px;background:var(--color-surface-hover);color:var(--color-text-secondary);font-size:10px;font-weight:500}.dep-viewer__detail-status{font-size:10px}.dep-viewer__detail-status--default{color:var(--color-text-tertiary)}.dep-viewer__detail-status--active{color:var(--color-primary-400)}.dep-viewer__detail-status--success{color:var(--color-success-default)}.dep-viewer__detail-status--warning{color:var(--color-warning-default)}.dep-viewer__detail-status--error{color:var(--color-error-default)}.dep-viewer__detail-status--muted{color:var(--color-text-disabled)}.dep-viewer__detail-desc{color:var(--color-text-secondary);margin:0 0 6px;line-height:1.4}.dep-viewer__detail-meta{display:flex;flex-wrap:wrap;gap:8px;color:var(--color-text-tertiary);font-size:11px}.dep-viewer__detail-deps{margin-top:8px;padding-top:8px;border-top:1px solid var(--color-border)}.dep-viewer__detail-deps ul{list-style:none;margin:4px 0 0;padding:0}.dep-viewer__detail-deps li{padding:2px 0;color:var(--color-text-secondary)}.dep-viewer__dep-relation{display:inline-block;padding:1px 4px;background:var(--color-surface-hover);border-radius:3px;font-size:10px;font-weight:500;color:var(--color-text-secondary)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
13809
14107
  }
13810
14108
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: DependencyViewerComponent, decorators: [{
13811
14109
  type: Component,
13812
- args: [{ selector: 'lc-dependency-viewer', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, host: { '[class]': '"dep-viewer"', '[style.height]': 'height()' }, template: "<!-- Toolbar -->\n@if (showToolbar()) {\n <div class=\"dep-viewer__toolbar\">\n <button class=\"dep-viewer__btn\" (click)=\"zoomIn()\" title=\"Zoom in\">+</button>\n <span class=\"dep-viewer__zoom\">{{ zoom() }}%</span>\n <button class=\"dep-viewer__btn\" (click)=\"zoomOut()\" title=\"Zoom out\">\u2212</button>\n <button class=\"dep-viewer__btn dep-viewer__btn--reset\" (click)=\"resetZoom()\" title=\"Reset\">\u27F2</button>\n <span class=\"dep-viewer__direction-label\">{{ direction() === 'horizontal' ? '\u2192' : '\u2193' }}</span>\n </div>\n}\n\n<!-- Canvas -->\n<div\n class=\"dep-viewer__canvas\"\n (mousedown)=\"onMouseDown($event)\"\n (wheel)=\"onWheel($event)\"\n (click)=\"deselectNode()\"\n>\n <svg\n [attr.width]=\"svgWidth()\"\n [attr.height]=\"svgHeight()\"\n [attr.viewBox]=\"viewBox()\"\n [style.transform]=\"transform()\"\n class=\"dep-viewer__svg\"\n >\n <!-- Arrow markers for cross-reference edges -->\n <defs>\n <marker id=\"arrow-default\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-neutral-400)\"/></marker>\n <marker id=\"arrow-blocks\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-error-default)\"/></marker>\n <marker id=\"arrow-references\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-info-default, #3b82f6)\"/></marker>\n <marker id=\"arrow-requires\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-warning-default)\"/></marker>\n <marker id=\"arrow-extends\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-primary-400)\"/></marker>\n <marker id=\"arrow-implements\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-success-default)\"/></marker>\n <marker id=\"arrow-uses\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-neutral-300)\"/></marker>\n <marker id=\"arrow-depends\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-neutral-400)\"/></marker>\n </defs>\n\n <!-- Edges -->\n @for (edge of layout().edges; track edge.id) {\n <path\n [attr.d]=\"edge.path\"\n [attr.stroke]=\"edge.color\"\n [attr.stroke-width]=\"edgeWidth()\"\n [attr.stroke-dasharray]=\"edge.dashed ? '6 3' : 'none'\"\n [attr.marker-end]=\"edge.isCrossRef ? 'url(#arrow-' + edge.marker + ')' : ''\"\n fill=\"none\"\n class=\"dep-viewer__edge\"\n [class.dep-viewer__edge--cross-ref]=\"edge.isCrossRef\"\n />\n @if (showEdgeLabels() && edge.isCrossRef && edge.label) {\n <text\n [attr.x]=\"edge.labelX\"\n [attr.y]=\"edge.labelY\"\n class=\"dep-viewer__edge-label\"\n [attr.fill]=\"edge.color\"\n text-anchor=\"middle\"\n >{{ edge.label }}</text>\n }\n }\n\n <!-- Nodes -->\n @for (node of layout().nodes; track node.id) {\n <g\n class=\"dep-viewer__node\"\n [class.dep-viewer__node--selected]=\"selectedNodeId() === node.id\"\n (click)=\"selectNode(node.id, $event)\"\n (dblclick)=\"expandNode(node.id, $event)\"\n >\n <!-- Opaque backdrop. The status fills are translucent tints (in the dark\n theme --color-success-50 & co. resolve to rgba(\u2026, 0.18)), so without\n something solid beneath them the edges routed under a node show\n straight through it. This keeps the tint compositing against the\n viewer's own surface instead of against whatever passes behind. -->\n <rect\n [attr.x]=\"node.x\"\n [attr.y]=\"node.y\"\n [attr.width]=\"node.width\"\n [attr.height]=\"node.height\"\n rx=\"6\" ry=\"6\"\n class=\"dep-viewer__node-backdrop\"\n />\n <rect\n class=\"dep-viewer__node-fill\"\n [attr.x]=\"node.x\"\n [attr.y]=\"node.y\"\n [attr.width]=\"node.width\"\n [attr.height]=\"node.height\"\n rx=\"6\" ry=\"6\"\n [attr.fill]=\"getNodeBg(node)\"\n [attr.stroke]=\"selectedNodeId() === node.id ? 'var(--color-primary-500)' : getNodeBorder(node)\"\n stroke-width=\"1.5\"\n />\n <text\n [attr.x]=\"node.x + node.width / 2\"\n [attr.y]=\"node.y + node.height / 2 + 4\"\n text-anchor=\"middle\"\n [attr.fill]=\"getNodeText(node)\"\n class=\"dep-viewer__node-label\"\n >{{ node.label }}</text>\n\n <!-- \"+N\" marker: this node has more neighbours than are shown -->\n @if (node.moreCount) {\n <g class=\"dep-viewer__more\">\n <rect\n [attr.x]=\"node.x + node.width - 26\"\n [attr.y]=\"node.y - 8\"\n width=\"32\" height=\"16\" rx=\"8\" ry=\"8\"\n />\n <text\n [attr.x]=\"node.x + node.width - 10\"\n [attr.y]=\"node.y + 3\"\n text-anchor=\"middle\"\n >+{{ node.moreCount }}</text>\n </g>\n }\n\n <!-- Collapse/expand toggle -->\n @if (hasChildren(node.id)) {\n <g\n class=\"dep-viewer__toggle\"\n (click)=\"toggleCollapse(node.id, $event)\"\n >\n @if (direction() === 'horizontal') {\n <circle\n [attr.cx]=\"node.x + node.width + 10\"\n [attr.cy]=\"node.y + node.height / 2\"\n r=\"8\"\n fill=\"var(--color-neutral-100)\" stroke=\"var(--color-neutral-300)\" stroke-width=\"1\"\n />\n <text\n [attr.x]=\"node.x + node.width + 10\"\n [attr.y]=\"node.y + node.height / 2 + 4\"\n text-anchor=\"middle\"\n class=\"dep-viewer__toggle-text\"\n >{{ isCollapsed(node.id) ? '+' : '\u2212' }}</text>\n } @else {\n <circle\n [attr.cx]=\"node.x + node.width / 2\"\n [attr.cy]=\"node.y + node.height + 10\"\n r=\"8\"\n fill=\"var(--color-neutral-100)\" stroke=\"var(--color-neutral-300)\" stroke-width=\"1\"\n />\n <text\n [attr.x]=\"node.x + node.width / 2\"\n [attr.y]=\"node.y + node.height + 14\"\n text-anchor=\"middle\"\n class=\"dep-viewer__toggle-text\"\n >{{ isCollapsed(node.id) ? '+' : '\u2212' }}</text>\n }\n </g>\n }\n </g>\n }\n </svg>\n</div>\n\n<!-- Legend -->\n@if (legendItems().length || typeLegendItems().length) {\n <div class=\"dep-viewer__legend\">\n @for (item of legendItems(); track item.label) {\n <span class=\"dep-viewer__legend-item\">\n <svg width=\"24\" height=\"10\">\n <line\n x1=\"0\" y1=\"5\" x2=\"24\" y2=\"5\"\n [attr.stroke]=\"item.color\"\n stroke-width=\"2\"\n [attr.stroke-dasharray]=\"item.dashed ? '4 2' : 'none'\"\n />\n </svg>\n <span class=\"dep-viewer__legend-text\">{{ item.label }}</span>\n </span>\n }\n @for (item of typeLegendItems(); track item.type) {\n <span class=\"dep-viewer__legend-item\">\n <svg width=\"10\" height=\"10\">\n <rect width=\"10\" height=\"10\" rx=\"2\" ry=\"2\" [attr.fill]=\"item.color\" />\n </svg>\n <span class=\"dep-viewer__legend-text\">{{ item.type }}</span>\n </span>\n }\n </div>\n}\n\n<!-- Detail panel -->\n@if (selectedNode(); as node) {\n <div class=\"dep-viewer__detail\">\n <div class=\"dep-viewer__detail-header\">\n <span class=\"dep-viewer__detail-status dep-viewer__detail-status--{{ node.status }}\">\u25CF</span>\n <strong>{{ node.label }}</strong>\n @if (node.type) {\n <span class=\"dep-viewer__detail-type\">{{ node.type }}</span>\n }\n </div>\n @if (node.description) {\n <p class=\"dep-viewer__detail-desc\">{{ node.description }}</p>\n }\n <div class=\"dep-viewer__detail-meta\">\n <span>Depth: {{ node.depth }}</span>\n <span>Children: {{ node.outgoingCount }}</span>\n <span>Depends on: {{ node.incomingCount }}</span>\n </div>\n @if (selectedDependsOn().length) {\n <div class=\"dep-viewer__detail-deps\">\n <strong>Dependencies:</strong>\n <ul>\n @for (dep of selectedDependsOn(); track dep.id) {\n <li>\n <span class=\"dep-viewer__dep-relation\">{{\n dep.relationLabel || getRelationLabel(dep.relation || 'depends')\n }}</span>\n \u2192 {{ dep.id }}\n </li>\n }\n </ul>\n </div>\n }\n </div>\n}\n", styles: [":host{display:block;position:relative;overflow:hidden;border:1px solid var(--color-border);border-radius:8px;background:var(--color-surface)}.dep-viewer__toolbar{display:flex;align-items:center;gap:4px;padding:8px 12px;border-bottom:1px solid var(--color-border);background:var(--color-surface-2);-webkit-user-select:none;user-select:none}.dep-viewer__btn{width:28px;height:28px;display:flex;align-items:center;justify-content:center;border:1px solid var(--color-border);border-radius:4px;background:var(--color-surface);color:var(--color-text-secondary);cursor:pointer;font-size:14px;line-height:1}.dep-viewer__btn:hover{background:var(--color-surface-hover)}.dep-viewer__btn--reset{margin-left:4px}.dep-viewer__zoom{font-size:12px;min-width:40px;text-align:center;color:var(--color-text-secondary)}.dep-viewer__direction-label{margin-left:8px;font-size:14px;color:var(--color-text-tertiary)}.dep-viewer__canvas{cursor:grab;overflow:hidden;width:100%;height:100%}.dep-viewer__canvas:active{cursor:grabbing}.dep-viewer__svg{transform-origin:0 0;transition:transform .1s ease-out}.dep-viewer__edge{pointer-events:none;transition:opacity .15s}.dep-viewer__edge--cross-ref{opacity:.8}.dep-viewer__edge-label{font-size:9px;font-weight:500;pointer-events:none;-webkit-user-select:none;user-select:none}.dep-viewer__node{cursor:pointer;transition:filter .15s}.dep-viewer__node:hover{filter:brightness(.96)}.dep-viewer__node--selected rect{stroke-width:2.5}.dep-viewer__node-backdrop{fill:var(--color-surface)}.dep-viewer__node-label{font-size:12px;font-weight:500;pointer-events:none;-webkit-user-select:none;user-select:none}.dep-viewer__toggle{cursor:pointer}.dep-viewer__toggle:hover circle{fill:var(--color-surface-hover)}.dep-viewer__more{pointer-events:none}.dep-viewer__more rect{fill:var(--color-surface-2);stroke:var(--color-border-strong);stroke-width:1}.dep-viewer__more text{font-size:9px;font-weight:600;fill:var(--color-text-secondary);-webkit-user-select:none;user-select:none}.dep-viewer__toggle-text{font-size:11px;font-weight:600;fill:var(--color-text-secondary);pointer-events:none;-webkit-user-select:none;user-select:none}.dep-viewer__legend{display:flex;flex-wrap:wrap;gap:12px;padding:6px 12px;border-top:1px solid var(--color-border);background:var(--color-surface-2)}.dep-viewer__legend-item{display:flex;align-items:center;gap:4px}.dep-viewer__legend-text{font-size:11px;color:var(--color-text-secondary)}.dep-viewer__detail{position:absolute;bottom:12px;right:12px;width:240px;padding:12px;background:var(--color-surface-2);border:1px solid var(--color-border);border-radius:8px;box-shadow:var(--elevation-1);font-size:12px;z-index:10}.dep-viewer__detail-header{display:flex;align-items:center;gap:6px;margin-bottom:4px;font-size:13px}.dep-viewer__detail-type{margin-left:auto;padding:1px 6px;border-radius:3px;background:var(--color-surface-hover);color:var(--color-text-secondary);font-size:10px;font-weight:500}.dep-viewer__detail-status{font-size:10px}.dep-viewer__detail-status--default{color:var(--color-text-tertiary)}.dep-viewer__detail-status--active{color:var(--color-primary-400)}.dep-viewer__detail-status--success{color:var(--color-success-default)}.dep-viewer__detail-status--warning{color:var(--color-warning-default)}.dep-viewer__detail-status--error{color:var(--color-error-default)}.dep-viewer__detail-status--muted{color:var(--color-text-disabled)}.dep-viewer__detail-desc{color:var(--color-text-secondary);margin:0 0 6px;line-height:1.4}.dep-viewer__detail-meta{display:flex;flex-wrap:wrap;gap:8px;color:var(--color-text-tertiary);font-size:11px}.dep-viewer__detail-deps{margin-top:8px;padding-top:8px;border-top:1px solid var(--color-border)}.dep-viewer__detail-deps ul{list-style:none;margin:4px 0 0;padding:0}.dep-viewer__detail-deps li{padding:2px 0;color:var(--color-text-secondary)}.dep-viewer__dep-relation{display:inline-block;padding:1px 4px;background:var(--color-surface-hover);border-radius:3px;font-size:10px;font-weight:500;color:var(--color-text-secondary)}\n"] }]
14110
+ args: [{ selector: 'lc-dependency-viewer', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, host: { '[class]': '"dep-viewer"', '[style.height]': 'height()' }, template: "<!-- Toolbar -->\n@if (showToolbar()) {\n <div class=\"dep-viewer__toolbar\">\n <button class=\"dep-viewer__btn\" (click)=\"zoomIn()\" title=\"Zoom in\">+</button>\n <span class=\"dep-viewer__zoom\">{{ zoom() }}%</span>\n <button class=\"dep-viewer__btn\" (click)=\"zoomOut()\" title=\"Zoom out\">\u2212</button>\n <button class=\"dep-viewer__btn dep-viewer__btn--reset\" (click)=\"resetZoom()\" title=\"Reset\">\u27F2</button>\n <span class=\"dep-viewer__direction-label\">{{ direction() === 'horizontal' ? '\u2192' : '\u2193' }}</span>\n </div>\n}\n\n<!-- Canvas -->\n<div\n class=\"dep-viewer__canvas\"\n (mousedown)=\"onMouseDown($event)\"\n (wheel)=\"onWheel($event)\"\n (click)=\"deselectNode()\"\n>\n <svg\n [attr.width]=\"svgWidth()\"\n [attr.height]=\"svgHeight()\"\n [attr.viewBox]=\"viewBox()\"\n [style.transform]=\"transform()\"\n class=\"dep-viewer__svg\"\n >\n <!-- Arrow markers for cross-reference edges -->\n <defs>\n <marker id=\"arrow-default\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-neutral-400)\"/></marker>\n <marker id=\"arrow-blocks\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-error-default)\"/></marker>\n <marker id=\"arrow-references\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-info-default, #3b82f6)\"/></marker>\n <marker id=\"arrow-requires\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-warning-default)\"/></marker>\n <marker id=\"arrow-extends\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-primary-400)\"/></marker>\n <marker id=\"arrow-implements\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-success-default)\"/></marker>\n <marker id=\"arrow-uses\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-neutral-300)\"/></marker>\n <marker id=\"arrow-depends\" markerWidth=\"8\" markerHeight=\"6\" refX=\"8\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L8,3 L0,6 Z\" fill=\"var(--color-neutral-400)\"/></marker>\n </defs>\n\n <!-- Edges. Their labels are deliberately NOT drawn here \u2014 see below. -->\n @for (edge of layout().edges; track edge.id) {\n <path\n [attr.d]=\"edge.path\"\n [attr.stroke]=\"edge.color\"\n [attr.stroke-width]=\"edgeWidth()\"\n [attr.stroke-dasharray]=\"edge.dashed ? '6 3' : 'none'\"\n [attr.marker-end]=\"edge.isCrossRef ? 'url(#arrow-' + edge.marker + ')' : ''\"\n fill=\"none\"\n class=\"dep-viewer__edge\"\n [class.dep-viewer__edge--cross-ref]=\"edge.isCrossRef\"\n />\n }\n\n <!-- Nodes -->\n @for (node of layout().nodes; track node.id) {\n <g\n class=\"dep-viewer__node\"\n [class.dep-viewer__node--selected]=\"selectedNodeId() === node.id\"\n (click)=\"selectNode(node.id, $event)\"\n (dblclick)=\"expandNode(node.id, $event)\"\n >\n <!-- Opaque backdrop. The status fills are translucent tints (in the dark\n theme --color-success-50 & co. resolve to rgba(\u2026, 0.18)), so without\n something solid beneath them the edges routed under a node show\n straight through it. This keeps the tint compositing against the\n viewer's own surface instead of against whatever passes behind. -->\n <rect\n [attr.x]=\"node.x\"\n [attr.y]=\"node.y\"\n [attr.width]=\"node.width\"\n [attr.height]=\"node.height\"\n rx=\"6\" ry=\"6\"\n class=\"dep-viewer__node-backdrop\"\n />\n <rect\n class=\"dep-viewer__node-fill\"\n [attr.x]=\"node.x\"\n [attr.y]=\"node.y\"\n [attr.width]=\"node.width\"\n [attr.height]=\"node.height\"\n rx=\"6\" ry=\"6\"\n [attr.fill]=\"getNodeBg(node)\"\n [attr.stroke]=\"selectedNodeId() === node.id ? 'var(--color-primary-500)' : getNodeBorder(node)\"\n stroke-width=\"1.5\"\n />\n <text\n [attr.x]=\"node.x + node.width / 2\"\n [attr.y]=\"node.y + node.height / 2 + 4\"\n text-anchor=\"middle\"\n [attr.fill]=\"getNodeText(node)\"\n class=\"dep-viewer__node-label\"\n >{{ node.label }}</text>\n\n <!-- \"+N\" marker: this node has more neighbours than are shown -->\n @if (node.moreCount) {\n <g class=\"dep-viewer__more\">\n <rect\n [attr.x]=\"node.x + node.width - 26\"\n [attr.y]=\"node.y - 8\"\n width=\"32\" height=\"16\" rx=\"8\" ry=\"8\"\n />\n <text\n [attr.x]=\"node.x + node.width - 10\"\n [attr.y]=\"node.y + 3\"\n text-anchor=\"middle\"\n >+{{ node.moreCount }}</text>\n </g>\n }\n\n <!-- Collapse/expand toggle -->\n @if (hasChildren(node.id)) {\n <g\n class=\"dep-viewer__toggle\"\n (click)=\"toggleCollapse(node.id, $event)\"\n >\n @if (direction() === 'horizontal') {\n <circle\n [attr.cx]=\"node.x + node.width + 10\"\n [attr.cy]=\"node.y + node.height / 2\"\n r=\"8\"\n fill=\"var(--color-neutral-100)\" stroke=\"var(--color-neutral-300)\" stroke-width=\"1\"\n />\n <text\n [attr.x]=\"node.x + node.width + 10\"\n [attr.y]=\"node.y + node.height / 2 + 4\"\n text-anchor=\"middle\"\n class=\"dep-viewer__toggle-text\"\n >{{ isCollapsed(node.id) ? '+' : '\u2212' }}</text>\n } @else {\n <circle\n [attr.cx]=\"node.x + node.width / 2\"\n [attr.cy]=\"node.y + node.height + 10\"\n r=\"8\"\n fill=\"var(--color-neutral-100)\" stroke=\"var(--color-neutral-300)\" stroke-width=\"1\"\n />\n <text\n [attr.x]=\"node.x + node.width / 2\"\n [attr.y]=\"node.y + node.height + 14\"\n text-anchor=\"middle\"\n class=\"dep-viewer__toggle-text\"\n >{{ isCollapsed(node.id) ? '+' : '\u2212' }}</text>\n }\n </g>\n }\n </g>\n }\n\n <!-- Edge labels last: SVG paints in document order, so anything drawn before\n the nodes is covered by them. A label sits at the midpoint of its edge,\n which frequently lands on a node \u2014 it has to come after. The halo (see\n the stylesheet) keeps it legible over whatever it lands on. -->\n @if (showEdgeLabels()) {\n @for (edge of layout().edges; track edge.id) {\n @if (edge.isCrossRef && edge.label) {\n <text\n [attr.x]=\"edge.labelX\"\n [attr.y]=\"edge.labelY\"\n class=\"dep-viewer__edge-label\"\n [attr.fill]=\"edge.color\"\n text-anchor=\"middle\"\n >{{ edge.label }}</text>\n }\n }\n }\n </svg>\n</div>\n\n<!-- Legend -->\n@if (legendItems().length || typeLegendItems().length) {\n <div class=\"dep-viewer__legend\">\n @for (item of legendItems(); track item.label) {\n <span class=\"dep-viewer__legend-item\">\n <svg width=\"24\" height=\"10\">\n <line\n x1=\"0\" y1=\"5\" x2=\"24\" y2=\"5\"\n [attr.stroke]=\"item.color\"\n stroke-width=\"2\"\n [attr.stroke-dasharray]=\"item.dashed ? '4 2' : 'none'\"\n />\n </svg>\n <span class=\"dep-viewer__legend-text\">{{ item.label }}</span>\n </span>\n }\n @for (item of typeLegendItems(); track item.type) {\n <span class=\"dep-viewer__legend-item\">\n <svg width=\"10\" height=\"10\">\n <rect width=\"10\" height=\"10\" rx=\"2\" ry=\"2\" [attr.fill]=\"item.color\" />\n </svg>\n <span class=\"dep-viewer__legend-text\">{{ item.type }}</span>\n </span>\n }\n </div>\n}\n\n<!-- Detail panel -->\n@if (selectedNode(); as node) {\n <div class=\"dep-viewer__detail\">\n <div class=\"dep-viewer__detail-header\">\n <span class=\"dep-viewer__detail-status dep-viewer__detail-status--{{ node.status }}\">\u25CF</span>\n <strong>{{ node.label }}</strong>\n @if (node.type) {\n <span class=\"dep-viewer__detail-type\">{{ node.type }}</span>\n }\n </div>\n @if (node.description) {\n <p class=\"dep-viewer__detail-desc\">{{ node.description }}</p>\n }\n <div class=\"dep-viewer__detail-meta\">\n <span>Depth: {{ node.depth }}</span>\n <span>Children: {{ node.outgoingCount }}</span>\n <span>Depends on: {{ node.incomingCount }}</span>\n </div>\n @if (selectedDependsOn().length) {\n <div class=\"dep-viewer__detail-deps\">\n <strong>Dependencies:</strong>\n <ul>\n @for (dep of selectedDependsOn(); track dep.id) {\n <li>\n <span class=\"dep-viewer__dep-relation\">{{\n dep.relationLabel || getRelationLabel(dep.relation || 'depends')\n }}</span>\n \u2192 {{ dep.id }}\n </li>\n }\n </ul>\n </div>\n }\n </div>\n}\n", styles: [":host{display:block;position:relative;overflow:hidden;border:1px solid var(--color-border);border-radius:8px;background:var(--color-surface)}.dep-viewer__toolbar{display:flex;align-items:center;gap:4px;padding:8px 12px;border-bottom:1px solid var(--color-border);background:var(--color-surface-2);-webkit-user-select:none;user-select:none}.dep-viewer__btn{width:28px;height:28px;display:flex;align-items:center;justify-content:center;border:1px solid var(--color-border);border-radius:4px;background:var(--color-surface);color:var(--color-text-secondary);cursor:pointer;font-size:14px;line-height:1}.dep-viewer__btn:hover{background:var(--color-surface-hover)}.dep-viewer__btn--reset{margin-left:4px}.dep-viewer__zoom{font-size:12px;min-width:40px;text-align:center;color:var(--color-text-secondary)}.dep-viewer__direction-label{margin-left:8px;font-size:14px;color:var(--color-text-tertiary)}.dep-viewer__canvas{cursor:grab;overflow:hidden;width:100%;height:100%}.dep-viewer__canvas:active{cursor:grabbing}.dep-viewer__svg{transform-origin:0 0;transition:transform .1s ease-out}.dep-viewer__edge{pointer-events:none;transition:opacity .15s}.dep-viewer__edge--cross-ref{opacity:.8}.dep-viewer__edge-label{font-size:9px;font-weight:500;pointer-events:none;-webkit-user-select:none;user-select:none;dominant-baseline:central;paint-order:stroke;stroke:var(--color-surface);stroke-width:3px;stroke-linejoin:round}.dep-viewer__node{cursor:pointer;transition:filter .15s}.dep-viewer__node:hover{filter:brightness(.96)}.dep-viewer__node--selected rect{stroke-width:2.5}.dep-viewer__node-backdrop{fill:var(--color-surface)}.dep-viewer__node-label{font-size:12px;font-weight:500;pointer-events:none;-webkit-user-select:none;user-select:none}.dep-viewer__toggle{cursor:pointer}.dep-viewer__toggle:hover circle{fill:var(--color-surface-hover)}.dep-viewer__more{pointer-events:none}.dep-viewer__more rect{fill:var(--color-surface-2);stroke:var(--color-border-strong);stroke-width:1}.dep-viewer__more text{font-size:9px;font-weight:600;fill:var(--color-text-secondary);-webkit-user-select:none;user-select:none}.dep-viewer__toggle-text{font-size:11px;font-weight:600;fill:var(--color-text-secondary);pointer-events:none;-webkit-user-select:none;user-select:none}.dep-viewer__legend{display:flex;flex-wrap:wrap;gap:12px;padding:6px 12px;border-top:1px solid var(--color-border);background:var(--color-surface-2)}.dep-viewer__legend-item{display:flex;align-items:center;gap:4px}.dep-viewer__legend-text{font-size:11px;color:var(--color-text-secondary)}.dep-viewer__detail{position:absolute;bottom:12px;right:12px;width:240px;padding:12px;background:var(--color-surface-2);border:1px solid var(--color-border);border-radius:8px;box-shadow:var(--elevation-1);font-size:12px;z-index:10}.dep-viewer__detail-header{display:flex;align-items:center;gap:6px;margin-bottom:4px;font-size:13px}.dep-viewer__detail-type{margin-left:auto;padding:1px 6px;border-radius:3px;background:var(--color-surface-hover);color:var(--color-text-secondary);font-size:10px;font-weight:500}.dep-viewer__detail-status{font-size:10px}.dep-viewer__detail-status--default{color:var(--color-text-tertiary)}.dep-viewer__detail-status--active{color:var(--color-primary-400)}.dep-viewer__detail-status--success{color:var(--color-success-default)}.dep-viewer__detail-status--warning{color:var(--color-warning-default)}.dep-viewer__detail-status--error{color:var(--color-error-default)}.dep-viewer__detail-status--muted{color:var(--color-text-disabled)}.dep-viewer__detail-desc{color:var(--color-text-secondary);margin:0 0 6px;line-height:1.4}.dep-viewer__detail-meta{display:flex;flex-wrap:wrap;gap:8px;color:var(--color-text-tertiary);font-size:11px}.dep-viewer__detail-deps{margin-top:8px;padding-top:8px;border-top:1px solid var(--color-border)}.dep-viewer__detail-deps ul{list-style:none;margin:4px 0 0;padding:0}.dep-viewer__detail-deps li{padding:2px 0;color:var(--color-text-secondary)}.dep-viewer__dep-relation{display:inline-block;padding:1px 4px;background:var(--color-surface-hover);border-radius:3px;font-size:10px;font-weight:500;color:var(--color-text-secondary)}\n"] }]
13813
14111
  }], ctorParameters: () => [], propDecorators: { root: [{ type: i0.Input, args: [{ isSignal: true, alias: "root", required: true }] }], direction: [{ type: i0.Input, args: [{ isSignal: true, alias: "direction", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], showToolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "showToolbar", required: false }] }], showEdgeLabels: [{ type: i0.Input, args: [{ isSignal: true, alias: "showEdgeLabels", required: false }] }], edgeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "edgeWidth", required: false }] }], anchorNodeId: [{ type: i0.Input, args: [{ isSignal: true, alias: "anchorNodeId", required: false }] }], typeColors: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeColors", required: false }] }], hiddenRelations: [{ type: i0.Input, args: [{ isSignal: true, alias: "hiddenRelations", required: false }] }], hiddenTypes: [{ type: i0.Input, args: [{ isSignal: true, alias: "hiddenTypes", required: false }] }], nodeSelect: [{ type: i0.Output, args: ["nodeSelect"] }], nodeExpand: [{ type: i0.Output, args: ["nodeExpand"] }], onMouseMove: [{
13814
14112
  type: HostListener,
13815
14113
  args: ['document:mousemove', ['$event']]
@@ -13964,22 +14262,31 @@ const BADGE_STATUS_COLOR = {
13964
14262
  modified: 'var(--color-warning-default, #d97706)',
13965
14263
  removed: 'var(--color-error-default, #dc2626)',
13966
14264
  muted: 'var(--color-neutral-300)',
14265
+ success: 'var(--color-success-default, #16a34a)',
14266
+ busy: 'var(--color-primary-500, #6366f1)',
13967
14267
  };
13968
14268
  function nodeType(node) {
13969
14269
  return node.type ?? (node.children ? 'folder' : 'file');
13970
14270
  }
14271
+ function isExpandable(node) {
14272
+ return !!node.children?.length;
14273
+ }
13971
14274
  /**
13972
- * Tree view component for visualizing file / folder hierarchies such as a
13973
- * complete GitHub project.
14275
+ * Tree view component for visualizing hierarchies — file / folder trees as
14276
+ * well as arbitrary domain object trees with custom node types.
13974
14277
  *
13975
14278
  * Features:
13976
- * - Recursive folder / file rendering from a single `nodes` input
14279
+ * - Recursive rendering from a single `nodes` input; any node with children
14280
+ * can expand / collapse
13977
14281
  * - Automatic file-type icons by extension and well-known file name,
13978
- * with open / closed folder icons
13979
- * - Expand / collapse folders, with expand-all / collapse-all helpers
14282
+ * with open / closed folder icons (for `file` / `folder` nodes)
14283
+ * - Custom node types: colored type dots or explicit icons via `type`,
14284
+ * `color`, `icon`
14285
+ * - Expand / collapse, with expand-all / collapse-all helpers
13980
14286
  * - Two-way bound selection and a `nodeClick` event
13981
14287
  * - Indentation guide lines for readability
13982
- * - Optional per-node status badges (added / modified / removed)
14288
+ * - Optional per-node status badges (added / modified / removed) and status
14289
+ * indicators (`success` ✓, `busy` pulse)
13983
14290
  * - Keyboard accessible (Enter / Space to toggle or select)
13984
14291
  * - Dark / light theme support via design tokens
13985
14292
  *
@@ -14070,10 +14377,9 @@ class TreeViewComponent {
14070
14377
  const walk = (nodes, parentPath) => {
14071
14378
  for (const node of nodes) {
14072
14379
  const id = node.id ?? `${parentPath}/${node.name}`;
14073
- if (nodeType(node) === 'folder') {
14380
+ if (node.children?.length) {
14074
14381
  next.set(id, value);
14075
- if (node.children)
14076
- walk(node.children, id);
14382
+ walk(node.children, id);
14077
14383
  }
14078
14384
  }
14079
14385
  };
@@ -14090,7 +14396,7 @@ class TreeViewComponent {
14090
14396
  nodes.forEach((node, index) => {
14091
14397
  const id = node.id ?? `${parentPath}/${node.name}`;
14092
14398
  const type = nodeType(node);
14093
- const hasChildren = type === 'folder' && !!node.children?.length;
14399
+ const hasChildren = isExpandable(node);
14094
14400
  const expanded = hasChildren && this.isExpanded(id, node, overrides);
14095
14401
  const isLast = index === nodes.length - 1;
14096
14402
  out.push({
@@ -14101,6 +14407,7 @@ class TreeViewComponent {
14101
14407
  hasChildren,
14102
14408
  expanded,
14103
14409
  icon: this.resolveIcon(node, type, expanded),
14410
+ color: node.color,
14104
14411
  badge: node.badge,
14105
14412
  status: node.status ?? 'default',
14106
14413
  disabled: node.disabled ?? false,
@@ -14117,7 +14424,11 @@ class TreeViewComponent {
14117
14424
  return node.icon;
14118
14425
  if (type === 'folder')
14119
14426
  return expanded ? FOLDER_OPEN_ICON : FOLDER_ICON;
14120
- return node.name ? resolveFileIcon(node.name) : FILE_FALLBACK_ICON;
14427
+ if (type === 'file') {
14428
+ return node.name ? resolveFileIcon(node.name) : FILE_FALLBACK_ICON;
14429
+ }
14430
+ // Custom domain type without explicit icon: render a colored type dot.
14431
+ return null;
14121
14432
  }
14122
14433
  findNode(nodes, parentPath, targetId) {
14123
14434
  for (const node of nodes) {
@@ -14133,11 +14444,11 @@ class TreeViewComponent {
14133
14444
  return null;
14134
14445
  }
14135
14446
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: TreeViewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
14136
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: TreeViewComponent, isStandalone: true, selector: "lc-tree-view", inputs: { nodes: { classPropertyName: "nodes", publicName: "nodes", isSignal: true, isRequired: true, transformFunction: null }, selectedId: { classPropertyName: "selectedId", publicName: "selectedId", isSignal: true, isRequired: false, transformFunction: null }, showGuides: { classPropertyName: "showGuides", publicName: "showGuides", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, iconSize: { classPropertyName: "iconSize", publicName: "iconSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedId: "selectedIdChange", nodeClick: "nodeClick" }, host: { classAttribute: "lc-tree-view" }, ngImport: i0, template: "@if (showToolbar()) {\n <div class=\"lc-tree-view__toolbar\">\n <button type=\"button\" class=\"lc-tree-view__tool-btn\" (click)=\"expandAll()\" title=\"Expand all\">\n <lc-icon name=\"fold-down\" size=\"xs\" [decorative]=\"true\" />\n <span>Expand all</span>\n </button>\n <button type=\"button\" class=\"lc-tree-view__tool-btn\" (click)=\"collapseAll()\" title=\"Collapse all\">\n <lc-icon name=\"fold-up\" size=\"xs\" [decorative]=\"true\" />\n <span>Collapse all</span>\n </button>\n </div>\n}\n\n<ul class=\"lc-tree-view__list\" role=\"tree\">\n @for (node of visibleNodes(); track trackById($index, node)) {\n <li\n class=\"lc-tree-view__item\"\n [class.lc-tree-view__item--selected]=\"node.id === selectedId()\"\n [class.lc-tree-view__item--disabled]=\"node.disabled\"\n [class.lc-tree-view__item--folder]=\"node.type === 'folder'\"\n role=\"treeitem\"\n [attr.aria-level]=\"node.depth + 1\"\n [attr.aria-expanded]=\"node.hasChildren ? node.expanded : null\"\n [attr.aria-selected]=\"node.id === selectedId()\"\n [attr.aria-disabled]=\"node.disabled ? true : null\"\n [tabindex]=\"node.disabled ? -1 : 0\"\n (click)=\"onNodeClick(node)\"\n (keydown)=\"onKeydown(node, $event)\"\n >\n <!-- indentation + guide lines -->\n @for (hasSibling of node.ancestorHasSibling; track $index) {\n <span\n class=\"lc-tree-view__indent\"\n [class.lc-tree-view__indent--guide]=\"showGuides() && hasSibling\"\n ></span>\n }\n\n <!-- expand / collapse chevron -->\n <span class=\"lc-tree-view__chevron\">\n @if (node.hasChildren) {\n <button\n type=\"button\"\n class=\"lc-tree-view__chevron-btn\"\n [class.lc-tree-view__chevron-btn--expanded]=\"node.expanded\"\n (click)=\"onToggleClick(node, $event)\"\n [attr.aria-label]=\"node.expanded ? 'Collapse' : 'Expand'\"\n tabindex=\"-1\"\n >\n <lc-icon name=\"chevron-right\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </span>\n\n <!-- type / file icon -->\n <span\n class=\"lc-tree-view__icon\"\n [style.color]=\"\n node.status === 'default' ? null : badgeColor(node.status)\n \"\n >\n <lc-icon [name]=\"node.icon\" [size]=\"iconSize()\" [decorative]=\"true\" />\n </span>\n\n <!-- label -->\n <span class=\"lc-tree-view__label\">{{ node.name }}</span>\n\n <!-- badge -->\n @if (node.badge) {\n <span\n class=\"lc-tree-view__badge\"\n [style.color]=\"badgeColor(node.status)\"\n [style.border-color]=\"badgeColor(node.status)\"\n >{{ node.badge }}</span\n >\n }\n </li>\n }\n</ul>\n", styles: [":host{display:block;border:1px solid var(--color-neutral-200);border-radius:8px;background:var(--color-surface);overflow:hidden;font-size:13px;color:var(--color-text-primary)}.lc-tree-view__toolbar{display:flex;align-items:center;gap:4px;padding:6px 8px;border-bottom:1px solid var(--color-neutral-200);background:var(--color-neutral-50);-webkit-user-select:none;user-select:none}.lc-tree-view__tool-btn{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;border:1px solid transparent;border-radius:4px;background:transparent;color:var(--color-text-secondary);font-size:12px;cursor:pointer;line-height:1}.lc-tree-view__tool-btn:hover{background:var(--color-neutral-100);color:var(--color-text-primary)}.lc-tree-view__list{list-style:none;margin:0;padding:4px 0}.lc-tree-view__item{display:flex;align-items:center;height:26px;padding-right:8px;cursor:pointer;-webkit-user-select:none;user-select:none;position:relative;outline:none}.lc-tree-view__item:hover{background:var(--color-neutral-100)}.lc-tree-view__item:focus-visible{box-shadow:inset 0 0 0 2px var(--color-primary-400)}.lc-tree-view__item--selected{background:var(--color-primary-50)}.lc-tree-view__item--selected:hover{background:var(--color-primary-100, var(--color-primary-50))}.lc-tree-view__item--selected .lc-tree-view__label{color:var(--color-primary-700);font-weight:500}.lc-tree-view__item--disabled{opacity:.5;cursor:not-allowed}.lc-tree-view__item--disabled:hover{background:transparent}.lc-tree-view__indent{flex:0 0 18px;width:18px;height:100%;position:relative}.lc-tree-view__indent--guide:before{content:\"\";position:absolute;left:50%;top:0;bottom:0;width:1px;background:var(--color-neutral-200)}.lc-tree-view__chevron{flex:0 0 18px;width:18px;display:flex;align-items:center;justify-content:center;color:var(--color-text-tertiary)}.lc-tree-view__chevron-btn{display:flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;border:none;background:transparent;color:inherit;cursor:pointer;border-radius:3px;transition:transform .12s ease}.lc-tree-view__chevron-btn:hover{background:var(--color-neutral-200)}.lc-tree-view__chevron-btn--expanded{transform:rotate(90deg)}.lc-tree-view__icon{display:flex;align-items:center;margin-right:6px;color:var(--color-text-tertiary)}.lc-tree-view__item--folder .lc-tree-view__icon{color:var(--color-primary-500, #6366f1)}.lc-tree-view__label{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--color-text-primary)}.lc-tree-view__badge{flex:0 0 auto;margin-left:8px;padding:1px 6px;border:1px solid;border-radius:10px;font-size:10px;line-height:1.4;font-weight:600;text-transform:uppercase;letter-spacing:.02em}\n"], dependencies: [{ kind: "component", type: IconComponent, selector: "lc-icon", inputs: ["name", "variant", "size", "color", "ariaLabel", "decorative", "strict"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14447
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.10", type: TreeViewComponent, isStandalone: true, selector: "lc-tree-view", inputs: { nodes: { classPropertyName: "nodes", publicName: "nodes", isSignal: true, isRequired: true, transformFunction: null }, selectedId: { classPropertyName: "selectedId", publicName: "selectedId", isSignal: true, isRequired: false, transformFunction: null }, showGuides: { classPropertyName: "showGuides", publicName: "showGuides", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, iconSize: { classPropertyName: "iconSize", publicName: "iconSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedId: "selectedIdChange", nodeClick: "nodeClick" }, host: { classAttribute: "lc-tree-view" }, ngImport: i0, template: "@if (showToolbar()) {\n <div class=\"lc-tree-view__toolbar\">\n <button type=\"button\" class=\"lc-tree-view__tool-btn\" (click)=\"expandAll()\" title=\"Expand all\">\n <lc-icon name=\"fold-down\" size=\"xs\" [decorative]=\"true\" />\n <span>Expand all</span>\n </button>\n <button type=\"button\" class=\"lc-tree-view__tool-btn\" (click)=\"collapseAll()\" title=\"Collapse all\">\n <lc-icon name=\"fold-up\" size=\"xs\" [decorative]=\"true\" />\n <span>Collapse all</span>\n </button>\n </div>\n}\n\n<ul class=\"lc-tree-view__list\" role=\"tree\">\n @for (node of visibleNodes(); track trackById($index, node)) {\n <li\n class=\"lc-tree-view__item\"\n [class.lc-tree-view__item--selected]=\"node.id === selectedId()\"\n [class.lc-tree-view__item--disabled]=\"node.disabled\"\n [class.lc-tree-view__item--folder]=\"node.type === 'folder'\"\n role=\"treeitem\"\n [attr.aria-level]=\"node.depth + 1\"\n [attr.aria-expanded]=\"node.hasChildren ? node.expanded : null\"\n [attr.aria-selected]=\"node.id === selectedId()\"\n [attr.aria-disabled]=\"node.disabled ? true : null\"\n [attr.aria-busy]=\"node.status === 'busy' ? true : null\"\n [tabindex]=\"node.disabled ? -1 : 0\"\n (click)=\"onNodeClick(node)\"\n (keydown)=\"onKeydown(node, $event)\"\n >\n <!-- indentation + guide lines -->\n @for (hasSibling of node.ancestorHasSibling; track $index) {\n <span\n class=\"lc-tree-view__indent\"\n [class.lc-tree-view__indent--guide]=\"showGuides() && hasSibling\"\n ></span>\n }\n\n <!-- expand / collapse chevron -->\n <span class=\"lc-tree-view__chevron\">\n @if (node.hasChildren) {\n <button\n type=\"button\"\n class=\"lc-tree-view__chevron-btn\"\n [class.lc-tree-view__chevron-btn--expanded]=\"node.expanded\"\n (click)=\"onToggleClick(node, $event)\"\n [attr.aria-label]=\"node.expanded ? 'Collapse' : 'Expand'\"\n tabindex=\"-1\"\n >\n <lc-icon name=\"chevron-right\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </span>\n\n <!-- type / file icon, or type dot for custom node types -->\n <span\n class=\"lc-tree-view__icon\"\n [style.color]=\"\n node.color ?? (node.status === 'default' ? null : badgeColor(node.status))\n \"\n >\n @if (node.icon; as icon) {\n <lc-icon [name]=\"icon\" [size]=\"iconSize()\" [decorative]=\"true\" />\n } @else {\n <span\n class=\"lc-tree-view__type-dot\"\n [style.background-color]=\"node.color ?? null\"\n ></span>\n }\n </span>\n\n <!-- label -->\n <span class=\"lc-tree-view__label\">{{ node.name }}</span>\n\n <!-- badge -->\n @if (node.badge) {\n <span\n class=\"lc-tree-view__badge\"\n [style.color]=\"badgeColor(node.status)\"\n [style.border-color]=\"badgeColor(node.status)\"\n >{{ node.badge }}</span\n >\n }\n\n <!-- status indicator -->\n @if (node.status === 'success') {\n <span class=\"lc-tree-view__status lc-tree-view__status--success\">\n <lc-icon name=\"check\" size=\"xs\" [decorative]=\"true\" />\n </span>\n } @else if (node.status === 'busy') {\n <span class=\"lc-tree-view__status lc-tree-view__status--busy\"></span>\n }\n </li>\n }\n</ul>\n", styles: [":host{display:block;border:1px solid var(--color-neutral-200);border-radius:8px;background:var(--color-surface);overflow:hidden;font-size:13px;color:var(--color-text-primary)}.lc-tree-view__toolbar{display:flex;align-items:center;gap:4px;padding:6px 8px;border-bottom:1px solid var(--color-neutral-200);background:var(--color-neutral-50);-webkit-user-select:none;user-select:none}.lc-tree-view__tool-btn{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;border:1px solid transparent;border-radius:4px;background:transparent;color:var(--color-text-secondary);font-size:12px;cursor:pointer;line-height:1}.lc-tree-view__tool-btn:hover{background:var(--color-neutral-100);color:var(--color-text-primary)}.lc-tree-view__list{list-style:none;margin:0;padding:4px 0}.lc-tree-view__item{display:flex;align-items:center;height:26px;padding-right:8px;cursor:pointer;-webkit-user-select:none;user-select:none;position:relative;outline:none}.lc-tree-view__item:hover{background:var(--color-neutral-100)}.lc-tree-view__item:focus-visible{box-shadow:inset 0 0 0 2px var(--color-primary-400)}.lc-tree-view__item--selected{background:var(--color-primary-50)}.lc-tree-view__item--selected:hover{background:var(--color-primary-100, var(--color-primary-50))}.lc-tree-view__item--selected .lc-tree-view__label{color:var(--color-primary-700);font-weight:500}.lc-tree-view__item--disabled{opacity:.5;cursor:not-allowed}.lc-tree-view__item--disabled:hover{background:transparent}.lc-tree-view__indent{flex:0 0 18px;width:18px;height:100%;position:relative}.lc-tree-view__indent--guide:before{content:\"\";position:absolute;left:50%;top:0;bottom:0;width:1px;background:var(--color-neutral-200)}.lc-tree-view__chevron{flex:0 0 18px;width:18px;display:flex;align-items:center;justify-content:center;color:var(--color-text-tertiary)}.lc-tree-view__chevron-btn{display:flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;border:none;background:transparent;color:inherit;cursor:pointer;border-radius:3px;transition:transform .12s ease}.lc-tree-view__chevron-btn:hover{background:var(--color-neutral-200)}.lc-tree-view__chevron-btn--expanded{transform:rotate(90deg)}.lc-tree-view__icon{display:flex;align-items:center;margin-right:6px;color:var(--color-text-tertiary)}.lc-tree-view__item--folder .lc-tree-view__icon{color:var(--color-primary-500, #6366f1)}.lc-tree-view__label{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--color-text-primary)}.lc-tree-view__badge{flex:0 0 auto;margin-left:8px;padding:1px 6px;border:1px solid;border-radius:10px;font-size:10px;line-height:1.4;font-weight:600;text-transform:uppercase;letter-spacing:.02em}.lc-tree-view__type-dot{display:block;width:8px;height:8px;border-radius:50%;background:var(--color-neutral-400)}.lc-tree-view__status{flex:0 0 auto;display:inline-flex;align-items:center;margin-left:8px}.lc-tree-view__status--success{color:var(--color-success-default, #16a34a)}.lc-tree-view__status--busy{width:8px;height:8px;border-radius:50%;background:var(--color-primary-500, #6366f1);animation:lc-tree-view-pulse 1.2s ease-in-out infinite}@keyframes lc-tree-view-pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.35;transform:scale(.75)}}@media(prefers-reduced-motion:reduce){.lc-tree-view__status--busy{animation:none}}\n"], dependencies: [{ kind: "component", type: IconComponent, selector: "lc-icon", inputs: ["name", "variant", "size", "color", "ariaLabel", "decorative", "strict"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
14137
14448
  }
14138
14449
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: TreeViewComponent, decorators: [{
14139
14450
  type: Component,
14140
- args: [{ selector: 'lc-tree-view', standalone: true, imports: [IconComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'lc-tree-view' }, template: "@if (showToolbar()) {\n <div class=\"lc-tree-view__toolbar\">\n <button type=\"button\" class=\"lc-tree-view__tool-btn\" (click)=\"expandAll()\" title=\"Expand all\">\n <lc-icon name=\"fold-down\" size=\"xs\" [decorative]=\"true\" />\n <span>Expand all</span>\n </button>\n <button type=\"button\" class=\"lc-tree-view__tool-btn\" (click)=\"collapseAll()\" title=\"Collapse all\">\n <lc-icon name=\"fold-up\" size=\"xs\" [decorative]=\"true\" />\n <span>Collapse all</span>\n </button>\n </div>\n}\n\n<ul class=\"lc-tree-view__list\" role=\"tree\">\n @for (node of visibleNodes(); track trackById($index, node)) {\n <li\n class=\"lc-tree-view__item\"\n [class.lc-tree-view__item--selected]=\"node.id === selectedId()\"\n [class.lc-tree-view__item--disabled]=\"node.disabled\"\n [class.lc-tree-view__item--folder]=\"node.type === 'folder'\"\n role=\"treeitem\"\n [attr.aria-level]=\"node.depth + 1\"\n [attr.aria-expanded]=\"node.hasChildren ? node.expanded : null\"\n [attr.aria-selected]=\"node.id === selectedId()\"\n [attr.aria-disabled]=\"node.disabled ? true : null\"\n [tabindex]=\"node.disabled ? -1 : 0\"\n (click)=\"onNodeClick(node)\"\n (keydown)=\"onKeydown(node, $event)\"\n >\n <!-- indentation + guide lines -->\n @for (hasSibling of node.ancestorHasSibling; track $index) {\n <span\n class=\"lc-tree-view__indent\"\n [class.lc-tree-view__indent--guide]=\"showGuides() && hasSibling\"\n ></span>\n }\n\n <!-- expand / collapse chevron -->\n <span class=\"lc-tree-view__chevron\">\n @if (node.hasChildren) {\n <button\n type=\"button\"\n class=\"lc-tree-view__chevron-btn\"\n [class.lc-tree-view__chevron-btn--expanded]=\"node.expanded\"\n (click)=\"onToggleClick(node, $event)\"\n [attr.aria-label]=\"node.expanded ? 'Collapse' : 'Expand'\"\n tabindex=\"-1\"\n >\n <lc-icon name=\"chevron-right\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </span>\n\n <!-- type / file icon -->\n <span\n class=\"lc-tree-view__icon\"\n [style.color]=\"\n node.status === 'default' ? null : badgeColor(node.status)\n \"\n >\n <lc-icon [name]=\"node.icon\" [size]=\"iconSize()\" [decorative]=\"true\" />\n </span>\n\n <!-- label -->\n <span class=\"lc-tree-view__label\">{{ node.name }}</span>\n\n <!-- badge -->\n @if (node.badge) {\n <span\n class=\"lc-tree-view__badge\"\n [style.color]=\"badgeColor(node.status)\"\n [style.border-color]=\"badgeColor(node.status)\"\n >{{ node.badge }}</span\n >\n }\n </li>\n }\n</ul>\n", styles: [":host{display:block;border:1px solid var(--color-neutral-200);border-radius:8px;background:var(--color-surface);overflow:hidden;font-size:13px;color:var(--color-text-primary)}.lc-tree-view__toolbar{display:flex;align-items:center;gap:4px;padding:6px 8px;border-bottom:1px solid var(--color-neutral-200);background:var(--color-neutral-50);-webkit-user-select:none;user-select:none}.lc-tree-view__tool-btn{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;border:1px solid transparent;border-radius:4px;background:transparent;color:var(--color-text-secondary);font-size:12px;cursor:pointer;line-height:1}.lc-tree-view__tool-btn:hover{background:var(--color-neutral-100);color:var(--color-text-primary)}.lc-tree-view__list{list-style:none;margin:0;padding:4px 0}.lc-tree-view__item{display:flex;align-items:center;height:26px;padding-right:8px;cursor:pointer;-webkit-user-select:none;user-select:none;position:relative;outline:none}.lc-tree-view__item:hover{background:var(--color-neutral-100)}.lc-tree-view__item:focus-visible{box-shadow:inset 0 0 0 2px var(--color-primary-400)}.lc-tree-view__item--selected{background:var(--color-primary-50)}.lc-tree-view__item--selected:hover{background:var(--color-primary-100, var(--color-primary-50))}.lc-tree-view__item--selected .lc-tree-view__label{color:var(--color-primary-700);font-weight:500}.lc-tree-view__item--disabled{opacity:.5;cursor:not-allowed}.lc-tree-view__item--disabled:hover{background:transparent}.lc-tree-view__indent{flex:0 0 18px;width:18px;height:100%;position:relative}.lc-tree-view__indent--guide:before{content:\"\";position:absolute;left:50%;top:0;bottom:0;width:1px;background:var(--color-neutral-200)}.lc-tree-view__chevron{flex:0 0 18px;width:18px;display:flex;align-items:center;justify-content:center;color:var(--color-text-tertiary)}.lc-tree-view__chevron-btn{display:flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;border:none;background:transparent;color:inherit;cursor:pointer;border-radius:3px;transition:transform .12s ease}.lc-tree-view__chevron-btn:hover{background:var(--color-neutral-200)}.lc-tree-view__chevron-btn--expanded{transform:rotate(90deg)}.lc-tree-view__icon{display:flex;align-items:center;margin-right:6px;color:var(--color-text-tertiary)}.lc-tree-view__item--folder .lc-tree-view__icon{color:var(--color-primary-500, #6366f1)}.lc-tree-view__label{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--color-text-primary)}.lc-tree-view__badge{flex:0 0 auto;margin-left:8px;padding:1px 6px;border:1px solid;border-radius:10px;font-size:10px;line-height:1.4;font-weight:600;text-transform:uppercase;letter-spacing:.02em}\n"] }]
14451
+ args: [{ selector: 'lc-tree-view', standalone: true, imports: [IconComponent], changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'lc-tree-view' }, template: "@if (showToolbar()) {\n <div class=\"lc-tree-view__toolbar\">\n <button type=\"button\" class=\"lc-tree-view__tool-btn\" (click)=\"expandAll()\" title=\"Expand all\">\n <lc-icon name=\"fold-down\" size=\"xs\" [decorative]=\"true\" />\n <span>Expand all</span>\n </button>\n <button type=\"button\" class=\"lc-tree-view__tool-btn\" (click)=\"collapseAll()\" title=\"Collapse all\">\n <lc-icon name=\"fold-up\" size=\"xs\" [decorative]=\"true\" />\n <span>Collapse all</span>\n </button>\n </div>\n}\n\n<ul class=\"lc-tree-view__list\" role=\"tree\">\n @for (node of visibleNodes(); track trackById($index, node)) {\n <li\n class=\"lc-tree-view__item\"\n [class.lc-tree-view__item--selected]=\"node.id === selectedId()\"\n [class.lc-tree-view__item--disabled]=\"node.disabled\"\n [class.lc-tree-view__item--folder]=\"node.type === 'folder'\"\n role=\"treeitem\"\n [attr.aria-level]=\"node.depth + 1\"\n [attr.aria-expanded]=\"node.hasChildren ? node.expanded : null\"\n [attr.aria-selected]=\"node.id === selectedId()\"\n [attr.aria-disabled]=\"node.disabled ? true : null\"\n [attr.aria-busy]=\"node.status === 'busy' ? true : null\"\n [tabindex]=\"node.disabled ? -1 : 0\"\n (click)=\"onNodeClick(node)\"\n (keydown)=\"onKeydown(node, $event)\"\n >\n <!-- indentation + guide lines -->\n @for (hasSibling of node.ancestorHasSibling; track $index) {\n <span\n class=\"lc-tree-view__indent\"\n [class.lc-tree-view__indent--guide]=\"showGuides() && hasSibling\"\n ></span>\n }\n\n <!-- expand / collapse chevron -->\n <span class=\"lc-tree-view__chevron\">\n @if (node.hasChildren) {\n <button\n type=\"button\"\n class=\"lc-tree-view__chevron-btn\"\n [class.lc-tree-view__chevron-btn--expanded]=\"node.expanded\"\n (click)=\"onToggleClick(node, $event)\"\n [attr.aria-label]=\"node.expanded ? 'Collapse' : 'Expand'\"\n tabindex=\"-1\"\n >\n <lc-icon name=\"chevron-right\" size=\"xs\" [decorative]=\"true\" />\n </button>\n }\n </span>\n\n <!-- type / file icon, or type dot for custom node types -->\n <span\n class=\"lc-tree-view__icon\"\n [style.color]=\"\n node.color ?? (node.status === 'default' ? null : badgeColor(node.status))\n \"\n >\n @if (node.icon; as icon) {\n <lc-icon [name]=\"icon\" [size]=\"iconSize()\" [decorative]=\"true\" />\n } @else {\n <span\n class=\"lc-tree-view__type-dot\"\n [style.background-color]=\"node.color ?? null\"\n ></span>\n }\n </span>\n\n <!-- label -->\n <span class=\"lc-tree-view__label\">{{ node.name }}</span>\n\n <!-- badge -->\n @if (node.badge) {\n <span\n class=\"lc-tree-view__badge\"\n [style.color]=\"badgeColor(node.status)\"\n [style.border-color]=\"badgeColor(node.status)\"\n >{{ node.badge }}</span\n >\n }\n\n <!-- status indicator -->\n @if (node.status === 'success') {\n <span class=\"lc-tree-view__status lc-tree-view__status--success\">\n <lc-icon name=\"check\" size=\"xs\" [decorative]=\"true\" />\n </span>\n } @else if (node.status === 'busy') {\n <span class=\"lc-tree-view__status lc-tree-view__status--busy\"></span>\n }\n </li>\n }\n</ul>\n", styles: [":host{display:block;border:1px solid var(--color-neutral-200);border-radius:8px;background:var(--color-surface);overflow:hidden;font-size:13px;color:var(--color-text-primary)}.lc-tree-view__toolbar{display:flex;align-items:center;gap:4px;padding:6px 8px;border-bottom:1px solid var(--color-neutral-200);background:var(--color-neutral-50);-webkit-user-select:none;user-select:none}.lc-tree-view__tool-btn{display:inline-flex;align-items:center;gap:4px;padding:4px 8px;border:1px solid transparent;border-radius:4px;background:transparent;color:var(--color-text-secondary);font-size:12px;cursor:pointer;line-height:1}.lc-tree-view__tool-btn:hover{background:var(--color-neutral-100);color:var(--color-text-primary)}.lc-tree-view__list{list-style:none;margin:0;padding:4px 0}.lc-tree-view__item{display:flex;align-items:center;height:26px;padding-right:8px;cursor:pointer;-webkit-user-select:none;user-select:none;position:relative;outline:none}.lc-tree-view__item:hover{background:var(--color-neutral-100)}.lc-tree-view__item:focus-visible{box-shadow:inset 0 0 0 2px var(--color-primary-400)}.lc-tree-view__item--selected{background:var(--color-primary-50)}.lc-tree-view__item--selected:hover{background:var(--color-primary-100, var(--color-primary-50))}.lc-tree-view__item--selected .lc-tree-view__label{color:var(--color-primary-700);font-weight:500}.lc-tree-view__item--disabled{opacity:.5;cursor:not-allowed}.lc-tree-view__item--disabled:hover{background:transparent}.lc-tree-view__indent{flex:0 0 18px;width:18px;height:100%;position:relative}.lc-tree-view__indent--guide:before{content:\"\";position:absolute;left:50%;top:0;bottom:0;width:1px;background:var(--color-neutral-200)}.lc-tree-view__chevron{flex:0 0 18px;width:18px;display:flex;align-items:center;justify-content:center;color:var(--color-text-tertiary)}.lc-tree-view__chevron-btn{display:flex;align-items:center;justify-content:center;width:16px;height:16px;padding:0;border:none;background:transparent;color:inherit;cursor:pointer;border-radius:3px;transition:transform .12s ease}.lc-tree-view__chevron-btn:hover{background:var(--color-neutral-200)}.lc-tree-view__chevron-btn--expanded{transform:rotate(90deg)}.lc-tree-view__icon{display:flex;align-items:center;margin-right:6px;color:var(--color-text-tertiary)}.lc-tree-view__item--folder .lc-tree-view__icon{color:var(--color-primary-500, #6366f1)}.lc-tree-view__label{flex:1 1 auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--color-text-primary)}.lc-tree-view__badge{flex:0 0 auto;margin-left:8px;padding:1px 6px;border:1px solid;border-radius:10px;font-size:10px;line-height:1.4;font-weight:600;text-transform:uppercase;letter-spacing:.02em}.lc-tree-view__type-dot{display:block;width:8px;height:8px;border-radius:50%;background:var(--color-neutral-400)}.lc-tree-view__status{flex:0 0 auto;display:inline-flex;align-items:center;margin-left:8px}.lc-tree-view__status--success{color:var(--color-success-default, #16a34a)}.lc-tree-view__status--busy{width:8px;height:8px;border-radius:50%;background:var(--color-primary-500, #6366f1);animation:lc-tree-view-pulse 1.2s ease-in-out infinite}@keyframes lc-tree-view-pulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.35;transform:scale(.75)}}@media(prefers-reduced-motion:reduce){.lc-tree-view__status--busy{animation:none}}\n"] }]
14141
14452
  }], propDecorators: { nodes: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodes", required: true }] }], selectedId: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedId", required: false }] }, { type: i0.Output, args: ["selectedIdChange"] }], showGuides: [{ type: i0.Input, args: [{ isSignal: true, alias: "showGuides", required: false }] }], showToolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "showToolbar", required: false }] }], iconSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconSize", required: false }] }], nodeClick: [{ type: i0.Output, args: ["nodeClick"] }] } });
14142
14453
 
14143
14454
  const DEFAULT_COLORS$1 = [
@@ -16199,7 +16510,7 @@ class ComboboxComponent {
16199
16510
  useExisting: forwardRef(() => ComboboxComponent),
16200
16511
  multi: true,
16201
16512
  },
16202
- ], 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-primary);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(--border-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-primary);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(--border-radius-sm, .25rem);flex-shrink:0}.lc-combobox__clear:hover{color:var(--color-text-primary);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(--border-radius-md, .375rem);box-shadow:var(--elevation-3, 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-primary);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", "strict"] }, { 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 });
16513
+ ], 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-primary);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(--border-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-primary);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(--border-radius-sm, .25rem);flex-shrink:0}.lc-combobox__clear:hover{color:var(--color-text-primary);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(--border-radius-md, .375rem);box-shadow:var(--elevation-3, 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-primary);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", "strict"] }, { kind: "component", type: SpinnerComponent, selector: "lc-spinner", inputs: ["size", "message"] }, { kind: "component", type: ChipComponent, selector: "lc-chip", inputs: ["variant", "size", "icon", "removable", "clickable", "disabled"], outputs: ["remove", "chipClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
16203
16514
  }
16204
16515
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImport: i0, type: ComboboxComponent, decorators: [{
16205
16516
  type: Component,
@@ -16419,5 +16730,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.10", ngImpo
16419
16730
  * Generated bundle index. Do not edit.
16420
16731
  */
16421
16732
 
16422
- export { AccordionComponent, AccordionContentDirective, AccordionGroupComponent, AccordionHeaderDirective, 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, ColorBackgroundDark, 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, ColorSidebarDark, ColorSuccessDark, ColorSuccessDefault, ColorSuccessLight, ColorSurfaceDarkBase, ColorSurfaceDarkRaised, ColorSurfaceDarkSunken, ColorTextDarkPrimary, ColorTextDarkSecondary, ColorTextDarkTertiary, ColorWarningDark, ColorWarningDefault, ColorWarningLight, ComboboxComponent, ConfirmDialogComponent, ConfirmService, ContainerComponent, DateRangePickerComponent, DatepickerComponent, DependencyViewerComponent, DescriptionListComponent, DiffViewerComponent, DividerComponent, DocumentViewerComponent, DonutChartComponent, DrawerComponent, Elevation1, Elevation2, Elevation3, Elevation4, EmailInputComponent, EmptyStateComponent, ErrorDisplayComponent, FILE_FALLBACK_ICON, FOLDER_ICON, FOLDER_OPEN_ICON, FieldGroupComponent, FileUploadComponent, FilterBarComponent, FooterComponent, FunnelChartComponent, GalleryComponent, GanttChartComponent, GaugeComponent, HeaderComponent, HeatmapComponent, HeroComponent, ICON_ALIASES, ICON_NAMES, IconComponent, InputComponent, KanbanBoardComponent, LC_LOGO_BASE_PATH, LineChartComponent, ListComponent, ListItemTemplateDirective, LogViewerComponent, LogoComponent, MarkdownComponent, MenuComponent, ModalComponent, NotificationCenterComponent, NumberInputComponent, PageHeaderComponent, PageLayoutComponent, PaginationComponent, PasswordInputComponent, PieChartComponent, PipelineComponent, PopoverComponent, ProgressBarComponent, ProgressRingComponent, RadarChartComponent, RadioComponent, RatingComponent, RichTextEditorComponent, ScatterPlotComponent, SearchInputComponent, SectionComponent, SelectComponent, SidenavComponent, SizeContentMaxWidth, 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, StageListComponent, StatTrendComponent, StepperComponent, SwitchComponent, TabComponent, TableCellDirective, TableComponent, TabsComponent, TagInputComponent, TextareaComponent, ThemeService, TimelineComponent, ToastComponent, ToastService, ToggleGroupComponent, ToolbarComponent, TooltipContentComponent, TooltipDirective, TreeViewComponent, TypographyComponent, TypographyFontFamilyBase, TypographyFontFamilyMono, TypographyFontSize2xl, TypographyFontSize3xl, TypographyFontSize4xl, TypographyFontSize5xl, TypographyFontSize6xl, TypographyFontSizeBase, TypographyFontSizeLg, TypographyFontSizeSm, TypographyFontSizeXl, TypographyFontSizeXs, TypographyFontWeightBold, TypographyFontWeightMedium, TypographyFontWeightNormal, TypographyFontWeightSemibold, TypographyLineHeightNormal, TypographyLineHeightRelaxed, TypographyLineHeightTight, VerificationCodeInputComponent, WaterfallChartComponent, isValidIconName, resolveFileIcon };
16733
+ export { AccordionComponent, AccordionContentDirective, AccordionGroupComponent, AccordionHeaderDirective, 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, ColorBackgroundDark, 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, ColorSidebarDark, ColorSuccessDark, ColorSuccessDefault, ColorSuccessLight, ColorSurfaceDarkBase, ColorSurfaceDarkRaised, ColorSurfaceDarkSunken, ColorTextDarkPrimary, ColorTextDarkSecondary, ColorTextDarkTertiary, ColorWarningDark, ColorWarningDefault, ColorWarningLight, ComboboxComponent, ConfirmDialogComponent, ConfirmService, ContainerComponent, DateRangePickerComponent, DatepickerComponent, DependencyViewerComponent, DescriptionListComponent, DiffViewerComponent, DividerComponent, DocumentViewerComponent, DonutChartComponent, DrawerComponent, Elevation1, Elevation2, Elevation3, Elevation4, EmailInputComponent, EmptyStateComponent, ErrorDisplayComponent, FILE_FALLBACK_ICON, FOLDER_ICON, FOLDER_OPEN_ICON, FieldGroupComponent, FileUploadComponent, FilterBarComponent, FooterComponent, FunnelChartComponent, GalleryComponent, GanttChartComponent, GaugeComponent, HeaderComponent, HeatmapComponent, HeroComponent, ICON_ALIASES, ICON_NAMES, IconComponent, InputComponent, KanbanBoardComponent, LC_LOGO_BASE_PATH, LineChartComponent, ListComponent, ListItemTemplateDirective, LogViewerComponent, LogoComponent, MarkdownComponent, MenuComponent, ModalComponent, NotificationCenterComponent, NumberInputComponent, PageHeaderComponent, PageLayoutComponent, PaginationComponent, PasswordInputComponent, PieChartComponent, PipelineComponent, PopoverComponent, ProgressBarComponent, ProgressRingComponent, RadarChartComponent, RadioComponent, RatingComponent, RichTextEditorComponent, ScatterPlotComponent, SearchInputComponent, SectionComponent, SelectComponent, SidenavComponent, SizeContentMaxWidth, 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, SplitPaneComponent, StackComponent, StackedBarChartComponent, StageListComponent, StatTrendComponent, StepperComponent, SwitchComponent, TabComponent, TableCellDirective, TableComponent, TabsComponent, TagInputComponent, TextareaComponent, ThemeService, TimelineComponent, ToastComponent, ToastService, ToggleGroupComponent, ToolbarComponent, TooltipContentComponent, TooltipDirective, TreeViewComponent, TypographyComponent, TypographyFontFamilyBase, TypographyFontFamilyMono, TypographyFontSize2xl, TypographyFontSize3xl, TypographyFontSize4xl, TypographyFontSize5xl, TypographyFontSize6xl, TypographyFontSizeBase, TypographyFontSizeLg, TypographyFontSizeSm, TypographyFontSizeXl, TypographyFontSizeXs, TypographyFontWeightBold, TypographyFontWeightMedium, TypographyFontWeightNormal, TypographyFontWeightSemibold, TypographyLineHeightNormal, TypographyLineHeightRelaxed, TypographyLineHeightTight, VerificationCodeInputComponent, WaterfallChartComponent, isValidIconName, resolveFileIcon };
16423
16734
  //# sourceMappingURL=life-cockpit-angular-ui-kit.mjs.map