@forgeroute/ui 0.4.70 → 0.4.71

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.
@@ -869,7 +869,7 @@ var QuickFilterTabsComponent = class QuickFilterTabsComponent {
869
869
  </div>
870
870
  `,
871
871
  isInline: true,
872
- styles: [":host{display:block;min-width:0}.tabs-outer{position:relative;display:inline-flex;align-items:center;max-width:100%;min-width:0}.tabs{display:flex;align-items:center;gap:2px;background:#f1f5f9;border-radius:8px;padding:3px;overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch;min-width:0;max-width:100%}.tabs::-webkit-scrollbar{display:none}.tab{display:inline-flex;align-items:center;gap:6px;padding:6px 14px;border-radius:6px;border:none;background:transparent;font-size:.8125rem;font-weight:500;color:#475569;cursor:pointer;font-family:inherit;transition:background .1s,color .1s;white-space:nowrap;flex-shrink:0}.tab:hover{color:#1e293b}.tab.active{background:#fff;color:#1e293b;box-shadow:0 1px 3px #00000014}.tab:focus-visible{outline:2px solid #60a5fa;outline-offset:2px}.tab.tab-disabled{opacity:.45;cursor:default;pointer-events:none}.tab-count{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;border-radius:999px;font-size:.6875rem;font-weight:700;background:#e2e8f0;color:#475569;line-height:1}.active .tab-count,.active-count{background:#1e293b;color:#fff}.more-btn{gap:4px}.more-dropdown{position:absolute;top:calc(100% + 4px);right:0;z-index:100;background:#fff;border:1px solid #e2e8f0;border-radius:8px;box-shadow:0 4px 16px #0000001a;min-width:180px;padding:4px;max-height:260px;overflow-y:auto}.more-option{display:flex;align-items:center;justify-content:space-between;width:100%;padding:8px 12px;background:none;border:none;cursor:pointer;font-size:.875rem;color:#334155;font-family:inherit;border-radius:5px;transition:background .1s;text-align:left;gap:12px}.more-option:hover{background:#f8fafc}.more-option.more-option-active{background:#f1f5f9;color:#1e293b;font-weight:500}.more-option:focus-visible{outline:2px solid #60a5fa;outline-offset:-2px}.more-option-label{flex:1}.more-option-count{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;border-radius:999px;font-size:.6875rem;font-weight:700;line-height:1;background:#e2e8f0;color:#475569}.more-option-active .more-option-count{background:#1e293b;color:#fff}@media(max-width:1024px){.tab{padding:7px 12px}}@media(pointer:coarse){.tab{min-height:44px}}@media(max-width:640px){:host{align-self:flex-start;max-width:100%}.tab{padding:10px 12px;font-size:.8125rem;min-height:44px}.more-option{padding:12px 14px}}\n"],
872
+ styles: [":host{display:block;min-width:0}.tabs-outer{position:relative;display:inline-flex;align-items:center;max-width:100%;min-width:0}.tabs{display:flex;flex-wrap:wrap;align-items:center;gap:2px;background:#f1f5f9;border-radius:8px;padding:3px;overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch;min-width:0;max-width:100%}.tabs::-webkit-scrollbar{display:none}.tab{display:inline-flex;align-items:center;gap:6px;padding:6px 14px;border-radius:6px;border:none;background:transparent;font-size:.8125rem;font-weight:500;color:#475569;cursor:pointer;font-family:inherit;transition:background .1s,color .1s;white-space:nowrap;flex-shrink:0}.tab:hover{color:#1e293b}.tab.active{background:#fff;color:#1e293b;box-shadow:0 1px 3px #00000014}.tab:focus-visible{outline:2px solid #60a5fa;outline-offset:2px}.tab.tab-disabled{opacity:.45;cursor:default;pointer-events:none}.tab-count{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;border-radius:999px;font-size:.6875rem;font-weight:700;background:#e2e8f0;color:#475569;line-height:1}.active .tab-count,.active-count{background:#1e293b;color:#fff}.more-btn{gap:4px}.more-dropdown{position:absolute;top:calc(100% + 4px);right:0;z-index:100;background:#fff;border:1px solid #e2e8f0;border-radius:8px;box-shadow:0 4px 16px #0000001a;min-width:180px;padding:4px;max-height:260px;overflow-y:auto}.more-option{display:flex;align-items:center;justify-content:space-between;width:100%;padding:8px 12px;background:none;border:none;cursor:pointer;font-size:.875rem;color:#334155;font-family:inherit;border-radius:5px;transition:background .1s;text-align:left;gap:12px}.more-option:hover{background:#f8fafc}.more-option.more-option-active{background:#f1f5f9;color:#1e293b;font-weight:500}.more-option:focus-visible{outline:2px solid #60a5fa;outline-offset:-2px}.more-option-label{flex:1}.more-option-count{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;border-radius:999px;font-size:.6875rem;font-weight:700;line-height:1;background:#e2e8f0;color:#475569}.more-option-active .more-option-count{background:#1e293b;color:#fff}@media(max-width:1024px){.tab{padding:7px 12px}}@media(pointer:coarse){.tab{min-height:44px}}@media(max-width:640px){:host{align-self:flex-start;max-width:100%}.tab{padding:10px 12px;font-size:.8125rem;min-height:44px}.more-option{padding:12px 14px}}\n"],
873
873
  dependencies: [{
874
874
  kind: "pipe",
875
875
  type: DecimalPipe,
@@ -964,7 +964,7 @@ i0.ɵɵngDeclareClassMetadata({
964
964
  }
965
965
  </div>
966
966
  `,
967
- styles: [":host{display:block;min-width:0}.tabs-outer{position:relative;display:inline-flex;align-items:center;max-width:100%;min-width:0}.tabs{display:flex;align-items:center;gap:2px;background:#f1f5f9;border-radius:8px;padding:3px;overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch;min-width:0;max-width:100%}.tabs::-webkit-scrollbar{display:none}.tab{display:inline-flex;align-items:center;gap:6px;padding:6px 14px;border-radius:6px;border:none;background:transparent;font-size:.8125rem;font-weight:500;color:#475569;cursor:pointer;font-family:inherit;transition:background .1s,color .1s;white-space:nowrap;flex-shrink:0}.tab:hover{color:#1e293b}.tab.active{background:#fff;color:#1e293b;box-shadow:0 1px 3px #00000014}.tab:focus-visible{outline:2px solid #60a5fa;outline-offset:2px}.tab.tab-disabled{opacity:.45;cursor:default;pointer-events:none}.tab-count{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;border-radius:999px;font-size:.6875rem;font-weight:700;background:#e2e8f0;color:#475569;line-height:1}.active .tab-count,.active-count{background:#1e293b;color:#fff}.more-btn{gap:4px}.more-dropdown{position:absolute;top:calc(100% + 4px);right:0;z-index:100;background:#fff;border:1px solid #e2e8f0;border-radius:8px;box-shadow:0 4px 16px #0000001a;min-width:180px;padding:4px;max-height:260px;overflow-y:auto}.more-option{display:flex;align-items:center;justify-content:space-between;width:100%;padding:8px 12px;background:none;border:none;cursor:pointer;font-size:.875rem;color:#334155;font-family:inherit;border-radius:5px;transition:background .1s;text-align:left;gap:12px}.more-option:hover{background:#f8fafc}.more-option.more-option-active{background:#f1f5f9;color:#1e293b;font-weight:500}.more-option:focus-visible{outline:2px solid #60a5fa;outline-offset:-2px}.more-option-label{flex:1}.more-option-count{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;border-radius:999px;font-size:.6875rem;font-weight:700;line-height:1;background:#e2e8f0;color:#475569}.more-option-active .more-option-count{background:#1e293b;color:#fff}@media(max-width:1024px){.tab{padding:7px 12px}}@media(pointer:coarse){.tab{min-height:44px}}@media(max-width:640px){:host{align-self:flex-start;max-width:100%}.tab{padding:10px 12px;font-size:.8125rem;min-height:44px}.more-option{padding:12px 14px}}\n"]
967
+ styles: [":host{display:block;min-width:0}.tabs-outer{position:relative;display:inline-flex;align-items:center;max-width:100%;min-width:0}.tabs{display:flex;flex-wrap:wrap;align-items:center;gap:2px;background:#f1f5f9;border-radius:8px;padding:3px;overflow-x:auto;scrollbar-width:none;-webkit-overflow-scrolling:touch;min-width:0;max-width:100%}.tabs::-webkit-scrollbar{display:none}.tab{display:inline-flex;align-items:center;gap:6px;padding:6px 14px;border-radius:6px;border:none;background:transparent;font-size:.8125rem;font-weight:500;color:#475569;cursor:pointer;font-family:inherit;transition:background .1s,color .1s;white-space:nowrap;flex-shrink:0}.tab:hover{color:#1e293b}.tab.active{background:#fff;color:#1e293b;box-shadow:0 1px 3px #00000014}.tab:focus-visible{outline:2px solid #60a5fa;outline-offset:2px}.tab.tab-disabled{opacity:.45;cursor:default;pointer-events:none}.tab-count{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;border-radius:999px;font-size:.6875rem;font-weight:700;background:#e2e8f0;color:#475569;line-height:1}.active .tab-count,.active-count{background:#1e293b;color:#fff}.more-btn{gap:4px}.more-dropdown{position:absolute;top:calc(100% + 4px);right:0;z-index:100;background:#fff;border:1px solid #e2e8f0;border-radius:8px;box-shadow:0 4px 16px #0000001a;min-width:180px;padding:4px;max-height:260px;overflow-y:auto}.more-option{display:flex;align-items:center;justify-content:space-between;width:100%;padding:8px 12px;background:none;border:none;cursor:pointer;font-size:.875rem;color:#334155;font-family:inherit;border-radius:5px;transition:background .1s;text-align:left;gap:12px}.more-option:hover{background:#f8fafc}.more-option.more-option-active{background:#f1f5f9;color:#1e293b;font-weight:500}.more-option:focus-visible{outline:2px solid #60a5fa;outline-offset:-2px}.more-option-label{flex:1}.more-option-count{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 6px;border-radius:999px;font-size:.6875rem;font-weight:700;line-height:1;background:#e2e8f0;color:#475569}.more-option-active .more-option-count{background:#1e293b;color:#fff}@media(max-width:1024px){.tab{padding:7px 12px}}@media(pointer:coarse){.tab{min-height:44px}}@media(max-width:640px){:host{align-self:flex-start;max-width:100%}.tab{padding:10px 12px;font-size:.8125rem;min-height:44px}.more-option{padding:12px 14px}}\n"]
968
968
  }]
969
969
  }],
970
970
  ctorParameters: () => [],
