@pegasusheavy/ngx-tailwindcss 0.3.6 → 0.4.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 { InjectionToken, makeEnvironmentProviders, inject, PLATFORM_ID, DestroyRef, signal, Injectable, APP_INITIALIZER, computed, LOCALE_ID, effect, ElementRef, Renderer2, booleanAttribute, HostListener, Input, Directive, numberAttribute, NgZone, EventEmitter, Output, ChangeDetectionStrategy, Component, HostBinding, forwardRef, ViewChild, input, output, ContentChildren, ContentChild, viewChild, model, contentChildren } from '@angular/core';
2
+ import { InjectionToken, makeEnvironmentProviders, inject, PLATFORM_ID, DestroyRef, signal, Injectable, APP_INITIALIZER, computed, LOCALE_ID, effect, ElementRef, Renderer2, booleanAttribute, HostListener, Input, Directive, numberAttribute, NgZone, EventEmitter, Output, ChangeDetectionStrategy, Component, HostBinding, forwardRef, ViewChild, input, output, ContentChildren, ContentChild, viewChild, contentChildren } from '@angular/core';
3
3
  import * as i1 from '@angular/common';
4
4
  import { DOCUMENT, isPlatformBrowser, CommonModule, NgStyle } from '@angular/common';
5
5
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@@ -3848,8 +3848,8 @@ const BUTTON_VARIANTS = {
3848
3848
  warning: 'bg-amber-500 hover:bg-amber-600 active:bg-amber-700 text-white shadow-sm hover:shadow focus-visible:ring-amber-500',
3849
3849
  danger: 'bg-rose-600 hover:bg-rose-700 active:bg-rose-800 text-white shadow-sm hover:shadow focus-visible:ring-rose-500',
3850
3850
  info: 'bg-cyan-600 hover:bg-cyan-700 active:bg-cyan-800 text-white shadow-sm hover:shadow focus-visible:ring-cyan-500',
3851
- ghost: 'bg-transparent hover:bg-slate-100 dark:hover:bg-zinc-800 active:bg-slate-200 dark:active:bg-zinc-700 text-slate-700 dark:text-zinc-300 focus-visible:ring-slate-500',
3852
- outline: 'bg-white dark:bg-zinc-800 border-2 border-slate-500 dark:border-zinc-500 hover:border-slate-600 dark:hover:border-zinc-400 hover:bg-slate-100 dark:hover:bg-zinc-700 active:bg-slate-200 dark:active:bg-zinc-600 text-slate-800 dark:text-zinc-200 shadow-sm focus-visible:ring-slate-500',
3851
+ ghost: 'bg-transparent hover:bg-slate-100 active:bg-slate-200 text-slate-700 focus-visible:ring-slate-500',
3852
+ outline: 'bg-white border-2 border-slate-500 hover:border-slate-600 hover:bg-slate-100 active:bg-slate-200 text-slate-800 shadow-sm focus-visible:ring-slate-500',
3853
3853
  link: 'bg-transparent text-blue-600 hover:text-blue-700 hover:underline active:text-blue-800 focus-visible:ring-blue-500 p-0 shadow-none',
3854
3854
  };
3855
3855
  const BUTTON_SIZES = {
@@ -4307,7 +4307,7 @@ class TwInputComponent {
4307
4307
  useExisting: forwardRef(() => TwInputComponent),
4308
4308
  multi: true,
4309
4309
  },
4310
- ], viewQueries: [{ propertyName: "inputElement", first: true, predicate: ["inputElement"], descendants: true }], ngImport: i0, template: "@if (label) {\n <label [for]=\"inputId\" class=\"block text-sm font-medium text-slate-700 mb-1.5\">\n {{ label }}\n @if (required) {\n <span class=\"text-rose-500 ml-0.5\">*</span>\n }\n </label>\n}\n\n<div [class]=\"wrapperClasses()\">\n <!-- Prefix -->\n <ng-content select=\"[twInputPrefix]\"></ng-content>\n\n <!-- Input element -->\n <input\n #inputElement\n [id]=\"inputId\"\n [type]=\"type\"\n [class]=\"computedClasses()\"\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [readonly]=\"readonly\"\n [required]=\"required\"\n [attr.aria-invalid]=\"hasError\"\n [attr.aria-describedby]=\"describedBy\"\n [attr.autocomplete]=\"autocomplete\"\n [attr.inputmode]=\"inputmode\"\n [attr.pattern]=\"pattern\"\n [attr.min]=\"min\"\n [attr.max]=\"max\"\n [attr.minlength]=\"minlength\"\n [attr.maxlength]=\"maxlength\"\n [attr.step]=\"step\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n (focus)=\"onFocus.emit($event)\"\n />\n\n <!-- Suffix -->\n <ng-content select=\"[twInputSuffix]\"></ng-content>\n\n <!-- Clear button -->\n @if (clearable && value && !disabled && !readonly) {\n <button\n type=\"button\"\n class=\"flex items-center justify-center w-5 h-5 text-slate-400 hover:text-slate-600 transition-colors\"\n (click)=\"clear()\"\n aria-label=\"Clear input\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n }\n</div>\n\n@if (hint && !error) {\n <span class=\"block text-xs text-slate-500 mt-1.5\">{{ hint }}</span>\n}\n\n@if (error) {\n <span class=\"block text-xs text-rose-600 mt-1.5\" role=\"alert\">{{ error }}</span>\n}\n\n<ng-content select=\"tw-hint, [twHint], tw-error, [twError]\"></ng-content>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4310
+ ], viewQueries: [{ propertyName: "inputElement", first: true, predicate: ["inputElement"], descendants: true }], ngImport: i0, template: "@if (label) {\n <label [for]=\"inputId\" class=\"block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5\">\n {{ label }}\n @if (required) {\n <span class=\"text-rose-500 ml-0.5\">*</span>\n }\n </label>\n}\n\n<div [class]=\"wrapperClasses()\">\n <!-- Prefix -->\n <ng-content select=\"[twInputPrefix]\"></ng-content>\n\n <!-- Input element -->\n <input\n #inputElement\n [id]=\"inputId\"\n [type]=\"type\"\n [class]=\"computedClasses()\"\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [readonly]=\"readonly\"\n [required]=\"required\"\n [attr.aria-invalid]=\"hasError\"\n [attr.aria-describedby]=\"describedBy\"\n [attr.autocomplete]=\"autocomplete\"\n [attr.inputmode]=\"inputmode\"\n [attr.pattern]=\"pattern\"\n [attr.min]=\"min\"\n [attr.max]=\"max\"\n [attr.minlength]=\"minlength\"\n [attr.maxlength]=\"maxlength\"\n [attr.step]=\"step\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n (focus)=\"onFocus.emit($event)\"\n />\n\n <!-- Suffix -->\n <ng-content select=\"[twInputSuffix]\"></ng-content>\n\n <!-- Clear button -->\n @if (clearable && value && !disabled && !readonly) {\n <button\n type=\"button\"\n class=\"flex items-center justify-center w-5 h-5 text-slate-400 hover:text-slate-600 dark:text-slate-500 dark:hover:text-slate-300 transition-colors\"\n (click)=\"clear()\"\n aria-label=\"Clear input\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n }\n</div>\n\n@if (hint && !error) {\n <span class=\"block text-xs text-slate-500 dark:text-slate-400 mt-1.5\">{{ hint }}</span>\n}\n\n@if (error) {\n <span class=\"block text-xs text-rose-600 mt-1.5\" role=\"alert\">{{ error }}</span>\n}\n\n<ng-content select=\"tw-hint, [twHint], tw-error, [twError]\"></ng-content>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4311
4311
  }
4312
4312
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TwInputComponent, decorators: [{
4313
4313
  type: Component,
@@ -4319,7 +4319,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
4319
4319
  },
4320
4320
  ], host: {
4321
4321
  class: 'block',
4322
- }, template: "@if (label) {\n <label [for]=\"inputId\" class=\"block text-sm font-medium text-slate-700 mb-1.5\">\n {{ label }}\n @if (required) {\n <span class=\"text-rose-500 ml-0.5\">*</span>\n }\n </label>\n}\n\n<div [class]=\"wrapperClasses()\">\n <!-- Prefix -->\n <ng-content select=\"[twInputPrefix]\"></ng-content>\n\n <!-- Input element -->\n <input\n #inputElement\n [id]=\"inputId\"\n [type]=\"type\"\n [class]=\"computedClasses()\"\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [readonly]=\"readonly\"\n [required]=\"required\"\n [attr.aria-invalid]=\"hasError\"\n [attr.aria-describedby]=\"describedBy\"\n [attr.autocomplete]=\"autocomplete\"\n [attr.inputmode]=\"inputmode\"\n [attr.pattern]=\"pattern\"\n [attr.min]=\"min\"\n [attr.max]=\"max\"\n [attr.minlength]=\"minlength\"\n [attr.maxlength]=\"maxlength\"\n [attr.step]=\"step\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n (focus)=\"onFocus.emit($event)\"\n />\n\n <!-- Suffix -->\n <ng-content select=\"[twInputSuffix]\"></ng-content>\n\n <!-- Clear button -->\n @if (clearable && value && !disabled && !readonly) {\n <button\n type=\"button\"\n class=\"flex items-center justify-center w-5 h-5 text-slate-400 hover:text-slate-600 transition-colors\"\n (click)=\"clear()\"\n aria-label=\"Clear input\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n }\n</div>\n\n@if (hint && !error) {\n <span class=\"block text-xs text-slate-500 mt-1.5\">{{ hint }}</span>\n}\n\n@if (error) {\n <span class=\"block text-xs text-rose-600 mt-1.5\" role=\"alert\">{{ error }}</span>\n}\n\n<ng-content select=\"tw-hint, [twHint], tw-error, [twError]\"></ng-content>\n" }]
4322
+ }, template: "@if (label) {\n <label [for]=\"inputId\" class=\"block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5\">\n {{ label }}\n @if (required) {\n <span class=\"text-rose-500 ml-0.5\">*</span>\n }\n </label>\n}\n\n<div [class]=\"wrapperClasses()\">\n <!-- Prefix -->\n <ng-content select=\"[twInputPrefix]\"></ng-content>\n\n <!-- Input element -->\n <input\n #inputElement\n [id]=\"inputId\"\n [type]=\"type\"\n [class]=\"computedClasses()\"\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [readonly]=\"readonly\"\n [required]=\"required\"\n [attr.aria-invalid]=\"hasError\"\n [attr.aria-describedby]=\"describedBy\"\n [attr.autocomplete]=\"autocomplete\"\n [attr.inputmode]=\"inputmode\"\n [attr.pattern]=\"pattern\"\n [attr.min]=\"min\"\n [attr.max]=\"max\"\n [attr.minlength]=\"minlength\"\n [attr.maxlength]=\"maxlength\"\n [attr.step]=\"step\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n (focus)=\"onFocus.emit($event)\"\n />\n\n <!-- Suffix -->\n <ng-content select=\"[twInputSuffix]\"></ng-content>\n\n <!-- Clear button -->\n @if (clearable && value && !disabled && !readonly) {\n <button\n type=\"button\"\n class=\"flex items-center justify-center w-5 h-5 text-slate-400 hover:text-slate-600 dark:text-slate-500 dark:hover:text-slate-300 transition-colors\"\n (click)=\"clear()\"\n aria-label=\"Clear input\"\n >\n <svg class=\"w-4 h-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n }\n</div>\n\n@if (hint && !error) {\n <span class=\"block text-xs text-slate-500 dark:text-slate-400 mt-1.5\">{{ hint }}</span>\n}\n\n@if (error) {\n <span class=\"block text-xs text-rose-600 mt-1.5\" role=\"alert\">{{ error }}</span>\n}\n\n<ng-content select=\"tw-hint, [twHint], tw-error, [twError]\"></ng-content>\n" }]
4323
4323
  }], propDecorators: { inputElement: [{
4324
4324
  type: ViewChild,
4325
4325
  args: ['inputElement']
@@ -4452,7 +4452,7 @@ class TwTextareaComponent {
4452
4452
  useExisting: forwardRef(() => TwTextareaComponent),
4453
4453
  multi: true,
4454
4454
  },
4455
- ], viewQueries: [{ propertyName: "textareaElement", first: true, predicate: ["textareaElement"], descendants: true }], ngImport: i0, template: "@if (label) {\n <label [for]=\"inputId\" class=\"block text-sm font-medium text-slate-700 mb-1.5\">\n {{ label }}\n @if (required) {\n <span class=\"text-rose-500 ml-0.5\">*</span>\n }\n </label>\n}\n\n<textarea\n #textareaElement\n [id]=\"inputId\"\n [class]=\"computedClasses()\"\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [readonly]=\"readonly\"\n [required]=\"required\"\n [rows]=\"rows\"\n [attr.aria-invalid]=\"hasError\"\n [attr.minlength]=\"minlength\"\n [attr.maxlength]=\"maxlength\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n></textarea>\n\n@if (hint && !error) {\n <span class=\"block text-xs text-slate-500 mt-1.5\">{{ hint }}</span>\n}\n\n@if (error) {\n <span class=\"block text-xs text-rose-600 mt-1.5\" role=\"alert\">{{ error }}</span>\n}\n\n@if (maxlength && showCount) {\n <span class=\"block text-xs text-slate-400 mt-1 text-right\">\n {{ value.length }} / {{ maxlength }}\n </span>\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4455
+ ], viewQueries: [{ propertyName: "textareaElement", first: true, predicate: ["textareaElement"], descendants: true }], ngImport: i0, template: "@if (label) {\n <label [for]=\"inputId\" class=\"block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5\">\n {{ label }}\n @if (required) {\n <span class=\"text-rose-500 ml-0.5\">*</span>\n }\n </label>\n}\n\n<textarea\n #textareaElement\n [id]=\"inputId\"\n [class]=\"computedClasses()\"\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [readonly]=\"readonly\"\n [required]=\"required\"\n [rows]=\"rows\"\n [attr.aria-invalid]=\"hasError\"\n [attr.minlength]=\"minlength\"\n [attr.maxlength]=\"maxlength\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n></textarea>\n\n@if (hint && !error) {\n <span class=\"block text-xs text-slate-500 dark:text-slate-400 mt-1.5\">{{ hint }}</span>\n}\n\n@if (error) {\n <span class=\"block text-xs text-rose-600 mt-1.5\" role=\"alert\">{{ error }}</span>\n}\n\n@if (maxlength && showCount) {\n <span class=\"block text-xs text-slate-400 mt-1 text-right\">\n {{ value.length }} / {{ maxlength }}\n </span>\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
4456
4456
  }
4457
4457
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TwTextareaComponent, decorators: [{
4458
4458
  type: Component,
@@ -4464,7 +4464,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
4464
4464
  },
4465
4465
  ], host: {
4466
4466
  class: 'block',
4467
- }, template: "@if (label) {\n <label [for]=\"inputId\" class=\"block text-sm font-medium text-slate-700 mb-1.5\">\n {{ label }}\n @if (required) {\n <span class=\"text-rose-500 ml-0.5\">*</span>\n }\n </label>\n}\n\n<textarea\n #textareaElement\n [id]=\"inputId\"\n [class]=\"computedClasses()\"\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [readonly]=\"readonly\"\n [required]=\"required\"\n [rows]=\"rows\"\n [attr.aria-invalid]=\"hasError\"\n [attr.minlength]=\"minlength\"\n [attr.maxlength]=\"maxlength\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n></textarea>\n\n@if (hint && !error) {\n <span class=\"block text-xs text-slate-500 mt-1.5\">{{ hint }}</span>\n}\n\n@if (error) {\n <span class=\"block text-xs text-rose-600 mt-1.5\" role=\"alert\">{{ error }}</span>\n}\n\n@if (maxlength && showCount) {\n <span class=\"block text-xs text-slate-400 mt-1 text-right\">\n {{ value.length }} / {{ maxlength }}\n </span>\n}\n" }]
4467
+ }, template: "@if (label) {\n <label [for]=\"inputId\" class=\"block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5\">\n {{ label }}\n @if (required) {\n <span class=\"text-rose-500 ml-0.5\">*</span>\n }\n </label>\n}\n\n<textarea\n #textareaElement\n [id]=\"inputId\"\n [class]=\"computedClasses()\"\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [readonly]=\"readonly\"\n [required]=\"required\"\n [rows]=\"rows\"\n [attr.aria-invalid]=\"hasError\"\n [attr.minlength]=\"minlength\"\n [attr.maxlength]=\"maxlength\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n></textarea>\n\n@if (hint && !error) {\n <span class=\"block text-xs text-slate-500 dark:text-slate-400 mt-1.5\">{{ hint }}</span>\n}\n\n@if (error) {\n <span class=\"block text-xs text-rose-600 mt-1.5\" role=\"alert\">{{ error }}</span>\n}\n\n@if (maxlength && showCount) {\n <span class=\"block text-xs text-slate-400 mt-1 text-right\">\n {{ value.length }} / {{ maxlength }}\n </span>\n}\n" }]
4468
4468
  }], propDecorators: { textareaElement: [{
4469
4469
  type: ViewChild,
4470
4470
  args: ['textareaElement']
@@ -9799,19 +9799,16 @@ class TwPaginationComponent {
9799
9799
  const rightSiblingIndex = Math.min(current + siblings, total);
9800
9800
  const showLeftEllipsis = leftSiblingIndex > 2;
9801
9801
  const showRightEllipsis = rightSiblingIndex < total - 1;
9802
- // Use unique ellipsis identifiers to avoid duplicate keys in @for tracking
9803
- const LEFT_ELLIPSIS = '...left';
9804
- const RIGHT_ELLIPSIS = '...right';
9805
9802
  if (!showLeftEllipsis && showRightEllipsis) {
9806
9803
  const leftRange = range(1, 3 + siblings * 2);
9807
- return [...leftRange.map(String), RIGHT_ELLIPSIS, String(total)];
9804
+ return [...leftRange.map(String), '...', String(total)];
9808
9805
  }
9809
9806
  if (showLeftEllipsis && !showRightEllipsis) {
9810
9807
  const rightRange = range(total - (3 + siblings * 2) + 1, total);
9811
- return ['1', LEFT_ELLIPSIS, ...rightRange.map(String)];
9808
+ return ['1', '...', ...rightRange.map(String)];
9812
9809
  }
9813
9810
  const middleRange = range(leftSiblingIndex, rightSiblingIndex);
9814
- return ['1', LEFT_ELLIPSIS, ...middleRange.map(String), RIGHT_ELLIPSIS, String(total)];
9811
+ return ['1', '...', ...middleRange.map(String), '...', String(total)];
9815
9812
  }, ...(ngDevMode ? [{ debugName: "visiblePages" }] : []));
9816
9813
  goToPage(page) {
9817
9814
  if (page >= 1 && page <= this.totalPagesValue() && page !== this._currentPage()) {
@@ -9886,11 +9883,11 @@ class TwPaginationComponent {
9886
9883
  return baseClasses.join(' ');
9887
9884
  }
9888
9885
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TwPaginationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
9889
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: TwPaginationComponent, isStandalone: true, selector: "tw-pagination", inputs: { currentPage: "currentPage", totalPages: "totalPages", totalItems: "totalItems", itemsPerPage: "itemsPerPage", siblingCount: "siblingCount", size: "size", variant: "variant", showFirstLast: "showFirstLast" }, outputs: { pageChange: "pageChange" }, ngImport: i0, template: "@switch (variantValue()) {\n @case ('simple') {\n <div class=\"flex items-center justify-between\">\n <button\n type=\"button\"\n [disabled]=\"currentPageValue() <= 1\"\n [class]=\"simpleButtonClasses(currentPageValue() <= 1)\"\n (click)=\"goToPage(currentPageValue() - 1)\"\n >\n <svg\n class=\"w-5 h-5 mr-1\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M15 19l-7-7 7-7\" />\n </svg>\n Previous\n </button>\n <span class=\"text-sm text-slate-600\">\n Page {{ currentPageValue() }} of {{ totalPagesValue() }}\n </span>\n <button\n type=\"button\"\n [disabled]=\"currentPageValue() >= totalPagesValue()\"\n [class]=\"simpleButtonClasses(currentPageValue() >= totalPagesValue())\"\n (click)=\"goToPage(currentPageValue() + 1)\"\n >\n Next\n <svg\n class=\"w-5 h-5 ml-1\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n </div>\n }\n @default {\n <nav class=\"flex items-center gap-1\" aria-label=\"Pagination\">\n <!-- Previous -->\n <button\n type=\"button\"\n [disabled]=\"currentPageValue() <= 1\"\n [class]=\"navButtonClasses(currentPageValue() <= 1)\"\n (click)=\"goToPage(currentPageValue() - 1)\"\n aria-label=\"Previous page\"\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M15 19l-7-7 7-7\" />\n </svg>\n </button>\n\n <!-- Page numbers -->\n @for (page of visiblePages(); track page) {\n @if (page.startsWith('...')) {\n <span [class]=\"ellipsisClasses()\">...</span>\n } @else {\n <button\n type=\"button\"\n [class]=\"pageButtonClasses(+page === currentPageValue())\"\n [attr.aria-current]=\"+page === currentPageValue() ? 'page' : null\"\n (click)=\"goToPage(+page)\"\n >\n {{ page }}\n </button>\n }\n }\n\n <!-- Next -->\n <button\n type=\"button\"\n [disabled]=\"currentPageValue() >= totalPagesValue()\"\n [class]=\"navButtonClasses(currentPageValue() >= totalPagesValue())\"\n (click)=\"goToPage(currentPageValue() + 1)\"\n aria-label=\"Next page\"\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n </nav>\n }\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }] });
9886
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: TwPaginationComponent, isStandalone: true, selector: "tw-pagination", inputs: { currentPage: "currentPage", totalPages: "totalPages", totalItems: "totalItems", itemsPerPage: "itemsPerPage", siblingCount: "siblingCount", size: "size", variant: "variant", showFirstLast: "showFirstLast" }, outputs: { pageChange: "pageChange" }, ngImport: i0, template: "@switch (variantValue()) {\n @case ('simple') {\n <div class=\"flex items-center justify-between\">\n <button\n type=\"button\"\n [disabled]=\"currentPageValue() <= 1\"\n [class]=\"simpleButtonClasses(currentPageValue() <= 1)\"\n (click)=\"goToPage(currentPageValue() - 1)\"\n >\n <svg\n class=\"w-5 h-5 mr-1\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M15 19l-7-7 7-7\" />\n </svg>\n Previous\n </button>\n <span class=\"text-sm text-slate-600\">\n Page {{ currentPageValue() }} of {{ totalPagesValue() }}\n </span>\n <button\n type=\"button\"\n [disabled]=\"currentPageValue() >= totalPagesValue()\"\n [class]=\"simpleButtonClasses(currentPageValue() >= totalPagesValue())\"\n (click)=\"goToPage(currentPageValue() + 1)\"\n >\n Next\n <svg\n class=\"w-5 h-5 ml-1\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n </div>\n }\n @default {\n <nav class=\"flex items-center gap-1\" aria-label=\"Pagination\">\n <!-- Previous -->\n <button\n type=\"button\"\n [disabled]=\"currentPageValue() <= 1\"\n [class]=\"navButtonClasses(currentPageValue() <= 1)\"\n (click)=\"goToPage(currentPageValue() - 1)\"\n aria-label=\"Previous page\"\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M15 19l-7-7 7-7\" />\n </svg>\n </button>\n\n <!-- Page numbers -->\n @for (page of visiblePages(); track page) {\n @if (page === '...') {\n <span [class]=\"ellipsisClasses()\">...</span>\n } @else {\n <button\n type=\"button\"\n [class]=\"pageButtonClasses(+page === currentPageValue())\"\n [attr.aria-current]=\"+page === currentPageValue() ? 'page' : null\"\n (click)=\"goToPage(+page)\"\n >\n {{ page }}\n </button>\n }\n }\n\n <!-- Next -->\n <button\n type=\"button\"\n [disabled]=\"currentPageValue() >= totalPagesValue()\"\n [class]=\"navButtonClasses(currentPageValue() >= totalPagesValue())\"\n (click)=\"goToPage(currentPageValue() + 1)\"\n aria-label=\"Next page\"\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n </nav>\n }\n}\n", dependencies: [{ kind: "ngmodule", type: CommonModule }] });
9890
9887
  }
9891
9888
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TwPaginationComponent, decorators: [{
9892
9889
  type: Component,
9893
- args: [{ selector: 'tw-pagination', standalone: true, imports: [CommonModule], template: "@switch (variantValue()) {\n @case ('simple') {\n <div class=\"flex items-center justify-between\">\n <button\n type=\"button\"\n [disabled]=\"currentPageValue() <= 1\"\n [class]=\"simpleButtonClasses(currentPageValue() <= 1)\"\n (click)=\"goToPage(currentPageValue() - 1)\"\n >\n <svg\n class=\"w-5 h-5 mr-1\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M15 19l-7-7 7-7\" />\n </svg>\n Previous\n </button>\n <span class=\"text-sm text-slate-600\">\n Page {{ currentPageValue() }} of {{ totalPagesValue() }}\n </span>\n <button\n type=\"button\"\n [disabled]=\"currentPageValue() >= totalPagesValue()\"\n [class]=\"simpleButtonClasses(currentPageValue() >= totalPagesValue())\"\n (click)=\"goToPage(currentPageValue() + 1)\"\n >\n Next\n <svg\n class=\"w-5 h-5 ml-1\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n </div>\n }\n @default {\n <nav class=\"flex items-center gap-1\" aria-label=\"Pagination\">\n <!-- Previous -->\n <button\n type=\"button\"\n [disabled]=\"currentPageValue() <= 1\"\n [class]=\"navButtonClasses(currentPageValue() <= 1)\"\n (click)=\"goToPage(currentPageValue() - 1)\"\n aria-label=\"Previous page\"\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M15 19l-7-7 7-7\" />\n </svg>\n </button>\n\n <!-- Page numbers -->\n @for (page of visiblePages(); track page) {\n @if (page.startsWith('...')) {\n <span [class]=\"ellipsisClasses()\">...</span>\n } @else {\n <button\n type=\"button\"\n [class]=\"pageButtonClasses(+page === currentPageValue())\"\n [attr.aria-current]=\"+page === currentPageValue() ? 'page' : null\"\n (click)=\"goToPage(+page)\"\n >\n {{ page }}\n </button>\n }\n }\n\n <!-- Next -->\n <button\n type=\"button\"\n [disabled]=\"currentPageValue() >= totalPagesValue()\"\n [class]=\"navButtonClasses(currentPageValue() >= totalPagesValue())\"\n (click)=\"goToPage(currentPageValue() + 1)\"\n aria-label=\"Next page\"\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n </nav>\n }\n}\n" }]
9890
+ args: [{ selector: 'tw-pagination', standalone: true, imports: [CommonModule], template: "@switch (variantValue()) {\n @case ('simple') {\n <div class=\"flex items-center justify-between\">\n <button\n type=\"button\"\n [disabled]=\"currentPageValue() <= 1\"\n [class]=\"simpleButtonClasses(currentPageValue() <= 1)\"\n (click)=\"goToPage(currentPageValue() - 1)\"\n >\n <svg\n class=\"w-5 h-5 mr-1\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M15 19l-7-7 7-7\" />\n </svg>\n Previous\n </button>\n <span class=\"text-sm text-slate-600\">\n Page {{ currentPageValue() }} of {{ totalPagesValue() }}\n </span>\n <button\n type=\"button\"\n [disabled]=\"currentPageValue() >= totalPagesValue()\"\n [class]=\"simpleButtonClasses(currentPageValue() >= totalPagesValue())\"\n (click)=\"goToPage(currentPageValue() + 1)\"\n >\n Next\n <svg\n class=\"w-5 h-5 ml-1\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n >\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n </div>\n }\n @default {\n <nav class=\"flex items-center gap-1\" aria-label=\"Pagination\">\n <!-- Previous -->\n <button\n type=\"button\"\n [disabled]=\"currentPageValue() <= 1\"\n [class]=\"navButtonClasses(currentPageValue() <= 1)\"\n (click)=\"goToPage(currentPageValue() - 1)\"\n aria-label=\"Previous page\"\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M15 19l-7-7 7-7\" />\n </svg>\n </button>\n\n <!-- Page numbers -->\n @for (page of visiblePages(); track page) {\n @if (page === '...') {\n <span [class]=\"ellipsisClasses()\">...</span>\n } @else {\n <button\n type=\"button\"\n [class]=\"pageButtonClasses(+page === currentPageValue())\"\n [attr.aria-current]=\"+page === currentPageValue() ? 'page' : null\"\n (click)=\"goToPage(+page)\"\n >\n {{ page }}\n </button>\n }\n }\n\n <!-- Next -->\n <button\n type=\"button\"\n [disabled]=\"currentPageValue() >= totalPagesValue()\"\n [class]=\"navButtonClasses(currentPageValue() >= totalPagesValue())\"\n (click)=\"goToPage(currentPageValue() + 1)\"\n aria-label=\"Next page\"\n >\n <svg class=\"w-5 h-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </button>\n </nav>\n }\n}\n" }]
9894
9891
  }], propDecorators: { currentPage: [{
9895
9892
  type: Input
9896
9893
  }], totalPages: [{
@@ -13210,8 +13207,6 @@ class TwVolumeDialComponent {
13210
13207
  a11y = inject(MusicAccessibilityService);
13211
13208
  dialSvg = viewChild('dialSvg', ...(ngDevMode ? [{ debugName: "dialSvg" }] : []));
13212
13209
  // Inputs
13213
- /** Initial/bound value - supports two-way binding with [(value)] */
13214
- value = model(0, ...(ngDevMode ? [{ debugName: "value" }] : []));
13215
13210
  min = input(0, ...(ngDevMode ? [{ debugName: "min" }] : []));
13216
13211
  max = input(100, ...(ngDevMode ? [{ debugName: "max" }] : []));
13217
13212
  step = input(1, ...(ngDevMode ? [{ debugName: "step" }] : []));
@@ -13238,8 +13233,10 @@ class TwVolumeDialComponent {
13238
13233
  announceChanges = input(true, ...(ngDevMode ? [{ debugName: "announceChanges" }] : [])); // Announce value changes to screen readers
13239
13234
  reducedMotion = input('auto', ...(ngDevMode ? [{ debugName: "reducedMotion" }] : [])); // Respect reduced motion preference
13240
13235
  // Outputs
13241
- // Note: valueChange is automatically created by the model() signal
13236
+ valueChange = output();
13242
13237
  hapticTrigger = output(); // Haptic feedback trigger points
13238
+ // Internal state
13239
+ value = signal(0, ...(ngDevMode ? [{ debugName: "value" }] : []));
13243
13240
  isDragging = false;
13244
13241
  startAngle = 0;
13245
13242
  startValue = 0;
@@ -13490,6 +13487,7 @@ class TwVolumeDialComponent {
13490
13487
  if (newValue !== this.value()) {
13491
13488
  this.value.set(newValue);
13492
13489
  this.onChangeFn(newValue);
13490
+ this.valueChange.emit(newValue);
13493
13491
  // Announce to screen readers (debounced)
13494
13492
  if (this.announceChanges()) {
13495
13493
  this.a11y.announceValueChange(this.label() || 'Volume', this.displayValue(), this.unit());
@@ -13510,7 +13508,7 @@ class TwVolumeDialComponent {
13510
13508
  // Disabled state is handled via input
13511
13509
  }
13512
13510
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TwVolumeDialComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
13513
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: TwVolumeDialComponent, isStandalone: true, selector: "tw-volume-dial", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showValue: { classPropertyName: "showValue", publicName: "showValue", isSignal: true, isRequired: false, transformFunction: null }, showTicks: { classPropertyName: "showTicks", publicName: "showTicks", isSignal: true, isRequired: false, transformFunction: null }, showLabel: { classPropertyName: "showLabel", publicName: "showLabel", isSignal: true, isRequired: false, transformFunction: null }, showLedRing: { classPropertyName: "showLedRing", publicName: "showLedRing", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, unit: { classPropertyName: "unit", publicName: "unit", isSignal: true, isRequired: false, transformFunction: null }, centerDetent: { classPropertyName: "centerDetent", publicName: "centerDetent", isSignal: true, isRequired: false, transformFunction: null }, detentValue: { classPropertyName: "detentValue", publicName: "detentValue", isSignal: true, isRequired: false, transformFunction: null }, classOverride: { classPropertyName: "classOverride", publicName: "classOverride", isSignal: true, isRequired: false, transformFunction: null }, customSize: { classPropertyName: "customSize", publicName: "customSize", isSignal: true, isRequired: false, transformFunction: null }, customStrokeWidth: { classPropertyName: "customStrokeWidth", publicName: "customStrokeWidth", isSignal: true, isRequired: false, transformFunction: null }, hapticFeedback: { classPropertyName: "hapticFeedback", publicName: "hapticFeedback", isSignal: true, isRequired: false, transformFunction: null }, touchGuard: { classPropertyName: "touchGuard", publicName: "touchGuard", isSignal: true, isRequired: false, transformFunction: null }, minTouchDuration: { classPropertyName: "minTouchDuration", publicName: "minTouchDuration", isSignal: true, isRequired: false, transformFunction: null }, announceChanges: { classPropertyName: "announceChanges", publicName: "announceChanges", isSignal: true, isRequired: false, transformFunction: null }, reducedMotion: { classPropertyName: "reducedMotion", publicName: "reducedMotion", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", hapticTrigger: "hapticTrigger" }, host: { classAttribute: "inline-block" }, providers: [
13511
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: TwVolumeDialComponent, isStandalone: true, selector: "tw-volume-dial", inputs: { min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, showValue: { classPropertyName: "showValue", publicName: "showValue", isSignal: true, isRequired: false, transformFunction: null }, showTicks: { classPropertyName: "showTicks", publicName: "showTicks", isSignal: true, isRequired: false, transformFunction: null }, showLabel: { classPropertyName: "showLabel", publicName: "showLabel", isSignal: true, isRequired: false, transformFunction: null }, showLedRing: { classPropertyName: "showLedRing", publicName: "showLedRing", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, unit: { classPropertyName: "unit", publicName: "unit", isSignal: true, isRequired: false, transformFunction: null }, centerDetent: { classPropertyName: "centerDetent", publicName: "centerDetent", isSignal: true, isRequired: false, transformFunction: null }, detentValue: { classPropertyName: "detentValue", publicName: "detentValue", isSignal: true, isRequired: false, transformFunction: null }, classOverride: { classPropertyName: "classOverride", publicName: "classOverride", isSignal: true, isRequired: false, transformFunction: null }, customSize: { classPropertyName: "customSize", publicName: "customSize", isSignal: true, isRequired: false, transformFunction: null }, customStrokeWidth: { classPropertyName: "customStrokeWidth", publicName: "customStrokeWidth", isSignal: true, isRequired: false, transformFunction: null }, hapticFeedback: { classPropertyName: "hapticFeedback", publicName: "hapticFeedback", isSignal: true, isRequired: false, transformFunction: null }, touchGuard: { classPropertyName: "touchGuard", publicName: "touchGuard", isSignal: true, isRequired: false, transformFunction: null }, minTouchDuration: { classPropertyName: "minTouchDuration", publicName: "minTouchDuration", isSignal: true, isRequired: false, transformFunction: null }, announceChanges: { classPropertyName: "announceChanges", publicName: "announceChanges", isSignal: true, isRequired: false, transformFunction: null }, reducedMotion: { classPropertyName: "reducedMotion", publicName: "reducedMotion", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange", hapticTrigger: "hapticTrigger" }, host: { classAttribute: "inline-block" }, providers: [
13514
13512
  {
13515
13513
  provide: NG_VALUE_ACCESSOR,
13516
13514
  useExisting: forwardRef(() => TwVolumeDialComponent),
@@ -13529,7 +13527,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
13529
13527
  ], host: {
13530
13528
  class: 'inline-block',
13531
13529
  }, template: "<div\n [class]=\"containerClasses()\"\n [style.width.px]=\"sizeConfig().size\"\n [style.height.px]=\"sizeConfig().size + (showValue() ? 24 : 0)\"\n>\n <!-- SVG Dial -->\n <svg\n #dialSvg\n [attr.width]=\"sizeConfig().size\"\n [attr.height]=\"sizeConfig().size\"\n [attr.viewBox]=\"'0 0 ' + sizeConfig().size + ' ' + sizeConfig().size\"\n class=\"cursor-pointer select-none\"\n [class.opacity-50]=\"disabled()\"\n [class.cursor-not-allowed]=\"disabled()\"\n (mousedown)=\"onMouseDown($event)\"\n (touchstart)=\"onTouchStart($event)\"\n (keydown)=\"onKeyDown($event)\"\n [attr.tabindex]=\"disabled() ? -1 : 0\"\n role=\"slider\"\n [attr.aria-valuenow]=\"value()\"\n [attr.aria-valuemin]=\"min()\"\n [attr.aria-valuemax]=\"max()\"\n [attr.aria-label]=\"label() || 'Volume dial'\"\n [attr.aria-disabled]=\"disabled()\"\n >\n <!-- Track (background arc) -->\n <path\n [attr.d]=\"trackPath()\"\n fill=\"none\"\n [attr.stroke-width]=\"sizeConfig().strokeWidth\"\n [class]=\"variantConfig().track\"\n stroke-linecap=\"round\"\n />\n\n <!-- Fill (value arc) -->\n <path\n [attr.d]=\"fillPath()\"\n fill=\"none\"\n [attr.stroke-width]=\"sizeConfig().strokeWidth\"\n [class]=\"variantConfig().fill\"\n stroke-linecap=\"round\"\n />\n\n <!-- Tick marks -->\n @if (showTicks()) {\n @for (tick of tickMarks(); track tick.angle) {\n <line\n [attr.x1]=\"tick.x1\"\n [attr.y1]=\"tick.y1\"\n [attr.x2]=\"tick.x2\"\n [attr.y2]=\"tick.y2\"\n [class]=\"\n tick.isMajor\n ? 'stroke-slate-400 dark:stroke-slate-500'\n : 'stroke-slate-300 dark:stroke-slate-600'\n \"\n [attr.stroke-width]=\"tick.isMajor ? 2 : 1\"\n />\n }\n }\n\n <!-- Knob center -->\n <circle\n [attr.cx]=\"center()\"\n [attr.cy]=\"center()\"\n [attr.r]=\"knobRadius()\"\n [class]=\"variantConfig().knob\"\n stroke-width=\"2\"\n />\n\n <!-- Indicator line -->\n <line\n [attr.x1]=\"center()\"\n [attr.y1]=\"center()\"\n [attr.x2]=\"indicatorEnd().x\"\n [attr.y2]=\"indicatorEnd().y\"\n [class]=\"variantConfig().indicator\"\n [attr.stroke-width]=\"sizeConfig().strokeWidth - 1\"\n stroke-linecap=\"round\"\n />\n\n <!-- LED ring (for led variant) -->\n @if (variant() === 'led' && showLedRing()) {\n @for (led of ledSegments(); track led.index) {\n <circle\n [attr.cx]=\"led.x\"\n [attr.cy]=\"led.y\"\n [attr.r]=\"3\"\n [class]=\"led.active ? 'fill-emerald-400' : 'fill-slate-700'\"\n />\n }\n }\n </svg>\n\n <!-- Value display -->\n @if (showValue()) {\n <div [class]=\"valueClasses()\">{{ displayValue() }}{{ unit() }}</div>\n }\n\n <!-- Label -->\n @if (label() && showLabel()) {\n <div\n class=\"absolute -bottom-6 left-1/2 -translate-x-1/2 text-xs text-slate-500 dark:text-slate-400 whitespace-nowrap\"\n >\n {{ label() }}\n </div>\n }\n</div>\n" }]
13532
- }], propDecorators: { dialSvg: [{ type: i0.ViewChild, args: ['dialSvg', { isSignal: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], showValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "showValue", required: false }] }], showTicks: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTicks", required: false }] }], showLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLabel", required: false }] }], showLedRing: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLedRing", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], unit: [{ type: i0.Input, args: [{ isSignal: true, alias: "unit", required: false }] }], centerDetent: [{ type: i0.Input, args: [{ isSignal: true, alias: "centerDetent", required: false }] }], detentValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "detentValue", required: false }] }], classOverride: [{ type: i0.Input, args: [{ isSignal: true, alias: "classOverride", required: false }] }], customSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "customSize", required: false }] }], customStrokeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "customStrokeWidth", required: false }] }], hapticFeedback: [{ type: i0.Input, args: [{ isSignal: true, alias: "hapticFeedback", required: false }] }], touchGuard: [{ type: i0.Input, args: [{ isSignal: true, alias: "touchGuard", required: false }] }], minTouchDuration: [{ type: i0.Input, args: [{ isSignal: true, alias: "minTouchDuration", required: false }] }], announceChanges: [{ type: i0.Input, args: [{ isSignal: true, alias: "announceChanges", required: false }] }], reducedMotion: [{ type: i0.Input, args: [{ isSignal: true, alias: "reducedMotion", required: false }] }], hapticTrigger: [{ type: i0.Output, args: ["hapticTrigger"] }] } });
13530
+ }], propDecorators: { dialSvg: [{ type: i0.ViewChild, args: ['dialSvg', { isSignal: true }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], showValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "showValue", required: false }] }], showTicks: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTicks", required: false }] }], showLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLabel", required: false }] }], showLedRing: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLedRing", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], unit: [{ type: i0.Input, args: [{ isSignal: true, alias: "unit", required: false }] }], centerDetent: [{ type: i0.Input, args: [{ isSignal: true, alias: "centerDetent", required: false }] }], detentValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "detentValue", required: false }] }], classOverride: [{ type: i0.Input, args: [{ isSignal: true, alias: "classOverride", required: false }] }], customSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "customSize", required: false }] }], customStrokeWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "customStrokeWidth", required: false }] }], hapticFeedback: [{ type: i0.Input, args: [{ isSignal: true, alias: "hapticFeedback", required: false }] }], touchGuard: [{ type: i0.Input, args: [{ isSignal: true, alias: "touchGuard", required: false }] }], minTouchDuration: [{ type: i0.Input, args: [{ isSignal: true, alias: "minTouchDuration", required: false }] }], announceChanges: [{ type: i0.Input, args: [{ isSignal: true, alias: "announceChanges", required: false }] }], reducedMotion: [{ type: i0.Input, args: [{ isSignal: true, alias: "reducedMotion", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], hapticTrigger: [{ type: i0.Output, args: ["hapticTrigger"] }] } });
13533
13531
 