@@ -2524,6 +2524,7 @@ var ModalComponent = class ModalComponent {
2524
2524
  this.open = input(false, ...ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []);
2525
2525
  this.title = input("", ...ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []);
2526
2526
  this.description = input("", ...ngDevMode ? [{ debugName: "description" }] : /* istanbul ignore next */ []);
2527
+ this.describedBy = input(null, ...ngDevMode ? [{ debugName: "describedBy" }] : /* istanbul ignore next */ []);
2527
2528
  this.maxWidth = input("460px", ...ngDevMode ? [{ debugName: "maxWidth" }] : /* istanbul ignore next */ []);
2528
2529
  this.bodyOverflow = input("auto", ...ngDevMode ? [{ debugName: "bodyOverflow" }] : /* istanbul ignore next */ []);
2529
2530
  this.dialogTestId = input("", ...ngDevMode ? [{ debugName: "dialogTestId" }] : /* istanbul ignore next */ []);
@@ -2537,6 +2538,8 @@ var ModalComponent = class ModalComponent {
2537
2538
  this.returnFocusTo = input(null, ...ngDevMode ? [{ debugName: "returnFocusTo" }] : /* istanbul ignore next */ []);
2538
2539
  this.kiosk = computed(() => this.layout() === "kiosk", ...ngDevMode ? [{ debugName: "kiosk" }] : /* istanbul ignore next */ []);
2539
2540
  this.titleId = `m-title-${Math.random().toString(36).slice(2, 8)}`;
2541
+ this.descId = `m-desc-${Math.random().toString(36).slice(2, 8)}`;
2542
+ this.describedById = computed(() => this.describedBy()?.trim() || (this.description() ? this.descId : null), ...ngDevMode ? [{ debugName: "describedById" }] : /* istanbul ignore next */ []);
2540
2543
  this.el = inject(ElementRef);
2541
2544
  this.ren = inject(Renderer2);
2542
2545
  this.doc = inject(DOCUMENT);
@@ -2674,6 +2677,13 @@ var ModalComponent = class ModalComponent {
2674
2677
  isRequired: false,
2675
2678
  transformFunction: null
2676
2679
  },
2680
+ describedBy: {
2681
+ classPropertyName: "describedBy",
2682
+ publicName: "describedBy",
2683
+ isSignal: true,
2684
+ isRequired: false,
2685
+ transformFunction: null
2686
+ },
2677
2687
  maxWidth: {
2678
2688
  classPropertyName: "maxWidth",
2679
2689
  publicName: "maxWidth",
@@ -2751,13 +2761,13 @@ var ModalComponent = class ModalComponent {
2751
2761
  template: `
2752
2762
  @if (open()) {
2753
2763
  <div class="m-backdrop" [class.m-kiosk-backdrop]="kiosk()" (click)="dismiss('backdrop')"></div>
2754
- <div class="m-modal" [class.m-kiosk]="kiosk()" role="dialog" aria-modal="true" tabindex="-1" [attr.aria-labelledby]="titleId" [attr.aria-busy]="busy() ? 'true' : null" [attr.data-testid]="dialogTestId() || null" [style.max-width]="maxWidth()">
2764
+ <div class="m-modal" [class.m-kiosk]="kiosk()" role="dialog" aria-modal="true" tabindex="-1" [attr.aria-labelledby]="titleId" [attr.aria-describedby]="describedById()" [attr.aria-busy]="busy() ? 'true' : null" [attr.data-testid]="dialogTestId() || null" [style.max-width]="maxWidth()">
2755
2765
  <span class="m-focus-guard" data-ui-focus-guard="start" tabindex="0" (focus)="onGuardFocus('start', $event)"></span>
2756
2766
  <div class="m-header">
2757
2767
  <div class="m-header-text">
2758
2768
  <h3 class="m-title" [id]="titleId">{{ title() }}</h3>
2759
2769
  @if (description()) {
2760
- <p class="m-description">{{ description() }}</p>
2770
+ <p class="m-description" [id]="descId">{{ description() }}</p>
2761
2771
  }
2762
2772
  </div>
2763
2773
  <ng-content select="[modal-header-end]" />
@@ -2802,13 +2812,13 @@ i0.ɵɵngDeclareClassMetadata({
2802
2812
  template: `
2803
2813
  @if (open()) {
2804
2814
  <div class="m-backdrop" [class.m-kiosk-backdrop]="kiosk()" (click)="dismiss('backdrop')"></div>
2805
- <div class="m-modal" [class.m-kiosk]="kiosk()" role="dialog" aria-modal="true" tabindex="-1" [attr.aria-labelledby]="titleId" [attr.aria-busy]="busy() ? 'true' : null" [attr.data-testid]="dialogTestId() || null" [style.max-width]="maxWidth()">
2815
+ <div class="m-modal" [class.m-kiosk]="kiosk()" role="dialog" aria-modal="true" tabindex="-1" [attr.aria-labelledby]="titleId" [attr.aria-describedby]="describedById()" [attr.aria-busy]="busy() ? 'true' : null" [attr.data-testid]="dialogTestId() || null" [style.max-width]="maxWidth()">
2806
2816
  <span class="m-focus-guard" data-ui-focus-guard="start" tabindex="0" (focus)="onGuardFocus('start', $event)"></span>
2807
2817
  <div class="m-header">
2808
2818
  <div class="m-header-text">
2809
2819
  <h3 class="m-title" [id]="titleId">{{ title() }}</h3>
2810
2820
  @if (description()) {
2811
- <p class="m-description">{{ description() }}</p>
2821
+ <p class="m-description" [id]="descId">{{ description() }}</p>
2812
2822
  }
2813
2823
  </div>
2814
2824
  <ng-content select="[modal-header-end]" />
@@ -2861,6 +2871,14 @@ i0.ɵɵngDeclareClassMetadata({
2861
2871
  required: false
2862
2872
  }]
2863
2873
  }],
2874
+ describedBy: [{
2875
+ type: i0.Input,
2876
+ args: [{
2877
+ isSignal: true,
2878
+ alias: "describedBy",
2879
+ required: false
2880
+ }]
2881
+ }],
2864
2882
  maxWidth: [{
2865
2883
  type: i0.Input,
2866
2884
  args: [{
@@ -6276,6 +6294,7 @@ var TableToolbarComponent = class TableToolbarComponent {
6276
6294
  "open",
6277
6295
  "title",
6278
6296
  "description",
6297
+ "describedBy",
6279
6298
  "maxWidth",
6280
6299
  "bodyOverflow",
6281
6300
  "dialogTestId",
@@ -10491,6 +10510,7 @@ var ImportExportComponent = class ImportExportComponent {
10491
10510
  "open",
10492
10511
  "title",
10493
10512
  "description",
10513
+ "describedBy",
10494
10514
  "maxWidth",
10495
10515
  "bodyOverflow",
10496
10516
  "dialogTestId",
@@ -10973,6 +10993,10 @@ var ConfirmOutletComponent = class ConfirmOutletComponent {
10973
10993
  constructor() {
10974
10994
  this.svc = inject(ConfirmService);
10975
10995
  this._isPrimary = computed(() => this.svc.primaryOutlet() === this, ...ngDevMode ? [{ debugName: "_isPrimary" }] : /* istanbul ignore next */ []);
10996
+ this._uid = Math.random().toString(36).slice(2, 8);
10997
+ this.msgId = `ui-confirm-msg-${this._uid}`;
10998
+ this.detailsId = `ui-confirm-details-${this._uid}`;
10999
+ this._describedBy = computed(() => this.svc.active()?.details.length ? `${this.msgId} ${this.detailsId}` : this.msgId, ...ngDevMode ? [{ debugName: "_describedBy" }] : /* istanbul ignore next */ []);
10976
11000
  }
10977
11001
  ngOnInit() {
10978
11002
  this.svc.registerOutlet(this);
@@ -11005,10 +11029,11 @@ var ConfirmOutletComponent = class ConfirmOutletComponent {
11005
11029
  [open]="svc.open()"
11006
11030
  [title]="svc.active()!.title"
11007
11031
  maxWidth="420px"
11032
+ [describedBy]="_describedBy()"
11008
11033
  (close)="svc.dismiss(false)">
11009
- <p class="confirm-msg" data-testid="confirm-message">{{ svc.active()!.message }}</p>
11034
+ <p class="confirm-msg" data-testid="confirm-message" [id]="msgId">{{ svc.active()!.message }}</p>
11010
11035
  @if (svc.active()!.details.length) {
11011
- <ul class="confirm-details" data-testid="confirm-details" aria-label="What will happen">
11036
+ <ul class="confirm-details" data-testid="confirm-details" aria-label="What will happen" [id]="detailsId">
11012
11037
  @for (d of svc.active()!.details; track $index) { <li>{{ d }}</li> }
11013
11038
  </ul>
11014
11039
  }
@@ -11036,6 +11061,7 @@ var ConfirmOutletComponent = class ConfirmOutletComponent {
11036
11061
  "open",
11037
11062
  "title",
11038
11063
  "description",
11064
+ "describedBy",
11039
11065
  "maxWidth",
11040
11066
  "bodyOverflow",
11041
11067
  "dialogTestId",
@@ -11101,10 +11127,11 @@ i0.ɵɵngDeclareClassMetadata({
11101
11127
  [open]="svc.open()"
11102
11128
  [title]="svc.active()!.title"
11103
11129
  maxWidth="420px"
11130
+ [describedBy]="_describedBy()"
11104
11131
  (close)="svc.dismiss(false)">
11105
- <p class="confirm-msg" data-testid="confirm-message">{{ svc.active()!.message }}</p>
11132
+ <p class="confirm-msg" data-testid="confirm-message" [id]="msgId">{{ svc.active()!.message }}</p>
11106
11133
  @if (svc.active()!.details.length) {
11107
- <ul class="confirm-details" data-testid="confirm-details" aria-label="What will happen">
11134
+ <ul class="confirm-details" data-testid="confirm-details" aria-label="What will happen" [id]="detailsId">
11108
11135
  @for (d of svc.active()!.details; track $index) { <li>{{ d }}</li> }
11109
11136
  </ul>
11110
11137
  }
@@ -12365,6 +12392,9 @@ var DataTableComponent = class DataTableComponent {
12365
12392
  this._confirmingBulk = false;
12366
12393
  this._rowDeleteTarget = signal(null, ...ngDevMode ? [{ debugName: "_rowDeleteTarget" }] : /* istanbul ignore next */ []);
12367
12394
  this._rowDeleting = signal(false, ...ngDevMode ? [{ debugName: "_rowDeleting" }] : /* istanbul ignore next */ []);
12395
+ this._confirmUid = Math.random().toString(36).slice(2, 8);
12396
+ this._confirmMsgId = `dt-confirm-msg-${this._confirmUid}`;
12397
+ this._confirmDetailsId = `dt-confirm-details-${this._confirmUid}`;
12368
12398
  this._rowDeleteConfig = signal({
12369
12399
  title: "Delete",
12370
12400
  message: "Delete this record? This cannot be undone.",
@@ -13931,10 +13961,11 @@ var DataTableComponent = class DataTableComponent {
13931
13961
  [open]="true"
13932
13962
  [title]="_rowDeleteConfig().title"
13933
13963
  maxWidth="420px"
13964
+ [describedBy]="_rowDeleteConfig().details?.length ? _confirmMsgId + ' ' + _confirmDetailsId : _confirmMsgId"
13934
13965
  (close)="_rowDeleteTarget.set(null)">
13935
- <p class="dt-confirm-msg" data-testid="confirm-message">{{ _rowDeleteConfig().message }}</p>
13966
+ <p class="dt-confirm-msg" data-testid="confirm-message" [id]="_confirmMsgId">{{ _rowDeleteConfig().message }}</p>
13936
13967
  @if (_rowDeleteConfig().details?.length) {
13937
- <ul class="dt-confirm-details" data-testid="confirm-details" aria-label="What will happen">
13968
+ <ul class="dt-confirm-details" data-testid="confirm-details" aria-label="What will happen" [id]="_confirmDetailsId">
13938
13969
  @for (d of _rowDeleteConfig().details; track $index) { <li>{{ d }}</li> }
13939
13970
  </ul>
13940
13971
  }
@@ -14274,6 +14305,7 @@ var DataTableComponent = class DataTableComponent {
14274
14305
  "open",
14275
14306
  "title",
14276
14307
  "description",
14308
+ "describedBy",
14277
14309
  "maxWidth",
14278
14310
  "bodyOverflow",
14279
14311
  "dialogTestId",
@@ -15014,10 +15046,11 @@ i0.ɵɵngDeclareClassMetadata({
15014
15046
  [open]="true"
15015
15047
  [title]="_rowDeleteConfig().title"
15016
15048
  maxWidth="420px"
15049
+ [describedBy]="_rowDeleteConfig().details?.length ? _confirmMsgId + ' ' + _confirmDetailsId : _confirmMsgId"
15017
15050
  (close)="_rowDeleteTarget.set(null)">
15018
- <p class="dt-confirm-msg" data-testid="confirm-message">{{ _rowDeleteConfig().message }}</p>
15051
+ <p class="dt-confirm-msg" data-testid="confirm-message" [id]="_confirmMsgId">{{ _rowDeleteConfig().message }}</p>
15019
15052
  @if (_rowDeleteConfig().details?.length) {
15020
- <ul class="dt-confirm-details" data-testid="confirm-details" aria-label="What will happen">
15053
+ <ul class="dt-confirm-details" data-testid="confirm-details" aria-label="What will happen" [id]="_confirmDetailsId">
15021
15054
  @for (d of _rowDeleteConfig().details; track $index) { <li>{{ d }}</li> }
15022
15055
  </ul>
15023
15056
  }
@@ -29500,8 +29533,8 @@ var NotesComponent = class NotesComponent {
29500
29533
  <ui-pagination [page]="page() + 1" [pageSize]="pageSize" [totalItems]="totalElements()" (pageChange)="loadPage($event - 1)" />
29501
29534
  }
29502
29535
 
29503
- <ui-modal [open]="!!deletingId()" title="Delete Note" maxWidth="420px" dialogTestId="delete-note-modal" (close)="deletingId.set(null)">
29504
- <p class="note-delete-msg" data-testid="delete-note-message">Delete this note? It is permanently removed from this record and cannot be undone.</p>
29536
+ <ui-modal [open]="!!deletingId()" title="Delete Note" maxWidth="420px" dialogTestId="delete-note-modal" describedBy="ui-note-delete-msg" (close)="deletingId.set(null)">
29537
+ <p class="note-delete-msg" id="ui-note-delete-msg" data-testid="delete-note-message">Delete this note? It is permanently removed from this record and cannot be undone.</p>
29505
29538
  <ui-button modal-footer-left variant="secondary" size="sm" data-testid="delete-note-cancel" (click)="deletingId.set(null)">Cancel</ui-button>
29506
29539
  <ui-button modal-footer-right variant="danger" size="sm" data-testid="delete-note-confirm" [loading]="saving()" (click)="deleteNote()">Delete Note</ui-button>
29507
29540
  </ui-modal>
@@ -29575,6 +29608,7 @@ var NotesComponent = class NotesComponent {
29575
29608
  "open",
29576
29609
  "title",
29577
29610
  "description",
29611
+ "describedBy",
29578
29612
  "maxWidth",
29579
29613
  "bodyOverflow",
29580
29614
  "dialogTestId",
@@ -29750,8 +29784,8 @@ i0.ɵɵngDeclareClassMetadata({
29750
29784
  <ui-pagination [page]="page() + 1" [pageSize]="pageSize" [totalItems]="totalElements()" (pageChange)="loadPage($event - 1)" />
29751
29785
  }
29752
29786
 
29753
- <ui-modal [open]="!!deletingId()" title="Delete Note" maxWidth="420px" dialogTestId="delete-note-modal" (close)="deletingId.set(null)">
29754
- <p class="note-delete-msg" data-testid="delete-note-message">Delete this note? It is permanently removed from this record and cannot be undone.</p>
29787
+ <ui-modal [open]="!!deletingId()" title="Delete Note" maxWidth="420px" dialogTestId="delete-note-modal" describedBy="ui-note-delete-msg" (close)="deletingId.set(null)">
29788
+ <p class="note-delete-msg" id="ui-note-delete-msg" data-testid="delete-note-message">Delete this note? It is permanently removed from this record and cannot be undone.</p>
29755
29789
  <ui-button modal-footer-left variant="secondary" size="sm" data-testid="delete-note-cancel" (click)="deletingId.set(null)">Cancel</ui-button>
29756
29790
  <ui-button modal-footer-right variant="danger" size="sm" data-testid="delete-note-confirm" [loading]="saving()" (click)="deleteNote()">Delete Note</ui-button>
29757
29791
  </ui-modal>
@@ -45250,6 +45284,7 @@ var ImageGalleryDialogComponent = class ImageGalleryDialogComponent {
45250
45284
  "open",
45251
45285
  "title",
45252
45286
  "description",
45287
+ "describedBy",
45253
45288
  "maxWidth",
45254
45289
  "bodyOverflow",
45255
45290
  "dialogTestId",
@@ -52522,6 +52557,17 @@ var ConversationsComponent = class ConversationsComponent {
52522
52557
  this.send = output();
52523
52558
  this.filesSelected = output();
52524
52559
  this.attachmentRemove = output();
52560
+ this.linkGroups = input(null, ...ngDevMode ? [{ debugName: "linkGroups" }] : /* istanbul ignore next */ []);
52561
+ this.linkLoading = input(false, ...ngDevMode ? [{ debugName: "linkLoading" }] : /* istanbul ignore next */ []);
52562
+ this.link = model(null, ...ngDevMode ? [{ debugName: "link" }] : /* istanbul ignore next */ []);
52563
+ this.linkLabel = input("Link a record", ...ngDevMode ? [{ debugName: "linkLabel" }] : /* istanbul ignore next */ []);
52564
+ this.linkPickerTitle = input("Link a Record", ...ngDevMode ? [{ debugName: "linkPickerTitle" }] : /* istanbul ignore next */ []);
52565
+ this.linkSearchPlaceholder = input("Search...", ...ngDevMode ? [{ debugName: "linkSearchPlaceholder" }] : /* istanbul ignore next */ []);
52566
+ this.linkSearchLabel = input("Search records", ...ngDevMode ? [{ debugName: "linkSearchLabel" }] : /* istanbul ignore next */ []);
52567
+ this.linkEmptyText = input("No records found.", ...ngDevMode ? [{ debugName: "linkEmptyText" }] : /* istanbul ignore next */ []);
52568
+ this.linkLoadingText = input("Loading...", ...ngDevMode ? [{ debugName: "linkLoadingText" }] : /* istanbul ignore next */ []);
52569
+ this.removeLinkLabel = input("Remove link", ...ngDevMode ? [{ debugName: "removeLinkLabel" }] : /* istanbul ignore next */ []);
52570
+ this.linkPickerOpened = output();
52525
52571
  this.mediaOpen = output();
52526
52572
  this.messageAction = output();
52527
52573
  this.loadEarlier = output();
@@ -52555,7 +52601,18 @@ var ConversationsComponent = class ConversationsComponent {
52555
52601
  this.emptyText = computed(() => this.filtering() ? this.filteredEmptyMessage() : this.canWrite() ? this.emptyMessage() : this.emptyReadOnlyMessage(), ...ngDevMode ? [{ debugName: "emptyText" }] : /* istanbul ignore next */ []);
52556
52602
  this.messageRows = computed(() => groupMessagesByDay(this.messages(), this.clock()), ...ngDevMode ? [{ debugName: "messageRows" }] : /* istanbul ignore next */ []);
52557
52603
  this.contextVisible = computed(() => this.hasContext() && this.contextOpen() && !!this.selected(), ...ngDevMode ? [{ debugName: "contextVisible" }] : /* istanbul ignore next */ []);
52558
- this.canSend = computed(() => !this.composerDisabled() && !this.sendBlocked() && !this.sending() && (this.draft().trim().length > 0 || !!this.attachment()) && this.draft().trim().length <= this.maxLength(), ...ngDevMode ? [{ debugName: "canSend" }] : /* istanbul ignore next */ []);
52604
+ this.canSend = computed(() => !this.composerDisabled() && !this.sendBlocked() && !this.sending() && (this.draft().trim().length > 0 || !!this.attachment() || !!this.link()) && this.draft().trim().length <= this.maxLength(), ...ngDevMode ? [{ debugName: "canSend" }] : /* istanbul ignore next */ []);
52605
+ this.pickerOpen = signal(false, ...ngDevMode ? [{ debugName: "pickerOpen" }] : /* istanbul ignore next */ []);
52606
+ this.linkQuery = signal("", ...ngDevMode ? [{ debugName: "linkQuery" }] : /* istanbul ignore next */ []);
52607
+ this.visibleLinkGroups = computed(() => {
52608
+ const q = this.linkQuery().trim().toLowerCase();
52609
+ const groups = this.linkGroups() ?? [];
52610
+ if (!q) return groups.filter((g) => g.items.length > 0);
52611
+ return groups.map((g) => ({
52612
+ ...g,
52613
+ items: g.items.filter((i) => i.label.toLowerCase().includes(q))
52614
+ })).filter((g) => g.items.length > 0);
52615
+ }, ...ngDevMode ? [{ debugName: "visibleLinkGroups" }] : /* istanbul ignore next */ []);
52559
52616
  this.editorHtml = signal("", ...ngDevMode ? [{ debugName: "editorHtml" }] : /* istanbul ignore next */ []);
52560
52617
  this.richHide = RICH_COMPOSER_HIDE;
52561
52618
  effect(() => {
@@ -52632,10 +52689,31 @@ var ConversationsComponent = class ConversationsComponent {
52632
52689
  }
52633
52690
  sendMessage() {
52634
52691
  if (!this.canSend()) return;
52635
- this.send.emit(this.richText() ? {
52692
+ const link = this.link();
52693
+ const base = this.richText() ? {
52636
52694
  text: this.draft().trim(),
52637
52695
  html: sanitizeRichTextHtml(this.editorHtml(), { mode: "paste" })
52638
- } : { text: this.draft().trim() });
52696
+ } : { text: this.draft().trim() };
52697
+ this.send.emit(link ? {
52698
+ ...base,
52699
+ link
52700
+ } : base);
52701
+ }
52702
+ openLinkPicker() {
52703
+ this.linkQuery.set("");
52704
+ this.pickerOpen.set(true);
52705
+ this.linkPickerOpened.emit();
52706
+ }
52707
+ closeLinkPicker() {
52708
+ this.pickerOpen.set(false);
52709
+ }
52710
+ pickLink(group, item) {
52711
+ this.link.set({
52712
+ type: group.type,
52713
+ id: item.id,
52714
+ label: item.label
52715
+ });
52716
+ this.pickerOpen.set(false);
52639
52717
  }
52640
52718
  clock() {
52641
52719
  return this.now() ?? /* @__PURE__ */ new Date();
@@ -53261,6 +53339,76 @@ var ConversationsComponent = class ConversationsComponent {
53261
53339
  isSignal: true,
53262
53340
  isRequired: false,
53263
53341
  transformFunction: null
53342
+ },
53343
+ linkGroups: {
53344
+ classPropertyName: "linkGroups",
53345
+ publicName: "linkGroups",
53346
+ isSignal: true,
53347
+ isRequired: false,
53348
+ transformFunction: null
53349
+ },
53350
+ linkLoading: {
53351
+ classPropertyName: "linkLoading",
53352
+ publicName: "linkLoading",
53353
+ isSignal: true,
53354
+ isRequired: false,
53355
+ transformFunction: null
53356
+ },
53357
+ link: {
53358
+ classPropertyName: "link",
53359
+ publicName: "link",
53360
+ isSignal: true,
53361
+ isRequired: false,
53362
+ transformFunction: null
53363
+ },
53364
+ linkLabel: {
53365
+ classPropertyName: "linkLabel",
53366
+ publicName: "linkLabel",
53367
+ isSignal: true,
53368
+ isRequired: false,
53369
+ transformFunction: null
53370
+ },
53371
+ linkPickerTitle: {
53372
+ classPropertyName: "linkPickerTitle",
53373
+ publicName: "linkPickerTitle",
53374
+ isSignal: true,
53375
+ isRequired: false,
53376
+ transformFunction: null
53377
+ },
53378
+ linkSearchPlaceholder: {
53379
+ classPropertyName: "linkSearchPlaceholder",
53380
+ publicName: "linkSearchPlaceholder",
53381
+ isSignal: true,
53382
+ isRequired: false,
53383
+ transformFunction: null
53384
+ },
53385
+ linkSearchLabel: {
53386
+ classPropertyName: "linkSearchLabel",
53387
+ publicName: "linkSearchLabel",
53388
+ isSignal: true,
53389
+ isRequired: false,
53390
+ transformFunction: null
53391
+ },
53392
+ linkEmptyText: {
53393
+ classPropertyName: "linkEmptyText",
53394
+ publicName: "linkEmptyText",
53395
+ isSignal: true,
53396
+ isRequired: false,
53397
+ transformFunction: null
53398
+ },
53399
+ linkLoadingText: {
53400
+ classPropertyName: "linkLoadingText",
53401
+ publicName: "linkLoadingText",
53402
+ isSignal: true,
53403
+ isRequired: false,
53404
+ transformFunction: null
53405
+ },
53406
+ removeLinkLabel: {
53407
+ classPropertyName: "removeLinkLabel",
53408
+ publicName: "removeLinkLabel",
53409
+ isSignal: true,
53410
+ isRequired: false,
53411
+ transformFunction: null
53264
53412
  }
53265
53413
  },
53266
53414
  outputs: {
@@ -53280,6 +53428,8 @@ var ConversationsComponent = class ConversationsComponent {
53280
53428
  send: "send",
53281
53429
  filesSelected: "filesSelected",
53282
53430
  attachmentRemove: "attachmentRemove",
53431
+ link: "linkChange",
53432
+ linkPickerOpened: "linkPickerOpened",
53283
53433
  mediaOpen: "mediaOpen",
53284
53434
  messageAction: "messageAction",
53285
53435
  loadEarlier: "loadEarlier",
@@ -53495,6 +53645,13 @@ var ConversationsComponent = class ConversationsComponent {
53495
53645
  }
53496
53646
  @if (row.message.bodyHtml) { <ui-conversation-rich-body [html]="row.message.bodyHtml" data-testid="msg-rich-body" /> }
53497
53647
  @else if (row.message.body) { <p class="cv-body">{{ row.message.body }}</p> }
53648
+ @if (row.message.link; as msgLink) {
53649
+ @if (msgLink.url) {
53650
+ <a class="cv-link-chip" [href]="msgLink.url" data-testid="msg-link"><svg class="cv-link-icon" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.07 0l3-3a5 5 0 0 0-7.07-7.07l-1.5 1.5"/><path d="M14 11a5 5 0 0 0-7.07 0l-3 3a5 5 0 0 0 7.07 7.07l1.5-1.5"/></svg><span class="cv-link-label">{{ msgLink.label }}</span></a>
53651
+ } @else {
53652
+ <span class="cv-link-chip" data-testid="msg-link"><svg class="cv-link-icon" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.07 0l3-3a5 5 0 0 0-7.07-7.07l-1.5 1.5"/><path d="M14 11a5 5 0 0 0-7.07 0l-3 3a5 5 0 0 0 7.07 7.07l1.5-1.5"/></svg><span class="cv-link-label">{{ msgLink.label }}</span></span>
53653
+ }
53654
+ }
53498
53655
  <span class="cv-meta">
53499
53656
  @if (row.message.sender) { <span class="cv-sender" data-testid="msg-sender">{{ row.message.sender }}</span> }
53500
53657
  {{ time(row.message) }}
@@ -53527,7 +53684,20 @@ var ConversationsComponent = class ConversationsComponent {
53527
53684
  </ui-button>
53528
53685
  </div>
53529
53686
  }
53687
+ @if (link(); as chosen) {
53688
+ <div class="cv-link-preview" data-testid="link-preview">
53689
+ <span class="cv-link-chip cv-link-chip-compose"><svg class="cv-link-icon" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.07 0l3-3a5 5 0 0 0-7.07-7.07l-1.5 1.5"/><path d="M14 11a5 5 0 0 0-7.07 0l-3 3a5 5 0 0 0 7.07 7.07l1.5-1.5"/></svg><span class="cv-link-label" data-testid="link-preview-label">{{ chosen.label }}</span></span>
53690
+ <ui-button class="cv-link-remove" variant="icon-danger" [ariaLabel]="removeLinkLabel()" [disabled]="sending()" (click)="link.set(null)" data-testid="link-remove">
53691
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
53692
+ </ui-button>
53693
+ </div>
53694
+ }
53530
53695
  <div class="cv-compose-row">
53696
+ @if (linkGroups() !== null) {
53697
+ <ui-button class="cv-link-btn" variant="icon" [ariaLabel]="linkLabel()" [uiTooltip]="linkLabel()" [disabled]="sending() || composerDisabled()" (click)="openLinkPicker()" data-testid="link-btn">
53698
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.07 0l3-3a5 5 0 0 0-7.07-7.07l-1.5 1.5"/><path d="M14 11a5 5 0 0 0-7.07 0l-3 3a5 5 0 0 0 7.07 7.07l1.5-1.5"/></svg>
53699
+ </ui-button>
53700
+ }
53531
53701
  @if (attachEnabled()) {
53532
53702
  <ui-button class="cv-attach-btn" variant="icon" [ariaLabel]="attachLabel()" [disabled]="uploading() || composerDisabled()" [loading]="uploading()" (click)="openFilePicker()" data-testid="attach-btn">
53533
53703
  <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg>
@@ -53560,6 +53730,28 @@ var ConversationsComponent = class ConversationsComponent {
53560
53730
  }
53561
53731
  </section>
53562
53732
 
53733
+ @if (linkGroups() !== null) {
53734
+ <ui-modal [open]="pickerOpen()" [title]="linkPickerTitle()" maxWidth="480px" bodyOverflow="hidden" dialogTestId="link-picker" (close)="closeLinkPicker()">
53735
+ <ui-search-input [placeholder]="linkSearchPlaceholder()" [ariaLabel]="linkSearchLabel()" [(value)]="linkQuery" data-testid="link-search" />
53736
+ <div class="cv-link-list" data-testid="link-list">
53737
+ @if (linkLoading()) {
53738
+ <p class="cv-link-note" role="status" data-testid="link-loading">{{ linkLoadingText() }}</p>
53739
+ } @else if (visibleLinkGroups().length === 0) {
53740
+ <p class="cv-link-note" data-testid="link-empty">{{ linkEmptyText() }}</p>
53741
+ } @else {
53742
+ @for (group of visibleLinkGroups(); track group.type) {
53743
+ <div class="cv-link-section" role="group" [attr.aria-label]="group.label" data-testid="link-section">
53744
+ <div class="cv-link-section-label" aria-hidden="true">{{ group.label }}</div>
53745
+ @for (item of group.items; track item.id) {
53746
+ <button type="button" class="cv-link-item" data-testid="link-item" (click)="pickLink(group, item)">{{ item.label }}</button>
53747
+ }
53748
+ </div>
53749
+ }
53750
+ }
53751
+ </div>
53752
+ </ui-modal>
53753
+ }
53754
+
53563
53755
  <!-- RIGHT: contact panel (the host's content) -->
53564
53756
  @if (contextVisible()) {
53565
53757
  <aside class="cv-context" #contextPanel tabindex="-1" [attr.aria-label]="contextTitle()" (keydown.escape)="closeContext(true)" data-testid="context-panel">
@@ -53575,7 +53767,7 @@ var ConversationsComponent = class ConversationsComponent {
53575
53767
  </div>
53576
53768
  `,
53577
53769
  isInline: true,
53578
- styles: [":host{display:flex;flex:1;min-height:0;min-width:0}.cv-sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.cv-layout{display:grid;grid-template-columns:340px 1fr;flex:1;min-height:0;min-width:0;background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 1px 3px #0000000f;border:1px solid #e2e8f0}.cv-layout.has-context{grid-template-columns:340px 1fr 280px}.cv-list-panel{display:flex;flex-direction:column;min-height:0;border-right:1px solid #e2e8f0;background:#f8fafc;overflow:hidden}.cv-list-header{display:flex;align-items:center;justify-content:space-between;padding:20px 20px 12px}.cv-list-title{margin:0;font-size:1.125rem;font-weight:700;color:#1e293b}.cv-search,.cv-filters{padding:0 16px 12px}.cv-list{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain}.cv-item{display:flex;gap:12px;padding:14px 16px;border-bottom:1px solid #f1f5f9;transition:background .15s}.cv-item.unread{background:#fffbeb}.cv-item.selected{background:#eff6ff;box-shadow:inset 3px 0 #2563eb}.cv-item-info{flex:1;min-width:0}.cv-row-arrow{flex:0 0 auto;align-self:center;color:#94a3b8}.cv-item-top{display:flex;justify-content:space-between;align-items:center;margin-bottom:4px}.cv-name{font-size:.875rem;font-weight:600;color:#1e293b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.cv-time{font-size:.6875rem;color:#64748b;flex-shrink:0;margin-left:8px}.cv-subtitle{font-size:.6875rem;color:#64748b;display:block;margin-bottom:2px}.cv-item-bottom{display:flex;justify-content:space-between;align-items:center;gap:8px}.cv-preview{margin:0;font-size:.8125rem;color:#64748b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0}.cv-item-right{display:flex;align-items:center;gap:6px;flex-shrink:0}.cv-delete-btn{opacity:0;transition:opacity .15s}.cv-item:hover .cv-delete-btn,.cv-item:focus-within .cv-delete-btn,.cv-delete-btn:focus-within{opacity:1}@media(hover:none){.cv-delete-btn{opacity:1}}.cv-loading{padding:8px 0}.cv-skeleton{display:flex;gap:12px;align-items:flex-start;padding:14px 16px;border-bottom:1px solid #f1f5f9}.cv-skel-content{flex:1;min-width:0;display:flex;flex-direction:column;gap:6px;padding-top:2px}.cv-skel-row{display:flex;justify-content:space-between;gap:8px}.cv-error{display:flex;flex-direction:column;align-items:center;gap:12px;padding:24px 16px;text-align:center}.cv-thread-panel{display:flex;flex-direction:column;min-width:0;min-height:0;overflow:hidden;background:#fff}.cv-thread-header{display:flex;align-items:center;gap:12px;padding:16px 20px;border-bottom:1px solid #e2e8f0;background:#fff}.cv-back-btn{display:none}.cv-thread-header-info{display:flex;align-items:center;gap:12px;flex:1;min-width:0}.cv-thread-header-text{min-width:0;display:flex;flex-direction:column}.cv-thread-name{margin:0;font-size:.9375rem;font-weight:600;color:#1e293b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.cv-thread-name:focus{outline:none}.cv-thread-name:focus-visible{outline:2px solid #2563eb;outline-offset:2px;border-radius:4px}.cv-thread-subtitle{font-size:.75rem;color:#64748b}.cv-thread-actions{display:flex;align-items:center;gap:8px}.cv-messages{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;padding:20px 24px;display:flex;flex-direction:column;gap:4px;background:#f8fafc}.cv-messages:focus-visible{outline:2px solid #2563eb;outline-offset:-2px}.cv-messages-loading{display:flex;flex-direction:column;gap:12px;padding:20px 0}.cv-msg-skeleton{display:flex}.cv-msg-skeleton.outbound{justify-content:flex-end}.cv-messages-error{display:flex;flex-direction:column;align-items:center;gap:12px;padding:24px 16px;text-align:center}.cv-load-earlier{display:flex;justify-content:center;padding:4px 0 12px}.cv-sender{font-weight:600}.cv-msg-action{padding:0;border:0;background:none;color:inherit;font:inherit;text-decoration:underline;cursor:pointer}.cv-msg-action:focus-visible{outline:2px solid currentColor;outline-offset:2px;border-radius:2px}.cv-messages-empty{display:flex;align-items:center;justify-content:center;flex:1}.cv-messages-empty p{margin:0;font-size:.875rem;color:#64748b;text-align:center}.cv-date-separator{display:flex;align-items:center;justify-content:center;padding:16px 0 8px}.cv-date-separator span{padding:4px 14px;border-radius:12px;background:#e2e8f0;font-size:.6875rem;font-weight:600;color:#475569;text-transform:uppercase;letter-spacing:.3px}.cv-bubble{max-width:70%;padding:10px 14px;border-radius:16px;position:relative;word-break:break-word;overflow-wrap:break-word}.cv-bubble.inbound{align-self:flex-start;background:#fff;border:1px solid #e2e8f0;border-bottom-left-radius:4px}.cv-bubble.inbound .cv-body,.cv-bubble.inbound .cv-rich{color:#1e293b}.cv-bubble.inbound .cv-meta{color:#64748b}.cv-bubble.outbound{align-self:flex-end;background:#2563eb;border-bottom-right-radius:4px}.cv-bubble.outbound .cv-body,.cv-bubble.outbound .cv-rich,.cv-bubble.outbound .cv-meta{color:#fff}@media(prefers-reduced-motion:no-preference){.cv-bubble{animation:cv-appear .2s ease}.cv-context{animation:cv-slide-in .25s ease}}.cv-body{margin:0;font-size:.875rem;line-height:1.45;word-break:break-word;white-space:pre-wrap}.cv-media{margin-bottom:6px}.cv-image-btn{display:block;padding:0;border:0;background:none;cursor:pointer;border-radius:8px}.cv-image-btn:focus-visible{outline:2px solid #fff;outline-offset:2px;box-shadow:0 0 0 4px #2563eb}.cv-image{display:block;max-width:240px;max-height:240px;border-radius:8px;object-fit:cover}.cv-video{max-width:240px;max-height:200px;border-radius:8px}.cv-file-link{font-size:.8125rem;color:#fff;text-decoration:underline}.cv-bubble.inbound .cv-file-link{color:#2563eb}.cv-meta{display:flex;align-items:center;gap:6px;justify-content:flex-end;font-size:.6875rem;margin-top:4px}.cv-status{font-weight:500}.cv-status.failed{color:#fff;font-weight:700}.cv-bubble.inbound .cv-status.failed{color:#dc2626}.cv-compose{display:flex;flex-direction:column;padding:12px 20px 16px;padding-bottom:calc(16px + env(safe-area-inset-bottom,0px));border-top:1px solid #e2e8f0;background:#fff}.cv-compose-row{display:flex;align-items:flex-end;gap:10px}.cv-compose-input{flex:1;min-width:0}.cv-attach-btn,.cv-send-btn{flex-shrink:0}.cv-attachment{display:flex;align-items:center;gap:8px;padding:8px 12px;margin-bottom:8px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px}.cv-attachment-thumb{width:48px;height:48px;object-fit:cover;border-radius:6px}.cv-attachment-name{font-size:.8125rem;color:#475569;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cv-attachment-remove{flex-shrink:0}.cv-compose-status{margin:4px 0 0;font-size:.8125rem;color:#64748b}.cv-compose-status:empty{margin:0}.cv-context-backdrop{display:none}.cv-context{width:100%;min-width:0;border-left:1px solid #e2e8f0;background:#fff;display:flex;flex-direction:column;overflow-y:auto}.cv-context-header{display:flex;justify-content:space-between;align-items:center;padding:20px;border-bottom:1px solid #e2e8f0}.cv-context-header h3{margin:0;font-size:.875rem;font-weight:700;color:#1e293b}.cv-context-body{padding:20px;display:flex;flex-direction:column;gap:16px}@keyframes cv-appear{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}@keyframes cv-slide-in{0%{transform:translate(100%)}to{transform:translate(0)}}@media(max-width:1100px){.cv-layout.has-context{grid-template-columns:280px 1fr 240px}}@media(max-width:768px){.cv-layout,.cv-layout.has-context{grid-template-columns:1fr;height:100%}.cv-hidden-mobile{display:none!important}.cv-back-btn{display:inline-flex}.cv-context-backdrop{display:block;position:fixed;inset:0;background:#00000059;z-index:99}.cv-context{position:fixed;top:0;right:0;bottom:0;width:300px;max-width:85vw;z-index:100;box-shadow:-4px 0 20px #0000001a}.cv-bubble{max-width:85%}.cv-messages,.cv-thread-header{padding:12px 16px}}@media(max-width:480px){.cv-thread-subtitle{display:none}.cv-compose{padding:8px 12px 12px;padding-bottom:calc(12px + env(safe-area-inset-bottom,0px))}.cv-compose-row{flex-wrap:wrap}.cv-compose-input{order:-1;flex:1 1 100%}.cv-send-btn{margin-inline-start:auto}.cv-list-header{padding:12px 16px 10px}.cv-context{width:100%;max-width:100vw}}\n"],
53770
+ styles: [":host{display:flex;flex:1;min-height:0;min-width:0}.cv-sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.cv-layout{display:grid;grid-template-columns:340px 1fr;flex:1;min-height:0;min-width:0;background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 1px 3px #0000000f;border:1px solid #e2e8f0}.cv-layout.has-context{grid-template-columns:340px 1fr 280px}.cv-list-panel{display:flex;flex-direction:column;min-height:0;border-right:1px solid #e2e8f0;background:#f8fafc;overflow:hidden}.cv-list-header{display:flex;align-items:center;justify-content:space-between;padding:20px 20px 12px}.cv-list-title{margin:0;font-size:1.125rem;font-weight:700;color:#1e293b}.cv-search,.cv-filters{padding:0 16px 12px}.cv-list{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain}.cv-item{display:flex;gap:12px;padding:14px 16px;border-bottom:1px solid #f1f5f9;transition:background .15s}.cv-item.unread{background:#fffbeb}.cv-item.selected{background:#eff6ff;box-shadow:inset 3px 0 #2563eb}.cv-item-info{flex:1;min-width:0}.cv-row-arrow{flex:0 0 auto;align-self:center;color:#94a3b8}.cv-item-top{display:flex;justify-content:space-between;align-items:center;margin-bottom:4px}.cv-name{font-size:.875rem;font-weight:600;color:#1e293b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.cv-time{font-size:.6875rem;color:#64748b;flex-shrink:0;margin-left:8px}.cv-subtitle{font-size:.6875rem;color:#64748b;display:block;margin-bottom:2px}.cv-item-bottom{display:flex;justify-content:space-between;align-items:center;gap:8px}.cv-preview{margin:0;font-size:.8125rem;color:#64748b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0}.cv-item-right{display:flex;align-items:center;gap:6px;flex-shrink:0}.cv-delete-btn{opacity:0;transition:opacity .15s}.cv-item:hover .cv-delete-btn,.cv-item:focus-within .cv-delete-btn,.cv-delete-btn:focus-within{opacity:1}@media(hover:none){.cv-delete-btn{opacity:1}}.cv-loading{padding:8px 0}.cv-skeleton{display:flex;gap:12px;align-items:flex-start;padding:14px 16px;border-bottom:1px solid #f1f5f9}.cv-skel-content{flex:1;min-width:0;display:flex;flex-direction:column;gap:6px;padding-top:2px}.cv-skel-row{display:flex;justify-content:space-between;gap:8px}.cv-error{display:flex;flex-direction:column;align-items:center;gap:12px;padding:24px 16px;text-align:center}.cv-thread-panel{display:flex;flex-direction:column;min-width:0;min-height:0;overflow:hidden;background:#fff}.cv-thread-header{display:flex;align-items:center;gap:12px;padding:16px 20px;border-bottom:1px solid #e2e8f0;background:#fff}.cv-back-btn{display:none}.cv-thread-header-info{display:flex;align-items:center;gap:12px;flex:1;min-width:0}.cv-thread-header-text{min-width:0;display:flex;flex-direction:column}.cv-thread-name{margin:0;font-size:.9375rem;font-weight:600;color:#1e293b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.cv-thread-name:focus{outline:none}.cv-thread-name:focus-visible{outline:2px solid #2563eb;outline-offset:2px;border-radius:4px}.cv-thread-subtitle{font-size:.75rem;color:#64748b}.cv-thread-actions{display:flex;align-items:center;gap:8px}.cv-messages{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;padding:20px 24px;display:flex;flex-direction:column;gap:4px;background:#f8fafc}.cv-messages:focus-visible{outline:2px solid #2563eb;outline-offset:-2px}.cv-messages-loading{display:flex;flex-direction:column;gap:12px;padding:20px 0}.cv-msg-skeleton{display:flex}.cv-msg-skeleton.outbound{justify-content:flex-end}.cv-messages-error{display:flex;flex-direction:column;align-items:center;gap:12px;padding:24px 16px;text-align:center}.cv-load-earlier{display:flex;justify-content:center;padding:4px 0 12px}.cv-sender{font-weight:600}.cv-link-chip{display:inline-flex;align-items:center;gap:6px;max-width:100%;margin-top:6px;padding:3px 10px;border-radius:999px;border:1px solid transparent;font-size:.8rem;font-weight:600;text-decoration:none}.cv-link-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.cv-link-icon{flex:none}.cv-bubble.inbound .cv-link-chip{background:#eff6ff;border-color:#bfdbfe;color:#1d4ed8}.cv-bubble.outbound .cv-link-chip{background:#ffffff2e;border-color:#ffffff73;color:#fff}.cv-link-chip:is(a):hover{filter:brightness(.96);text-decoration:underline}.cv-link-chip:is(a):focus-visible{outline:2px solid currentColor;outline-offset:2px}.cv-link-preview{display:flex;align-items:center;gap:4px;margin-bottom:8px}.cv-link-chip-compose{margin-top:0;background:#eff6ff;border-color:#bfdbfe;color:#1d4ed8}.cv-link-list{display:flex;flex-direction:column;gap:4px;margin-top:12px;max-height:320px;overflow-y:auto}.cv-link-section-label{padding:8px 4px 4px;font-size:.7rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#64748b}.cv-link-item{display:block;width:100%;padding:10px 12px;border:0;border-radius:8px;background:none;text-align:start;font:inherit;font-size:.875rem;color:#1e293b;cursor:pointer}.cv-link-item:hover{background:#f1f5f9}.cv-link-item:focus-visible{outline:2px solid #2563eb;outline-offset:-2px}.cv-link-note{margin:16px 0;text-align:center;font-size:.875rem;color:#64748b}@media(pointer:coarse),(max-width:480px){.cv-link-item{min-height:44px}}.cv-msg-action{padding:0;border:0;background:none;color:inherit;font:inherit;text-decoration:underline;cursor:pointer}.cv-msg-action:focus-visible{outline:2px solid currentColor;outline-offset:2px;border-radius:2px}.cv-messages-empty{display:flex;align-items:center;justify-content:center;flex:1}.cv-messages-empty p{margin:0;font-size:.875rem;color:#64748b;text-align:center}.cv-date-separator{display:flex;align-items:center;justify-content:center;padding:16px 0 8px}.cv-date-separator span{padding:4px 14px;border-radius:12px;background:#e2e8f0;font-size:.6875rem;font-weight:600;color:#475569;text-transform:uppercase;letter-spacing:.3px}.cv-bubble{max-width:70%;padding:10px 14px;border-radius:16px;position:relative;word-break:break-word;overflow-wrap:break-word}.cv-bubble.inbound{align-self:flex-start;background:#fff;border:1px solid #e2e8f0;border-bottom-left-radius:4px}.cv-bubble.inbound .cv-body,.cv-bubble.inbound .cv-rich{color:#1e293b}.cv-bubble.inbound .cv-meta{color:#64748b}.cv-bubble.outbound{align-self:flex-end;background:#2563eb;border-bottom-right-radius:4px}.cv-bubble.outbound .cv-body,.cv-bubble.outbound .cv-rich,.cv-bubble.outbound .cv-meta{color:#fff}@media(prefers-reduced-motion:no-preference){.cv-bubble{animation:cv-appear .2s ease}.cv-context{animation:cv-slide-in .25s ease}}.cv-body{margin:0;font-size:.875rem;line-height:1.45;word-break:break-word;white-space:pre-wrap}.cv-media{margin-bottom:6px}.cv-image-btn{display:block;padding:0;border:0;background:none;cursor:pointer;border-radius:8px}.cv-image-btn:focus-visible{outline:2px solid #fff;outline-offset:2px;box-shadow:0 0 0 4px #2563eb}.cv-image{display:block;max-width:240px;max-height:240px;border-radius:8px;object-fit:cover}.cv-video{max-width:240px;max-height:200px;border-radius:8px}.cv-file-link{font-size:.8125rem;color:#fff;text-decoration:underline}.cv-bubble.inbound .cv-file-link{color:#2563eb}.cv-meta{display:flex;align-items:center;gap:6px;justify-content:flex-end;font-size:.6875rem;margin-top:4px}.cv-status{font-weight:500}.cv-status.failed{color:#fff;font-weight:700}.cv-bubble.inbound .cv-status.failed{color:#dc2626}.cv-compose{display:flex;flex-direction:column;padding:12px 20px 16px;padding-bottom:calc(16px + env(safe-area-inset-bottom,0px));border-top:1px solid #e2e8f0;background:#fff}.cv-compose-row{display:flex;align-items:flex-end;gap:10px}.cv-compose-input{flex:1;min-width:0}.cv-attach-btn,.cv-send-btn{flex-shrink:0}.cv-attachment{display:flex;align-items:center;gap:8px;padding:8px 12px;margin-bottom:8px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px}.cv-attachment-thumb{width:48px;height:48px;object-fit:cover;border-radius:6px}.cv-attachment-name{font-size:.8125rem;color:#475569;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cv-attachment-remove{flex-shrink:0}.cv-compose-status{margin:4px 0 0;font-size:.8125rem;color:#64748b}.cv-compose-status:empty{margin:0}.cv-context-backdrop{display:none}.cv-context{width:100%;min-width:0;border-left:1px solid #e2e8f0;background:#fff;display:flex;flex-direction:column;overflow-y:auto}.cv-context-header{display:flex;justify-content:space-between;align-items:center;padding:20px;border-bottom:1px solid #e2e8f0}.cv-context-header h3{margin:0;font-size:.875rem;font-weight:700;color:#1e293b}.cv-context-body{padding:20px;display:flex;flex-direction:column;gap:16px}@keyframes cv-appear{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}@keyframes cv-slide-in{0%{transform:translate(100%)}to{transform:translate(0)}}@media(max-width:1100px){.cv-layout.has-context{grid-template-columns:280px 1fr 240px}}@media(max-width:768px){.cv-layout,.cv-layout.has-context{grid-template-columns:1fr;height:100%}.cv-hidden-mobile{display:none!important}.cv-back-btn{display:inline-flex}.cv-context-backdrop{display:block;position:fixed;inset:0;background:#00000059;z-index:99}.cv-context{position:fixed;top:0;right:0;bottom:0;width:300px;max-width:85vw;z-index:100;box-shadow:-4px 0 20px #0000001a}.cv-bubble{max-width:85%}.cv-messages,.cv-thread-header{padding:12px 16px}}@media(max-width:480px){.cv-thread-subtitle{display:none}.cv-compose{padding:8px 12px 12px;padding-bottom:calc(12px + env(safe-area-inset-bottom,0px))}.cv-compose-row{flex-wrap:wrap}.cv-compose-input{order:-1;flex:1 1 100%}.cv-send-btn{margin-inline-start:auto}.cv-list-header{padding:12px 16px 10px}.cv-context{width:100%;max-width:100vw}}\n"],
53579
53771
  dependencies: [
53580
53772
  {
53581
53773
  kind: "ngmodule",
@@ -53861,6 +54053,28 @@ var ConversationsComponent = class ConversationsComponent {
53861
54053
  type: ConversationRichBodyComponent,
53862
54054
  selector: "ui-conversation-rich-body",
53863
54055
  inputs: ["html"]
54056
+ },
54057
+ {
54058
+ kind: "component",
54059
+ type: ModalComponent,
54060
+ selector: "ui-modal",
54061
+ inputs: [
54062
+ "open",
54063
+ "title",
54064
+ "description",
54065
+ "describedBy",
54066
+ "maxWidth",
54067
+ "bodyOverflow",
54068
+ "dialogTestId",
54069
+ "hideClose",
54070
+ "closable",
54071
+ "layout",
54072
+ "closeOnBackdrop",
54073
+ "closeOnEscape",
54074
+ "busy",
54075
+ "returnFocusTo"
54076
+ ],
54077
+ outputs: ["close"]
53864
54078
  }
53865
54079
  ],
53866
54080
  changeDetection: i0.ChangeDetectionStrategy.OnPush
@@ -53894,7 +54108,8 @@ i0.ɵɵngDeclareClassMetadata({
53894
54108
  SkeletonComponent,
53895
54109
  TextareaInputComponent,
53896
54110
  TooltipDirective,
53897
- ConversationRichBodyComponent
54111
+ ConversationRichBodyComponent,
54112
+ ModalComponent
53898
54113
  ],
53899
54114
  template: `
53900
54115
  <div class="cv-layout" [class.has-context]="contextVisible()" data-testid="conversations">
@@ -54075,6 +54290,13 @@ i0.ɵɵngDeclareClassMetadata({
54075
54290
  }
54076
54291
  @if (row.message.bodyHtml) { <ui-conversation-rich-body [html]="row.message.bodyHtml" data-testid="msg-rich-body" /> }
54077
54292
  @else if (row.message.body) { <p class="cv-body">{{ row.message.body }}</p> }
54293
+ @if (row.message.link; as msgLink) {
54294
+ @if (msgLink.url) {
54295
+ <a class="cv-link-chip" [href]="msgLink.url" data-testid="msg-link"><svg class="cv-link-icon" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.07 0l3-3a5 5 0 0 0-7.07-7.07l-1.5 1.5"/><path d="M14 11a5 5 0 0 0-7.07 0l-3 3a5 5 0 0 0 7.07 7.07l1.5-1.5"/></svg><span class="cv-link-label">{{ msgLink.label }}</span></a>
54296
+ } @else {
54297
+ <span class="cv-link-chip" data-testid="msg-link"><svg class="cv-link-icon" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.07 0l3-3a5 5 0 0 0-7.07-7.07l-1.5 1.5"/><path d="M14 11a5 5 0 0 0-7.07 0l-3 3a5 5 0 0 0 7.07 7.07l1.5-1.5"/></svg><span class="cv-link-label">{{ msgLink.label }}</span></span>
54298
+ }
54299
+ }
54078
54300
  <span class="cv-meta">
54079
54301
  @if (row.message.sender) { <span class="cv-sender" data-testid="msg-sender">{{ row.message.sender }}</span> }
54080
54302
  {{ time(row.message) }}
@@ -54107,7 +54329,20 @@ i0.ɵɵngDeclareClassMetadata({
54107
54329
  </ui-button>
54108
54330
  </div>
54109
54331
  }
54332
+ @if (link(); as chosen) {
54333
+ <div class="cv-link-preview" data-testid="link-preview">
54334
+ <span class="cv-link-chip cv-link-chip-compose"><svg class="cv-link-icon" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.07 0l3-3a5 5 0 0 0-7.07-7.07l-1.5 1.5"/><path d="M14 11a5 5 0 0 0-7.07 0l-3 3a5 5 0 0 0 7.07 7.07l1.5-1.5"/></svg><span class="cv-link-label" data-testid="link-preview-label">{{ chosen.label }}</span></span>
54335
+ <ui-button class="cv-link-remove" variant="icon-danger" [ariaLabel]="removeLinkLabel()" [disabled]="sending()" (click)="link.set(null)" data-testid="link-remove">
54336
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
54337
+ </ui-button>
54338
+ </div>
54339
+ }
54110
54340
  <div class="cv-compose-row">
54341
+ @if (linkGroups() !== null) {
54342
+ <ui-button class="cv-link-btn" variant="icon" [ariaLabel]="linkLabel()" [uiTooltip]="linkLabel()" [disabled]="sending() || composerDisabled()" (click)="openLinkPicker()" data-testid="link-btn">
54343
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M10 13a5 5 0 0 0 7.07 0l3-3a5 5 0 0 0-7.07-7.07l-1.5 1.5"/><path d="M14 11a5 5 0 0 0-7.07 0l-3 3a5 5 0 0 0 7.07 7.07l1.5-1.5"/></svg>
54344
+ </ui-button>
54345
+ }
54111
54346
  @if (attachEnabled()) {
54112
54347
  <ui-button class="cv-attach-btn" variant="icon" [ariaLabel]="attachLabel()" [disabled]="uploading() || composerDisabled()" [loading]="uploading()" (click)="openFilePicker()" data-testid="attach-btn">
54113
54348
  <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg>
@@ -54140,6 +54375,28 @@ i0.ɵɵngDeclareClassMetadata({
54140
54375
  }
54141
54376
  </section>
54142
54377
 
54378
+ @if (linkGroups() !== null) {
54379
+ <ui-modal [open]="pickerOpen()" [title]="linkPickerTitle()" maxWidth="480px" bodyOverflow="hidden" dialogTestId="link-picker" (close)="closeLinkPicker()">
54380
+ <ui-search-input [placeholder]="linkSearchPlaceholder()" [ariaLabel]="linkSearchLabel()" [(value)]="linkQuery" data-testid="link-search" />
54381
+ <div class="cv-link-list" data-testid="link-list">
54382
+ @if (linkLoading()) {
54383
+ <p class="cv-link-note" role="status" data-testid="link-loading">{{ linkLoadingText() }}</p>
54384
+ } @else if (visibleLinkGroups().length === 0) {
54385
+ <p class="cv-link-note" data-testid="link-empty">{{ linkEmptyText() }}</p>
54386
+ } @else {
54387
+ @for (group of visibleLinkGroups(); track group.type) {
54388
+ <div class="cv-link-section" role="group" [attr.aria-label]="group.label" data-testid="link-section">
54389
+ <div class="cv-link-section-label" aria-hidden="true">{{ group.label }}</div>
54390
+ @for (item of group.items; track item.id) {
54391
+ <button type="button" class="cv-link-item" data-testid="link-item" (click)="pickLink(group, item)">{{ item.label }}</button>
54392
+ }
54393
+ </div>
54394
+ }
54395
+ }
54396
+ </div>
54397
+ </ui-modal>
54398
+ }
54399
+
54143
54400
  <!-- RIGHT: contact panel (the host's content) -->
54144
54401
  @if (contextVisible()) {
54145
54402
  <aside class="cv-context" #contextPanel tabindex="-1" [attr.aria-label]="contextTitle()" (keydown.escape)="closeContext(true)" data-testid="context-panel">
@@ -54154,7 +54411,7 @@ i0.ɵɵngDeclareClassMetadata({
54154
54411
  }
54155
54412
  </div>
54156
54413
  `,
54157
- styles: [":host{display:flex;flex:1;min-height:0;min-width:0}.cv-sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.cv-layout{display:grid;grid-template-columns:340px 1fr;flex:1;min-height:0;min-width:0;background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 1px 3px #0000000f;border:1px solid #e2e8f0}.cv-layout.has-context{grid-template-columns:340px 1fr 280px}.cv-list-panel{display:flex;flex-direction:column;min-height:0;border-right:1px solid #e2e8f0;background:#f8fafc;overflow:hidden}.cv-list-header{display:flex;align-items:center;justify-content:space-between;padding:20px 20px 12px}.cv-list-title{margin:0;font-size:1.125rem;font-weight:700;color:#1e293b}.cv-search,.cv-filters{padding:0 16px 12px}.cv-list{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain}.cv-item{display:flex;gap:12px;padding:14px 16px;border-bottom:1px solid #f1f5f9;transition:background .15s}.cv-item.unread{background:#fffbeb}.cv-item.selected{background:#eff6ff;box-shadow:inset 3px 0 #2563eb}.cv-item-info{flex:1;min-width:0}.cv-row-arrow{flex:0 0 auto;align-self:center;color:#94a3b8}.cv-item-top{display:flex;justify-content:space-between;align-items:center;margin-bottom:4px}.cv-name{font-size:.875rem;font-weight:600;color:#1e293b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.cv-time{font-size:.6875rem;color:#64748b;flex-shrink:0;margin-left:8px}.cv-subtitle{font-size:.6875rem;color:#64748b;display:block;margin-bottom:2px}.cv-item-bottom{display:flex;justify-content:space-between;align-items:center;gap:8px}.cv-preview{margin:0;font-size:.8125rem;color:#64748b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0}.cv-item-right{display:flex;align-items:center;gap:6px;flex-shrink:0}.cv-delete-btn{opacity:0;transition:opacity .15s}.cv-item:hover .cv-delete-btn,.cv-item:focus-within .cv-delete-btn,.cv-delete-btn:focus-within{opacity:1}@media(hover:none){.cv-delete-btn{opacity:1}}.cv-loading{padding:8px 0}.cv-skeleton{display:flex;gap:12px;align-items:flex-start;padding:14px 16px;border-bottom:1px solid #f1f5f9}.cv-skel-content{flex:1;min-width:0;display:flex;flex-direction:column;gap:6px;padding-top:2px}.cv-skel-row{display:flex;justify-content:space-between;gap:8px}.cv-error{display:flex;flex-direction:column;align-items:center;gap:12px;padding:24px 16px;text-align:center}.cv-thread-panel{display:flex;flex-direction:column;min-width:0;min-height:0;overflow:hidden;background:#fff}.cv-thread-header{display:flex;align-items:center;gap:12px;padding:16px 20px;border-bottom:1px solid #e2e8f0;background:#fff}.cv-back-btn{display:none}.cv-thread-header-info{display:flex;align-items:center;gap:12px;flex:1;min-width:0}.cv-thread-header-text{min-width:0;display:flex;flex-direction:column}.cv-thread-name{margin:0;font-size:.9375rem;font-weight:600;color:#1e293b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.cv-thread-name:focus{outline:none}.cv-thread-name:focus-visible{outline:2px solid #2563eb;outline-offset:2px;border-radius:4px}.cv-thread-subtitle{font-size:.75rem;color:#64748b}.cv-thread-actions{display:flex;align-items:center;gap:8px}.cv-messages{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;padding:20px 24px;display:flex;flex-direction:column;gap:4px;background:#f8fafc}.cv-messages:focus-visible{outline:2px solid #2563eb;outline-offset:-2px}.cv-messages-loading{display:flex;flex-direction:column;gap:12px;padding:20px 0}.cv-msg-skeleton{display:flex}.cv-msg-skeleton.outbound{justify-content:flex-end}.cv-messages-error{display:flex;flex-direction:column;align-items:center;gap:12px;padding:24px 16px;text-align:center}.cv-load-earlier{display:flex;justify-content:center;padding:4px 0 12px}.cv-sender{font-weight:600}.cv-msg-action{padding:0;border:0;background:none;color:inherit;font:inherit;text-decoration:underline;cursor:pointer}.cv-msg-action:focus-visible{outline:2px solid currentColor;outline-offset:2px;border-radius:2px}.cv-messages-empty{display:flex;align-items:center;justify-content:center;flex:1}.cv-messages-empty p{margin:0;font-size:.875rem;color:#64748b;text-align:center}.cv-date-separator{display:flex;align-items:center;justify-content:center;padding:16px 0 8px}.cv-date-separator span{padding:4px 14px;border-radius:12px;background:#e2e8f0;font-size:.6875rem;font-weight:600;color:#475569;text-transform:uppercase;letter-spacing:.3px}.cv-bubble{max-width:70%;padding:10px 14px;border-radius:16px;position:relative;word-break:break-word;overflow-wrap:break-word}.cv-bubble.inbound{align-self:flex-start;background:#fff;border:1px solid #e2e8f0;border-bottom-left-radius:4px}.cv-bubble.inbound .cv-body,.cv-bubble.inbound .cv-rich{color:#1e293b}.cv-bubble.inbound .cv-meta{color:#64748b}.cv-bubble.outbound{align-self:flex-end;background:#2563eb;border-bottom-right-radius:4px}.cv-bubble.outbound .cv-body,.cv-bubble.outbound .cv-rich,.cv-bubble.outbound .cv-meta{color:#fff}@media(prefers-reduced-motion:no-preference){.cv-bubble{animation:cv-appear .2s ease}.cv-context{animation:cv-slide-in .25s ease}}.cv-body{margin:0;font-size:.875rem;line-height:1.45;word-break:break-word;white-space:pre-wrap}.cv-media{margin-bottom:6px}.cv-image-btn{display:block;padding:0;border:0;background:none;cursor:pointer;border-radius:8px}.cv-image-btn:focus-visible{outline:2px solid #fff;outline-offset:2px;box-shadow:0 0 0 4px #2563eb}.cv-image{display:block;max-width:240px;max-height:240px;border-radius:8px;object-fit:cover}.cv-video{max-width:240px;max-height:200px;border-radius:8px}.cv-file-link{font-size:.8125rem;color:#fff;text-decoration:underline}.cv-bubble.inbound .cv-file-link{color:#2563eb}.cv-meta{display:flex;align-items:center;gap:6px;justify-content:flex-end;font-size:.6875rem;margin-top:4px}.cv-status{font-weight:500}.cv-status.failed{color:#fff;font-weight:700}.cv-bubble.inbound .cv-status.failed{color:#dc2626}.cv-compose{display:flex;flex-direction:column;padding:12px 20px 16px;padding-bottom:calc(16px + env(safe-area-inset-bottom,0px));border-top:1px solid #e2e8f0;background:#fff}.cv-compose-row{display:flex;align-items:flex-end;gap:10px}.cv-compose-input{flex:1;min-width:0}.cv-attach-btn,.cv-send-btn{flex-shrink:0}.cv-attachment{display:flex;align-items:center;gap:8px;padding:8px 12px;margin-bottom:8px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px}.cv-attachment-thumb{width:48px;height:48px;object-fit:cover;border-radius:6px}.cv-attachment-name{font-size:.8125rem;color:#475569;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cv-attachment-remove{flex-shrink:0}.cv-compose-status{margin:4px 0 0;font-size:.8125rem;color:#64748b}.cv-compose-status:empty{margin:0}.cv-context-backdrop{display:none}.cv-context{width:100%;min-width:0;border-left:1px solid #e2e8f0;background:#fff;display:flex;flex-direction:column;overflow-y:auto}.cv-context-header{display:flex;justify-content:space-between;align-items:center;padding:20px;border-bottom:1px solid #e2e8f0}.cv-context-header h3{margin:0;font-size:.875rem;font-weight:700;color:#1e293b}.cv-context-body{padding:20px;display:flex;flex-direction:column;gap:16px}@keyframes cv-appear{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}@keyframes cv-slide-in{0%{transform:translate(100%)}to{transform:translate(0)}}@media(max-width:1100px){.cv-layout.has-context{grid-template-columns:280px 1fr 240px}}@media(max-width:768px){.cv-layout,.cv-layout.has-context{grid-template-columns:1fr;height:100%}.cv-hidden-mobile{display:none!important}.cv-back-btn{display:inline-flex}.cv-context-backdrop{display:block;position:fixed;inset:0;background:#00000059;z-index:99}.cv-context{position:fixed;top:0;right:0;bottom:0;width:300px;max-width:85vw;z-index:100;box-shadow:-4px 0 20px #0000001a}.cv-bubble{max-width:85%}.cv-messages,.cv-thread-header{padding:12px 16px}}@media(max-width:480px){.cv-thread-subtitle{display:none}.cv-compose{padding:8px 12px 12px;padding-bottom:calc(12px + env(safe-area-inset-bottom,0px))}.cv-compose-row{flex-wrap:wrap}.cv-compose-input{order:-1;flex:1 1 100%}.cv-send-btn{margin-inline-start:auto}.cv-list-header{padding:12px 16px 10px}.cv-context{width:100%;max-width:100vw}}\n"]
54414
+ styles: [":host{display:flex;flex:1;min-height:0;min-width:0}.cv-sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.cv-layout{display:grid;grid-template-columns:340px 1fr;flex:1;min-height:0;min-width:0;background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 1px 3px #0000000f;border:1px solid #e2e8f0}.cv-layout.has-context{grid-template-columns:340px 1fr 280px}.cv-list-panel{display:flex;flex-direction:column;min-height:0;border-right:1px solid #e2e8f0;background:#f8fafc;overflow:hidden}.cv-list-header{display:flex;align-items:center;justify-content:space-between;padding:20px 20px 12px}.cv-list-title{margin:0;font-size:1.125rem;font-weight:700;color:#1e293b}.cv-search,.cv-filters{padding:0 16px 12px}.cv-list{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain}.cv-item{display:flex;gap:12px;padding:14px 16px;border-bottom:1px solid #f1f5f9;transition:background .15s}.cv-item.unread{background:#fffbeb}.cv-item.selected{background:#eff6ff;box-shadow:inset 3px 0 #2563eb}.cv-item-info{flex:1;min-width:0}.cv-row-arrow{flex:0 0 auto;align-self:center;color:#94a3b8}.cv-item-top{display:flex;justify-content:space-between;align-items:center;margin-bottom:4px}.cv-name{font-size:.875rem;font-weight:600;color:#1e293b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.cv-time{font-size:.6875rem;color:#64748b;flex-shrink:0;margin-left:8px}.cv-subtitle{font-size:.6875rem;color:#64748b;display:block;margin-bottom:2px}.cv-item-bottom{display:flex;justify-content:space-between;align-items:center;gap:8px}.cv-preview{margin:0;font-size:.8125rem;color:#64748b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1;min-width:0}.cv-item-right{display:flex;align-items:center;gap:6px;flex-shrink:0}.cv-delete-btn{opacity:0;transition:opacity .15s}.cv-item:hover .cv-delete-btn,.cv-item:focus-within .cv-delete-btn,.cv-delete-btn:focus-within{opacity:1}@media(hover:none){.cv-delete-btn{opacity:1}}.cv-loading{padding:8px 0}.cv-skeleton{display:flex;gap:12px;align-items:flex-start;padding:14px 16px;border-bottom:1px solid #f1f5f9}.cv-skel-content{flex:1;min-width:0;display:flex;flex-direction:column;gap:6px;padding-top:2px}.cv-skel-row{display:flex;justify-content:space-between;gap:8px}.cv-error{display:flex;flex-direction:column;align-items:center;gap:12px;padding:24px 16px;text-align:center}.cv-thread-panel{display:flex;flex-direction:column;min-width:0;min-height:0;overflow:hidden;background:#fff}.cv-thread-header{display:flex;align-items:center;gap:12px;padding:16px 20px;border-bottom:1px solid #e2e8f0;background:#fff}.cv-back-btn{display:none}.cv-thread-header-info{display:flex;align-items:center;gap:12px;flex:1;min-width:0}.cv-thread-header-text{min-width:0;display:flex;flex-direction:column}.cv-thread-name{margin:0;font-size:.9375rem;font-weight:600;color:#1e293b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.cv-thread-name:focus{outline:none}.cv-thread-name:focus-visible{outline:2px solid #2563eb;outline-offset:2px;border-radius:4px}.cv-thread-subtitle{font-size:.75rem;color:#64748b}.cv-thread-actions{display:flex;align-items:center;gap:8px}.cv-messages{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain;padding:20px 24px;display:flex;flex-direction:column;gap:4px;background:#f8fafc}.cv-messages:focus-visible{outline:2px solid #2563eb;outline-offset:-2px}.cv-messages-loading{display:flex;flex-direction:column;gap:12px;padding:20px 0}.cv-msg-skeleton{display:flex}.cv-msg-skeleton.outbound{justify-content:flex-end}.cv-messages-error{display:flex;flex-direction:column;align-items:center;gap:12px;padding:24px 16px;text-align:center}.cv-load-earlier{display:flex;justify-content:center;padding:4px 0 12px}.cv-sender{font-weight:600}.cv-link-chip{display:inline-flex;align-items:center;gap:6px;max-width:100%;margin-top:6px;padding:3px 10px;border-radius:999px;border:1px solid transparent;font-size:.8rem;font-weight:600;text-decoration:none}.cv-link-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.cv-link-icon{flex:none}.cv-bubble.inbound .cv-link-chip{background:#eff6ff;border-color:#bfdbfe;color:#1d4ed8}.cv-bubble.outbound .cv-link-chip{background:#ffffff2e;border-color:#ffffff73;color:#fff}.cv-link-chip:is(a):hover{filter:brightness(.96);text-decoration:underline}.cv-link-chip:is(a):focus-visible{outline:2px solid currentColor;outline-offset:2px}.cv-link-preview{display:flex;align-items:center;gap:4px;margin-bottom:8px}.cv-link-chip-compose{margin-top:0;background:#eff6ff;border-color:#bfdbfe;color:#1d4ed8}.cv-link-list{display:flex;flex-direction:column;gap:4px;margin-top:12px;max-height:320px;overflow-y:auto}.cv-link-section-label{padding:8px 4px 4px;font-size:.7rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#64748b}.cv-link-item{display:block;width:100%;padding:10px 12px;border:0;border-radius:8px;background:none;text-align:start;font:inherit;font-size:.875rem;color:#1e293b;cursor:pointer}.cv-link-item:hover{background:#f1f5f9}.cv-link-item:focus-visible{outline:2px solid #2563eb;outline-offset:-2px}.cv-link-note{margin:16px 0;text-align:center;font-size:.875rem;color:#64748b}@media(pointer:coarse),(max-width:480px){.cv-link-item{min-height:44px}}.cv-msg-action{padding:0;border:0;background:none;color:inherit;font:inherit;text-decoration:underline;cursor:pointer}.cv-msg-action:focus-visible{outline:2px solid currentColor;outline-offset:2px;border-radius:2px}.cv-messages-empty{display:flex;align-items:center;justify-content:center;flex:1}.cv-messages-empty p{margin:0;font-size:.875rem;color:#64748b;text-align:center}.cv-date-separator{display:flex;align-items:center;justify-content:center;padding:16px 0 8px}.cv-date-separator span{padding:4px 14px;border-radius:12px;background:#e2e8f0;font-size:.6875rem;font-weight:600;color:#475569;text-transform:uppercase;letter-spacing:.3px}.cv-bubble{max-width:70%;padding:10px 14px;border-radius:16px;position:relative;word-break:break-word;overflow-wrap:break-word}.cv-bubble.inbound{align-self:flex-start;background:#fff;border:1px solid #e2e8f0;border-bottom-left-radius:4px}.cv-bubble.inbound .cv-body,.cv-bubble.inbound .cv-rich{color:#1e293b}.cv-bubble.inbound .cv-meta{color:#64748b}.cv-bubble.outbound{align-self:flex-end;background:#2563eb;border-bottom-right-radius:4px}.cv-bubble.outbound .cv-body,.cv-bubble.outbound .cv-rich,.cv-bubble.outbound .cv-meta{color:#fff}@media(prefers-reduced-motion:no-preference){.cv-bubble{animation:cv-appear .2s ease}.cv-context{animation:cv-slide-in .25s ease}}.cv-body{margin:0;font-size:.875rem;line-height:1.45;word-break:break-word;white-space:pre-wrap}.cv-media{margin-bottom:6px}.cv-image-btn{display:block;padding:0;border:0;background:none;cursor:pointer;border-radius:8px}.cv-image-btn:focus-visible{outline:2px solid #fff;outline-offset:2px;box-shadow:0 0 0 4px #2563eb}.cv-image{display:block;max-width:240px;max-height:240px;border-radius:8px;object-fit:cover}.cv-video{max-width:240px;max-height:200px;border-radius:8px}.cv-file-link{font-size:.8125rem;color:#fff;text-decoration:underline}.cv-bubble.inbound .cv-file-link{color:#2563eb}.cv-meta{display:flex;align-items:center;gap:6px;justify-content:flex-end;font-size:.6875rem;margin-top:4px}.cv-status{font-weight:500}.cv-status.failed{color:#fff;font-weight:700}.cv-bubble.inbound .cv-status.failed{color:#dc2626}.cv-compose{display:flex;flex-direction:column;padding:12px 20px 16px;padding-bottom:calc(16px + env(safe-area-inset-bottom,0px));border-top:1px solid #e2e8f0;background:#fff}.cv-compose-row{display:flex;align-items:flex-end;gap:10px}.cv-compose-input{flex:1;min-width:0}.cv-attach-btn,.cv-send-btn{flex-shrink:0}.cv-attachment{display:flex;align-items:center;gap:8px;padding:8px 12px;margin-bottom:8px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px}.cv-attachment-thumb{width:48px;height:48px;object-fit:cover;border-radius:6px}.cv-attachment-name{font-size:.8125rem;color:#475569;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.cv-attachment-remove{flex-shrink:0}.cv-compose-status{margin:4px 0 0;font-size:.8125rem;color:#64748b}.cv-compose-status:empty{margin:0}.cv-context-backdrop{display:none}.cv-context{width:100%;min-width:0;border-left:1px solid #e2e8f0;background:#fff;display:flex;flex-direction:column;overflow-y:auto}.cv-context-header{display:flex;justify-content:space-between;align-items:center;padding:20px;border-bottom:1px solid #e2e8f0}.cv-context-header h3{margin:0;font-size:.875rem;font-weight:700;color:#1e293b}.cv-context-body{padding:20px;display:flex;flex-direction:column;gap:16px}@keyframes cv-appear{0%{opacity:0;transform:translateY(4px)}to{opacity:1;transform:translateY(0)}}@keyframes cv-slide-in{0%{transform:translate(100%)}to{transform:translate(0)}}@media(max-width:1100px){.cv-layout.has-context{grid-template-columns:280px 1fr 240px}}@media(max-width:768px){.cv-layout,.cv-layout.has-context{grid-template-columns:1fr;height:100%}.cv-hidden-mobile{display:none!important}.cv-back-btn{display:inline-flex}.cv-context-backdrop{display:block;position:fixed;inset:0;background:#00000059;z-index:99}.cv-context{position:fixed;top:0;right:0;bottom:0;width:300px;max-width:85vw;z-index:100;box-shadow:-4px 0 20px #0000001a}.cv-bubble{max-width:85%}.cv-messages,.cv-thread-header{padding:12px 16px}}@media(max-width:480px){.cv-thread-subtitle{display:none}.cv-compose{padding:8px 12px 12px;padding-bottom:calc(12px + env(safe-area-inset-bottom,0px))}.cv-compose-row{flex-wrap:wrap}.cv-compose-input{order:-1;flex:1 1 100%}.cv-send-btn{margin-inline-start:auto}.cv-list-header{padding:12px 16px 10px}.cv-context{width:100%;max-width:100vw}}\n"]
54158
54415
  }]
54159
54416
  }],
54160
54417
  ctorParameters: () => [],
@@ -54900,6 +55157,93 @@ i0.ɵɵngDeclareClassMetadata({
54900
55157
  type: i0.Output,
54901
55158
  args: ["attachmentRemove"]
54902
55159
  }],
55160
+ linkGroups: [{
55161
+ type: i0.Input,
55162
+ args: [{
55163
+ isSignal: true,
55164
+ alias: "linkGroups",
55165
+ required: false
55166
+ }]
55167
+ }],
55168
+ linkLoading: [{
55169
+ type: i0.Input,
55170
+ args: [{
55171
+ isSignal: true,
55172
+ alias: "linkLoading",
55173
+ required: false
55174
+ }]
55175
+ }],
55176
+ link: [{
55177
+ type: i0.Input,
55178
+ args: [{
55179
+ isSignal: true,
55180
+ alias: "link",
55181
+ required: false
55182
+ }]
55183
+ }, {
55184
+ type: i0.Output,
55185
+ args: ["linkChange"]
55186
+ }],
55187
+ linkLabel: [{
55188
+ type: i0.Input,
55189
+ args: [{
55190
+ isSignal: true,
55191
+ alias: "linkLabel",
55192
+ required: false
55193
+ }]
55194
+ }],
55195
+ linkPickerTitle: [{
55196
+ type: i0.Input,
55197
+ args: [{
55198
+ isSignal: true,
55199
+ alias: "linkPickerTitle",
55200
+ required: false
55201
+ }]
55202
+ }],
55203
+ linkSearchPlaceholder: [{
55204
+ type: i0.Input,
55205
+ args: [{
55206
+ isSignal: true,
55207
+ alias: "linkSearchPlaceholder",
55208
+ required: false
55209
+ }]
55210
+ }],
55211
+ linkSearchLabel: [{
55212
+ type: i0.Input,
55213
+ args: [{
55214
+ isSignal: true,
55215
+ alias: "linkSearchLabel",
55216
+ required: false
55217
+ }]
55218
+ }],
55219
+ linkEmptyText: [{
55220
+ type: i0.Input,
55221
+ args: [{
55222
+ isSignal: true,
55223
+ alias: "linkEmptyText",
55224
+ required: false
55225
+ }]
55226
+ }],
55227
+ linkLoadingText: [{
55228
+ type: i0.Input,
55229
+ args: [{
55230
+ isSignal: true,
55231
+ alias: "linkLoadingText",
55232
+ required: false
55233
+ }]
55234
+ }],
55235
+ removeLinkLabel: [{
55236
+ type: i0.Input,
55237
+ args: [{
55238
+ isSignal: true,
55239
+ alias: "removeLinkLabel",
55240
+ required: false
55241
+ }]
55242
+ }],
55243
+ linkPickerOpened: [{
55244
+ type: i0.Output,
55245
+ args: ["linkPickerOpened"]
55246
+ }],
54903
55247
  mediaOpen: [{
54904
55248
  type: i0.Output,
54905
55249
  args: ["mediaOpen"]