13534
13532
  // RMS calculation window size (samples)
13535
13533
  const RMS_WINDOW_SIZE = 1024;
@@ -19135,7 +19133,7 @@ class TwChannelStripComponent {
19135
19133
  useExisting: forwardRef(() => TwChannelStripComponent),
19136
19134
  multi: true,
19137
19135
  },
19138
- ], ngImport: i0, template: "<div [class]=\"containerClasses()\">\n <!-- Channel Label with Signal Indicator -->\n @if (showLabel()) {\n <div [class]=\"labelClasses()\" class=\"relative flex items-center justify-center gap-1\">\n <!-- Signal Present Indicator -->\n @if (showSignalIndicator()) {\n <div\n class=\"w-2 h-2 rounded-full transition-all duration-150\"\n [class.bg-green-500]=\"signalPresent()\"\n [class.shadow-[0_0_6px_rgba(34,197,94,0.8)]]=\"signalPresent()\"\n [class.bg-slate-600]=\"!signalPresent()\"\n title=\"Signal Present\"\n ></div>\n }\n <span class=\"text-slate-400\">{{ channelNumber() }}</span>\n <span class=\"text-white\">{{ label() }}</span>\n </div>\n }\n\n <!-- Input Gain Control -->\n @if (showInputGain()) {\n <div class=\"flex flex-col items-center gap-1\">\n <tw-volume-dial\n [ngModel]=\"inputGain()\"\n (ngModelChange)=\"onInputGainChange($event)\"\n [min]=\"-20\"\n [max]=\"20\"\n [step]=\"1\"\n [size]=\"size() === 'lg' ? 'sm' : 'xs'\"\n [variant]=\"variant() === 'vintage' ? 'vintage' : 'minimal'\"\n [disabled]=\"disabled()\"\n [centerDetent]=\"true\"\n [detentValue]=\"0\"\n label=\"\"\n [showValue]=\"false\"\n ></tw-volume-dial>\n <span class=\"text-[9px] text-slate-500 font-mono\">{{ formatGain(inputGain()) }}dB</span>\n <span class=\"text-[8px] text-slate-600\">GAIN</span>\n </div>\n }\n\n <!-- Record Button -->\n @if (showRecord()) {\n <button\n type=\"button\"\n [class]=\"buttonSize()\"\n class=\"rounded-full flex items-center justify-center font-bold transition-all\"\n [class.bg-red-600]=\"record()\"\n [class.text-white]=\"record()\"\n [class.bg-slate-700]=\"!record()\"\n [class.text-slate-400]=\"!record()\"\n [class.hover:bg-red-500]=\"!record()\"\n [class.animate-pulse]=\"record()\"\n [disabled]=\"disabled()\"\n (click)=\"toggleRecord()\"\n title=\"Record Arm\"\n >\n R\n </button>\n }\n\n <!-- Aux Send Knobs -->\n @if (showAuxSends()) {\n <div class=\"flex flex-col gap-1 w-full border-t border-slate-700/50 pt-2\">\n <span class=\"text-[8px] text-slate-500 text-center\">SENDS</span>\n <div\n class=\"grid gap-1\"\n [class.grid-cols-2]=\"auxSendCount() > 2\"\n [class.grid-cols-1]=\"auxSendCount() <= 2\"\n >\n @for (aux of auxSendArray(); track aux.index) {\n <div class=\"flex flex-col items-center\">\n <tw-volume-dial\n [ngModel]=\"aux.value\"\n (ngModelChange)=\"onAuxSendChange(aux.index, $event)\"\n [min]=\"0\"\n [max]=\"100\"\n [step]=\"1\"\n size=\"xs\"\n [variant]=\"variant() === 'vintage' ? 'vintage' : 'minimal'\"\n [disabled]=\"disabled() || mute()\"\n label=\"\"\n [showValue]=\"false\"\n ></tw-volume-dial>\n <span class=\"text-[8px] text-slate-500 truncate max-w-full\">{{ aux.label }}</span>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- VU Meter -->\n @if (showMeter()) {\n <div class=\"flex-1 flex items-center justify-center py-1\">\n @if (stereo()) {\n <tw-vu-meter\n [leftValue]=\"meterLevel()\"\n [rightValue]=\"meterLevelRight()\"\n [stereo]=\"true\"\n [segmentCount]=\"size() === 'sm' ? 8 : 12\"\n [height]=\"size() === 'sm' ? 60 : size() === 'lg' ? 100 : 80\"\n variant=\"led\"\n ></tw-vu-meter>\n } @else {\n <tw-vu-meter\n [value]=\"meterLevel()\"\n [segmentCount]=\"size() === 'sm' ? 8 : 12\"\n [height]=\"size() === 'sm' ? 60 : size() === 'lg' ? 100 : 80\"\n variant=\"led\"\n ></tw-vu-meter>\n }\n </div>\n }\n\n <!-- Volume Fader / Dial -->\n <tw-volume-dial\n [ngModel]=\"volume()\"\n (ngModelChange)=\"onVolumeChange($event)\"\n [min]=\"0\"\n [max]=\"100\"\n [step]=\"1\"\n [size]=\"dialSize()\"\n [variant]=\"variant() === 'vintage' ? 'vintage' : 'modern'\"\n [disabled]=\"disabled() || mute()\"\n label=\"\"\n [showValue]=\"size() !== 'sm'\"\n ></tw-volume-dial>\n\n <!-- Pan Control -->\n @if (showPan()) {\n <tw-volume-dial\n [ngModel]=\"pan()\"\n (ngModelChange)=\"onPanChange($event)\"\n [min]=\"-100\"\n [max]=\"100\"\n [step]=\"1\"\n [size]=\"size() === 'lg' ? 'md' : 'sm'\"\n [variant]=\"variant() === 'vintage' ? 'vintage' : 'minimal'\"\n [disabled]=\"disabled()\"\n [centerDetent]=\"true\"\n [detentValue]=\"0\"\n label=\"\"\n [showValue]=\"false\"\n ></tw-volume-dial>\n <span class=\"text-[10px] text-slate-500\">PAN</span>\n }\n\n <!-- Mute / Solo Buttons -->\n @if (showMuteSolo()) {\n <div class=\"flex gap-1\">\n <button\n type=\"button\"\n [class]=\"buttonSize()\"\n class=\"rounded flex items-center justify-center font-bold transition-all\"\n [class.bg-amber-500]=\"mute()\"\n [class.text-black]=\"mute()\"\n [class.bg-slate-700]=\"!mute()\"\n [class.text-slate-400]=\"!mute()\"\n [class.hover:bg-amber-400]=\"!mute()\"\n [disabled]=\"disabled()\"\n (click)=\"toggleMute()\"\n title=\"Mute\"\n >\n M\n </button>\n <button\n type=\"button\"\n [class]=\"buttonSize()\"\n class=\"rounded flex items-center justify-center font-bold transition-all\"\n [class.bg-green-500]=\"solo()\"\n [class.text-black]=\"solo()\"\n [class.bg-slate-700]=\"!solo()\"\n [class.text-slate-400]=\"!solo()\"\n [class.hover:bg-green-400]=\"!solo()\"\n [disabled]=\"disabled()\"\n (click)=\"toggleSolo()\"\n title=\"Solo\"\n >\n S\n </button>\n </div>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: TwVolumeDialComponent, selector: "tw-volume-dial", inputs: ["value", "min", "max", "step", "variant", "size", "disabled", "showValue", "showTicks", "showLabel", "showLedRing", "label", "unit", "centerDetent", "detentValue", "classOverride", "customSize", "customStrokeWidth", "hapticFeedback", "touchGuard", "minTouchDuration", "announceChanges", "reducedMotion"], outputs: ["valueChange", "hapticTrigger"] }, { kind: "component", type: TwVuMeterComponent, selector: "tw-vu-meter", inputs: ["value", "leftValue", "rightValue", "min", "max", "stereo", "variant", "orientation", "meterMode", "segmentCount", "height", "width", "barWidth", "gapSize", "rmsDecayRate", "customHeight", "customWidth", "customBarWidth", "customGapSize", "showPeak", "peakHoldTime", "peakDecayRate", "showClip", "clipThreshold", "showScale", "showChannelLabels", "label", "labelPosition", "yellowThreshold", "redThreshold", "classOverride"], outputs: ["clipDetected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
19136
+ ], ngImport: i0, template: "<div [class]=\"containerClasses()\">\n <!-- Channel Label with Signal Indicator -->\n @if (showLabel()) {\n <div [class]=\"labelClasses()\" class=\"relative flex items-center justify-center gap-1\">\n <!-- Signal Present Indicator -->\n @if (showSignalIndicator()) {\n <div\n class=\"w-2 h-2 rounded-full transition-all duration-150\"\n [class.bg-green-500]=\"signalPresent()\"\n [class.shadow-[0_0_6px_rgba(34,197,94,0.8)]]=\"signalPresent()\"\n [class.bg-slate-600]=\"!signalPresent()\"\n title=\"Signal Present\"\n ></div>\n }\n <span class=\"text-slate-400\">{{ channelNumber() }}</span>\n <span class=\"text-white\">{{ label() }}</span>\n </div>\n }\n\n <!-- Input Gain Control -->\n @if (showInputGain()) {\n <div class=\"flex flex-col items-center gap-1\">\n <tw-volume-dial\n [ngModel]=\"inputGain()\"\n (ngModelChange)=\"onInputGainChange($event)\"\n [min]=\"-20\"\n [max]=\"20\"\n [step]=\"1\"\n [size]=\"size() === 'lg' ? 'sm' : 'xs'\"\n [variant]=\"variant() === 'vintage' ? 'vintage' : 'minimal'\"\n [disabled]=\"disabled()\"\n [centerDetent]=\"true\"\n [detentValue]=\"0\"\n label=\"\"\n [showValue]=\"false\"\n ></tw-volume-dial>\n <span class=\"text-[9px] text-slate-500 font-mono\">{{ formatGain(inputGain()) }}dB</span>\n <span class=\"text-[8px] text-slate-600\">GAIN</span>\n </div>\n }\n\n <!-- Record Button -->\n @if (showRecord()) {\n <button\n type=\"button\"\n [class]=\"buttonSize()\"\n class=\"rounded-full flex items-center justify-center font-bold transition-all\"\n [class.bg-red-600]=\"record()\"\n [class.text-white]=\"record()\"\n [class.bg-slate-700]=\"!record()\"\n [class.text-slate-400]=\"!record()\"\n [class.hover:bg-red-500]=\"!record()\"\n [class.animate-pulse]=\"record()\"\n [disabled]=\"disabled()\"\n (click)=\"toggleRecord()\"\n title=\"Record Arm\"\n >\n R\n </button>\n }\n\n <!-- Aux Send Knobs -->\n @if (showAuxSends()) {\n <div class=\"flex flex-col gap-1 w-full border-t border-slate-700/50 pt-2\">\n <span class=\"text-[8px] text-slate-500 text-center\">SENDS</span>\n <div\n class=\"grid gap-1\"\n [class.grid-cols-2]=\"auxSendCount() > 2\"\n [class.grid-cols-1]=\"auxSendCount() <= 2\"\n >\n @for (aux of auxSendArray(); track aux.index) {\n <div class=\"flex flex-col items-center\">\n <tw-volume-dial\n [ngModel]=\"aux.value\"\n (ngModelChange)=\"onAuxSendChange(aux.index, $event)\"\n [min]=\"0\"\n [max]=\"100\"\n [step]=\"1\"\n size=\"xs\"\n [variant]=\"variant() === 'vintage' ? 'vintage' : 'minimal'\"\n [disabled]=\"disabled() || mute()\"\n label=\"\"\n [showValue]=\"false\"\n ></tw-volume-dial>\n <span class=\"text-[8px] text-slate-500 truncate max-w-full\">{{ aux.label }}</span>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- VU Meter -->\n @if (showMeter()) {\n <div class=\"flex-1 flex items-center justify-center py-1\">\n @if (stereo()) {\n <tw-vu-meter\n [leftValue]=\"meterLevel()\"\n [rightValue]=\"meterLevelRight()\"\n [stereo]=\"true\"\n [segmentCount]=\"size() === 'sm' ? 8 : 12\"\n [height]=\"size() === 'sm' ? 60 : size() === 'lg' ? 100 : 80\"\n variant=\"led\"\n ></tw-vu-meter>\n } @else {\n <tw-vu-meter\n [value]=\"meterLevel()\"\n [segmentCount]=\"size() === 'sm' ? 8 : 12\"\n [height]=\"size() === 'sm' ? 60 : size() === 'lg' ? 100 : 80\"\n variant=\"led\"\n ></tw-vu-meter>\n }\n </div>\n }\n\n <!-- Volume Fader / Dial -->\n <tw-volume-dial\n [ngModel]=\"volume()\"\n (ngModelChange)=\"onVolumeChange($event)\"\n [min]=\"0\"\n [max]=\"100\"\n [step]=\"1\"\n [size]=\"dialSize()\"\n [variant]=\"variant() === 'vintage' ? 'vintage' : 'modern'\"\n [disabled]=\"disabled() || mute()\"\n label=\"\"\n [showValue]=\"size() !== 'sm'\"\n ></tw-volume-dial>\n\n <!-- Pan Control -->\n @if (showPan()) {\n <tw-volume-dial\n [ngModel]=\"pan()\"\n (ngModelChange)=\"onPanChange($event)\"\n [min]=\"-100\"\n [max]=\"100\"\n [step]=\"1\"\n [size]=\"size() === 'lg' ? 'md' : 'sm'\"\n [variant]=\"variant() === 'vintage' ? 'vintage' : 'minimal'\"\n [disabled]=\"disabled()\"\n [centerDetent]=\"true\"\n [detentValue]=\"0\"\n label=\"\"\n [showValue]=\"false\"\n ></tw-volume-dial>\n <span class=\"text-[10px] text-slate-500\">PAN</span>\n }\n\n <!-- Mute / Solo Buttons -->\n @if (showMuteSolo()) {\n <div class=\"flex gap-1\">\n <button\n type=\"button\"\n [class]=\"buttonSize()\"\n class=\"rounded flex items-center justify-center font-bold transition-all\"\n [class.bg-amber-500]=\"mute()\"\n [class.text-black]=\"mute()\"\n [class.bg-slate-700]=\"!mute()\"\n [class.text-slate-400]=\"!mute()\"\n [class.hover:bg-amber-400]=\"!mute()\"\n [disabled]=\"disabled()\"\n (click)=\"toggleMute()\"\n title=\"Mute\"\n >\n M\n </button>\n <button\n type=\"button\"\n [class]=\"buttonSize()\"\n class=\"rounded flex items-center justify-center font-bold transition-all\"\n [class.bg-green-500]=\"solo()\"\n [class.text-black]=\"solo()\"\n [class.bg-slate-700]=\"!solo()\"\n [class.text-slate-400]=\"!solo()\"\n [class.hover:bg-green-400]=\"!solo()\"\n [disabled]=\"disabled()\"\n (click)=\"toggleSolo()\"\n title=\"Solo\"\n >\n S\n </button>\n </div>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: TwVolumeDialComponent, selector: "tw-volume-dial", inputs: ["min", "max", "step", "variant", "size", "disabled", "showValue", "showTicks", "showLabel", "showLedRing", "label", "unit", "centerDetent", "detentValue", "classOverride", "customSize", "customStrokeWidth", "hapticFeedback", "touchGuard", "minTouchDuration", "announceChanges", "reducedMotion"], outputs: ["valueChange", "hapticTrigger"] }, { kind: "component", type: TwVuMeterComponent, selector: "tw-vu-meter", inputs: ["value", "leftValue", "rightValue", "min", "max", "stereo", "variant", "orientation", "meterMode", "segmentCount", "height", "width", "barWidth", "gapSize", "rmsDecayRate", "customHeight", "customWidth", "customBarWidth", "customGapSize", "showPeak", "peakHoldTime", "peakDecayRate", "showClip", "clipThreshold", "showScale", "showChannelLabels", "label", "labelPosition", "yellowThreshold", "redThreshold", "classOverride"], outputs: ["clipDetected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
19139
19137
  }
19140
19138
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TwChannelStripComponent, decorators: [{
19141
19139
  type: Component,
@@ -32083,6 +32081,17 @@ const TW_MUSIC_COMPONENTS = [
32083
32081
  // Convenience array for directives only
32084
32082
  const TW_MUSIC_DIRECTIVES = [TwTouchGuardDirective, TwMidiLearnDirective];
32085
32083
 
32084
+ /**
32085
+ * Utility for truly dynamic imports that bundlers cannot statically analyze.
32086
+ * This prevents build errors when optional peer dependencies (Tauri/Electron) are not installed.
32087
+ */
32088
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
32089
+ function dynamicImport(modulePath) {
32090
+ // Use Function constructor to hide the import from static analysis
32091
+ // This prevents bundlers from trying to resolve these modules at build time
32092
+ return new Function('modulePath', 'return import(modulePath)')(modulePath);
32093
+ }
32094
+
32086
32095
  /**
32087
32096
  * Service for detecting and interacting with the native platform
32088
32097
  * Supports Tauri, Electron, and web browsers
@@ -32131,7 +32140,7 @@ class NativeAppPlatformService {
32131
32140
  return 'web';
32132
32141
  }
32133
32142
  checkTauri() {
32134
- return typeof window !== 'undefined' && '__TAURI__' in window;
32143
+ return false; // Tauri support disabled
32135
32144
  }
32136
32145
  checkElectron() {
32137
32146
  return (typeof window !== 'undefined' &&
@@ -32170,18 +32179,12 @@ class NativeAppPlatformService {
32170
32179
  // Window control methods
32171
32180
  async minimize() {
32172
32181
  if (this._isTauri()) {
32173
- try {
32174
- const tauriWindow = await import('@tauri-apps/api/window');
32175
- const win = tauriWindow.getCurrentWindow();
32176
- await win.minimize();
32177
- }
32178
- catch (e) {
32179
- console.warn('Tauri minimize failed:', e);
32180
- }
32182
+ // Tauri support disabled
32183
+ return;
32181
32184
  }
32182
32185
  else if (this._isElectron()) {
32183
32186
  try {
32184
- const electron = await import('electron');
32187
+ const electron = await dynamicImport('electron');
32185
32188
  electron.ipcRenderer.send('window-minimize');
32186
32189
  }
32187
32190
  catch (e) {
@@ -32193,7 +32196,7 @@ class NativeAppPlatformService {
32193
32196
  async maximize() {
32194
32197
  if (this._isTauri()) {
32195
32198
  try {
32196
- const tauriWindow = await import('@tauri-apps/api/window');
32199
+ const tauriWindow = await dynamicImport('@tauri-apps/api/window');
32197
32200
  const win = tauriWindow.getCurrentWindow();
32198
32201
  if (await win.isMaximized()) {
32199
32202
  await win.unmaximize();
@@ -32210,7 +32213,7 @@ class NativeAppPlatformService {
32210
32213
  }
32211
32214
  else if (this._isElectron()) {
32212
32215
  try {
32213
- const electron = await import('electron');
32216
+ const electron = await dynamicImport('electron');
32214
32217
  electron.ipcRenderer.send('window-maximize');
32215
32218
  }
32216
32219
  catch (e) {
@@ -32221,7 +32224,7 @@ class NativeAppPlatformService {
32221
32224
  async close() {
32222
32225
  if (this._isTauri()) {
32223
32226
  try {
32224
- const tauriWindow = await import('@tauri-apps/api/window');
32227
+ const tauriWindow = await dynamicImport('@tauri-apps/api/window');
32225
32228
  const win = tauriWindow.getCurrentWindow();
32226
32229
  await win.close();
32227
32230
  }
@@ -32231,7 +32234,7 @@ class NativeAppPlatformService {
32231
32234
  }
32232
32235
  else if (this._isElectron()) {
32233
32236
  try {
32234
- const electron = await import('electron');
32237
+ const electron = await dynamicImport('electron');
32235
32238
  electron.ipcRenderer.send('window-close');
32236
32239
  }
32237
32240
  catch (e) {
@@ -32245,7 +32248,7 @@ class NativeAppPlatformService {
32245
32248
  async toggleFullscreen() {
32246
32249
  if (this._isTauri()) {
32247
32250
  try {
32248
- const tauriWindow = await import('@tauri-apps/api/window');
32251
+ const tauriWindow = await dynamicImport('@tauri-apps/api/window');
32249
32252
  const win = tauriWindow.getCurrentWindow();
32250
32253
  if (await win.isFullscreen()) {
32251
32254
  await win.setFullscreen(false);
@@ -32260,7 +32263,7 @@ class NativeAppPlatformService {
32260
32263
  }
32261
32264
  else if (this._isElectron()) {
32262
32265
  try {
32263
- const electron = await import('electron');
32266
+ const electron = await dynamicImport('electron');
32264
32267
  electron.ipcRenderer.send('window-fullscreen');
32265
32268
  }
32266
32269
  catch (e) {
@@ -32279,7 +32282,7 @@ class NativeAppPlatformService {
32279
32282
  async setTitle(title) {
32280
32283
  if (this._isTauri()) {
32281
32284
  try {
32282
- const tauriWindow = await import('@tauri-apps/api/window');
32285
+ const tauriWindow = await dynamicImport('@tauri-apps/api/window');
32283
32286
  const win = tauriWindow.getCurrentWindow();
32284
32287
  await win.setTitle(title);
32285
32288
  }
@@ -32289,7 +32292,7 @@ class NativeAppPlatformService {
32289
32292
  }
32290
32293
  else if (this._isElectron()) {
32291
32294
  try {
32292
- const electron = await import('electron');
32295
+ const electron = await dynamicImport('electron');
32293
32296
  electron.ipcRenderer.send('window-set-title', title);
32294
32297
  }
32295
32298
  catch (e) {
@@ -40180,7 +40183,7 @@ class TwTerminalComponent {
40180
40183
  // Computed classes
40181
40184
  containerClasses = computed(() => {
40182
40185
  const variant = this.variant();
40183
- const base = 'flex flex-col h-full overflow-hidden';
40186
+ const base = 'flex flex-col h-full overflow-hidden rounded-lg';
40184
40187
  const variantClasses = {
40185
40188
  default: 'bg-gray-900 text-gray-100',
40186
40189
  dark: 'bg-black text-gray-200',
@@ -40339,7 +40342,7 @@ class TwTerminalComponent {
40339
40342
  <!-- Input area -->
40340
40343
  @if (showInput()) {
40341
40344
  <div
40342
- class="flex items-center gap-2 px-4 py-2 border-t border-gray-700 bg-gray-800"
40345
+ class="flex items-center gap-2 px-4 py-2 border-t border-gray-700 bg-gray-800 rounded-b-lg"
40343
40346
  >
40344
40347
  <span class="text-green-400 font-mono">{{ prompt() }}</span>
40345
40348
  <input
@@ -40437,7 +40440,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
40437
40440
  <!-- Input area -->
40438
40441
  @if (showInput()) {
40439
40442
  <div
40440
- class="flex items-center gap-2 px-4 py-2 border-t border-gray-700 bg-gray-800"
40443
+ class="flex items-center gap-2 px-4 py-2 border-t border-gray-700 bg-gray-800 rounded-b-lg"
40441
40444
  >
40442
40445
  <span class="text-green-400 font-mono">{{ prompt() }}</span>
40443
40446
  <input
@@ -40567,7 +40570,7 @@ class TwLogViewerComponent {
40567
40570
  }
40568
40571
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: TwLogViewerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
40569
40572
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.6", type: TwLogViewerComponent, isStandalone: true, selector: "tw-log-viewer", inputs: { entries: { classPropertyName: "entries", publicName: "entries", isSignal: true, isRequired: false, transformFunction: null }, showSource: { classPropertyName: "showSource", publicName: "showSource", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { entrySelect: "entrySelect", clearLogs: "clearLogs", exportClick: "exportClick" }, host: { classAttribute: "tw-log-viewer block" }, ngImport: i0, template: `
40570
- <div class="flex flex-col h-full bg-white dark:bg-gray-900 overflow-hidden">
40573
+ <div class="flex flex-col h-full bg-white dark:bg-gray-900 rounded-lg overflow-hidden">
40571
40574
  <!-- Toolbar -->
40572
40575
  <div
40573
40576
  class="flex items-center gap-2 px-3 py-2 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800"
@@ -40717,7 +40720,7 @@ class TwLogViewerComponent {
40717
40720
 
40718
40721
  <!-- Status bar -->
40719
40722
  <div
40720
- class="flex items-center justify-between px-3 py-1.5 text-xs text-gray-500 dark:text-gray-400 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800"
40723
+ class="flex items-center justify-between px-3 py-1.5 text-xs text-gray-500 dark:text-gray-400 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 rounded-b-lg"
40721
40724
  >
40722
40725
  <span>{{ filteredEntries().length }} / {{ entries().length }} entries</span>
40723
40726
  <span>
@@ -40738,7 +40741,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
40738
40741
  standalone: true,
40739
40742
  imports: [CommonModule, FormsModule],
40740
40743
  template: `
40741
- <div class="flex flex-col h-full bg-white dark:bg-gray-900 overflow-hidden">
40744
+ <div class="flex flex-col h-full bg-white dark:bg-gray-900 rounded-lg overflow-hidden">
40742
40745
  <!-- Toolbar -->
40743
40746
  <div
40744
40747
  class="flex items-center gap-2 px-3 py-2 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800"
@@ -40888,7 +40891,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
40888
40891
 
40889
40892
  <!-- Status bar -->
40890
40893
  <div
40891
- class="flex items-center justify-between px-3 py-1.5 text-xs text-gray-500 dark:text-gray-400 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800"
40894
+ class="flex items-center justify-between px-3 py-1.5 text-xs text-gray-500 dark:text-gray-400 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 rounded-b-lg"
40892
40895
  >
40893
40896
  <span>{{ filteredEntries().length }} / {{ entries().length }} entries</span>
40894
40897
  <span>
@@ -43054,7 +43057,7 @@ class NativeStorageService {
43054
43057
  }
43055
43058
  if (this.platformService.isElectron()) {
43056
43059
  try {
43057
- const electron = await import('electron');
43060
+ const electron = await dynamicImport('electron');
43058
43061
  return electron.ipcRenderer.invoke('secure-storage-get', key);
43059
43062
  }
43060
43063
  catch (err) {
@@ -43081,7 +43084,7 @@ class NativeStorageService {
43081
43084
  }
43082
43085
  if (this.platformService.isElectron()) {
43083
43086
  try {
43084
- const electron = await import('electron');
43087
+ const electron = await dynamicImport('electron');
43085
43088
  await electron.ipcRenderer.invoke('secure-storage-set', key, value);
43086
43089
  }
43087
43090
  catch (err) {
@@ -43108,7 +43111,7 @@ class NativeStorageService {
43108
43111
  }
43109
43112
  if (this.platformService.isElectron()) {
43110
43113
  try {
43111
- const electron = await import('electron');
43114
+ const electron = await dynamicImport('electron');
43112
43115
  await electron.ipcRenderer.invoke('secure-storage-remove', key);
43113
43116
  }
43114
43117
  catch (err) {
@@ -43247,7 +43250,7 @@ class NativeIpcService {
43247
43250
  async invoke(command, payload) {
43248
43251
  if (this.platformService.isTauri()) {
43249
43252
  try {
43250
- const tauriCore = (await import('@tauri-apps/api/core'));
43253
+ const tauriCore = (await dynamicImport('@tauri-apps/api/core'));
43251
43254
  const data = await tauriCore.invoke(command, payload);
43252
43255
  return { success: true, data };
43253
43256
  }
@@ -43257,7 +43260,7 @@ class NativeIpcService {
43257
43260
  }
43258
43261
  if (this.platformService.isElectron()) {
43259
43262
  try {
43260
- const electron = await import('electron');
43263
+ const electron = await dynamicImport('electron');
43261
43264
  const data = await electron.ipcRenderer.invoke(command, payload);
43262
43265
  return { success: true, data };
43263
43266
  }
@@ -43273,7 +43276,7 @@ class NativeIpcService {
43273
43276
  async send(channel, payload) {
43274
43277
  if (this.platformService.isTauri()) {
43275
43278
  try {
43276
- const tauriEvent = (await import('@tauri-apps/api/event'));
43279
+ const tauriEvent = (await dynamicImport('@tauri-apps/api/event'));
43277
43280
  await tauriEvent.emit(channel, payload);
43278
43281
  }
43279
43282
  catch (e) {
@@ -43283,7 +43286,7 @@ class NativeIpcService {
43283
43286
  }
43284
43287
  if (this.platformService.isElectron()) {
43285
43288
  try {
43286
- const electron = await import('electron');
43289
+ const electron = await dynamicImport('electron');
43287
43290
  electron.ipcRenderer.send(channel, payload);
43288
43291
  }
43289
43292
  catch (e) {
@@ -43305,7 +43308,7 @@ class NativeIpcService {
43305
43308
  // Platform-specific setup
43306
43309
  if (this.platformService.isTauri()) {
43307
43310
  try {
43308
- const tauriEvent = (await import('@tauri-apps/api/event'));
43311
+ const tauriEvent = (await dynamicImport('@tauri-apps/api/event'));
43309
43312
  const unlisten = await tauriEvent.listen(channel, (event) => {
43310
43313
  this.ngZone.run(() => {
43311
43314
  callback(event.payload);
@@ -43320,7 +43323,7 @@ class NativeIpcService {
43320
43323
  }
43321
43324
  if (this.platformService.isElectron()) {
43322
43325
  try {
43323
- const electron = await import('electron');
43326
+ const electron = await dynamicImport('electron');
43324
43327
  const handler = (_event, data) => {
43325
43328
  this.ngZone.run(() => {
43326
43329
  callback(data);
@@ -43352,7 +43355,7 @@ class NativeIpcService {
43352
43355
  async once(channel, callback) {
43353
43356
  if (this.platformService.isTauri()) {
43354
43357
  try {
43355
- const tauriEvent = (await import('@tauri-apps/api/event'));
43358
+ const tauriEvent = (await dynamicImport('@tauri-apps/api/event'));
43356
43359
  await tauriEvent.once(channel, (event) => {
43357
43360
  this.ngZone.run(() => {
43358
43361
  callback(event.payload);
@@ -43366,7 +43369,7 @@ class NativeIpcService {
43366
43369
  }
43367
43370
  if (this.platformService.isElectron()) {
43368
43371
  try {
43369
- const electron = await import('electron');
43372
+ const electron = await dynamicImport('electron');
43370
43373
  electron.ipcRenderer.once(channel, (_event, data) => {
43371
43374
  this.ngZone.run(() => {
43372
43375
  callback(data);
@@ -43424,149 +43427,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImpor
43424
43427
  args: [{ providedIn: 'root' }]
43425
43428
  }], ctorParameters: () => [] });
43426
43429
 
43427
- /**
43428
- * Dynamic import utilities that prevent bundlers from analyzing/resolving the imports at build time.
43429
- *
43430
- * The trick is to use a variable for the module specifier, which makes it impossible for bundlers
43431
- * to statically analyze the import. This is intentional for optional dependencies like Electron and Tauri.
43432
- */
43433
- /* eslint-disable @typescript-eslint/no-explicit-any */
43434
- /**
43435
- * Creates a dynamic import function that bundlers cannot statically analyze.
43436
- * This prevents build errors for optional dependencies that may not be installed.
43437
- */
43438
- function createDynamicImport() {
43439
- // Use Function constructor to create import at runtime, preventing static analysis
43440
- return new Function('modulePath', 'return import(modulePath)');
43441
- }
43442
- const dynamicImport = createDynamicImport();
43443
- /**
43444
- * Dynamically import the Electron module.
43445
- * This will only work at runtime when running in an Electron context.
43446
- * Returns null if Electron is not available.
43447
- */
43448
- async function importElectron() {
43449
- try {
43450
- const electron = await dynamicImport('electron');
43451
- return electron;
43452
- }
43453
- catch {
43454
- return null;
43455
- }
43456
- }
43457
- /**
43458
- * Dynamically import Tauri dialog plugin.
43459
- * This will only work at runtime when running in a Tauri context.
43460
- * Returns null if Tauri dialog is not available.
43461
- */
43462
- async function importTauriDialog() {
43463
- try {
43464
- const dialog = await dynamicImport('@tauri-apps/plugin-dialog');
43465
- return dialog;
43466
- }
43467
- catch {
43468
- return null;
43469
- }
43470
- }
43471
- /**
43472
- * Dynamically import Tauri notification plugin.
43473
- * This will only work at runtime when running in a Tauri context.
43474
- * Returns null if Tauri notification is not available.
43475
- */
43476
- async function importTauriNotification() {
43477
- try {
43478
- const notification = await dynamicImport('@tauri-apps/plugin-notification');
43479
- return notification;
43480
- }
43481
- catch {
43482
- return null;
43483
- }
43484
- }
43485
- /**
43486
- * Dynamically import Tauri process plugin.
43487
- * This will only work at runtime when running in a Tauri context.
43488
- * Returns null if Tauri process is not available.
43489
- */
43490
- async function importTauriProcess() {
43491
- try {
43492
- const process = await dynamicImport('@tauri-apps/plugin-process');
43493
- return process;
43494
- }
43495
- catch {
43496
- return null;
43497
- }
43498
- }
43499
- /**
43500
- * Dynamically import Tauri updater plugin.
43501
- * This will only work at runtime when running in a Tauri context.
43502
- * Returns null if Tauri updater is not available.
43503
- */
43504
- async function importTauriUpdater() {
43505
- try {
43506
- const updater = await dynamicImport('@tauri-apps/plugin-updater');
43507
- return updater;
43508
- }
43509
- catch {
43510
- return null;
43511
- }
43512
- }
43513
- /**
43514
- * Dynamically import Tauri app API.
43515
- * This will only work at runtime when running in a Tauri context.
43516
- * Returns null if Tauri app is not available.
43517
- */
43518
- async function importTauriApp() {
43519
- try {
43520
- const app = await dynamicImport('@tauri-apps/api/app');
43521
- return app;
43522
- }
43523
- catch {
43524
- return null;
43525
- }
43526
- }
43527
- /**
43528
- * Dynamically import Tauri menu API.
43529
- * This will only work at runtime when running in a Tauri context.
43530
- * Returns null if Tauri menu is not available.
43531
- */
43532
- async function importTauriMenu() {
43533
- try {
43534
- const menu = await dynamicImport('@tauri-apps/api/menu');
43535
- return menu;
43536
- }
43537
- catch {
43538
- return null;
43539
- }
43540
- }
43541
- /**
43542
- * Dynamically import Tauri tray API.
43543
- * This will only work at runtime when running in a Tauri context.
43544
- * Returns null if Tauri tray is not available.
43545
- */
43546
- async function importTauriTray() {
43547
- try {
43548
- const tray = await dynamicImport('@tauri-apps/api/tray');
43549
- return tray;
43550
- }
43551
- catch {
43552
- return null;
43553
- }
43554
- }
43555
- /**
43556
- * Dynamically import Tauri window API.
43557
- * This will only work at runtime when running in a Tauri context.
43558
- * Returns null if Tauri window is not available.
43559
- */
43560
- async function importTauriWindow() {
43561
- try {
43562
- const window = await dynamicImport('@tauri-apps/api/window');
43563
- return window;
43564
- }
43565
- catch {
43566
- return null;
43567
- }
43568
- }
43569
-
43570
43430
  const PLATFORM_TAURI$4 = 'tauri';
43571
43431
  const PLATFORM_ELECTRON$4 = 'electron';
43572
43432
  class FilePickerService {
@@ -43609,14 +43469,12 @@ class FilePickerService {
43609
43469
  }
43610
43470
  async openFileTauri(options) {
43611
43471
  try {
43612
- const dialog = await importTauriDialog();
43613
- if (!dialog)
43614
- return null;
43472
+ const { open } = await dynamicImport('@tauri-apps/plugin-dialog');
43615
43473
  const filters = options.filters?.map(f => ({
43616
43474
  name: f.name,
43617
43475
  extensions: f.extensions,
43618
43476
  }));
43619
- const result = await dialog.open({
43477
+ const result = await open({
43620
43478
  title: options.title,
43621
43479
  defaultPath: options.defaultPath,
43622
43480
  filters,
@@ -43640,10 +43498,8 @@ class FilePickerService {
43640
43498
  }
43641
43499
  async openFileElectron(options) {
43642
43500
  try {
43643
- const electron = await importElectron();
43644
- if (!electron?.ipcRenderer)
43645
- return null;
43646
- const result = await electron.ipcRenderer.invoke('show-open-dialog', {
43501
+ const { ipcRenderer } = await dynamicImport('electron');
43502
+ const result = await ipcRenderer.invoke('show-open-dialog', {
43647
43503
  title: options.title,
43648
43504
  defaultPath: options.defaultPath,
43649
43505
  properties: [
@@ -43703,14 +43559,12 @@ class FilePickerService {
43703
43559
  }
43704
43560
  async saveFileTauri(options) {
43705
43561
  try {
43706
- const dialog = await importTauriDialog();
43707
- if (!dialog)
43708
- return null;
43562
+ const { save } = await dynamicImport('@tauri-apps/plugin-dialog');
43709
43563
  const filters = options.filters?.map(f => ({
43710
43564
  name: f.name,
43711
43565
  extensions: f.extensions,
43712
43566
  }));
43713
- const result = await dialog.save({
43567
+ const result = await save({
43714
43568
  title: options.title,
43715
43569
  defaultPath: options.defaultPath,
43716
43570
  filters,
@@ -43724,10 +43578,8 @@ class FilePickerService {
43724
43578
  }
43725
43579
  async saveFileElectron(options) {
43726
43580
  try {
43727
- const electron = await importElectron();
43728
- if (!electron?.ipcRenderer)
43729
- return null;
43730
- const result = await electron.ipcRenderer.invoke('show-save-dialog', {
43581
+ const { ipcRenderer } = await dynamicImport('electron');
43582
+ const result = await ipcRenderer.invoke('show-save-dialog', {
43731
43583
  title: options.title,
43732
43584
  defaultPath: options.defaultPath,
43733
43585
  filters: options.filters?.map(f => ({
@@ -43751,10 +43603,8 @@ class FilePickerService {
43751
43603
  }
43752
43604
  async selectDirectoryTauri(options) {
43753
43605
  try {
43754
- const dialog = await importTauriDialog();
43755
- if (!dialog)
43756
- return null;
43757
- const result = await dialog.open({
43606
+ const { open } = await dynamicImport('@tauri-apps/plugin-dialog');
43607
+ const result = await open({
43758
43608
  title: options.title,
43759
43609
  defaultPath: options.defaultPath,
43760
43610
  directory: true,
@@ -43770,10 +43620,8 @@ class FilePickerService {
43770
43620
  }
43771
43621
  async selectDirectoryElectron(options) {
43772
43622
  try {
43773
- const electron = await importElectron();
43774
- if (!electron?.ipcRenderer)
43775
- return null;
43776
- const result = await electron.ipcRenderer.invoke('show-open-dialog', {
43623
+ const { ipcRenderer } = await dynamicImport('electron');
43624
+ const result = await ipcRenderer.invoke('show-open-dialog', {
43777
43625
  title: options.title,
43778
43626
  defaultPath: options.defaultPath,
43779
43627
  properties: ['openDirectory'],
@@ -43861,8 +43709,8 @@ class SystemTrayService {
43861
43709
  await tray.setIcon(icon);
43862
43710
  }
43863
43711
  else if (platform === PLATFORM_ELECTRON$3) {
43864
- const electron = await importElectron();
43865
- electron?.ipcRenderer?.send('tray-set-icon', icon);
43712
+ const { ipcRenderer } = await dynamicImport('electron');
43713
+ ipcRenderer.send('tray-set-icon', icon);
43866
43714
  }
43867
43715
  }
43868
43716
  async setTooltip(tooltip) {
@@ -43872,8 +43720,8 @@ class SystemTrayService {
43872
43720
  await tray.setTooltip(tooltip);
43873
43721
  }
43874
43722
  else if (platform === PLATFORM_ELECTRON$3) {
43875
- const electron = await importElectron();
43876
- electron?.ipcRenderer?.send('tray-set-tooltip', tooltip);
43723
+ const { ipcRenderer } = await dynamicImport('electron');
43724
+ ipcRenderer.send('tray-set-tooltip', tooltip);
43877
43725
  }
43878
43726
  }
43879
43727
  async setMenu(menu) {
@@ -43882,8 +43730,8 @@ class SystemTrayService {
43882
43730
  await this.setTauriMenu(menu);
43883
43731
  }
43884
43732
  else if (platform === PLATFORM_ELECTRON$3) {
43885
- const electron = await importElectron();
43886
- electron?.ipcRenderer?.send('tray-set-menu', this.convertMenuForElectron(menu));
43733
+ const { ipcRenderer } = await dynamicImport('electron');
43734
+ ipcRenderer.send('tray-set-menu', this.convertMenuForElectron(menu));
43887
43735
  }
43888
43736
  }
43889
43737
  async destroy() {
@@ -43892,16 +43740,14 @@ class SystemTrayService {
43892
43740
  this.trayInstance = null;
43893
43741
  }
43894
43742
  else if (platform === PLATFORM_ELECTRON$3) {
43895
- const electron = await importElectron();
43896
- electron?.ipcRenderer?.send('tray-destroy');
43743
+ const { ipcRenderer } = await dynamicImport('electron');
43744
+ ipcRenderer.send('tray-destroy');
43897
43745
  }
43898
43746
  this.isVisible.set(false);
43899
43747
  }
43900
43748
  async createTauriTray(config) {
43901
43749
  try {
43902
- const tauriTray = await importTauriTray();
43903
- if (!tauriTray)
43904
- return false;
43750
+ const tauriTray = await dynamicImport('@tauri-apps/api/tray');
43905
43751
  this.trayInstance = await tauriTray.TrayIcon.new({
43906
43752
  icon: config.icon,
43907
43753
  tooltip: config.tooltip,
@@ -43920,10 +43766,8 @@ class SystemTrayService {
43920
43766
  }
43921
43767
  async createElectronTray(config) {
43922
43768
  try {
43923
- const electron = await importElectron();
43924
- if (!electron?.ipcRenderer)
43925
- return false;
43926
- await electron.ipcRenderer.invoke('tray-create', {
43769
+ const { ipcRenderer } = await dynamicImport('electron');
43770
+ await ipcRenderer.invoke('tray-create', {
43927
43771
  icon: config.icon,
43928
43772
  tooltip: config.tooltip,
43929
43773
  menu: config.menu ? this.convertMenuForElectron(config.menu) : undefined,
@@ -43938,9 +43782,7 @@ class SystemTrayService {
43938
43782
  }
43939
43783
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
43940
43784
  async buildTauriMenu(items) {
43941
- const tauriMenu = await importTauriMenu();
43942
- if (!tauriMenu)
43943
- return null;
43785
+ const tauriMenu = await dynamicImport('@tauri-apps/api/menu');
43944
43786
  const { Menu, MenuItem, Submenu } = tauriMenu;
43945
43787
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
43946
43788
  const menuItems = [];
@@ -44027,9 +43869,7 @@ class NativeNotificationsService {
44027
43869
  const platform = this.platformService.platform();
44028
43870
  if (platform === PLATFORM_TAURI$2) {
44029
43871
  try {
44030
- const notification = await importTauriNotification();
44031
- if (!notification)
44032
- return false;
43872
+ const notification = await dynamicImport('@tauri-apps/plugin-notification');
44033
43873
  let granted = await notification.isPermissionGranted();
44034
43874
  if (!granted) {
44035
43875
  const result = await notification.requestPermission();
@@ -44080,8 +43920,8 @@ class NativeNotificationsService {
44080
43920
  }
44081
43921
  else if (platform === PLATFORM_ELECTRON$2) {
44082
43922
  try {
44083
- const electron = await importElectron();
44084
- electron?.ipcRenderer?.send('set-badge-count', count);
43923
+ const { ipcRenderer } = await dynamicImport('electron');
43924
+ ipcRenderer.send('set-badge-count', count);
44085
43925
  }
44086
43926
  catch (error) {
44087
43927
  console.error('Failed to set Electron badge count:', error);
@@ -44101,9 +43941,7 @@ class NativeNotificationsService {
44101
43941
  }
44102
43942
  async showTauriNotification(options) {
44103
43943
  try {
44104
- const notification = await importTauriNotification();
44105
- if (!notification)
44106
- return null;
43944
+ const notification = await dynamicImport('@tauri-apps/plugin-notification');
44107
43945
  const id = `notification-${Date.now()}`;
44108
43946
  await notification.sendNotification({
44109
43947
  title: options.title,
@@ -44119,11 +43957,9 @@ class NativeNotificationsService {
44119
43957
  }
44120
43958
  async showElectronNotification(options) {
44121
43959
  try {
44122
- const electron = await importElectron();
44123
- if (!electron?.ipcRenderer)
44124
- return null;
43960
+ const { ipcRenderer } = await dynamicImport('electron');
44125
43961
  const id = `notification-${Date.now()}`;
44126
- await electron.ipcRenderer.invoke('show-notification', {
43962
+ await ipcRenderer.invoke('show-notification', {
44127
43963
  title: options.title,
44128
43964
  body: options.body,
44129
43965
  icon: options.icon,
@@ -44185,16 +44021,11 @@ class DockService {
44185
44021
  const platform = this.platformService.platform();
44186
44022
  if (platform === PLATFORM_TAURI$1) {
44187
44023
  try {
44188
- const tauriWindow = await importTauriWindow();
44189
- if (!tauriWindow)
44190
- return;
44191
- const appWindow = tauriWindow.getCurrentWindow();
44192
- // Tauri doesn't have direct dock badge API, so we append badge to window title
44193
- const currentTitle = await appWindow.title();
44194
- // Remove any existing badge from title (e.g., "App (3)" -> "App")
44195
- const baseTitle = currentTitle.replace(/\s*\(\d+\)\s*$/, '');
44196
- const newTitle = text ? `${baseTitle} (${text})` : baseTitle;
44197
- await appWindow.setTitle(newTitle);
44024
+ const { getCurrentWindow } = await dynamicImport('@tauri-apps/api/window');
44025
+ // Tauri doesn't have direct dock badge API, use window title instead
44026
+ const appWindow = getCurrentWindow();
44027
+ // Badge functionality varies by platform in Tauri
44028
+ console.warn('Dock badge not directly supported in Tauri, consider using notifications');
44198
44029
  }
44199
44030
  catch (error) {
44200
44031
  console.error('Failed to set Tauri dock badge:', error);
@@ -44202,8 +44033,8 @@ class DockService {
44202
44033
  }
44203
44034
  else if (platform === PLATFORM_ELECTRON$1) {
44204
44035
  try {
44205
- const electron = await importElectron();
44206
- electron?.ipcRenderer?.send('set-dock-badge', text);
44036
+ const { ipcRenderer } = await dynamicImport('electron');
44037
+ ipcRenderer.send('set-dock-badge', text);
44207
44038
  }
44208
44039
  catch (error) {
44209
44040
  console.error('Failed to set Electron dock badge:', error);
@@ -44223,10 +44054,10 @@ class DockService {
44223
44054
  const platform = this.platformService.platform();
44224
44055
  if (platform === PLATFORM_ELECTRON$1) {
44225
44056
  try {
44226
- const electron = await importElectron();
44057
+ const { ipcRenderer } = await dynamicImport('electron');
44227
44058
  // Progress should be between 0 and 1, or -1 to clear
44228
44059
  const normalizedProgress = progress < 0 ? -1 : Math.min(1, Math.max(0, progress / 100));
44229
- electron?.ipcRenderer?.send('set-progress', normalizedProgress);
44060
+ ipcRenderer.send('set-progress', normalizedProgress);
44230
44061
  }
44231
44062
  catch (error) {
44232
44063
  console.error('Failed to set Electron progress:', error);
@@ -44247,10 +44078,8 @@ class DockService {
44247
44078
  const platform = this.platformService.platform();
44248
44079
  if (platform === PLATFORM_ELECTRON$1) {
44249
44080
  try {
44250
- const electron = await importElectron();
44251
- if (!electron?.ipcRenderer)
44252
- return -1;
44253
- return await electron.ipcRenderer.invoke('dock-bounce', type);
44081
+ const { ipcRenderer } = await dynamicImport('electron');
44082
+ return await ipcRenderer.invoke('dock-bounce', type);
44254
44083
  }
44255
44084
  catch (error) {
44256
44085
  console.error('Failed to bounce Electron dock:', error);
@@ -44266,8 +44095,8 @@ class DockService {
44266
44095
  const platform = this.platformService.platform();
44267
44096
  if (platform === PLATFORM_ELECTRON$1) {
44268
44097
  try {
44269
- const electron = await importElectron();
44270
- electron?.ipcRenderer?.send('cancel-dock-bounce', id);
44098
+ const { ipcRenderer } = await dynamicImport('electron');
44099
+ ipcRenderer.send('cancel-dock-bounce', id);
44271
44100
  }
44272
44101
  catch (error) {
44273
44102
  console.error('Failed to cancel Electron dock bounce:', error);
@@ -44281,8 +44110,8 @@ class DockService {
44281
44110
  const platform = this.platformService.platform();
44282
44111
  if (platform === PLATFORM_ELECTRON$1) {
44283
44112
  try {
44284
- const electron = await importElectron();
44285
- electron?.ipcRenderer?.send('set-dock-menu', this.convertMenuForElectron(items));
44113
+ const { ipcRenderer } = await dynamicImport('electron');
44114
+ ipcRenderer.send('set-dock-menu', this.convertMenuForElectron(items));
44286
44115
  }
44287
44116
  catch (error) {
44288
44117
  console.error('Failed to set Electron dock menu:', error);
@@ -44296,8 +44125,8 @@ class DockService {
44296
44125
  const platform = this.platformService.platform();
44297
44126
  if (platform === PLATFORM_ELECTRON$1) {
44298
44127
  try {
44299
- const electron = await importElectron();
44300
- electron?.ipcRenderer?.send('show-dock');
44128
+ const { ipcRenderer } = await dynamicImport('electron');
44129
+ ipcRenderer.send('show-dock');
44301
44130
  }
44302
44131
  catch (error) {
44303
44132
  console.error('Failed to show Electron dock:', error);
@@ -44311,8 +44140,8 @@ class DockService {
44311
44140
  const platform = this.platformService.platform();
44312
44141
  if (platform === PLATFORM_ELECTRON$1) {
44313
44142
  try {
44314
- const electron = await importElectron();
44315
- electron?.ipcRenderer?.send('hide-dock');
44143
+ const { ipcRenderer } = await dynamicImport('electron');
44144
+ ipcRenderer.send('hide-dock');
44316
44145
  }
44317
44146
  catch (error) {
44318
44147
  console.error('Failed to hide Electron dock:', error);
@@ -44326,10 +44155,8 @@ class DockService {
44326
44155
  const platform = this.platformService.platform();
44327
44156
  if (platform === PLATFORM_TAURI$1) {
44328
44157
  try {
44329
- const tauriWindow = await importTauriWindow();
44330
- if (!tauriWindow)
44331
- return;
44332
- const appWindow = tauriWindow.getCurrentWindow();
44158
+ const { getCurrentWindow } = await dynamicImport('@tauri-apps/api/window');
44159
+ const appWindow = getCurrentWindow();
44333
44160
  await appWindow.requestUserAttention(flash ? 2 : null); // 2 = Informational
44334
44161
  }
44335
44162
  catch (error) {
@@ -44338,8 +44165,8 @@ class DockService {
44338
44165
  }
44339
44166
  else if (platform === PLATFORM_ELECTRON$1) {
44340
44167
  try {
44341
- const electron = await importElectron();
44342
- electron?.ipcRenderer?.send('flash-frame', flash);
44168
+ const { ipcRenderer } = await dynamicImport('electron');
44169
+ ipcRenderer.send('flash-frame', flash);
44343
44170
  }
44344
44171
  catch (error) {
44345
44172
  console.error('Failed to flash Electron frame:', error);
@@ -44474,26 +44301,24 @@ class UpdateService {
44474
44301
  }
44475
44302
  async setupElectronListeners() {
44476
44303
  try {
44477
- const electron = await importElectron();
44478
- if (!electron?.ipcRenderer)
44479
- return;
44480
- electron.ipcRenderer.on('update-available', (_event, info) => {
44304
+ const { ipcRenderer } = await dynamicImport('electron');
44305
+ ipcRenderer.on('update-available', (_event, info) => {
44481
44306
  this.status.set('available');
44482
44307
  this.updateInfo.set(info);
44483
44308
  this.updateAvailable$.next(info);
44484
44309
  });
44485
- electron.ipcRenderer.on('update-not-available', () => {
44310
+ ipcRenderer.on('update-not-available', () => {
44486
44311
  this.status.set('not-available');
44487
44312
  });
44488
- electron.ipcRenderer.on('download-progress', (_event, progress) => {
44313
+ ipcRenderer.on('download-progress', (_event, progress) => {
44489
44314
  this.progress.set(progress);
44490
44315
  this.downloadProgress$.next(progress);
44491
44316
  });
44492
- electron.ipcRenderer.on('update-downloaded', () => {
44317
+ ipcRenderer.on('update-downloaded', () => {
44493
44318
  this.status.set('downloaded');
44494
44319
  this.updateDownloaded$.next();
44495
44320
  });
44496
- electron.ipcRenderer.on('update-error', (_event, error) => {
44321
+ ipcRenderer.on('update-error', (_event, error) => {
44497
44322
  this.status.set('error');
44498
44323
  this.error.set(error);
44499
44324
  this.updateError$.next(error);
@@ -44504,10 +44329,8 @@ class UpdateService {
44504
44329
  }
44505
44330
  }
44506
44331
  async checkTauriUpdates() {
44507
- const updater = await importTauriUpdater();
44508
- const app = await importTauriApp();
44509
- if (!updater || !app)
44510
- return null;
44332
+ const updater = await dynamicImport('@tauri-apps/plugin-updater');
44333
+ const app = await dynamicImport('@tauri-apps/api/app');
44511
44334
  const currentVersion = await app.getVersion();
44512
44335
  const update = await updater.check();
44513
44336
  if (update?.available) {
@@ -44526,10 +44349,8 @@ class UpdateService {
44526
44349
  return null;
44527
44350
  }
44528
44351
  async checkElectronUpdates() {
44529
- const electron = await importElectron();
44530
- if (!electron?.ipcRenderer)
44531
- return null;
44532
- const result = (await electron.ipcRenderer.invoke('check-for-updates'));
44352
+ const { ipcRenderer } = await dynamicImport('electron');
44353
+ const result = (await ipcRenderer.invoke('check-for-updates'));
44533
44354
  if (result?.updateAvailable) {
44534
44355
  const info = {
44535
44356
  currentVersion: result.currentVersion || '',
@@ -44546,12 +44367,10 @@ class UpdateService {
44546
44367
  return null;
44547
44368
  }
44548
44369
  async downloadTauriUpdate() {
44549
- const updater = await importTauriUpdater();
44550
- if (!updater)
44551
- return;
44370
+ const updater = await dynamicImport('@tauri-apps/plugin-updater');
44552
44371
  const update = await updater.check();
44553
44372
  if (update?.available) {
44554
- await update.downloadAndInstall(event => {
44373
+ await update.downloadAndInstall((event) => {
44555
44374
  if (event.event === 'Started') {
44556
44375
  const total = event.data?.contentLength || 0;
44557
44376
  this.progress.set({ percent: 0, bytesDownloaded: 0, bytesTotal: total });
@@ -44578,16 +44397,16 @@ class UpdateService {
44578
44397
  }
44579
44398
  }
44580
44399
  async downloadElectronUpdate() {
44581
- const electron = await importElectron();
44582
- electron?.ipcRenderer?.send('download-update');
44400
+ const { ipcRenderer } = await dynamicImport('electron');
44401
+ ipcRenderer.send('download-update');
44583
44402
  }
44584
44403
  async installTauriUpdate() {
44585
- const process = await importTauriProcess();
44586
- await process?.relaunch();
44404
+ const process = await dynamicImport('@tauri-apps/plugin-process');
44405
+ await process.relaunch();
44587
44406
  }
44588
44407
  async installElectronUpdate() {
44589
- const electron = await importElectron();
44590
- electron?.ipcRenderer?.send('quit-and-install');
44408
+ const { ipcRenderer } = await dynamicImport('electron');
44409
+ ipcRenderer.send('quit-and-install');
44591
44410
  }
44592
44411
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: UpdateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
44593
44412
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: UpdateService, providedIn: 'root' });