@forgeroute/ui 0.4.38 → 0.4.40

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.
@@ -8160,6 +8160,7 @@ var DetailCardComponent = class DetailCardComponent {
8160
8160
  this.editIcon = input(true, ...ngDevMode ? [{ debugName: "editIcon" }] : /* istanbul ignore next */ []);
8161
8161
  this.editAriaLabel = input("", ...ngDevMode ? [{ debugName: "editAriaLabel" }] : /* istanbul ignore next */ []);
8162
8162
  this.fill = input(false, ...ngDevMode ? [{ debugName: "fill" }] : /* istanbul ignore next */ []);
8163
+ this.maxBodyHeight = input("", ...ngDevMode ? [{ debugName: "maxBodyHeight" }] : /* istanbul ignore next */ []);
8163
8164
  this.selectable = input(false, ...ngDevMode ? [{ debugName: "selectable" }] : /* istanbul ignore next */ []);
8164
8165
  this.selected = model(false, ...ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []);
8165
8166
  this.selectedColor = input("#3b82f6", ...ngDevMode ? [{ debugName: "selectedColor" }] : /* istanbul ignore next */ []);
@@ -8342,6 +8343,13 @@ var DetailCardComponent = class DetailCardComponent {
8342
8343
  isRequired: false,
8343
8344
  transformFunction: null
8344
8345
  },
8346
+ maxBodyHeight: {
8347
+ classPropertyName: "maxBodyHeight",
8348
+ publicName: "maxBodyHeight",
8349
+ isSignal: true,
8350
+ isRequired: false,
8351
+ transformFunction: null
8352
+ },
8345
8353
  selectable: {
8346
8354
  classPropertyName: "selectable",
8347
8355
  publicName: "selectable",
@@ -8390,7 +8398,7 @@ var DetailCardComponent = class DetailCardComponent {
8390
8398
  <div class="dc" [class.dc-fill]="fill()" [class.dc-has-text-color]="!!textColor()" [class.dc-has-bg]="!!((selectable() && selected() && selectedBackground()) || background())" [class.dc-selectable]="selectable()" [class.dc-selected]="selectable() && selected()"
8391
8399
  [style.outline-color]="selectable() && selected() ? selectedColor() : null"
8392
8400
  [style.background]="(selectable() && selected() && selectedBackground()) || background() || null" (click)="onCardClick($event)"
8393
- [style.color]="textColor() || null" [style.--dc-text]="textColor() || null">
8401
+ [style.color]="textColor() || null" [style.--dc-text]="textColor() || null" [style.--dc-body-max-height]="maxBodyHeight() || null">
8394
8402
  @if (showHeader()) {
8395
8403
  <div class="dc-header" [class.collapsible]="collapsible()" [class.open]="isOpen()" [style.background]="headerBackground() || null" (click)="onHeaderClick($event)">
8396
8404
  @if (collapsible()) {
@@ -8433,7 +8441,7 @@ var DetailCardComponent = class DetailCardComponent {
8433
8441
  </div>
8434
8442
  `,
8435
8443
  isInline: true,
8436
- styles: [":host{display:block;margin-bottom:16px}:host(:last-child){margin-bottom:0}:host(.dc-fill-host){height:100%;margin-bottom:0}.dc.dc-fill{height:100%;box-sizing:border-box;display:flex;flex-direction:column}.dc.dc-fill .dc-body-wrap{flex:1 1 auto;min-height:0}.dc.dc-fill .dc-body{display:flex;flex-direction:column}.dc-selectable{cursor:pointer}.dc-selected{outline:2px solid #3b82f6;outline-offset:-1px}.dc{background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden}.dc-header{display:flex;align-items:center;flex-wrap:wrap;gap:8px 10px;padding:14px 18px}.dc-header.open{border-bottom:1px solid #f1f5f9}.dc-has-bg .dc-header.open{border-bottom-color:#94a3b859}.dc-header.collapsible{cursor:pointer;-webkit-user-select:none;user-select:none}@media(hover:hover){.dc-header.collapsible:hover{background:#f8fafc}}.dc-chevron{flex-shrink:0;color:var(--dc-text, #64748b)}.dc-chevron svg{transition:transform .2s}.dc-chevron svg.rotated{transform:rotate(-90deg)}.dc-header-text{display:flex;flex-direction:column;gap:2px;flex:1 1 160px;min-width:0;word-break:break-word;overflow-wrap:anywhere}.dc-title{font-size:.8125rem;font-weight:800;letter-spacing:.07em;color:var(--dc-text, #1e293b);line-height:1.3;text-transform:uppercase}.dc-title--as-written{text-transform:none}.dc-subtitle{font-size:.8125rem;color:var(--dc-text, #475569)}.dc-has-text-color .dc-subtitle{opacity:.8}.dc-has-text-color .dc-header.open{border-bottom-color:#94a3b859}.dc-edit{flex-shrink:0;margin-left:auto}.dc-edit-action{display:inline-flex;align-items:center;justify-content:center;gap:5px;font-family:inherit;font-size:.8rem;font-weight:500;line-height:1.4;color:#64748b;background:none;border:1px solid #e2e8f0;border-radius:6px;padding:4px 10px;cursor:pointer;white-space:nowrap;transition:background-color .15s ease,color .15s ease}.dc-edit-action:hover{background:#f1f5f9;color:#1e293b}.dc-edit-action:focus-visible{outline:2px solid var(--ui-focus, #3b82f6);outline-offset:2px}@media(pointer:coarse),(max-width:480px){.dc-edit-action{min-height:44px}}.dc-body-wrap{display:grid;grid-template-rows:1fr;transition:grid-template-rows .25s ease}.dc-body-wrap.collapsed{grid-template-rows:0fr}.dc-body{padding:16px 18px;min-height:0;overflow:hidden}.dc-footer{padding:10px 18px;border-top:1px solid #f1f5f9}.dc-footer:empty,.dc-footer.dc-footer--hidden{display:none}.dc-has-bg .dc-footer{border-top-color:#94a3b859}@media(max-width:480px){.dc-footer{padding:10px 14px}.dc-header{padding:12px 14px}.dc-body{padding:14px}.dc-edit{margin-left:0}}@media(prefers-reduced-motion:reduce){.dc-body-wrap,.dc-chevron svg{transition:none}}\n"],
8444
+ styles: [":host{display:block;margin-bottom:16px}:host(:last-child){margin-bottom:0}:host(.dc-fill-host){height:100%;margin-bottom:0}.dc.dc-fill{height:100%;box-sizing:border-box;display:flex;flex-direction:column}.dc.dc-fill .dc-body-wrap{flex:1 1 auto;min-height:0}.dc.dc-fill .dc-body{display:flex;flex-direction:column}.dc-selectable{cursor:pointer}.dc-selected{outline:2px solid #3b82f6;outline-offset:-1px}.dc{background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden}.dc-header{display:flex;align-items:center;flex-wrap:wrap;gap:8px 10px;padding:14px 18px}.dc-header.open{border-bottom:1px solid #f1f5f9}.dc-has-bg .dc-header.open{border-bottom-color:#94a3b859}.dc-header.collapsible{cursor:pointer;-webkit-user-select:none;user-select:none}@media(hover:hover){.dc-header.collapsible:hover{background:#f8fafc}}.dc-chevron{flex-shrink:0;color:var(--dc-text, #64748b)}.dc-chevron svg{transition:transform .2s}.dc-chevron svg.rotated{transform:rotate(-90deg)}.dc-header-text{display:flex;flex-direction:column;gap:2px;flex:1 1 160px;min-width:0;word-break:break-word;overflow-wrap:anywhere}.dc-title{font-size:.8125rem;font-weight:800;letter-spacing:.07em;color:var(--dc-text, #1e293b);line-height:1.3;text-transform:uppercase}.dc-title--as-written{text-transform:none}.dc-subtitle{font-size:.8125rem;color:var(--dc-text, #475569)}.dc-has-text-color .dc-subtitle{opacity:.8}.dc-has-text-color .dc-header.open{border-bottom-color:#94a3b859}.dc-edit{flex-shrink:0;margin-left:auto}.dc-edit-action{display:inline-flex;align-items:center;justify-content:center;gap:5px;font-family:inherit;font-size:.8rem;font-weight:500;line-height:1.4;color:#64748b;background:none;border:1px solid #e2e8f0;border-radius:6px;padding:4px 10px;cursor:pointer;white-space:nowrap;transition:background-color .15s ease,color .15s ease}.dc-edit-action:hover{background:#f1f5f9;color:#1e293b}.dc-edit-action:focus-visible{outline:2px solid var(--ui-focus, #3b82f6);outline-offset:2px}@media(pointer:coarse),(max-width:480px){.dc-edit-action{min-height:44px}}.dc-body-wrap{display:grid;grid-template-rows:1fr;transition:grid-template-rows .25s ease}.dc-body-wrap.collapsed{grid-template-rows:0fr}.dc-body{padding:16px 18px;min-height:0;overflow-x:hidden;overflow-y:auto;max-height:var(--dc-body-max-height, none)}.dc-footer{padding:10px 18px;border-top:1px solid #f1f5f9}.dc-footer:empty,.dc-footer.dc-footer--hidden{display:none}.dc-has-bg .dc-footer{border-top-color:#94a3b859}@media(max-width:480px){.dc-footer{padding:10px 14px}.dc-header{padding:12px 14px}.dc-body{padding:14px}.dc-edit{margin-left:0}}@media(prefers-reduced-motion:reduce){.dc-body-wrap,.dc-chevron svg{transition:none}}\n"],
8437
8445
  dependencies: [{
8438
8446
  kind: "component",
8439
8447
  type: ButtonComponent,
@@ -8491,7 +8499,7 @@ i0.ɵɵngDeclareClassMetadata({
8491
8499
  <div class="dc" [class.dc-fill]="fill()" [class.dc-has-text-color]="!!textColor()" [class.dc-has-bg]="!!((selectable() && selected() && selectedBackground()) || background())" [class.dc-selectable]="selectable()" [class.dc-selected]="selectable() && selected()"
8492
8500
  [style.outline-color]="selectable() && selected() ? selectedColor() : null"
8493
8501
  [style.background]="(selectable() && selected() && selectedBackground()) || background() || null" (click)="onCardClick($event)"
8494
- [style.color]="textColor() || null" [style.--dc-text]="textColor() || null">
8502
+ [style.color]="textColor() || null" [style.--dc-text]="textColor() || null" [style.--dc-body-max-height]="maxBodyHeight() || null">
8495
8503
  @if (showHeader()) {
8496
8504
  <div class="dc-header" [class.collapsible]="collapsible()" [class.open]="isOpen()" [style.background]="headerBackground() || null" (click)="onHeaderClick($event)">
8497
8505
  @if (collapsible()) {
@@ -8533,7 +8541,7 @@ i0.ɵɵngDeclareClassMetadata({
8533
8541
  <div class="dc-footer" [class.dc-footer--hidden]="!isOpen()" [style.background]="footerBackground() || null"><ng-content select="[card-footer]" /></div>
8534
8542
  </div>
8535
8543
  `,
8536
- styles: [":host{display:block;margin-bottom:16px}:host(:last-child){margin-bottom:0}:host(.dc-fill-host){height:100%;margin-bottom:0}.dc.dc-fill{height:100%;box-sizing:border-box;display:flex;flex-direction:column}.dc.dc-fill .dc-body-wrap{flex:1 1 auto;min-height:0}.dc.dc-fill .dc-body{display:flex;flex-direction:column}.dc-selectable{cursor:pointer}.dc-selected{outline:2px solid #3b82f6;outline-offset:-1px}.dc{background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden}.dc-header{display:flex;align-items:center;flex-wrap:wrap;gap:8px 10px;padding:14px 18px}.dc-header.open{border-bottom:1px solid #f1f5f9}.dc-has-bg .dc-header.open{border-bottom-color:#94a3b859}.dc-header.collapsible{cursor:pointer;-webkit-user-select:none;user-select:none}@media(hover:hover){.dc-header.collapsible:hover{background:#f8fafc}}.dc-chevron{flex-shrink:0;color:var(--dc-text, #64748b)}.dc-chevron svg{transition:transform .2s}.dc-chevron svg.rotated{transform:rotate(-90deg)}.dc-header-text{display:flex;flex-direction:column;gap:2px;flex:1 1 160px;min-width:0;word-break:break-word;overflow-wrap:anywhere}.dc-title{font-size:.8125rem;font-weight:800;letter-spacing:.07em;color:var(--dc-text, #1e293b);line-height:1.3;text-transform:uppercase}.dc-title--as-written{text-transform:none}.dc-subtitle{font-size:.8125rem;color:var(--dc-text, #475569)}.dc-has-text-color .dc-subtitle{opacity:.8}.dc-has-text-color .dc-header.open{border-bottom-color:#94a3b859}.dc-edit{flex-shrink:0;margin-left:auto}.dc-edit-action{display:inline-flex;align-items:center;justify-content:center;gap:5px;font-family:inherit;font-size:.8rem;font-weight:500;line-height:1.4;color:#64748b;background:none;border:1px solid #e2e8f0;border-radius:6px;padding:4px 10px;cursor:pointer;white-space:nowrap;transition:background-color .15s ease,color .15s ease}.dc-edit-action:hover{background:#f1f5f9;color:#1e293b}.dc-edit-action:focus-visible{outline:2px solid var(--ui-focus, #3b82f6);outline-offset:2px}@media(pointer:coarse),(max-width:480px){.dc-edit-action{min-height:44px}}.dc-body-wrap{display:grid;grid-template-rows:1fr;transition:grid-template-rows .25s ease}.dc-body-wrap.collapsed{grid-template-rows:0fr}.dc-body{padding:16px 18px;min-height:0;overflow:hidden}.dc-footer{padding:10px 18px;border-top:1px solid #f1f5f9}.dc-footer:empty,.dc-footer.dc-footer--hidden{display:none}.dc-has-bg .dc-footer{border-top-color:#94a3b859}@media(max-width:480px){.dc-footer{padding:10px 14px}.dc-header{padding:12px 14px}.dc-body{padding:14px}.dc-edit{margin-left:0}}@media(prefers-reduced-motion:reduce){.dc-body-wrap,.dc-chevron svg{transition:none}}\n"]
8544
+ styles: [":host{display:block;margin-bottom:16px}:host(:last-child){margin-bottom:0}:host(.dc-fill-host){height:100%;margin-bottom:0}.dc.dc-fill{height:100%;box-sizing:border-box;display:flex;flex-direction:column}.dc.dc-fill .dc-body-wrap{flex:1 1 auto;min-height:0}.dc.dc-fill .dc-body{display:flex;flex-direction:column}.dc-selectable{cursor:pointer}.dc-selected{outline:2px solid #3b82f6;outline-offset:-1px}.dc{background:#fff;border:1px solid #e2e8f0;border-radius:10px;overflow:hidden}.dc-header{display:flex;align-items:center;flex-wrap:wrap;gap:8px 10px;padding:14px 18px}.dc-header.open{border-bottom:1px solid #f1f5f9}.dc-has-bg .dc-header.open{border-bottom-color:#94a3b859}.dc-header.collapsible{cursor:pointer;-webkit-user-select:none;user-select:none}@media(hover:hover){.dc-header.collapsible:hover{background:#f8fafc}}.dc-chevron{flex-shrink:0;color:var(--dc-text, #64748b)}.dc-chevron svg{transition:transform .2s}.dc-chevron svg.rotated{transform:rotate(-90deg)}.dc-header-text{display:flex;flex-direction:column;gap:2px;flex:1 1 160px;min-width:0;word-break:break-word;overflow-wrap:anywhere}.dc-title{font-size:.8125rem;font-weight:800;letter-spacing:.07em;color:var(--dc-text, #1e293b);line-height:1.3;text-transform:uppercase}.dc-title--as-written{text-transform:none}.dc-subtitle{font-size:.8125rem;color:var(--dc-text, #475569)}.dc-has-text-color .dc-subtitle{opacity:.8}.dc-has-text-color .dc-header.open{border-bottom-color:#94a3b859}.dc-edit{flex-shrink:0;margin-left:auto}.dc-edit-action{display:inline-flex;align-items:center;justify-content:center;gap:5px;font-family:inherit;font-size:.8rem;font-weight:500;line-height:1.4;color:#64748b;background:none;border:1px solid #e2e8f0;border-radius:6px;padding:4px 10px;cursor:pointer;white-space:nowrap;transition:background-color .15s ease,color .15s ease}.dc-edit-action:hover{background:#f1f5f9;color:#1e293b}.dc-edit-action:focus-visible{outline:2px solid var(--ui-focus, #3b82f6);outline-offset:2px}@media(pointer:coarse),(max-width:480px){.dc-edit-action{min-height:44px}}.dc-body-wrap{display:grid;grid-template-rows:1fr;transition:grid-template-rows .25s ease}.dc-body-wrap.collapsed{grid-template-rows:0fr}.dc-body{padding:16px 18px;min-height:0;overflow-x:hidden;overflow-y:auto;max-height:var(--dc-body-max-height, none)}.dc-footer{padding:10px 18px;border-top:1px solid #f1f5f9}.dc-footer:empty,.dc-footer.dc-footer--hidden{display:none}.dc-has-bg .dc-footer{border-top-color:#94a3b859}@media(max-width:480px){.dc-footer{padding:10px 14px}.dc-header{padding:12px 14px}.dc-body{padding:14px}.dc-edit{margin-left:0}}@media(prefers-reduced-motion:reduce){.dc-body-wrap,.dc-chevron svg{transition:none}}\n"]
8537
8545
  }]
8538
8546
  }],
8539
8547
  propDecorators: {
@@ -8696,6 +8704,14 @@ i0.ɵɵngDeclareClassMetadata({
8696
8704
  required: false
8697
8705
  }]
8698
8706
  }],
8707
+ maxBodyHeight: [{
8708
+ type: i0.Input,
8709
+ args: [{
8710
+ isSignal: true,
8711
+ alias: "maxBodyHeight",
8712
+ required: false
8713
+ }]
8714
+ }],
8699
8715
  selectable: [{
8700
8716
  type: i0.Input,
8701
8717
  args: [{
@@ -8922,6 +8938,7 @@ var OptionCardComponent = class OptionCardComponent {
8922
8938
  "editIcon",
8923
8939
  "editAriaLabel",
8924
8940
  "fill",
8941
+ "maxBodyHeight",
8925
8942
  "selectable",
8926
8943
  "selected",
8927
8944
  "selectedColor",
@@ -9287,43 +9304,58 @@ i0.ɵɵngDeclareClassMetadata({
9287
9304
  }]
9288
9305
  }
9289
9306
  });
9290
- function parseCsvLine(line) {
9291
- const cells = [];
9292
- let i = 0;
9293
- while (i <= line.length) {
9294
- if (i === line.length) {
9295
- cells.push("");
9296
- break;
9297
- }
9298
- if (line[i] === "\"") {
9299
- let cell = "";
9300
- i++;
9301
- while (i < line.length) if (line[i] === "\"") {
9302
- if (line[i + 1] === "\"") {
9303
- cell += "\"";
9304
- i += 2;
9305
- } else {
9306
- i++;
9307
- break;
9308
- }
9309
- } else cell += line[i++];
9310
- if (line[i] === ",") i++;
9311
- cells.push(cell);
9312
- if (i >= line.length) break;
9313
- } else {
9314
- const end = line.indexOf(",", i);
9315
- if (end === -1) {
9316
- cells.push(line.slice(i));
9317
- break;
9318
- }
9319
- cells.push(line.slice(i, end));
9320
- i = end + 1;
9321
- }
9307
+ function detectCsvDelimiter(text) {
9308
+ const counts = {
9309
+ ",": 0,
9310
+ ";": 0,
9311
+ " ": 0
9312
+ };
9313
+ let quoted = false;
9314
+ for (const ch of text) if (ch === "\"") quoted = !quoted;
9315
+ else if (!quoted) {
9316
+ if (ch === "\n" || ch === "\r") break;
9317
+ if (ch in counts) counts[ch]++;
9322
9318
  }
9323
- return cells;
9319
+ return [
9320
+ ",",
9321
+ ";",
9322
+ " "
9323
+ ].reduce((best, d) => counts[d] > counts[best] ? d : best, ",");
9324
9324
  }
9325
9325
  function parseCsv(text) {
9326
- return text.split(/\r?\n/).filter((l) => l.trim()).map(parseCsvLine);
9326
+ const delimiter = detectCsvDelimiter(text);
9327
+ const rows = [];
9328
+ let row = [];
9329
+ let cell = "";
9330
+ let quoted = false;
9331
+ const endCell = () => {
9332
+ row.push(cell);
9333
+ cell = "";
9334
+ };
9335
+ const endRow = () => {
9336
+ endCell();
9337
+ if (row.some((c) => c.trim() !== "")) rows.push(row);
9338
+ row = [];
9339
+ };
9340
+ const src = text.replace(/^\uFEFF/, "");
9341
+ for (let i = 0; i < src.length; i++) {
9342
+ const ch = src[i];
9343
+ if (quoted) {
9344
+ if (ch !== "\"") cell += ch;
9345
+ else if (src[i + 1] === "\"") {
9346
+ cell += "\"";
9347
+ i++;
9348
+ } else quoted = false;
9349
+ } else if (ch === "\"" && cell === "") quoted = true;
9350
+ else if (ch === delimiter) endCell();
9351
+ else if (ch === "\r") {
9352
+ if (src[i + 1] === "\n") i++;
9353
+ endRow();
9354
+ } else if (ch === "\n") endRow();
9355
+ else cell += ch;
9356
+ }
9357
+ if (cell !== "" || row.length) endRow();
9358
+ return rows;
9327
9359
  }
9328
9360
  function csvCell(cell) {
9329
9361
  if (typeof cell === "number") return String(cell);
@@ -9354,6 +9386,9 @@ var ImportExportComponent = class ImportExportComponent {
9354
9386
  this.apiBase = inject(FORGEROUTE_CONFIG, { optional: true })?.baseUrl ?? "/api";
9355
9387
  this.toast = inject(ToastService);
9356
9388
  this.cdr = inject(ChangeDetectorRef);
9389
+ this.destroyRef = inject(DestroyRef);
9390
+ this.importElapsed = 0;
9391
+ this.importTimer = null;
9357
9392
  this.exporting = false;
9358
9393
  this.importOpen = false;
9359
9394
  this.step = "upload";
@@ -9365,6 +9400,7 @@ var ImportExportComponent = class ImportExportComponent {
9365
9400
  this.mappingError = null;
9366
9401
  this.importing = false;
9367
9402
  this.importResult = null;
9403
+ this.destroyRef.onDestroy(() => this.stopImportTimer());
9368
9404
  effect(() => {
9369
9405
  const v = this._configInput();
9370
9406
  if (v !== null) this.config = v;
@@ -9461,10 +9497,18 @@ var ImportExportComponent = class ImportExportComponent {
9461
9497
  onFileChange(event) {
9462
9498
  const file = event.target.files?.[0];
9463
9499
  if (!file) return;
9500
+ this.readCsvFile(file);
9501
+ }
9502
+ readCsvFile(file, encoding) {
9464
9503
  const reader = new FileReader();
9465
9504
  reader.onload = (e) => {
9505
+ const raw = e.target.result;
9506
+ if (!encoding && raw.includes("�")) {
9507
+ this.readCsvFile(file, "windows-1252");
9508
+ return;
9509
+ }
9466
9510
  try {
9467
- const parsed = parseCsv(e.target.result.trim());
9511
+ const parsed = parseCsv(raw.trim());
9468
9512
  if (parsed.length < 1) {
9469
9513
  this.fileError = "The CSV file appears to be empty.";
9470
9514
  this.cdr.detectChanges();
@@ -9486,7 +9530,8 @@ var ImportExportComponent = class ImportExportComponent {
9486
9530
  this.cdr.detectChanges();
9487
9531
  }
9488
9532
  };
9489
- reader.readAsText(file);
9533
+ if (encoding) reader.readAsText(file, encoding);
9534
+ else reader.readAsText(file);
9490
9535
  }
9491
9536
  autoMap() {
9492
9537
  this.columnMapping = {};
@@ -9505,6 +9550,19 @@ var ImportExportComponent = class ImportExportComponent {
9505
9550
  }
9506
9551
  this.columnMapping[field.key] = match ?? "";
9507
9552
  }
9553
+ const tokens = (t) => t.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
9554
+ const containsRun = (haystack, needle) => needle.length > 0 && haystack.some((_, i) => needle.every((w, j) => haystack[i + j] === w));
9555
+ for (const field of this.config.importFields ?? []) {
9556
+ if (this.columnMapping[field.key] || !field.required) continue;
9557
+ const used = new Set(Object.values(this.columnMapping).filter(Boolean));
9558
+ const terms = [
9559
+ field.label,
9560
+ field.key.replace(/([a-z])([A-Z])/g, "$1 $2"),
9561
+ ...this.config.columnAliases?.[field.key] ?? []
9562
+ ].map(tokens).filter((t) => t.join("").length >= 3);
9563
+ const candidates = this.csvHeaders.filter((h) => !used.has(h) && terms.some((t) => containsRun(tokens(h), t)));
9564
+ if (new Set(candidates).size === 1) this.columnMapping[field.key] = candidates[0];
9565
+ }
9508
9566
  }
9509
9567
  get canSubmit() {
9510
9568
  return (this.config.importFields ?? []).filter((f) => f.required).every((f) => !!this.columnMapping[f.key]);
@@ -9558,8 +9616,20 @@ var ImportExportComponent = class ImportExportComponent {
9558
9616
  return record;
9559
9617
  });
9560
9618
  }
9619
+ stopImportTimer() {
9620
+ if (this.importTimer !== null) {
9621
+ clearInterval(this.importTimer);
9622
+ this.importTimer = null;
9623
+ }
9624
+ }
9561
9625
  submitImport() {
9562
9626
  this.importing = true;
9627
+ this.importElapsed = 0;
9628
+ this.stopImportTimer();
9629
+ this.importTimer = setInterval(() => {
9630
+ this.importElapsed++;
9631
+ this.cdr.detectChanges();
9632
+ }, 1e3);
9563
9633
  this.cdr.detectChanges();
9564
9634
  const records = this.csvRows.map((row) => {
9565
9635
  const record = {};
@@ -9573,6 +9643,7 @@ var ImportExportComponent = class ImportExportComponent {
9573
9643
  });
9574
9644
  this.http.post(`${this.apiBase}${this.config.importEndpoint ?? ""}`, { records }).subscribe({
9575
9645
  next: (result) => {
9646
+ this.stopImportTimer();
9576
9647
  this.importing = false;
9577
9648
  this.importResult = {
9578
9649
  imported: result.imported,
@@ -9584,6 +9655,7 @@ var ImportExportComponent = class ImportExportComponent {
9584
9655
  this.cdr.detectChanges();
9585
9656
  },
9586
9657
  error: () => {
9658
+ this.stopImportTimer();
9587
9659
  this.importing = false;
9588
9660
  this.toast.error("Import failed. Please check your data and try again.");
9589
9661
  this.cdr.detectChanges();
@@ -9771,6 +9843,12 @@ var ImportExportComponent = class ImportExportComponent {
9771
9843
  </tbody>
9772
9844
  </table>
9773
9845
  </div>
9846
+ @if (importing) {
9847
+ <div class="ie-progress" role="status" aria-live="polite" data-testid="import-progress">
9848
+ <div class="ie-progress-track" aria-hidden="true"><span class="ie-progress-bar"></span></div>
9849
+ <p class="ie-progress-text" data-testid="import-progress-text">Importing {{ csvRows.length.toLocaleString('en-US') }} record(s)… {{ importElapsed }}s. Keep this window open until it finishes.</p>
9850
+ </div>
9851
+ }
9774
9852
  </div>
9775
9853
  }
9776
9854
 
@@ -9799,7 +9877,7 @@ var ImportExportComponent = class ImportExportComponent {
9799
9877
  @if (importResult.errors.length > 0) {
9800
9878
  <div class="ie-errors-list">
9801
9879
  <p class="ie-errors-title">Errors:</p>
9802
- @for (err of importResult.errors; track err.row) {
9880
+ @for (err of importResult.errors; track $index) {
9803
9881
  <div class="ie-error-row">
9804
9882
  <span class="ie-error-row-num">Row {{ err.row }}</span>
9805
9883
  <span>{{ err.message }}</span>
@@ -9855,7 +9933,7 @@ var ImportExportComponent = class ImportExportComponent {
9855
9933
  }
9856
9934
  @if (step === 'preview') {
9857
9935
  <div modal-footer-right class="ie-footer-right">
9858
- <ui-button variant="secondary" size="sm" (click)="closeImport()">Cancel</ui-button>
9936
+ <ui-button variant="secondary" size="sm" [disabled]="importing" (click)="closeImport()">Cancel</ui-button>
9859
9937
  <ui-button variant="primary" size="sm" [loading]="importing" (click)="submitImport()">Import {{ csvRows.length }} Record(s)</ui-button>
9860
9938
  </div>
9861
9939
  }
@@ -9869,7 +9947,7 @@ var ImportExportComponent = class ImportExportComponent {
9869
9947
  </ui-modal>
9870
9948
  `,
9871
9949
  isInline: true,
9872
- styles: [".ie-actions{display:flex;gap:8px}.ie-steps{display:flex;align-items:center;margin-bottom:24px;gap:0;list-style:none;padding:0}.ie-step{display:flex;align-items:center;gap:6px;flex-shrink:0}.ie-step-num{width:24px;height:24px;border-radius:50%;background:#e2e8f0;color:#64748b;font-size:.75rem;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0}.ie-step-label{font-size:.8125rem;color:#94a3b8;font-weight:500}.ie-step.active .ie-step-num{background:#3b82f6;color:#fff}.ie-step.active .ie-step-label{color:#1e293b;font-weight:600}.ie-step.done .ie-step-num{background:#22c55e;color:#fff}.ie-step.done .ie-step-label{color:#64748b}.ie-step-divider{flex:1;height:1px;background:#e2e8f0;margin:0 8px;min-width:16px}.ie-source-step{display:flex;flex-direction:column;gap:16px}.ie-upload-area{display:flex;flex-direction:column;gap:12px}.ie-template-hint{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:.875rem;color:#64748b;background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:10px 14px}.ie-file-hidden{display:none}.ie-dropzone{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:40px 24px;border:2px dashed #cbd5e1;border-radius:10px;cursor:pointer;transition:border-color .18s ease,background .18s ease}.ie-dropzone:hover,.ie-dropzone:focus-within{border-color:#3b82f6;background:#f0f7ff}.ie-dropzone.has-error{border-color:#ef4444}.ie-dropzone-text{font-size:.9375rem;font-weight:600;color:#1e293b}.ie-dropzone-sub{font-size:.8125rem;color:#94a3b8}.ie-mapping{display:flex;flex-direction:column;gap:12px}.ie-mapping-hint{font-size:.875rem;color:#64748b;margin:0}.ie-mapping-success{display:flex;align-items:flex-start;gap:8px;padding:10px 14px;background:#f0fdf4;border:1px solid #bbf7d0;border-radius:8px;font-size:.875rem;color:#15803d}.ie-mapping-success svg{flex-shrink:0;margin-top:1px;color:#16a34a}.ie-mapping-unmatched{display:flex;align-items:flex-start;gap:6px;font-size:.8125rem;color:#64748b;margin:0;padding-top:4px}.ie-mapping-unmatched svg{flex-shrink:0;margin-top:1px}.ie-mapping-unmatched em{font-style:normal;color:#475569}.ie-mapping-table{border:1px solid #e2e8f0;border-radius:8px;overflow:hidden}.ie-mapping-header{display:grid;grid-template-columns:1fr 1fr 1fr;padding:8px 12px;background:#f8fafc;border-bottom:1px solid #e2e8f0;font-size:.75rem;font-weight:600;color:#64748b;text-transform:uppercase}.ie-mapping-row{display:grid;grid-template-columns:1fr 1fr 1fr;align-items:start;padding:8px 12px;border-bottom:1px solid #f1f5f9;gap:8px}.ie-mapping-row:last-child{border-bottom:none}.ie-mapping-row.ie-mapping-row--required-missing{background:#fff7ed}.ie-mapping-row.ie-mapping-row--required-missing .ie-field-label{color:#c2410c}.ie-match-icon{color:#16a34a;margin-right:4px;vertical-align:-1px}.ie-field-label{display:flex;align-items:center;font-size:.875rem;color:#1e293b;font-weight:500}.ie-required{color:#dc2626;margin-left:2px}.ie-sample{font-size:.8125rem;color:#64748b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ie-preview{display:flex;flex-direction:column;gap:12px}.ie-preview-scroll{overflow-x:auto;border:1px solid #e2e8f0;border-radius:8px}.ie-preview-table{width:100%;border-collapse:collapse;font-size:.8125rem}.ie-preview-table th{background:#f8fafc;padding:8px 12px;text-align:left;font-size:.75rem;font-weight:600;color:#64748b;text-transform:uppercase;border-bottom:1px solid #e2e8f0;white-space:nowrap}.ie-preview-table td{padding:7px 12px;color:#1e293b;border-bottom:1px solid #f1f5f9;max-width:160px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ie-preview-table tr:last-child td{border-bottom:none}.ie-result{display:flex;flex-direction:column;gap:16px}.ie-result-summary{display:flex;flex-direction:column;gap:8px}.ie-result-stat{display:flex;align-items:center;gap:10px;padding:12px 16px;border-radius:8px;font-size:.9375rem}.ie-result-stat.success{background:#f0fdf4;color:#15803d}.ie-result-stat.success svg{stroke:#22c55e}.ie-result-stat.error{background:#fef2f2;color:#b91c1c}.ie-result-stat.error svg{stroke:#ef4444}.ie-result-stat.warning{background:#fffbeb;color:#b45309}.ie-result-stat.warning svg{stroke:#f59e0b}.ie-errors-list{border:1px solid #fecaca;border-radius:8px;overflow:hidden}.ie-errors-title{font-size:.8125rem;font-weight:600;color:#b91c1c;padding:8px 12px;background:#fef2f2;margin:0;border-bottom:1px solid #fecaca}.ie-error-row{display:flex;gap:12px;padding:7px 12px;border-bottom:1px solid #fff1f2;font-size:.8125rem;color:#374151}.ie-error-row:last-child{border-bottom:none}.ie-error-row-num{font-weight:600;color:#b91c1c;flex-shrink:0}.ie-warnings-list{border:1px solid #fde68a;border-radius:8px;overflow:hidden}.ie-warnings-title{font-size:.8125rem;font-weight:600;color:#b45309;padding:8px 12px;background:#fffbeb;margin:0;border-bottom:1px solid #fde68a}.ie-warning-row{display:flex;gap:12px;padding:7px 12px;border-bottom:1px solid #fef3c7;font-size:.8125rem;color:#374151}.ie-warning-row:last-child{border-bottom:none}.ie-warning-row-num{font-weight:600;color:#b45309;flex-shrink:0}.ie-error{font-size:.875rem;color:#dc2626;margin:0}.ie-footer-right{display:flex;gap:8px;align-items:center}@media(max-width:600px){.ie-step-label{display:none}.ie-dropzone{padding:24px 16px}.ie-mapping-header{grid-template-columns:1fr 1fr}.ie-mapping-header span:last-child{display:none}.ie-mapping-row{grid-template-columns:1fr 1fr}.ie-mapping-row .ie-sample{display:none}}\n"],
9950
+ styles: [".ie-actions{display:flex;gap:8px}.ie-steps{display:flex;align-items:center;margin-bottom:24px;gap:0;list-style:none;padding:0}.ie-step{display:flex;align-items:center;gap:6px;flex-shrink:0}.ie-step-num{width:24px;height:24px;border-radius:50%;background:#e2e8f0;color:#64748b;font-size:.75rem;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0}.ie-step-label{font-size:.8125rem;color:#64748b;font-weight:500}.ie-step.active .ie-step-num{background:#3b82f6;color:#fff}.ie-step.active .ie-step-label{color:#1e293b;font-weight:600}.ie-step.done .ie-step-num{background:#22c55e;color:#fff}.ie-step.done .ie-step-label{color:#64748b}.ie-step-divider{flex:1;height:1px;background:#e2e8f0;margin:0 8px;min-width:16px}.ie-source-step{display:flex;flex-direction:column;gap:16px}.ie-upload-area{display:flex;flex-direction:column;gap:12px}.ie-template-hint{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:.875rem;color:#64748b;background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:10px 14px}.ie-file-hidden{display:none}.ie-dropzone{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:40px 24px;border:2px dashed #cbd5e1;border-radius:10px;cursor:pointer;transition:border-color .18s ease,background .18s ease}.ie-dropzone:hover,.ie-dropzone:focus-within{border-color:#3b82f6;background:#f0f7ff}.ie-dropzone.has-error{border-color:#ef4444}.ie-dropzone-text{font-size:.9375rem;font-weight:600;color:#1e293b}.ie-dropzone-sub{font-size:.8125rem;color:#64748b}.ie-mapping{display:flex;flex-direction:column;gap:12px}.ie-mapping-hint{font-size:.875rem;color:#64748b;margin:0}.ie-mapping-success{display:flex;align-items:flex-start;gap:8px;padding:10px 14px;background:#f0fdf4;border:1px solid #bbf7d0;border-radius:8px;font-size:.875rem;color:#15803d}.ie-mapping-success svg{flex-shrink:0;margin-top:1px;color:#16a34a}.ie-mapping-unmatched{display:flex;align-items:flex-start;gap:6px;font-size:.8125rem;color:#64748b;margin:0;padding-top:4px}.ie-mapping-unmatched svg{flex-shrink:0;margin-top:1px}.ie-mapping-unmatched em{font-style:normal;color:#475569}.ie-mapping-table{border:1px solid #e2e8f0;border-radius:8px;overflow:hidden}.ie-mapping-header{display:grid;grid-template-columns:1fr 1fr 1fr;padding:8px 12px;background:#f8fafc;border-bottom:1px solid #e2e8f0;font-size:.75rem;font-weight:600;color:#64748b;text-transform:uppercase}.ie-mapping-row{display:grid;grid-template-columns:1fr 1fr 1fr;align-items:start;padding:8px 12px;border-bottom:1px solid #f1f5f9;gap:8px}.ie-mapping-row:last-child{border-bottom:none}.ie-mapping-row.ie-mapping-row--required-missing{background:#fff7ed}.ie-mapping-row.ie-mapping-row--required-missing .ie-field-label{color:#c2410c}.ie-match-icon{color:#16a34a;margin-right:4px;vertical-align:-1px}.ie-field-label{display:flex;align-items:center;font-size:.875rem;color:#1e293b;font-weight:500}.ie-required{color:#dc2626;margin-left:2px}.ie-sample{font-size:.8125rem;color:#64748b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ie-preview{display:flex;flex-direction:column;gap:12px}.ie-preview-scroll{overflow-x:auto;border:1px solid #e2e8f0;border-radius:8px}.ie-preview-table{width:100%;border-collapse:collapse;font-size:.8125rem}.ie-preview-table th{background:#f8fafc;padding:8px 12px;text-align:left;font-size:.75rem;font-weight:600;color:#64748b;text-transform:uppercase;border-bottom:1px solid #e2e8f0;white-space:nowrap}.ie-preview-table td{padding:7px 12px;color:#1e293b;border-bottom:1px solid #f1f5f9;max-width:160px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ie-preview-table tr:last-child td{border-bottom:none}.ie-result{display:flex;flex-direction:column;gap:16px}.ie-result-summary{display:flex;flex-direction:column;gap:8px}.ie-result-stat{display:flex;align-items:center;gap:10px;padding:12px 16px;border-radius:8px;font-size:.9375rem}.ie-result-stat.success{background:#f0fdf4;color:#15803d}.ie-result-stat.success svg{stroke:#22c55e}.ie-result-stat.error{background:#fef2f2;color:#b91c1c}.ie-result-stat.error svg{stroke:#ef4444}.ie-result-stat.warning{background:#fffbeb;color:#b45309}.ie-result-stat.warning svg{stroke:#f59e0b}.ie-progress{margin-top:14px}.ie-progress-track{height:6px;border-radius:999px;background:#e2e8f0;overflow:hidden;position:relative}.ie-progress-bar{position:absolute;inset:0 auto 0 0;width:35%;border-radius:999px;background:#2563eb;animation:ie-progress-slide 1.2s ease-in-out infinite}.ie-progress-text{margin:8px 0 0;font-size:.8125rem;color:#475569}@keyframes ie-progress-slide{0%{left:-35%}to{left:100%}}@media(prefers-reduced-motion:reduce){.ie-progress-bar{animation:none;width:100%;opacity:.5}}.ie-errors-list{border:1px solid #fecaca;border-radius:8px;overflow:hidden}.ie-errors-title{font-size:.8125rem;font-weight:600;color:#b91c1c;padding:8px 12px;background:#fef2f2;margin:0;border-bottom:1px solid #fecaca}.ie-error-row{display:flex;gap:12px;padding:7px 12px;border-bottom:1px solid #fff1f2;font-size:.8125rem;color:#374151}.ie-error-row:last-child{border-bottom:none}.ie-error-row-num{font-weight:600;color:#b91c1c;flex-shrink:0}.ie-warnings-list{border:1px solid #fde68a;border-radius:8px;overflow:hidden}.ie-warnings-title{font-size:.8125rem;font-weight:600;color:#b45309;padding:8px 12px;background:#fffbeb;margin:0;border-bottom:1px solid #fde68a}.ie-warning-row{display:flex;gap:12px;padding:7px 12px;border-bottom:1px solid #fef3c7;font-size:.8125rem;color:#374151}.ie-warning-row:last-child{border-bottom:none}.ie-warning-row-num{font-weight:600;color:#b45309;flex-shrink:0}.ie-error{font-size:.875rem;color:#dc2626;margin:0}.ie-footer-right{display:flex;gap:8px;align-items:center}@media(max-width:600px){.ie-step-label{display:none}.ie-dropzone{padding:24px 16px}.ie-mapping-header{grid-template-columns:1fr 1fr}.ie-mapping-header span:last-child{display:none}.ie-mapping-row{grid-template-columns:1fr 1fr}.ie-mapping-row .ie-sample{display:none}}\n"],
9873
9951
  dependencies: [
9874
9952
  {
9875
9953
  kind: "ngmodule",
@@ -10128,6 +10206,12 @@ i0.ɵɵngDeclareClassMetadata({
10128
10206
  </tbody>
10129
10207
  </table>
10130
10208
  </div>
10209
+ @if (importing) {
10210
+ <div class="ie-progress" role="status" aria-live="polite" data-testid="import-progress">
10211
+ <div class="ie-progress-track" aria-hidden="true"><span class="ie-progress-bar"></span></div>
10212
+ <p class="ie-progress-text" data-testid="import-progress-text">Importing {{ csvRows.length.toLocaleString('en-US') }} record(s)… {{ importElapsed }}s. Keep this window open until it finishes.</p>
10213
+ </div>
10214
+ }
10131
10215
  </div>
10132
10216
  }
10133
10217
 
@@ -10156,7 +10240,7 @@ i0.ɵɵngDeclareClassMetadata({
10156
10240
  @if (importResult.errors.length > 0) {
10157
10241
  <div class="ie-errors-list">
10158
10242
  <p class="ie-errors-title">Errors:</p>
10159
- @for (err of importResult.errors; track err.row) {
10243
+ @for (err of importResult.errors; track $index) {
10160
10244
  <div class="ie-error-row">
10161
10245
  <span class="ie-error-row-num">Row {{ err.row }}</span>
10162
10246
  <span>{{ err.message }}</span>
@@ -10212,7 +10296,7 @@ i0.ɵɵngDeclareClassMetadata({
10212
10296
  }
10213
10297
  @if (step === 'preview') {
10214
10298
  <div modal-footer-right class="ie-footer-right">
10215
- <ui-button variant="secondary" size="sm" (click)="closeImport()">Cancel</ui-button>
10299
+ <ui-button variant="secondary" size="sm" [disabled]="importing" (click)="closeImport()">Cancel</ui-button>
10216
10300
  <ui-button variant="primary" size="sm" [loading]="importing" (click)="submitImport()">Import {{ csvRows.length }} Record(s)</ui-button>
10217
10301
  </div>
10218
10302
  }
@@ -10225,7 +10309,7 @@ i0.ɵɵngDeclareClassMetadata({
10225
10309
 
10226
10310
  </ui-modal>
10227
10311
  `,
10228
- styles: [".ie-actions{display:flex;gap:8px}.ie-steps{display:flex;align-items:center;margin-bottom:24px;gap:0;list-style:none;padding:0}.ie-step{display:flex;align-items:center;gap:6px;flex-shrink:0}.ie-step-num{width:24px;height:24px;border-radius:50%;background:#e2e8f0;color:#64748b;font-size:.75rem;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0}.ie-step-label{font-size:.8125rem;color:#94a3b8;font-weight:500}.ie-step.active .ie-step-num{background:#3b82f6;color:#fff}.ie-step.active .ie-step-label{color:#1e293b;font-weight:600}.ie-step.done .ie-step-num{background:#22c55e;color:#fff}.ie-step.done .ie-step-label{color:#64748b}.ie-step-divider{flex:1;height:1px;background:#e2e8f0;margin:0 8px;min-width:16px}.ie-source-step{display:flex;flex-direction:column;gap:16px}.ie-upload-area{display:flex;flex-direction:column;gap:12px}.ie-template-hint{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:.875rem;color:#64748b;background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:10px 14px}.ie-file-hidden{display:none}.ie-dropzone{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:40px 24px;border:2px dashed #cbd5e1;border-radius:10px;cursor:pointer;transition:border-color .18s ease,background .18s ease}.ie-dropzone:hover,.ie-dropzone:focus-within{border-color:#3b82f6;background:#f0f7ff}.ie-dropzone.has-error{border-color:#ef4444}.ie-dropzone-text{font-size:.9375rem;font-weight:600;color:#1e293b}.ie-dropzone-sub{font-size:.8125rem;color:#94a3b8}.ie-mapping{display:flex;flex-direction:column;gap:12px}.ie-mapping-hint{font-size:.875rem;color:#64748b;margin:0}.ie-mapping-success{display:flex;align-items:flex-start;gap:8px;padding:10px 14px;background:#f0fdf4;border:1px solid #bbf7d0;border-radius:8px;font-size:.875rem;color:#15803d}.ie-mapping-success svg{flex-shrink:0;margin-top:1px;color:#16a34a}.ie-mapping-unmatched{display:flex;align-items:flex-start;gap:6px;font-size:.8125rem;color:#64748b;margin:0;padding-top:4px}.ie-mapping-unmatched svg{flex-shrink:0;margin-top:1px}.ie-mapping-unmatched em{font-style:normal;color:#475569}.ie-mapping-table{border:1px solid #e2e8f0;border-radius:8px;overflow:hidden}.ie-mapping-header{display:grid;grid-template-columns:1fr 1fr 1fr;padding:8px 12px;background:#f8fafc;border-bottom:1px solid #e2e8f0;font-size:.75rem;font-weight:600;color:#64748b;text-transform:uppercase}.ie-mapping-row{display:grid;grid-template-columns:1fr 1fr 1fr;align-items:start;padding:8px 12px;border-bottom:1px solid #f1f5f9;gap:8px}.ie-mapping-row:last-child{border-bottom:none}.ie-mapping-row.ie-mapping-row--required-missing{background:#fff7ed}.ie-mapping-row.ie-mapping-row--required-missing .ie-field-label{color:#c2410c}.ie-match-icon{color:#16a34a;margin-right:4px;vertical-align:-1px}.ie-field-label{display:flex;align-items:center;font-size:.875rem;color:#1e293b;font-weight:500}.ie-required{color:#dc2626;margin-left:2px}.ie-sample{font-size:.8125rem;color:#64748b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ie-preview{display:flex;flex-direction:column;gap:12px}.ie-preview-scroll{overflow-x:auto;border:1px solid #e2e8f0;border-radius:8px}.ie-preview-table{width:100%;border-collapse:collapse;font-size:.8125rem}.ie-preview-table th{background:#f8fafc;padding:8px 12px;text-align:left;font-size:.75rem;font-weight:600;color:#64748b;text-transform:uppercase;border-bottom:1px solid #e2e8f0;white-space:nowrap}.ie-preview-table td{padding:7px 12px;color:#1e293b;border-bottom:1px solid #f1f5f9;max-width:160px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ie-preview-table tr:last-child td{border-bottom:none}.ie-result{display:flex;flex-direction:column;gap:16px}.ie-result-summary{display:flex;flex-direction:column;gap:8px}.ie-result-stat{display:flex;align-items:center;gap:10px;padding:12px 16px;border-radius:8px;font-size:.9375rem}.ie-result-stat.success{background:#f0fdf4;color:#15803d}.ie-result-stat.success svg{stroke:#22c55e}.ie-result-stat.error{background:#fef2f2;color:#b91c1c}.ie-result-stat.error svg{stroke:#ef4444}.ie-result-stat.warning{background:#fffbeb;color:#b45309}.ie-result-stat.warning svg{stroke:#f59e0b}.ie-errors-list{border:1px solid #fecaca;border-radius:8px;overflow:hidden}.ie-errors-title{font-size:.8125rem;font-weight:600;color:#b91c1c;padding:8px 12px;background:#fef2f2;margin:0;border-bottom:1px solid #fecaca}.ie-error-row{display:flex;gap:12px;padding:7px 12px;border-bottom:1px solid #fff1f2;font-size:.8125rem;color:#374151}.ie-error-row:last-child{border-bottom:none}.ie-error-row-num{font-weight:600;color:#b91c1c;flex-shrink:0}.ie-warnings-list{border:1px solid #fde68a;border-radius:8px;overflow:hidden}.ie-warnings-title{font-size:.8125rem;font-weight:600;color:#b45309;padding:8px 12px;background:#fffbeb;margin:0;border-bottom:1px solid #fde68a}.ie-warning-row{display:flex;gap:12px;padding:7px 12px;border-bottom:1px solid #fef3c7;font-size:.8125rem;color:#374151}.ie-warning-row:last-child{border-bottom:none}.ie-warning-row-num{font-weight:600;color:#b45309;flex-shrink:0}.ie-error{font-size:.875rem;color:#dc2626;margin:0}.ie-footer-right{display:flex;gap:8px;align-items:center}@media(max-width:600px){.ie-step-label{display:none}.ie-dropzone{padding:24px 16px}.ie-mapping-header{grid-template-columns:1fr 1fr}.ie-mapping-header span:last-child{display:none}.ie-mapping-row{grid-template-columns:1fr 1fr}.ie-mapping-row .ie-sample{display:none}}\n"]
10312
+ styles: [".ie-actions{display:flex;gap:8px}.ie-steps{display:flex;align-items:center;margin-bottom:24px;gap:0;list-style:none;padding:0}.ie-step{display:flex;align-items:center;gap:6px;flex-shrink:0}.ie-step-num{width:24px;height:24px;border-radius:50%;background:#e2e8f0;color:#64748b;font-size:.75rem;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0}.ie-step-label{font-size:.8125rem;color:#64748b;font-weight:500}.ie-step.active .ie-step-num{background:#3b82f6;color:#fff}.ie-step.active .ie-step-label{color:#1e293b;font-weight:600}.ie-step.done .ie-step-num{background:#22c55e;color:#fff}.ie-step.done .ie-step-label{color:#64748b}.ie-step-divider{flex:1;height:1px;background:#e2e8f0;margin:0 8px;min-width:16px}.ie-source-step{display:flex;flex-direction:column;gap:16px}.ie-upload-area{display:flex;flex-direction:column;gap:12px}.ie-template-hint{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:.875rem;color:#64748b;background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px;padding:10px 14px}.ie-file-hidden{display:none}.ie-dropzone{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;padding:40px 24px;border:2px dashed #cbd5e1;border-radius:10px;cursor:pointer;transition:border-color .18s ease,background .18s ease}.ie-dropzone:hover,.ie-dropzone:focus-within{border-color:#3b82f6;background:#f0f7ff}.ie-dropzone.has-error{border-color:#ef4444}.ie-dropzone-text{font-size:.9375rem;font-weight:600;color:#1e293b}.ie-dropzone-sub{font-size:.8125rem;color:#64748b}.ie-mapping{display:flex;flex-direction:column;gap:12px}.ie-mapping-hint{font-size:.875rem;color:#64748b;margin:0}.ie-mapping-success{display:flex;align-items:flex-start;gap:8px;padding:10px 14px;background:#f0fdf4;border:1px solid #bbf7d0;border-radius:8px;font-size:.875rem;color:#15803d}.ie-mapping-success svg{flex-shrink:0;margin-top:1px;color:#16a34a}.ie-mapping-unmatched{display:flex;align-items:flex-start;gap:6px;font-size:.8125rem;color:#64748b;margin:0;padding-top:4px}.ie-mapping-unmatched svg{flex-shrink:0;margin-top:1px}.ie-mapping-unmatched em{font-style:normal;color:#475569}.ie-mapping-table{border:1px solid #e2e8f0;border-radius:8px;overflow:hidden}.ie-mapping-header{display:grid;grid-template-columns:1fr 1fr 1fr;padding:8px 12px;background:#f8fafc;border-bottom:1px solid #e2e8f0;font-size:.75rem;font-weight:600;color:#64748b;text-transform:uppercase}.ie-mapping-row{display:grid;grid-template-columns:1fr 1fr 1fr;align-items:start;padding:8px 12px;border-bottom:1px solid #f1f5f9;gap:8px}.ie-mapping-row:last-child{border-bottom:none}.ie-mapping-row.ie-mapping-row--required-missing{background:#fff7ed}.ie-mapping-row.ie-mapping-row--required-missing .ie-field-label{color:#c2410c}.ie-match-icon{color:#16a34a;margin-right:4px;vertical-align:-1px}.ie-field-label{display:flex;align-items:center;font-size:.875rem;color:#1e293b;font-weight:500}.ie-required{color:#dc2626;margin-left:2px}.ie-sample{font-size:.8125rem;color:#64748b;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ie-preview{display:flex;flex-direction:column;gap:12px}.ie-preview-scroll{overflow-x:auto;border:1px solid #e2e8f0;border-radius:8px}.ie-preview-table{width:100%;border-collapse:collapse;font-size:.8125rem}.ie-preview-table th{background:#f8fafc;padding:8px 12px;text-align:left;font-size:.75rem;font-weight:600;color:#64748b;text-transform:uppercase;border-bottom:1px solid #e2e8f0;white-space:nowrap}.ie-preview-table td{padding:7px 12px;color:#1e293b;border-bottom:1px solid #f1f5f9;max-width:160px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ie-preview-table tr:last-child td{border-bottom:none}.ie-result{display:flex;flex-direction:column;gap:16px}.ie-result-summary{display:flex;flex-direction:column;gap:8px}.ie-result-stat{display:flex;align-items:center;gap:10px;padding:12px 16px;border-radius:8px;font-size:.9375rem}.ie-result-stat.success{background:#f0fdf4;color:#15803d}.ie-result-stat.success svg{stroke:#22c55e}.ie-result-stat.error{background:#fef2f2;color:#b91c1c}.ie-result-stat.error svg{stroke:#ef4444}.ie-result-stat.warning{background:#fffbeb;color:#b45309}.ie-result-stat.warning svg{stroke:#f59e0b}.ie-progress{margin-top:14px}.ie-progress-track{height:6px;border-radius:999px;background:#e2e8f0;overflow:hidden;position:relative}.ie-progress-bar{position:absolute;inset:0 auto 0 0;width:35%;border-radius:999px;background:#2563eb;animation:ie-progress-slide 1.2s ease-in-out infinite}.ie-progress-text{margin:8px 0 0;font-size:.8125rem;color:#475569}@keyframes ie-progress-slide{0%{left:-35%}to{left:100%}}@media(prefers-reduced-motion:reduce){.ie-progress-bar{animation:none;width:100%;opacity:.5}}.ie-errors-list{border:1px solid #fecaca;border-radius:8px;overflow:hidden}.ie-errors-title{font-size:.8125rem;font-weight:600;color:#b91c1c;padding:8px 12px;background:#fef2f2;margin:0;border-bottom:1px solid #fecaca}.ie-error-row{display:flex;gap:12px;padding:7px 12px;border-bottom:1px solid #fff1f2;font-size:.8125rem;color:#374151}.ie-error-row:last-child{border-bottom:none}.ie-error-row-num{font-weight:600;color:#b91c1c;flex-shrink:0}.ie-warnings-list{border:1px solid #fde68a;border-radius:8px;overflow:hidden}.ie-warnings-title{font-size:.8125rem;font-weight:600;color:#b45309;padding:8px 12px;background:#fffbeb;margin:0;border-bottom:1px solid #fde68a}.ie-warning-row{display:flex;gap:12px;padding:7px 12px;border-bottom:1px solid #fef3c7;font-size:.8125rem;color:#374151}.ie-warning-row:last-child{border-bottom:none}.ie-warning-row-num{font-weight:600;color:#b45309;flex-shrink:0}.ie-error{font-size:.875rem;color:#dc2626;margin:0}.ie-footer-right{display:flex;gap:8px;align-items:center}@media(max-width:600px){.ie-step-label{display:none}.ie-dropzone{padding:24px 16px}.ie-mapping-header{grid-template-columns:1fr 1fr}.ie-mapping-header span:last-child{display:none}.ie-mapping-row{grid-template-columns:1fr 1fr}.ie-mapping-row .ie-sample{display:none}}\n"]
10229
10313
  }]
10230
10314
  }],
10231
10315
  ctorParameters: () => [],
@@ -11015,6 +11099,17 @@ var DataTableComponent = class DataTableComponent {
11015
11099
  });
11016
11100
  return true;
11017
11101
  }
11102
+ _actionKeyLabel(key) {
11103
+ const words = key.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[\s_-]+/).filter(Boolean).join(" ").toLowerCase();
11104
+ return words.charAt(0).toUpperCase() + words.slice(1);
11105
+ }
11106
+ _isActionEligible(def, row) {
11107
+ try {
11108
+ return !!def.eligible(row);
11109
+ } catch {
11110
+ return false;
11111
+ }
11112
+ }
11018
11113
  _bulkOptionsFor(def) {
11019
11114
  const opts = this._getOptionsForField(def.field);
11020
11115
  return def.exclude?.length ? opts.filter((o) => !this._isBulkExcluded(def, o.value)) : opts;
@@ -11075,7 +11170,7 @@ var DataTableComponent = class DataTableComponent {
11075
11170
  this._visibleColumns.set([]);
11076
11171
  }
11077
11172
  _hostConfirm(def, ctx, skippedNote, fallback) {
11078
- const fn = typeof def.confirm === "function" ? def.confirm : def.confirm?.[ctx.targetValue];
11173
+ const fn = typeof def.confirm === "function" ? def.confirm : def.action ? void 0 : def.confirm?.[ctx.targetValue];
11079
11174
  if (!fn) return null;
11080
11175
  const out = fn(ctx);
11081
11176
  const copy = typeof out === "string" ? { message: out } : out;
@@ -11089,6 +11184,7 @@ var DataTableComponent = class DataTableComponent {
11089
11184
  };
11090
11185
  }
11091
11186
  _bulkBlockedReason(def) {
11187
+ if (def.action) return def.eligible && this._actionSplit(def).eligible.length === 0 ? `None of the selected rows qualify for ${def.label.toLowerCase()}.` : "";
11092
11188
  if (def.type !== "delete" || !def.deletable) return "";
11093
11189
  return this._deleteSplit(def).deletable.length === 0 ? "None of the selected rows can be deleted." : "";
11094
11190
  }
@@ -11106,7 +11202,96 @@ var DataTableComponent = class DataTableComponent {
11106
11202
  blocked: selected.filter((r) => !def.deletable(r))
11107
11203
  };
11108
11204
  }
11205
+ _actionSplit(def) {
11206
+ const selected = this._items().filter((i) => this._selectedIds().has(i["id"]));
11207
+ if (!def.eligible) return {
11208
+ eligible: selected,
11209
+ refused: []
11210
+ };
11211
+ const ok = selected.map((r) => this._isActionEligible(def, r));
11212
+ return {
11213
+ eligible: selected.filter((_, i) => ok[i]),
11214
+ refused: selected.filter((_, i) => !ok[i])
11215
+ };
11216
+ }
11217
+ async _runDirectAction(def) {
11218
+ if (this._confirmingAction || this._runningAction()) return;
11219
+ const split = this._actionSplit(def);
11220
+ if (split.eligible.length === 0) return;
11221
+ const ids = split.eligible.map((r) => r["id"]);
11222
+ const skippedCount = split.refused.length;
11223
+ const lower = def.label.toLowerCase();
11224
+ const skippedNote = skippedCount === 0 ? "" : skippedCount === 1 ? ` 1 selected row doesn't qualify for ${lower} and will be skipped.` : ` ${skippedCount} selected rows don't qualify for ${lower} and will be skipped.`;
11225
+ const variant = def.variant === "danger" ? "danger" : "primary";
11226
+ this._confirmingAction = true;
11227
+ let confirmed = false;
11228
+ try {
11229
+ const defaultCopy = {
11230
+ title: def.label,
11231
+ message: `${def.label} for ${ids.length} ${this._countLabel(ids.length)}?${skippedNote}`,
11232
+ confirmLabel: def.label,
11233
+ variant
11234
+ };
11235
+ const hostCopy = def.confirm ? this._hostConfirm(def, {
11236
+ count: ids.length,
11237
+ skippedAlready: 0,
11238
+ skippedBlocked: skippedCount,
11239
+ selectedCount: this._selectedIds().size,
11240
+ itemLabel: this.itemLabel(),
11241
+ itemLabelPlural: this.itemLabelPlural() || pluralize(this.itemLabel()),
11242
+ columnLabel: def.label,
11243
+ targetValue: "",
11244
+ targetLabel: "",
11245
+ rows: split.eligible,
11246
+ skippedRows: split.refused
11247
+ }, skippedNote, {
11248
+ title: defaultCopy.title,
11249
+ confirmLabel: defaultCopy.confirmLabel,
11250
+ variant
11251
+ }) : null;
11252
+ confirmed = await this._confirmSvc.confirm(hostCopy ?? defaultCopy);
11253
+ } catch {
11254
+ confirmed = false;
11255
+ } finally {
11256
+ this._confirmingAction = false;
11257
+ }
11258
+ if (!confirmed) return;
11259
+ this._runningAction.set(def.type);
11260
+ const done = def.doneLabel?.trim() || "updated";
11261
+ const url = `${this.config.baseUrl}${this.endpoint()}/bulk/${def.type.slice(7)}`;
11262
+ this.http.post(url, { ids }).pipe(takeUntilDestroyed(this.destroyRef)).subscribe({
11263
+ next: (r) => {
11264
+ this._runningAction.set(null);
11265
+ const count = r == null ? ids.length : r.updated ?? r.deleted ?? 0;
11266
+ const totalSkipped = skippedCount + (r?.skipped ?? 0);
11267
+ const notAllowed = r?.notAllowed ?? 0;
11268
+ const msg = totalSkipped > 0 || notAllowed > 0 ? [
11269
+ `${count} ${done}`,
11270
+ totalSkipped > 0 ? `${totalSkipped} skipped` : "",
11271
+ notAllowed > 0 ? `${notAllowed} not allowed` : ""
11272
+ ].filter(Boolean).join(", ") + "." : `${count} ${this.itemLabel()}(s) ${done}.`;
11273
+ const serverMsg = humanReadableMessage(r?.message);
11274
+ if (serverMsg && count === 0) this._toast.error(serverMsg);
11275
+ else this._toast.show(serverMsg ? `${msg} ${serverMsg}` : msg);
11276
+ this.clearSelection();
11277
+ this.refresh();
11278
+ this.bulkAction.emit({
11279
+ type: def.type,
11280
+ ids,
11281
+ skippedCount
11282
+ });
11283
+ },
11284
+ error: (err) => {
11285
+ this._runningAction.set(null);
11286
+ this._toast.error(err?.error?.message ?? "Something went wrong. Please try again.");
11287
+ }
11288
+ });
11289
+ }
11109
11290
  onBulkActionClick(def) {
11291
+ if (def.action) {
11292
+ this._runDirectAction(def);
11293
+ return;
11294
+ }
11110
11295
  if (def.type === "delete") {
11111
11296
  const split = this._deleteSplit(def);
11112
11297
  if (def.deletable && split.deletable.length === 0) return;
@@ -11224,6 +11409,8 @@ var DataTableComponent = class DataTableComponent {
11224
11409
  variant: "primary"
11225
11410
  }) : null;
11226
11411
  confirmed = await this._confirmSvc.confirm(hostCopy ?? defaultCopy);
11412
+ } catch {
11413
+ confirmed = false;
11227
11414
  } finally {
11228
11415
  this._confirmingBulk = false;
11229
11416
  }
@@ -11547,6 +11734,18 @@ var DataTableComponent = class DataTableComponent {
11547
11734
  ...confirm ? { confirm } : {},
11548
11735
  ...deletable ? { deletable } : {}
11549
11736
  };
11737
+ if (typeof entry !== "string" && entry.action) {
11738
+ const label = entry.label?.trim() || this._actionKeyLabel(key);
11739
+ return {
11740
+ type: `action-${key}`,
11741
+ label,
11742
+ variant: entry.variant === "danger" ? "danger" : "secondary",
11743
+ action: true,
11744
+ ...entry.eligible ? { eligible: entry.eligible } : {},
11745
+ ...entry.doneLabel ? { doneLabel: entry.doneLabel } : {},
11746
+ ...confirm ? { confirm } : {}
11747
+ };
11748
+ }
11550
11749
  const exclude = typeof entry === "string" ? [] : entry.exclude ?? [];
11551
11750
  const col = this.columns().find((c) => c.key === key);
11552
11751
  const allowedFrom = typeof entry === "string" ? void 0 : entry.allowedFrom;
@@ -11563,7 +11762,7 @@ var DataTableComponent = class DataTableComponent {
11563
11762
  ...canChange ? { canChange } : {},
11564
11763
  ...confirm ? { confirm } : {}
11565
11764
  };
11566
- }).filter((def) => !def.field || !def.exclude?.length || this._bulkOptionsFor(def).length > 0), ...ngDevMode ? [{ debugName: "_bulkDefs" }] : /* istanbul ignore next */ []);
11765
+ }).filter((def) => !def.field || !def.exclude?.length || this._bulkOptionsFor(def).length > 0).filter((def, i, all) => !def.action || all.findIndex((d) => d.type === def.type) === i), ...ngDevMode ? [{ debugName: "_bulkDefs" }] : /* istanbul ignore next */ []);
11567
11766
  this._activeBulkType = signal(null, ...ngDevMode ? [{ debugName: "_activeBulkType" }] : /* istanbul ignore next */ []);
11568
11767
  this._applyingBulk = signal(false, ...ngDevMode ? [{ debugName: "_applyingBulk" }] : /* istanbul ignore next */ []);
11569
11768
  this._deletingBulk = signal(false, ...ngDevMode ? [{ debugName: "_deletingBulk" }] : /* istanbul ignore next */ []);
@@ -11603,6 +11802,8 @@ var DataTableComponent = class DataTableComponent {
11603
11802
  });
11604
11803
  return out;
11605
11804
  }, ...ngDevMode ? [{ debugName: "_rowNameSuffix" }] : /* istanbul ignore next */ []);
11805
+ this._runningAction = signal(null, ...ngDevMode ? [{ debugName: "_runningAction" }] : /* istanbul ignore next */ []);
11806
+ this._confirmingAction = false;
11606
11807
  this._confirmingBulk = false;
11607
11808
  this._rowDeleteTarget = signal(null, ...ngDevMode ? [{ debugName: "_rowDeleteTarget" }] : /* istanbul ignore next */ []);
11608
11809
  this._rowDeleting = signal(false, ...ngDevMode ? [{ debugName: "_rowDeleting" }] : /* istanbul ignore next */ []);
@@ -11739,6 +11940,14 @@ var DataTableComponent = class DataTableComponent {
11739
11940
  if (this.page()) this._loadSavedViews();
11740
11941
  this._fetchCounts();
11741
11942
  }
11943
+ _retryAfterError() {
11944
+ if (this._search()) {
11945
+ this._search.set("");
11946
+ this._page.set(1);
11947
+ this._fetchCounts();
11948
+ }
11949
+ this.load();
11950
+ }
11742
11951
  onSearchChange(val) {
11743
11952
  if (val === this._search()) return;
11744
11953
  this._search.set(val);
@@ -12475,8 +12684,8 @@ var DataTableComponent = class DataTableComponent {
12475
12684
  <div class="dt-host">
12476
12685
 
12477
12686
  <!-- ── HEADER ──────────────────────────────────────────────── -->
12478
- @if (!_hasLoaded()) {
12479
- <!-- Skeleton header: shaped to match real controls -->
12687
+ @if (!_hasLoaded() && !_error()) {
12688
+ <!-- Skeleton header: shaped to match real controls (only while the first load is running: a failed first load keeps no placeholder bars) -->
12480
12689
  @if (_showHeader()) {
12481
12690
  <div class="dt-sk-toolbar">
12482
12691
  <div class="dt-sk-toolbar-left">
@@ -12511,14 +12720,14 @@ var DataTableComponent = class DataTableComponent {
12511
12720
  @if (tableTitle()) {
12512
12721
  <h3 class="dt-title">{{ tableTitle() }}</h3>
12513
12722
  }
12514
- @if (showSearch() && !_emptyUnfiltered()) {
12723
+ @if (showSearch() && !_emptyUnfiltered() && !_error()) {
12515
12724
  <ui-search-input
12516
12725
  [value]="_search()"
12517
12726
  [placeholder]="_effectiveSearchPlaceholder()"
12518
- [loading]="_searchLoading()"
12727
+ [loading]="_searchLoading() && !_error()"
12519
12728
  (searchChange)="onSearchChange($event)" />
12520
12729
  }
12521
- @if (_effectiveTabs().length && !_emptyUnfiltered()) {
12730
+ @if (_effectiveTabs().length && !_emptyUnfiltered() && !_error()) {
12522
12731
  <ui-quick-filter-tabs
12523
12732
  [tabs]="_effectiveTabs()"
12524
12733
  [counts]="_tabCounts()"
@@ -12561,8 +12770,8 @@ var DataTableComponent = class DataTableComponent {
12561
12770
  } @else {
12562
12771
  @for (def of _bulkDefs(); track def.type) {
12563
12772
  <ui-button [variant]="def.variant ?? 'secondary'" size="sm"
12564
- [loading]="def.type === 'delete' && _deletingBulk()"
12565
- [disabled]="_deletingBulk() || !!_bulkBlockedReason(def)"
12773
+ [loading]="(def.type === 'delete' && _deletingBulk()) || (!!def.action && _runningAction() === def.type)"
12774
+ [disabled]="_deletingBulk() || !!_runningAction() || !!_bulkBlockedReason(def)"
12566
12775
  [ariaLabel]="_bulkBlockedReason(def) ? def.label + ' — ' + _bulkBlockedReason(def) : undefined"
12567
12776
  [attr.title]="_bulkBlockedReason(def) || null"
12568
12777
  (click)="onBulkActionClick(def)">{{ def.label }}</ui-button>
@@ -12692,7 +12901,7 @@ var DataTableComponent = class DataTableComponent {
12692
12901
  @else if (_error()) {
12693
12902
  <tr>
12694
12903
  <td [attr.colspan]="visibleCols().length + (_isSelectable() ? 1 : 0) + (_reorderOn() ? 1 : 0)" class="dt-state-cell">
12695
- <ui-error-state [message]="_error()!" [retryLabel]="_retryLabel()" (retry)="load()" />
12904
+ <ui-error-state [message]="_error()!" [retryLabel]="_retryLabel()" (retry)="_retryAfterError()" />
12696
12905
  </td>
12697
12906
  </tr>
12698
12907
  }
@@ -12954,7 +13163,7 @@ var DataTableComponent = class DataTableComponent {
12954
13163
  }
12955
13164
 
12956
13165
  } @else if (_error()) {
12957
- <ui-error-state [message]="_error()!" [retryLabel]="_retryLabel()" (retry)="load()" />
13166
+ <ui-error-state [message]="_error()!" [retryLabel]="_retryLabel()" (retry)="_retryAfterError()" />
12958
13167
 
12959
13168
  } @else if (_items().length === 0 && !_hasActiveFilters()) {
12960
13169
  <ui-empty-state iconType="table" [message]="emptyMessage()" />
@@ -13542,8 +13751,8 @@ i0.ɵɵngDeclareClassMetadata({
13542
13751
  <div class="dt-host">
13543
13752
 
13544
13753
  <!-- ── HEADER ──────────────────────────────────────────────── -->
13545
- @if (!_hasLoaded()) {
13546
- <!-- Skeleton header: shaped to match real controls -->
13754
+ @if (!_hasLoaded() && !_error()) {
13755
+ <!-- Skeleton header: shaped to match real controls (only while the first load is running: a failed first load keeps no placeholder bars) -->
13547
13756
  @if (_showHeader()) {
13548
13757
  <div class="dt-sk-toolbar">
13549
13758
  <div class="dt-sk-toolbar-left">
@@ -13578,14 +13787,14 @@ i0.ɵɵngDeclareClassMetadata({
13578
13787
  @if (tableTitle()) {
13579
13788
  <h3 class="dt-title">{{ tableTitle() }}</h3>
13580
13789
  }
13581
- @if (showSearch() && !_emptyUnfiltered()) {
13790
+ @if (showSearch() && !_emptyUnfiltered() && !_error()) {
13582
13791
  <ui-search-input
13583
13792
  [value]="_search()"
13584
13793
  [placeholder]="_effectiveSearchPlaceholder()"
13585
- [loading]="_searchLoading()"
13794
+ [loading]="_searchLoading() && !_error()"
13586
13795
  (searchChange)="onSearchChange($event)" />
13587
13796
  }
13588
- @if (_effectiveTabs().length && !_emptyUnfiltered()) {
13797
+ @if (_effectiveTabs().length && !_emptyUnfiltered() && !_error()) {
13589
13798
  <ui-quick-filter-tabs
13590
13799
  [tabs]="_effectiveTabs()"
13591
13800
  [counts]="_tabCounts()"
@@ -13628,8 +13837,8 @@ i0.ɵɵngDeclareClassMetadata({
13628
13837
  } @else {
13629
13838
  @for (def of _bulkDefs(); track def.type) {
13630
13839
  <ui-button [variant]="def.variant ?? 'secondary'" size="sm"
13631
- [loading]="def.type === 'delete' && _deletingBulk()"
13632
- [disabled]="_deletingBulk() || !!_bulkBlockedReason(def)"
13840
+ [loading]="(def.type === 'delete' && _deletingBulk()) || (!!def.action && _runningAction() === def.type)"
13841
+ [disabled]="_deletingBulk() || !!_runningAction() || !!_bulkBlockedReason(def)"
13633
13842
  [ariaLabel]="_bulkBlockedReason(def) ? def.label + ' — ' + _bulkBlockedReason(def) : undefined"
13634
13843
  [attr.title]="_bulkBlockedReason(def) || null"
13635
13844
  (click)="onBulkActionClick(def)">{{ def.label }}</ui-button>
@@ -13759,7 +13968,7 @@ i0.ɵɵngDeclareClassMetadata({
13759
13968
  @else if (_error()) {
13760
13969
  <tr>
13761
13970
  <td [attr.colspan]="visibleCols().length + (_isSelectable() ? 1 : 0) + (_reorderOn() ? 1 : 0)" class="dt-state-cell">
13762
- <ui-error-state [message]="_error()!" [retryLabel]="_retryLabel()" (retry)="load()" />
13971
+ <ui-error-state [message]="_error()!" [retryLabel]="_retryLabel()" (retry)="_retryAfterError()" />
13763
13972
  </td>
13764
13973
  </tr>
13765
13974
  }
@@ -14021,7 +14230,7 @@ i0.ɵɵngDeclareClassMetadata({
14021
14230
  }
14022
14231
 
14023
14232
  } @else if (_error()) {
14024
- <ui-error-state [message]="_error()!" [retryLabel]="_retryLabel()" (retry)="load()" />
14233
+ <ui-error-state [message]="_error()!" [retryLabel]="_retryLabel()" (retry)="_retryAfterError()" />
14025
14234
 
14026
14235
  } @else if (_items().length === 0 && !_hasActiveFilters()) {
14027
14236
  <ui-empty-state iconType="table" [message]="emptyMessage()" />
@@ -19071,7 +19280,7 @@ var ToggleBadgeComponent = class ToggleBadgeComponent {
19071
19280
  </span>
19072
19281
  `,
19073
19282
  isInline: true,
19074
- styles: [":host{display:inline-flex}.tb{display:inline-flex;align-items:center;border-radius:999px;border:1px solid #e2e8f0;background:#fff;color:#64748b;transition:all .15s ease}.tb:hover{background:#f8fafc;border-color:#94a3b8}.tb.active{background:#1e293b;color:#fff;border-color:#1e293b}.toggle-badge{display:inline-flex;align-items:center;gap:5px;padding:var(--ui-toggle-badge-pad-y, 3px) var(--ui-toggle-badge-pad-x, 10px);border-radius:999px;border:none;background:transparent;font-size:.75rem;font-weight:500;color:inherit;cursor:pointer;font-family:inherit;line-height:1}.toggle-badge:focus-visible,.tb-action:focus-visible{outline:2px solid #60a5fa;outline-offset:2px}.tb-has-actions .toggle-badge{padding-right:6px}.tb-actions{display:inline-flex;align-items:center;gap:1px;padding-right:4px}.tb-divider{width:1px;height:12px;background:currentColor;opacity:.25;margin-right:3px}.tb-action{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;border-radius:50%;background:transparent;color:inherit;cursor:pointer;opacity:.75;font-family:inherit;transition:background .15s ease,opacity .15s ease}.tb-action:hover:not(:disabled){opacity:1;background:#64748b2e}.tb.active .tb-action:hover:not(:disabled){background:#fff3}.tb-action:disabled{opacity:.35;cursor:not-allowed}@media(pointer:coarse){.tb-action{position:relative}.tb-action:after{content:\"\";position:absolute;inset:-12px}}\n"],
19283
+ styles: [":host{display:inline-flex}.tb{display:inline-flex;align-items:center;border-radius:999px;border:1px solid #e2e8f0;background:#fff;color:#64748b;transition:all .15s ease}.tb:hover{background:#f8fafc;border-color:#94a3b8}.tb.active{background:#1e293b;color:#fff;border-color:#1e293b}.toggle-badge{display:inline-flex;align-items:center;gap:5px;padding:var(--ui-toggle-badge-pad-y, 3px) var(--ui-toggle-badge-pad-x, 10px);border-radius:999px;border:none;background:transparent;font-size:.75rem;font-weight:500;color:inherit;cursor:pointer;font-family:inherit;line-height:1}.toggle-badge:focus-visible,.tb-action:focus-visible{outline:2px solid #60a5fa;outline-offset:2px}.tb-has-actions .toggle-badge{padding-right:6px}@media(pointer:coarse){.toggle-badge{position:relative}.toggle-badge:after{content:\"\";position:absolute;inset:-12px -4px}}.tb-actions{display:inline-flex;align-items:center;gap:1px;padding-right:4px}.tb-divider{width:1px;height:12px;background:currentColor;opacity:.25;margin-right:3px}.tb-action{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;border-radius:50%;background:transparent;color:inherit;cursor:pointer;opacity:.75;font-family:inherit;transition:background .15s ease,opacity .15s ease}.tb-action:hover:not(:disabled){opacity:1;background:#64748b2e}.tb.active .tb-action:hover:not(:disabled){background:#fff3}.tb-action:disabled{opacity:.35;cursor:not-allowed}@media(pointer:coarse){.tb-action{position:relative}.tb-action:after{content:\"\";position:absolute;inset:-12px}}\n"],
19075
19284
  dependencies: [{
19076
19285
  kind: "directive",
19077
19286
  type: TooltipDirective,
@@ -19113,7 +19322,7 @@ i0.ɵɵngDeclareClassMetadata({
19113
19322
  }
19114
19323
  </span>
19115
19324
  `,
19116
- styles: [":host{display:inline-flex}.tb{display:inline-flex;align-items:center;border-radius:999px;border:1px solid #e2e8f0;background:#fff;color:#64748b;transition:all .15s ease}.tb:hover{background:#f8fafc;border-color:#94a3b8}.tb.active{background:#1e293b;color:#fff;border-color:#1e293b}.toggle-badge{display:inline-flex;align-items:center;gap:5px;padding:var(--ui-toggle-badge-pad-y, 3px) var(--ui-toggle-badge-pad-x, 10px);border-radius:999px;border:none;background:transparent;font-size:.75rem;font-weight:500;color:inherit;cursor:pointer;font-family:inherit;line-height:1}.toggle-badge:focus-visible,.tb-action:focus-visible{outline:2px solid #60a5fa;outline-offset:2px}.tb-has-actions .toggle-badge{padding-right:6px}.tb-actions{display:inline-flex;align-items:center;gap:1px;padding-right:4px}.tb-divider{width:1px;height:12px;background:currentColor;opacity:.25;margin-right:3px}.tb-action{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;border-radius:50%;background:transparent;color:inherit;cursor:pointer;opacity:.75;font-family:inherit;transition:background .15s ease,opacity .15s ease}.tb-action:hover:not(:disabled){opacity:1;background:#64748b2e}.tb.active .tb-action:hover:not(:disabled){background:#fff3}.tb-action:disabled{opacity:.35;cursor:not-allowed}@media(pointer:coarse){.tb-action{position:relative}.tb-action:after{content:\"\";position:absolute;inset:-12px}}\n"]
19325
+ styles: [":host{display:inline-flex}.tb{display:inline-flex;align-items:center;border-radius:999px;border:1px solid #e2e8f0;background:#fff;color:#64748b;transition:all .15s ease}.tb:hover{background:#f8fafc;border-color:#94a3b8}.tb.active{background:#1e293b;color:#fff;border-color:#1e293b}.toggle-badge{display:inline-flex;align-items:center;gap:5px;padding:var(--ui-toggle-badge-pad-y, 3px) var(--ui-toggle-badge-pad-x, 10px);border-radius:999px;border:none;background:transparent;font-size:.75rem;font-weight:500;color:inherit;cursor:pointer;font-family:inherit;line-height:1}.toggle-badge:focus-visible,.tb-action:focus-visible{outline:2px solid #60a5fa;outline-offset:2px}.tb-has-actions .toggle-badge{padding-right:6px}@media(pointer:coarse){.toggle-badge{position:relative}.toggle-badge:after{content:\"\";position:absolute;inset:-12px -4px}}.tb-actions{display:inline-flex;align-items:center;gap:1px;padding-right:4px}.tb-divider{width:1px;height:12px;background:currentColor;opacity:.25;margin-right:3px}.tb-action{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;padding:0;border:none;border-radius:50%;background:transparent;color:inherit;cursor:pointer;opacity:.75;font-family:inherit;transition:background .15s ease,opacity .15s ease}.tb-action:hover:not(:disabled){opacity:1;background:#64748b2e}.tb.active .tb-action:hover:not(:disabled){background:#fff3}.tb-action:disabled{opacity:.35;cursor:not-allowed}@media(pointer:coarse){.tb-action{position:relative}.tb-action:after{content:\"\";position:absolute;inset:-12px}}\n"]
19117
19326
  }]
19118
19327
  }],
19119
19328
  propDecorators: {
@@ -24305,41 +24514,41 @@ var ChipComponent = class ChipComponent {
24305
24514
  }],
24306
24515
  ngImport: i0,
24307
24516
  template: `
24308
- <button
24309
- type="button"
24310
- class="chip"
24311
- [class.chip--active]="_resolvedActive()"
24312
- [class.chip--sm]="size() === 'sm'"
24313
- [class.chip--lg]="size() === 'lg'"
24314
- [class.chip--dismissible]="dismissible()"
24315
- [attr.aria-pressed]="dismissible() ? null : (_resolvedActive() ? 'true' : 'false')"
24316
- [disabled]="_disabled()"
24317
- (click)="onClick()"
24318
- (keydown.delete)="onDismissKey($event)"
24319
- (keydown.backspace)="onDismissKey($event)"
24320
- (blur)="_onTouched()">
24321
- {{ label() }}
24322
- @if (dismissible()) {
24323
- <span
24324
- class="chip-dismiss"
24325
- role="button"
24326
- [attr.aria-label]="'Remove ' + label()"
24327
- [attr.tabindex]="_disabled() ? null : 0"
24328
- (click)="onDismiss($event)"
24329
- (keydown.enter)="onDismissKey($event)"
24330
- (keydown.space)="onDismissKey($event)"
24331
- (keyup.space)="$event.preventDefault(); $event.stopPropagation()">
24517
+ @if (dismissible()) {
24518
+ <span
24519
+ class="chip chip--dismissible"
24520
+ [class.chip--active]="_resolvedActive()"
24521
+ [class.chip--sm]="size() === 'sm'"
24522
+ [class.chip--lg]="size() === 'lg'"
24523
+ [class.chip--disabled]="_disabled()"
24524
+ (keydown.delete)="onDismissKey($event)"
24525
+ (keydown.backspace)="onDismissKey($event)">
24526
+ <button type="button" class="chip-body" [disabled]="_disabled()" (click)="onClick()" (blur)="_onTouched()">{{ label() }}</button>
24527
+ <button type="button" class="chip-dismiss" [disabled]="_disabled()" [attr.aria-label]="'Remove ' + label()" (click)="onDismiss($event)">
24332
24528
  <svg width="10" height="10" viewBox="0 0 24 24" fill="none"
24333
- stroke="currentColor" stroke-width="2.5" aria-hidden="true">
24529
+ stroke="currentColor" stroke-width="2.5" aria-hidden="true" focusable="false">
24334
24530
  <line x1="18" y1="6" x2="6" y2="18"/>
24335
24531
  <line x1="6" y1="6" x2="18" y2="18"/>
24336
24532
  </svg>
24337
- </span>
24338
- }
24339
- </button>
24533
+ </button>
24534
+ </span>
24535
+ } @else {
24536
+ <button
24537
+ type="button"
24538
+ class="chip"
24539
+ [class.chip--active]="_resolvedActive()"
24540
+ [class.chip--sm]="size() === 'sm'"
24541
+ [class.chip--lg]="size() === 'lg'"
24542
+ [attr.aria-pressed]="_resolvedActive() ? 'true' : 'false'"
24543
+ [disabled]="_disabled()"
24544
+ (click)="onClick()"
24545
+ (blur)="_onTouched()">
24546
+ {{ label() }}
24547
+ </button>
24548
+ }
24340
24549
  `,
24341
24550
  isInline: true,
24342
- styles: [":host{display:contents}.chip{display:inline-flex;align-items:center;gap:5px;border-radius:999px;border:1.5px solid #d1d5db;background:#f9fafb;color:#374151;font-weight:500;font-family:inherit;cursor:pointer;-webkit-user-select:none;user-select:none;white-space:nowrap;transition:background .15s,border-color .15s,color .15s}.chip:hover:not(:disabled){border-color:#3b82f6;color:#3b82f6;background:#eff6ff}.chip:focus-visible{outline:2px solid #3b82f6;outline-offset:2px}.chip:disabled{opacity:.5;cursor:default}.chip--sm{padding:2px 10px;font-size:.75rem}.chip--lg{padding:6px 16px;font-size:.875rem}.chip--sm.chip--dismissible{padding-right:6px}.chip--lg.chip--dismissible{padding-right:10px}.chip--active{background:#3b82f6;border-color:#3b82f6;color:#fff}.chip--active:hover:not(:disabled){background:#2563eb;border-color:#2563eb;color:#fff}.chip-dismiss{display:inline-flex;align-items:center;justify-content:center;width:14px;height:14px;border-radius:50%;background:#0000001f;flex-shrink:0;transition:background .15s}.chip-dismiss:hover{background:#00000040}.chip-dismiss:focus-visible{outline:2px solid currentColor;outline-offset:1px}.chip--active .chip-dismiss{background:#ffffff4d}.chip--active .chip-dismiss:hover{background:#ffffff80}@media(pointer:coarse){.chip{min-height:44px}.chip--sm{min-height:40px;padding:6px 14px}.chip--lg{padding-left:18px;padding-right:18px}.chip--sm.chip--dismissible{padding-right:8px}.chip--lg.chip--dismissible{padding-right:12px}.chip-dismiss{width:24px;height:24px;position:relative}.chip-dismiss:after{content:\"\";position:absolute;inset:-10px}}@media(max-width:480px)and (pointer:fine){.chip-dismiss{position:relative}.chip-dismiss:after{content:\"\";position:absolute;inset:-15px}}\n"],
24551
+ styles: [":host{display:contents}.chip{display:inline-flex;align-items:center;gap:5px;border-radius:999px;border:1.5px solid #d1d5db;background:#f9fafb;color:#374151;font-weight:500;font-family:inherit;cursor:pointer;-webkit-user-select:none;user-select:none;white-space:nowrap;transition:background .15s,border-color .15s,color .15s}.chip:hover:not(:disabled):not(.chip--disabled){border-color:#3b82f6;color:#1d4ed8;background:#eff6ff}.chip:focus-visible,.chip:has(:focus-visible){outline:2px solid #3b82f6;outline-offset:2px}.chip:disabled,.chip.chip--disabled{opacity:.5;cursor:default}.chip--sm{padding:2px 10px;font-size:.75rem}.chip--lg{padding:6px 16px;font-size:.875rem}.chip--dismissible{padding:0;gap:0}.chip-body{display:inline-flex;align-items:center;align-self:stretch;background:none;border:0;margin:0;color:inherit;font:inherit;cursor:pointer;border-radius:999px 0 0 999px}.chip-body:focus-visible{outline:2px solid currentColor;outline-offset:-2px}.chip-body:disabled{cursor:default}.chip--sm .chip-body{padding:2px 4px 2px 10px}.chip--lg .chip-body{padding:6px 6px 6px 16px}.chip--sm.chip--dismissible .chip-dismiss{margin-right:6px}.chip--lg.chip--dismissible .chip-dismiss{margin-right:10px}.chip--active{background:#2563eb;border-color:#2563eb;color:#fff}.chip--active:hover:not(:disabled){background:#1d4ed8;border-color:#1d4ed8;color:#fff}.chip-dismiss{display:inline-flex;align-items:center;justify-content:center;width:14px;height:14px;border-radius:50%;border:0;padding:0;color:inherit;cursor:pointer;background:#0000001f;flex-shrink:0;transition:background .15s}.chip-dismiss:hover{background:#00000040}.chip-dismiss:focus-visible{outline:2px solid currentColor;outline-offset:1px}.chip--active .chip-dismiss{background:#ffffff4d}.chip--active .chip-dismiss:hover{background:#ffffff80}@media(pointer:coarse){.chip{min-height:44px}.chip--sm{min-height:40px;padding:6px 14px}.chip--lg{padding-left:18px;padding-right:18px}.chip--sm .chip-body{padding:6px 4px 6px 14px}.chip--lg .chip-body{padding-left:18px}.chip--sm.chip--dismissible .chip-dismiss{margin-right:8px}.chip--lg.chip--dismissible .chip-dismiss{margin-right:12px}.chip-dismiss{width:24px;height:24px;position:relative}.chip-dismiss:after{content:\"\";position:absolute;inset:-10px}}@media(max-width:480px)and (pointer:fine){.chip-dismiss{position:relative}.chip-dismiss:after{content:\"\";position:absolute;inset:-15px}}\n"],
24343
24552
  changeDetection: i0.ChangeDetectionStrategy.OnPush
24344
24553
  });
24345
24554
  }
@@ -24361,40 +24570,40 @@ i0.ɵɵngDeclareClassMetadata({
24361
24570
  multi: true
24362
24571
  }],
24363
24572
  template: `
24364
- <button
24365
- type="button"
24366
- class="chip"
24367
- [class.chip--active]="_resolvedActive()"
24368
- [class.chip--sm]="size() === 'sm'"
24369
- [class.chip--lg]="size() === 'lg'"
24370
- [class.chip--dismissible]="dismissible()"
24371
- [attr.aria-pressed]="dismissible() ? null : (_resolvedActive() ? 'true' : 'false')"
24372
- [disabled]="_disabled()"
24373
- (click)="onClick()"
24374
- (keydown.delete)="onDismissKey($event)"
24375
- (keydown.backspace)="onDismissKey($event)"
24376
- (blur)="_onTouched()">
24377
- {{ label() }}
24378
- @if (dismissible()) {
24379
- <span
24380
- class="chip-dismiss"
24381
- role="button"
24382
- [attr.aria-label]="'Remove ' + label()"
24383
- [attr.tabindex]="_disabled() ? null : 0"
24384
- (click)="onDismiss($event)"
24385
- (keydown.enter)="onDismissKey($event)"
24386
- (keydown.space)="onDismissKey($event)"
24387
- (keyup.space)="$event.preventDefault(); $event.stopPropagation()">
24573
+ @if (dismissible()) {
24574
+ <span
24575
+ class="chip chip--dismissible"
24576
+ [class.chip--active]="_resolvedActive()"
24577
+ [class.chip--sm]="size() === 'sm'"
24578
+ [class.chip--lg]="size() === 'lg'"
24579
+ [class.chip--disabled]="_disabled()"
24580
+ (keydown.delete)="onDismissKey($event)"
24581
+ (keydown.backspace)="onDismissKey($event)">
24582
+ <button type="button" class="chip-body" [disabled]="_disabled()" (click)="onClick()" (blur)="_onTouched()">{{ label() }}</button>
24583
+ <button type="button" class="chip-dismiss" [disabled]="_disabled()" [attr.aria-label]="'Remove ' + label()" (click)="onDismiss($event)">
24388
24584
  <svg width="10" height="10" viewBox="0 0 24 24" fill="none"
24389
- stroke="currentColor" stroke-width="2.5" aria-hidden="true">
24585
+ stroke="currentColor" stroke-width="2.5" aria-hidden="true" focusable="false">
24390
24586
  <line x1="18" y1="6" x2="6" y2="18"/>
24391
24587
  <line x1="6" y1="6" x2="18" y2="18"/>
24392
24588
  </svg>
24393
- </span>
24394
- }
24395
- </button>
24589
+ </button>
24590
+ </span>
24591
+ } @else {
24592
+ <button
24593
+ type="button"
24594
+ class="chip"
24595
+ [class.chip--active]="_resolvedActive()"
24596
+ [class.chip--sm]="size() === 'sm'"
24597
+ [class.chip--lg]="size() === 'lg'"
24598
+ [attr.aria-pressed]="_resolvedActive() ? 'true' : 'false'"
24599
+ [disabled]="_disabled()"
24600
+ (click)="onClick()"
24601
+ (blur)="_onTouched()">
24602
+ {{ label() }}
24603
+ </button>
24604
+ }
24396
24605
  `,
24397
- styles: [":host{display:contents}.chip{display:inline-flex;align-items:center;gap:5px;border-radius:999px;border:1.5px solid #d1d5db;background:#f9fafb;color:#374151;font-weight:500;font-family:inherit;cursor:pointer;-webkit-user-select:none;user-select:none;white-space:nowrap;transition:background .15s,border-color .15s,color .15s}.chip:hover:not(:disabled){border-color:#3b82f6;color:#3b82f6;background:#eff6ff}.chip:focus-visible{outline:2px solid #3b82f6;outline-offset:2px}.chip:disabled{opacity:.5;cursor:default}.chip--sm{padding:2px 10px;font-size:.75rem}.chip--lg{padding:6px 16px;font-size:.875rem}.chip--sm.chip--dismissible{padding-right:6px}.chip--lg.chip--dismissible{padding-right:10px}.chip--active{background:#3b82f6;border-color:#3b82f6;color:#fff}.chip--active:hover:not(:disabled){background:#2563eb;border-color:#2563eb;color:#fff}.chip-dismiss{display:inline-flex;align-items:center;justify-content:center;width:14px;height:14px;border-radius:50%;background:#0000001f;flex-shrink:0;transition:background .15s}.chip-dismiss:hover{background:#00000040}.chip-dismiss:focus-visible{outline:2px solid currentColor;outline-offset:1px}.chip--active .chip-dismiss{background:#ffffff4d}.chip--active .chip-dismiss:hover{background:#ffffff80}@media(pointer:coarse){.chip{min-height:44px}.chip--sm{min-height:40px;padding:6px 14px}.chip--lg{padding-left:18px;padding-right:18px}.chip--sm.chip--dismissible{padding-right:8px}.chip--lg.chip--dismissible{padding-right:12px}.chip-dismiss{width:24px;height:24px;position:relative}.chip-dismiss:after{content:\"\";position:absolute;inset:-10px}}@media(max-width:480px)and (pointer:fine){.chip-dismiss{position:relative}.chip-dismiss:after{content:\"\";position:absolute;inset:-15px}}\n"]
24606
+ styles: [":host{display:contents}.chip{display:inline-flex;align-items:center;gap:5px;border-radius:999px;border:1.5px solid #d1d5db;background:#f9fafb;color:#374151;font-weight:500;font-family:inherit;cursor:pointer;-webkit-user-select:none;user-select:none;white-space:nowrap;transition:background .15s,border-color .15s,color .15s}.chip:hover:not(:disabled):not(.chip--disabled){border-color:#3b82f6;color:#1d4ed8;background:#eff6ff}.chip:focus-visible,.chip:has(:focus-visible){outline:2px solid #3b82f6;outline-offset:2px}.chip:disabled,.chip.chip--disabled{opacity:.5;cursor:default}.chip--sm{padding:2px 10px;font-size:.75rem}.chip--lg{padding:6px 16px;font-size:.875rem}.chip--dismissible{padding:0;gap:0}.chip-body{display:inline-flex;align-items:center;align-self:stretch;background:none;border:0;margin:0;color:inherit;font:inherit;cursor:pointer;border-radius:999px 0 0 999px}.chip-body:focus-visible{outline:2px solid currentColor;outline-offset:-2px}.chip-body:disabled{cursor:default}.chip--sm .chip-body{padding:2px 4px 2px 10px}.chip--lg .chip-body{padding:6px 6px 6px 16px}.chip--sm.chip--dismissible .chip-dismiss{margin-right:6px}.chip--lg.chip--dismissible .chip-dismiss{margin-right:10px}.chip--active{background:#2563eb;border-color:#2563eb;color:#fff}.chip--active:hover:not(:disabled){background:#1d4ed8;border-color:#1d4ed8;color:#fff}.chip-dismiss{display:inline-flex;align-items:center;justify-content:center;width:14px;height:14px;border-radius:50%;border:0;padding:0;color:inherit;cursor:pointer;background:#0000001f;flex-shrink:0;transition:background .15s}.chip-dismiss:hover{background:#00000040}.chip-dismiss:focus-visible{outline:2px solid currentColor;outline-offset:1px}.chip--active .chip-dismiss{background:#ffffff4d}.chip--active .chip-dismiss:hover{background:#ffffff80}@media(pointer:coarse){.chip{min-height:44px}.chip--sm{min-height:40px;padding:6px 14px}.chip--lg{padding-left:18px;padding-right:18px}.chip--sm .chip-body{padding:6px 4px 6px 14px}.chip--lg .chip-body{padding-left:18px}.chip--sm.chip--dismissible .chip-dismiss{margin-right:8px}.chip--lg.chip--dismissible .chip-dismiss{margin-right:12px}.chip-dismiss{width:24px;height:24px;position:relative}.chip-dismiss:after{content:\"\";position:absolute;inset:-10px}}@media(max-width:480px)and (pointer:fine){.chip-dismiss{position:relative}.chip-dismiss:after{content:\"\";position:absolute;inset:-15px}}\n"]
24398
24607
  }]
24399
24608
  }],
24400
24609
  propDecorators: {
@@ -25142,12 +25351,17 @@ var DropdownComponent = class DropdownComponent {
25142
25351
  this.menuWidth = input("200px", ...ngDevMode ? [{ debugName: "menuWidth" }] : /* istanbul ignore next */ []);
25143
25352
  this.ariaLabel = input("", ...ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []);
25144
25353
  this.itemClick = output();
25354
+ this.align = input("auto", ...ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []);
25145
25355
  this.open = signal(false, ...ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []);
25146
25356
  this.loading = signal(false, ...ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []);
25357
+ this.alignEnd = signal(false, ...ngDevMode ? [{ debugName: "alignEnd" }] : /* istanbul ignore next */ []);
25358
+ this.openUp = signal(false, ...ngDevMode ? [{ debugName: "openUp" }] : /* istanbul ignore next */ []);
25359
+ this.menuMaxHeight = signal(null, ...ngDevMode ? [{ debugName: "menuMaxHeight" }] : /* istanbul ignore next */ []);
25147
25360
  this._loaded = signal(null, ...ngDevMode ? [{ debugName: "_loaded" }] : /* istanbul ignore next */ []);
25148
25361
  this._id = Math.random().toString(36).slice(2);
25149
25362
  this._host = inject(ElementRef);
25150
25363
  this._cdr = inject(ChangeDetectorRef);
25364
+ this._injector = inject(Injector);
25151
25365
  this._pendingFocus = null;
25152
25366
  this._typeahead = "";
25153
25367
  this._typeaheadTimer = null;
@@ -25261,13 +25475,45 @@ var DropdownComponent = class DropdownComponent {
25261
25475
  _setOpen(next) {
25262
25476
  if (this.open() === next) return;
25263
25477
  this.open.set(next);
25264
- if (next) _activeDropdownId.set(this._id);
25265
- else {
25478
+ if (next) {
25479
+ _activeDropdownId.set(this._id);
25480
+ afterNextRender(() => this._place(), { injector: this._injector });
25481
+ } else {
25266
25482
  this._pendingFocus = null;
25267
25483
  this._typeahead = "";
25268
25484
  if (_activeDropdownId() === this._id) _activeDropdownId.set("");
25485
+ this.alignEnd.set(false);
25486
+ this.openUp.set(false);
25487
+ this.menuMaxHeight.set(null);
25269
25488
  }
25270
25489
  }
25490
+ _place() {
25491
+ const host = this._host.nativeElement;
25492
+ const trigger = host.querySelector(".dd-trigger");
25493
+ const menu = host.querySelector(".dd-menu");
25494
+ if (!trigger || !menu || typeof window === "undefined") return;
25495
+ const MARGIN = 16, GAP = 6;
25496
+ this.alignEnd.set(false);
25497
+ this.openUp.set(false);
25498
+ this.menuMaxHeight.set(null);
25499
+ this._cdr.detectChanges();
25500
+ const t = trigger.getBoundingClientRect();
25501
+ const vw = document.documentElement.clientWidth || window.innerWidth;
25502
+ const vh = window.innerHeight;
25503
+ const width = menu.offsetWidth;
25504
+ const naturalHeight = menu.scrollHeight + 2;
25505
+ const mode = this.align();
25506
+ const fitsStart = t.left + width <= vw - MARGIN;
25507
+ const fitsEnd = t.right - width >= MARGIN;
25508
+ const end = mode === "end" || mode === "auto" && !fitsStart && (fitsEnd || t.right > vw / 2);
25509
+ const below = vh - t.bottom - GAP - MARGIN;
25510
+ const above = t.top - GAP - MARGIN;
25511
+ const up = naturalHeight > below && above > below;
25512
+ this.alignEnd.set(end);
25513
+ this.openUp.set(up);
25514
+ this.menuMaxHeight.set(Math.max(160, Math.floor(up ? above : below)));
25515
+ this._cdr.markForCheck();
25516
+ }
25271
25517
  _openAndFocus(which) {
25272
25518
  this._pendingFocus = which;
25273
25519
  this._setOpen(true);
@@ -25378,6 +25624,13 @@ var DropdownComponent = class DropdownComponent {
25378
25624
  isSignal: true,
25379
25625
  isRequired: false,
25380
25626
  transformFunction: null
25627
+ },
25628
+ align: {
25629
+ classPropertyName: "align",
25630
+ publicName: "align",
25631
+ isSignal: true,
25632
+ isRequired: false,
25633
+ transformFunction: null
25381
25634
  }
25382
25635
  },
25383
25636
  outputs: { itemClick: "itemClick" },
@@ -25389,18 +25642,18 @@ var DropdownComponent = class DropdownComponent {
25389
25642
  template: `
25390
25643
  <div class="dd-wrapper">
25391
25644
  @if (variant() === 'badge') {
25392
- <button class="dd-trigger dd-trigger-badge" [class.dd-active]="active()" [attr.id]="triggerId" [attr.aria-label]="_ariaLabel()" [attr.aria-expanded]="open()" aria-haspopup="menu" [attr.aria-controls]="open() ? menuId : null" (click)="toggle($event)" (keydown)="onTriggerKeydown($event)" (keyup)="onTriggerKeyup($event)">
25645
+ <button class="dd-trigger dd-trigger-badge" [class.dd-active]="active()" [class.dd-open]="open()" [attr.id]="triggerId" [attr.aria-label]="_ariaLabel()" [attr.aria-expanded]="open()" aria-haspopup="menu" [attr.aria-controls]="open() ? menuId : null" (click)="toggle($event)" (keydown)="onTriggerKeydown($event)" (keyup)="onTriggerKeyup($event)">
25393
25646
  {{ label() }}
25394
- <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
25647
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
25395
25648
  </button>
25396
25649
  } @else {
25397
- <button class="dd-trigger dd-trigger-btn" [class.dd-active]="active()" [attr.id]="triggerId" [attr.aria-label]="_ariaLabel()" [attr.aria-expanded]="open()" aria-haspopup="menu" [attr.aria-controls]="open() ? menuId : null" (click)="toggle($event)" (keydown)="onTriggerKeydown($event)" (keyup)="onTriggerKeyup($event)">
25650
+ <button class="dd-trigger dd-trigger-btn" [class.dd-active]="active()" [class.dd-open]="open()" [attr.id]="triggerId" [attr.aria-label]="_ariaLabel()" [attr.aria-expanded]="open()" aria-haspopup="menu" [attr.aria-controls]="open() ? menuId : null" (click)="toggle($event)" (keydown)="onTriggerKeydown($event)" (keyup)="onTriggerKeyup($event)">
25398
25651
  {{ label() }}
25399
- <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
25652
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
25400
25653
  </button>
25401
25654
  }
25402
25655
  @if (open()) {
25403
- <div class="dd-menu" role="menu" [attr.id]="menuId" [attr.aria-label]="_ariaLabel()" [attr.aria-labelledby]="_ariaLabel() ? null : triggerId" [style.min-width]="menuWidth()" (click)="$event.stopPropagation()" (keydown)="onMenuKeydown($event)">
25656
+ <div class="dd-menu" role="menu" [attr.id]="menuId" [attr.aria-label]="_ariaLabel()" [attr.aria-labelledby]="_ariaLabel() ? null : triggerId" [style.min-width]="menuWidth()" [class.dd-menu-end]="alignEnd()" [class.dd-menu-up]="openUp()" [style.max-height.px]="menuMaxHeight()" (click)="$event.stopPropagation()" (keydown)="onMenuKeydown($event)">
25404
25657
  @if (loading()) {
25405
25658
  <div class="dd-loading">
25406
25659
  <span class="dd-spinner"></span>
@@ -25409,7 +25662,10 @@ var DropdownComponent = class DropdownComponent {
25409
25662
  } @else {
25410
25663
  @for (item of displayItems(); track item.value) {
25411
25664
  <button type="button" class="dd-item" role="menuitem" tabindex="-1" [disabled]="item.disabled === true" [class.dd-item-active]="activeValue() === item.value" (click)="select(item)">
25412
- <span>{{ item.label }}</span>
25665
+ @if (item.icon) {
25666
+ <svg class="dd-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path [attr.d]="item.icon" /></svg>
25667
+ }
25668
+ <span class="dd-label">{{ item.label }}</span>
25413
25669
  @if (item.count !== undefined) {
25414
25670
  <span class="dd-count">{{ item.count }}</span>
25415
25671
  }
@@ -25421,7 +25677,7 @@ var DropdownComponent = class DropdownComponent {
25421
25677
  </div>
25422
25678
  `,
25423
25679
  isInline: true,
25424
- styles: [":host{display:inline-flex}.dd-wrapper{position:relative;display:inline-flex}.dd-trigger{display:inline-flex;align-items:center;gap:6px;cursor:pointer;font-family:inherit;transition:all .15s ease}.dd-trigger:focus-visible{outline:2px solid #60a5fa;outline-offset:2px}.dd-trigger-badge{padding:6px 14px;border-radius:999px;border:1px solid #e2e8f0;background:#fff;font-size:.8125rem;font-weight:500;color:#64748b}.dd-trigger-badge:hover{background:#f8fafc;border-color:#94a3b8}.dd-trigger-badge.dd-active{background:#1e293b;color:#fff;border-color:#1e293b}.dd-trigger-btn{padding:6px 12px;border-radius:8px;border:1px solid #e2e8f0;background:#fff;font-size:.8125rem;font-weight:500;color:#334155}.dd-trigger-btn:hover{background:#f8fafc;border-color:#94a3b8}.dd-trigger-btn.dd-active{background:#1e293b;color:#fff;border-color:#1e293b}.dd-menu{position:absolute;top:calc(100% + 6px);left:0;z-index:9999;background:#fff;border:1px solid #e2e8f0;border-radius:8px;box-shadow:0 4px 16px #0000001f;max-height:300px;overflow-y:auto;padding:4px;max-width:calc(100vw - 32px)}.dd-item{display:flex;align-items:center;justify-content:space-between;width:100%;padding:10px 12px;border:none;background:none;font-size:.8125rem;font-weight:500;color:#374151;cursor:pointer;font-family:inherit;border-radius:6px;text-align:left;transition:background .1s;border-bottom:1px solid #f1f5f9;min-height:44px}.dd-item:last-child{border-bottom:none}.dd-item:hover{background:#f8fafc}.dd-item:focus-visible{outline:2px solid #60a5fa;outline-offset:-2px}.dd-item.dd-item-active{background:#eff6ff;color:#2563eb}.dd-item:disabled{opacity:.5;cursor:not-allowed;background:none}.dd-count{font-size:.6875rem;font-weight:700;background:#f1f5f9;color:#64748b;padding:1px 7px;border-radius:999px;flex-shrink:0}.dd-loading{display:flex;align-items:center;gap:8px;padding:12px;color:#64748b;font-size:.8125rem}.dd-spinner{width:14px;height:14px;border-radius:50%;flex-shrink:0;border:2px solid #e2e8f0;border-top-color:#64748b;animation:dd-spin .6s linear infinite}@keyframes dd-spin{to{transform:rotate(360deg)}}@media(max-width:768px){.dd-trigger-badge,.dd-trigger-btn{min-height:44px}.dd-menu{max-height:50vh;left:auto;right:0}}@media(prefers-reduced-motion:reduce){.dd-spinner{animation-duration:2s}}\n"],
25680
+ styles: [":host{display:inline-flex}.dd-wrapper{position:relative;display:inline-flex}.dd-trigger{display:inline-flex;align-items:center;gap:6px;cursor:pointer;font-family:inherit;transition:all .15s ease}.dd-trigger:focus-visible{outline:2px solid #60a5fa;outline-offset:2px}.dd-trigger-badge{padding:6px 14px;border-radius:999px;border:1px solid #e2e8f0;background:#fff;font-size:.8125rem;font-weight:500;color:#64748b}.dd-trigger-badge:hover{background:#f8fafc;border-color:#94a3b8}.dd-trigger-badge.dd-active{background:#1e293b;color:#fff;border-color:#1e293b}.dd-trigger-btn{padding:6px 10px;border-radius:6px;border:1px solid #e2e8f0;background:#fff;font-size:.8125rem;font-weight:500;color:#334155;transition:background .1s,border-color .1s}.dd-trigger-btn:hover{background:#f8fafc}.dd-trigger-btn.dd-open{border-color:#1e293b;background:#f8fafc}.dd-trigger-btn.dd-active{background:#1e293b;color:#fff;border-color:#1e293b}.dd-menu{position:absolute;top:calc(100% + 6px);left:0;z-index:9999;background:#fff;border:1px solid #e2e8f0;border-radius:10px;box-shadow:0 4px 6px #0000000a,0 12px 28px #0000001a;max-height:70vh;overflow-y:auto;overflow-x:hidden;padding:0;max-width:calc(100vw - 32px);box-sizing:border-box}.dd-menu.dd-menu-end{left:auto;right:0}.dd-menu.dd-menu-up{top:auto;bottom:calc(100% + 6px)}.dd-item{display:flex;align-items:center;gap:10px;width:100%;padding:10px 14px;border:none;background:none;font-size:.875rem;font-weight:400;color:#1e293b;cursor:pointer;font-family:inherit;text-align:left;transition:background .1s;border-bottom:1px solid #f1f5f9;box-sizing:border-box}.dd-item:last-child{border-bottom:none}.dd-item:hover{background:#f8fafc}.dd-item:focus-visible{outline:2px solid #3b82f6;outline-offset:-2px}.dd-item.dd-item-active{background:#eff6ff;color:#2563eb}.dd-item:disabled{opacity:.5;cursor:not-allowed;background:none}.dd-icon{flex-shrink:0;color:#64748b}.dd-label{min-width:0}.dd-count{font-size:.6875rem;font-weight:700;margin-left:auto;background:#f1f5f9;color:#64748b;padding:1px 7px;border-radius:999px;flex-shrink:0}.dd-loading{display:flex;align-items:center;gap:8px;padding:12px;color:#64748b;font-size:.8125rem}.dd-spinner{width:14px;height:14px;border-radius:50%;flex-shrink:0;border:2px solid #e2e8f0;border-top-color:#64748b;animation:dd-spin .6s linear infinite}@keyframes dd-spin{to{transform:rotate(360deg)}}@media(pointer:coarse),(max-width:768px){.dd-trigger-badge,.dd-trigger-btn,.dd-item{min-height:44px}}@media(prefers-reduced-motion:reduce){.dd-spinner{animation-duration:2s}}\n"],
25425
25681
  changeDetection: i0.ChangeDetectionStrategy.OnPush
25426
25682
  });
25427
25683
  }
@@ -25444,18 +25700,18 @@ i0.ɵɵngDeclareClassMetadata({
25444
25700
  template: `
25445
25701
  <div class="dd-wrapper">
25446
25702
  @if (variant() === 'badge') {
25447
- <button class="dd-trigger dd-trigger-badge" [class.dd-active]="active()" [attr.id]="triggerId" [attr.aria-label]="_ariaLabel()" [attr.aria-expanded]="open()" aria-haspopup="menu" [attr.aria-controls]="open() ? menuId : null" (click)="toggle($event)" (keydown)="onTriggerKeydown($event)" (keyup)="onTriggerKeyup($event)">
25703
+ <button class="dd-trigger dd-trigger-badge" [class.dd-active]="active()" [class.dd-open]="open()" [attr.id]="triggerId" [attr.aria-label]="_ariaLabel()" [attr.aria-expanded]="open()" aria-haspopup="menu" [attr.aria-controls]="open() ? menuId : null" (click)="toggle($event)" (keydown)="onTriggerKeydown($event)" (keyup)="onTriggerKeyup($event)">
25448
25704
  {{ label() }}
25449
- <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
25705
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
25450
25706
  </button>
25451
25707
  } @else {
25452
- <button class="dd-trigger dd-trigger-btn" [class.dd-active]="active()" [attr.id]="triggerId" [attr.aria-label]="_ariaLabel()" [attr.aria-expanded]="open()" aria-haspopup="menu" [attr.aria-controls]="open() ? menuId : null" (click)="toggle($event)" (keydown)="onTriggerKeydown($event)" (keyup)="onTriggerKeyup($event)">
25708
+ <button class="dd-trigger dd-trigger-btn" [class.dd-active]="active()" [class.dd-open]="open()" [attr.id]="triggerId" [attr.aria-label]="_ariaLabel()" [attr.aria-expanded]="open()" aria-haspopup="menu" [attr.aria-controls]="open() ? menuId : null" (click)="toggle($event)" (keydown)="onTriggerKeydown($event)" (keyup)="onTriggerKeyup($event)">
25453
25709
  {{ label() }}
25454
- <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
25710
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg>
25455
25711
  </button>
25456
25712
  }
25457
25713
  @if (open()) {
25458
- <div class="dd-menu" role="menu" [attr.id]="menuId" [attr.aria-label]="_ariaLabel()" [attr.aria-labelledby]="_ariaLabel() ? null : triggerId" [style.min-width]="menuWidth()" (click)="$event.stopPropagation()" (keydown)="onMenuKeydown($event)">
25714
+ <div class="dd-menu" role="menu" [attr.id]="menuId" [attr.aria-label]="_ariaLabel()" [attr.aria-labelledby]="_ariaLabel() ? null : triggerId" [style.min-width]="menuWidth()" [class.dd-menu-end]="alignEnd()" [class.dd-menu-up]="openUp()" [style.max-height.px]="menuMaxHeight()" (click)="$event.stopPropagation()" (keydown)="onMenuKeydown($event)">
25459
25715
  @if (loading()) {
25460
25716
  <div class="dd-loading">
25461
25717
  <span class="dd-spinner"></span>
@@ -25464,7 +25720,10 @@ i0.ɵɵngDeclareClassMetadata({
25464
25720
  } @else {
25465
25721
  @for (item of displayItems(); track item.value) {
25466
25722
  <button type="button" class="dd-item" role="menuitem" tabindex="-1" [disabled]="item.disabled === true" [class.dd-item-active]="activeValue() === item.value" (click)="select(item)">
25467
- <span>{{ item.label }}</span>
25723
+ @if (item.icon) {
25724
+ <svg class="dd-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path [attr.d]="item.icon" /></svg>
25725
+ }
25726
+ <span class="dd-label">{{ item.label }}</span>
25468
25727
  @if (item.count !== undefined) {
25469
25728
  <span class="dd-count">{{ item.count }}</span>
25470
25729
  }
@@ -25475,7 +25734,7 @@ i0.ɵɵngDeclareClassMetadata({
25475
25734
  }
25476
25735
  </div>
25477
25736
  `,
25478
- styles: [":host{display:inline-flex}.dd-wrapper{position:relative;display:inline-flex}.dd-trigger{display:inline-flex;align-items:center;gap:6px;cursor:pointer;font-family:inherit;transition:all .15s ease}.dd-trigger:focus-visible{outline:2px solid #60a5fa;outline-offset:2px}.dd-trigger-badge{padding:6px 14px;border-radius:999px;border:1px solid #e2e8f0;background:#fff;font-size:.8125rem;font-weight:500;color:#64748b}.dd-trigger-badge:hover{background:#f8fafc;border-color:#94a3b8}.dd-trigger-badge.dd-active{background:#1e293b;color:#fff;border-color:#1e293b}.dd-trigger-btn{padding:6px 12px;border-radius:8px;border:1px solid #e2e8f0;background:#fff;font-size:.8125rem;font-weight:500;color:#334155}.dd-trigger-btn:hover{background:#f8fafc;border-color:#94a3b8}.dd-trigger-btn.dd-active{background:#1e293b;color:#fff;border-color:#1e293b}.dd-menu{position:absolute;top:calc(100% + 6px);left:0;z-index:9999;background:#fff;border:1px solid #e2e8f0;border-radius:8px;box-shadow:0 4px 16px #0000001f;max-height:300px;overflow-y:auto;padding:4px;max-width:calc(100vw - 32px)}.dd-item{display:flex;align-items:center;justify-content:space-between;width:100%;padding:10px 12px;border:none;background:none;font-size:.8125rem;font-weight:500;color:#374151;cursor:pointer;font-family:inherit;border-radius:6px;text-align:left;transition:background .1s;border-bottom:1px solid #f1f5f9;min-height:44px}.dd-item:last-child{border-bottom:none}.dd-item:hover{background:#f8fafc}.dd-item:focus-visible{outline:2px solid #60a5fa;outline-offset:-2px}.dd-item.dd-item-active{background:#eff6ff;color:#2563eb}.dd-item:disabled{opacity:.5;cursor:not-allowed;background:none}.dd-count{font-size:.6875rem;font-weight:700;background:#f1f5f9;color:#64748b;padding:1px 7px;border-radius:999px;flex-shrink:0}.dd-loading{display:flex;align-items:center;gap:8px;padding:12px;color:#64748b;font-size:.8125rem}.dd-spinner{width:14px;height:14px;border-radius:50%;flex-shrink:0;border:2px solid #e2e8f0;border-top-color:#64748b;animation:dd-spin .6s linear infinite}@keyframes dd-spin{to{transform:rotate(360deg)}}@media(max-width:768px){.dd-trigger-badge,.dd-trigger-btn{min-height:44px}.dd-menu{max-height:50vh;left:auto;right:0}}@media(prefers-reduced-motion:reduce){.dd-spinner{animation-duration:2s}}\n"]
25737
+ styles: [":host{display:inline-flex}.dd-wrapper{position:relative;display:inline-flex}.dd-trigger{display:inline-flex;align-items:center;gap:6px;cursor:pointer;font-family:inherit;transition:all .15s ease}.dd-trigger:focus-visible{outline:2px solid #60a5fa;outline-offset:2px}.dd-trigger-badge{padding:6px 14px;border-radius:999px;border:1px solid #e2e8f0;background:#fff;font-size:.8125rem;font-weight:500;color:#64748b}.dd-trigger-badge:hover{background:#f8fafc;border-color:#94a3b8}.dd-trigger-badge.dd-active{background:#1e293b;color:#fff;border-color:#1e293b}.dd-trigger-btn{padding:6px 10px;border-radius:6px;border:1px solid #e2e8f0;background:#fff;font-size:.8125rem;font-weight:500;color:#334155;transition:background .1s,border-color .1s}.dd-trigger-btn:hover{background:#f8fafc}.dd-trigger-btn.dd-open{border-color:#1e293b;background:#f8fafc}.dd-trigger-btn.dd-active{background:#1e293b;color:#fff;border-color:#1e293b}.dd-menu{position:absolute;top:calc(100% + 6px);left:0;z-index:9999;background:#fff;border:1px solid #e2e8f0;border-radius:10px;box-shadow:0 4px 6px #0000000a,0 12px 28px #0000001a;max-height:70vh;overflow-y:auto;overflow-x:hidden;padding:0;max-width:calc(100vw - 32px);box-sizing:border-box}.dd-menu.dd-menu-end{left:auto;right:0}.dd-menu.dd-menu-up{top:auto;bottom:calc(100% + 6px)}.dd-item{display:flex;align-items:center;gap:10px;width:100%;padding:10px 14px;border:none;background:none;font-size:.875rem;font-weight:400;color:#1e293b;cursor:pointer;font-family:inherit;text-align:left;transition:background .1s;border-bottom:1px solid #f1f5f9;box-sizing:border-box}.dd-item:last-child{border-bottom:none}.dd-item:hover{background:#f8fafc}.dd-item:focus-visible{outline:2px solid #3b82f6;outline-offset:-2px}.dd-item.dd-item-active{background:#eff6ff;color:#2563eb}.dd-item:disabled{opacity:.5;cursor:not-allowed;background:none}.dd-icon{flex-shrink:0;color:#64748b}.dd-label{min-width:0}.dd-count{font-size:.6875rem;font-weight:700;margin-left:auto;background:#f1f5f9;color:#64748b;padding:1px 7px;border-radius:999px;flex-shrink:0}.dd-loading{display:flex;align-items:center;gap:8px;padding:12px;color:#64748b;font-size:.8125rem}.dd-spinner{width:14px;height:14px;border-radius:50%;flex-shrink:0;border:2px solid #e2e8f0;border-top-color:#64748b;animation:dd-spin .6s linear infinite}@keyframes dd-spin{to{transform:rotate(360deg)}}@media(pointer:coarse),(max-width:768px){.dd-trigger-badge,.dd-trigger-btn,.dd-item{min-height:44px}}@media(prefers-reduced-motion:reduce){.dd-spinner{animation-duration:2s}}\n"]
25479
25738
  }]
25480
25739
  }],
25481
25740
  ctorParameters: () => [],
@@ -25547,6 +25806,14 @@ i0.ɵɵngDeclareClassMetadata({
25547
25806
  itemClick: [{
25548
25807
  type: i0.Output,
25549
25808
  args: ["itemClick"]
25809
+ }],
25810
+ align: [{
25811
+ type: i0.Input,
25812
+ args: [{
25813
+ isSignal: true,
25814
+ alias: "align",
25815
+ required: false
25816
+ }]
25550
25817
  }]
25551
25818
  }
25552
25819
  });
@@ -25728,7 +25995,7 @@ var FormSectionComponent = class FormSectionComponent {
25728
25995
  <div class="fs-fields"><ng-content /></div>
25729
25996
  `,
25730
25997
  isInline: true,
25731
- styles: [":host{display:block}.fs-divider{height:1px;background:#f1f5f9;margin:8px -24px 16px}.fs-label{font-size:.6875rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#94a3b8;margin:0 0 12px}.fs-fields{display:flex;flex-direction:column;gap:14px}\n"],
25998
+ styles: [":host{display:block}.fs-divider{height:1px;background:#f1f5f9;margin:8px -24px 16px}.fs-label{font-size:.6875rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#64748b;margin:0 0 12px}.fs-fields{display:flex;flex-direction:column;gap:14px}\n"],
25732
25999
  changeDetection: i0.ChangeDetectionStrategy.OnPush
25733
26000
  });
25734
26001
  }
@@ -25749,7 +26016,7 @@ i0.ɵɵngDeclareClassMetadata({
25749
26016
  @if (label()) { <p class="fs-label">{{ label() }}</p> }
25750
26017
  <div class="fs-fields"><ng-content /></div>
25751
26018
  `,
25752
- styles: [":host{display:block}.fs-divider{height:1px;background:#f1f5f9;margin:8px -24px 16px}.fs-label{font-size:.6875rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#94a3b8;margin:0 0 12px}.fs-fields{display:flex;flex-direction:column;gap:14px}\n"]
26019
+ styles: [":host{display:block}.fs-divider{height:1px;background:#f1f5f9;margin:8px -24px 16px}.fs-label{font-size:.6875rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:#64748b;margin:0 0 12px}.fs-fields{display:flex;flex-direction:column;gap:14px}\n"]
25753
26020
  }]
25754
26021
  }],
25755
26022
  propDecorators: {
@@ -42681,6 +42948,34 @@ const SETUP_INTEGRATIONS = {
42681
42948
  connectEndpoint: "/integrations/twilio/connect"
42682
42949
  }
42683
42950
  },
42951
+ stripe: {
42952
+ key: "stripe",
42953
+ letter: "S",
42954
+ color: "#635bff",
42955
+ title: "Stripe",
42956
+ icon: "bank",
42957
+ description: "Accept card and bank payments from your customers. Payouts go straight to your Stripe account.",
42958
+ statusEndpoint: "/integrations/stripe/status",
42959
+ connectedDetail: (s) => str(s["accountName"]) ? `Connected — ${str(s["accountName"])}` : "Connected",
42960
+ connect: {
42961
+ type: "oauth",
42962
+ authorizeEndpoint: "/integrations/stripe/connect"
42963
+ }
42964
+ },
42965
+ square: {
42966
+ key: "square",
42967
+ letter: "S",
42968
+ color: "#111827",
42969
+ title: "Square",
42970
+ icon: "bank",
42971
+ description: "Accept card and bank payments from your customers. Payouts go straight to your Square account.",
42972
+ statusEndpoint: "/integrations/square/status",
42973
+ connectedDetail: (s) => str(s["merchantId"]) ? `Connected — merchant ${str(s["merchantId"])}` : "Connected",
42974
+ connect: {
42975
+ type: "oauth",
42976
+ authorizeEndpoint: "/integrations/square/connect"
42977
+ }
42978
+ },
42684
42979
  gmail: {
42685
42980
  key: "gmail",
42686
42981
  letter: "G",
@@ -42885,7 +43180,7 @@ const DEFAULT_TEXTS = {
42885
43180
  importMore: "Import more",
42886
43181
  importRequired: "Import your data to continue, or skip this step.",
42887
43182
  importErrorsTitle: (errors) => `${errors} row${errors === 1 ? "" : "s"} could not be imported`,
42888
- importErrorsText: (imported, errors) => `${imported} imported. Fix the ${errors === 1 ? "row" : "rows"} listed in the import window in your file and import ${errors === 1 ? "it" : "them"} again (only the ${errors === 1 ? "row" : "rows"} that failed, so nothing is imported twice) to continue.`,
43183
+ importErrorsText: (_imported, errors) => `Nothing was imported yet. Fix the ${errors === 1 ? "row" : "rows"} listed in the import window in your file, then import the whole file again to continue.`,
42889
43184
  importAgain: "Import again",
42890
43185
  whatGetsImported: "WHAT GETS IMPORTED:",
42891
43186
  summaryTitle: "Your setup",
@@ -43044,6 +43339,9 @@ var SetupWizardComponent = class SetupWizardComponent {
43044
43339
  this.skipped.update((s) => new Set(s).add(key));
43045
43340
  this.advanceFrom(key);
43046
43341
  }
43342
+ completeSteps(...keys) {
43343
+ this.markStepsCompleted(keys);
43344
+ }
43047
43345
  markStepsCompleted(keys) {
43048
43346
  const known = new Set(this.steps().map((s) => s.key));
43049
43347
  for (const key of keys) if (known.has(key)) this.markCompleted(key);
@@ -43059,6 +43357,7 @@ var SetupWizardComponent = class SetupWizardComponent {
43059
43357
  async saveAndContinue() {
43060
43358
  if (this.busy()) return;
43061
43359
  const step = this.current();
43360
+ if (step.canContinue && !step.canContinue()) return;
43062
43361
  this.busy.set(true);
43063
43362
  this.error.set("");
43064
43363
  try {
@@ -43175,13 +43474,32 @@ var SetupWizardComponent = class SetupWizardComponent {
43175
43474
  this.areaCodeOpen.set(i.key);
43176
43475
  return;
43177
43476
  }
43477
+ this.busy.set(true);
43178
43478
  this.http.get(this.apiBase + i.connect.authorizeEndpoint).pipe(takeUntilDestroyed(this.destroyRef)).subscribe({
43179
43479
  next: (res) => {
43180
- if (res?.url) this.redirect(res.url);
43480
+ this.busy.set(false);
43481
+ const url = res?.url ?? res?.authUrl;
43482
+ if (!url) return;
43483
+ if (!this.isWebAddress(url)) {
43484
+ this.error.set(this.t().connectIntegrationFailed(i.title));
43485
+ return;
43486
+ }
43487
+ this.redirect(url);
43181
43488
  },
43182
- error: () => this.error.set(this.t().connectIntegrationFailed(i.title))
43489
+ error: () => {
43490
+ this.busy.set(false);
43491
+ this.error.set(this.t().connectIntegrationFailed(i.title));
43492
+ }
43183
43493
  });
43184
43494
  }
43495
+ isWebAddress(url) {
43496
+ try {
43497
+ const p = new URL(url, window.location.origin).protocol;
43498
+ return p === "https:" || p === "http:";
43499
+ } catch {
43500
+ return false;
43501
+ }
43502
+ }
43185
43503
  iconD(name) {
43186
43504
  return iconPath(name) ?? "";
43187
43505
  }
@@ -43323,7 +43641,7 @@ var SetupWizardComponent = class SetupWizardComponent {
43323
43641
  </ui-button>
43324
43642
  }
43325
43643
  } @else if (current().kind === 'integrations') {
43326
- <ui-button variant="primary" size="sm" [fullWidth]="narrow()" data-testid="wizard-save" [loading]="busy()" (click)="saveAndContinue()">{{ t().saveAndContinue }} <svg class="sw-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path [attr.d]="iconD('arrow-right')" /></svg></ui-button>
43644
+ <ui-button variant="primary" size="sm" [fullWidth]="narrow()" data-testid="wizard-save" [loading]="busy()" [disabled]="current().canContinue ? !current().canContinue!() : false" (click)="saveAndContinue()">{{ t().saveAndContinue }} <svg class="sw-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path [attr.d]="iconD('arrow-right')" /></svg></ui-button>
43327
43645
  } @else if (current().kind === 'import') {
43328
43646
  <ui-button variant="primary" size="sm" [fullWidth]="narrow()" data-testid="wizard-continue" [disabled]="importBlocked()" [ariaDescribedBy]="importBlocked() ? 'sw-continue-hint' : ''" (click)="continueFromImport()">{{ t().continue }} <svg class="sw-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path [attr.d]="iconD('arrow-right')" /></svg></ui-button>
43329
43647
  }
@@ -43337,6 +43655,7 @@ var SetupWizardComponent = class SetupWizardComponent {
43337
43655
  [ariaLabel]="current().extraTitle || current().label" headerBackground="rgb(248, 250, 252)" footerBackground="rgb(248, 250, 252)" data-testid="wizard-extra-card">
43338
43656
  <ng-container [ngTemplateOutlet]="left" [ngTemplateOutletContext]="{ $implicit: api }" />
43339
43657
  <p class="sw-extra-status" role="status" data-testid="wizard-extra-status">{{ current().extraFooter?.() }}</p>
43658
+ @if (error()) { <ui-alert variant="danger" data-testid="wizard-save-error">{{ error() }}</ui-alert> }
43340
43659
  @if (importBlocked()) { <p class="sw-hint sw-continue-hint" id="sw-continue-hint" data-testid="wizard-continue-hint">{{ t().importRequired }}</p> }
43341
43660
  <div card-footer class="sw-step-actions"><ng-container [ngTemplateOutlet]="actions" /></div>
43342
43661
  </ui-detail-card>
@@ -43384,7 +43703,7 @@ var SetupWizardComponent = class SetupWizardComponent {
43384
43703
  }
43385
43704
  }
43386
43705
  @case ('integrations') {
43387
- <div class="sw-provider-grid">
43706
+ <div class="sw-provider-grid" [class.sw-provider-grid--single]="(current().integrations?.length ?? 0) <= 1" data-testid="wizard-provider-grid">
43388
43707
  @for (it of current().integrations ?? []; track it.key) {
43389
43708
  <ui-option-card class="sw-integration" [attr.data-testid]="'integration-' + it.key" [title]="it.title" [description]="detailFor(it)" [letter]="it.letter || ''"
43390
43709
  [icon]="it.letter ? '' : (it.icon ?? 'link')" [color]="it.color || '#64748b'" [actionLabel]="isConnected(it) ? t().reconnect : t().connect"
@@ -43414,7 +43733,7 @@ var SetupWizardComponent = class SetupWizardComponent {
43414
43733
  </div>
43415
43734
  `,
43416
43735
  isInline: true,
43417
- styles: [".sw-page{display:flex;min-height:100vh;min-height:100dvh;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.sw-sidebar{width:260px;min-width:260px;background:#faf9f7;border-right:1px solid #e5e2dc;display:flex;flex-direction:column;justify-content:space-between;padding:28px 20px}.sw-brand{display:flex;align-items:center;gap:10px;margin-bottom:28px}.sw-brand-text{font-size:1.125rem;font-weight:700;color:#1e293b}.sw-step-nav{display:block}.sw-sidebar-bottom{margin-top:24px}.sw-progress-text{display:block;margin-top:8px;font-size:.75rem;color:#94a3b8}.sw-content{flex:1;min-width:0;background:#fff;display:flex;flex-direction:column;overflow-y:auto}.sw-content-header{display:flex;justify-content:space-between;align-items:center;gap:16px;padding:20px 32px;border-bottom:1px solid #f1f5f9}.sw-step-title{font-size:1.25rem;font-weight:700;color:#111827;margin:0}.sw-step-content{max-width:780px;padding:16px 48px 60px}.sw-intro{margin-bottom:24px}.sw-step-number{font-size:1.25rem;font-weight:800;line-height:1}.sw-split-tabs{display:block;margin-bottom:16px}.sw-hint.sw-continue-hint{margin:14px 0 0}.sw-extra-status{font-size:.8125rem;color:#64748b;margin:12px 0 0}.sw-extra-status:empty{display:none}.sw-hint{font-size:.8125rem;line-height:1.5;color:#64748b;margin:0 0 16px}.sw-import-tags{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:28px}.sw-tag{font-size:.6875rem;font-weight:700;letter-spacing:1px;color:#6b7280}.sw-provider-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:10px;margin-bottom:8px}@media(max-width:768px){.sw-provider-grid{grid-template-columns:1fr}}.sw-provider-grid .sw-integration{margin-bottom:0}.sw-area-code{display:flex;align-items:flex-end;gap:12px;margin-top:12px;max-width:320px}.sw-area-code ui-text-input{flex:1}.sw-content ui-alert{display:block;margin-bottom:16px}.sw-card{display:block}.sw-header-action{margin-left:auto;min-width:0;max-width:100%}.sw-step-actions{display:flex;justify-content:space-between;align-items:center}.sw-ico{flex-shrink:0;vertical-align:-2px}.sw-actions-right{display:flex;align-items:center;gap:16px;margin-left:auto}.sw-summary{display:block}.sw-done-hint{margin:16px 0 0}@media(max-width:768px){.sw-step-actions{flex-direction:column-reverse;align-items:stretch;gap:10px}.sw-actions-right{flex-direction:column-reverse;align-items:stretch;gap:10px;margin-left:0}.sw-page{flex-direction:column}.sw-sidebar{width:100%;min-width:unset;padding:16px}.sw-brand{margin-bottom:12px}.sw-sidebar-bottom{display:none}.sw-step-content{padding:12px 20px 40px}}\n"],
43736
+ styles: [".sw-page{display:flex;min-height:100vh;min-height:100dvh;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.sw-sidebar{width:260px;min-width:260px;background:#faf9f7;border-right:1px solid #e5e2dc;display:flex;flex-direction:column;justify-content:space-between;padding:28px 20px}.sw-brand{display:flex;align-items:center;gap:10px;margin-bottom:28px}.sw-brand-text{font-size:1.125rem;font-weight:700;color:#1e293b}.sw-step-nav{display:block}.sw-sidebar-bottom{margin-top:24px}.sw-progress-text{display:block;margin-top:8px;font-size:.75rem;color:#64748b}.sw-content{flex:1;min-width:0;background:#fff;display:flex;flex-direction:column;overflow-y:auto}.sw-content-header{display:flex;justify-content:space-between;align-items:center;gap:16px;padding:20px 32px;border-bottom:1px solid #f1f5f9}.sw-step-title{font-size:1.25rem;font-weight:700;color:#111827;margin:0}.sw-step-content{max-width:780px;padding:16px 48px 60px}.sw-intro{margin-bottom:24px}.sw-step-number{font-size:1.25rem;font-weight:800;line-height:1}.sw-split-tabs{display:block;margin-bottom:16px}.sw-hint.sw-continue-hint{margin:14px 0 0}.sw-extra-status{font-size:.8125rem;color:#64748b;margin:12px 0 0}.sw-extra-status:empty{display:none}.sw-hint{font-size:.8125rem;line-height:1.5;color:#64748b;margin:0 0 16px}.sw-import-tags{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:28px}.sw-tag{font-size:.6875rem;font-weight:700;letter-spacing:1px;color:#6b7280}.sw-provider-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:10px;margin-bottom:8px}.sw-provider-grid--single{grid-template-columns:1fr}@media(max-width:768px){.sw-provider-grid{grid-template-columns:1fr}}.sw-provider-grid .sw-integration{margin-bottom:0}.sw-area-code{display:flex;align-items:flex-end;gap:12px;margin-top:12px;max-width:320px}.sw-area-code ui-text-input{flex:1}.sw-content ui-alert{display:block;margin-bottom:16px}.sw-card{display:block}.sw-header-action{margin-left:auto;min-width:0;max-width:100%}.sw-step-actions{display:flex;justify-content:space-between;align-items:center}.sw-ico{flex-shrink:0;vertical-align:-2px}.sw-actions-right{display:flex;align-items:center;gap:16px;margin-left:auto}.sw-summary{display:block}.sw-done-hint{margin:16px 0 0}@media(max-width:768px){.sw-step-actions{flex-direction:column-reverse;align-items:stretch;gap:10px}.sw-actions-right{flex-direction:column-reverse;align-items:stretch;gap:10px;margin-left:0}.sw-page{flex-direction:column}.sw-sidebar{width:100%;min-width:unset;padding:16px}.sw-brand{margin-bottom:12px}.sw-sidebar-bottom{display:none}.sw-step-content{padding:12px 20px 40px}}\n"],
43418
43737
  dependencies: [
43419
43738
  {
43420
43739
  kind: "directive",
@@ -43541,6 +43860,7 @@ var SetupWizardComponent = class SetupWizardComponent {
43541
43860
  "editIcon",
43542
43861
  "editAriaLabel",
43543
43862
  "fill",
43863
+ "maxBodyHeight",
43544
43864
  "selectable",
43545
43865
  "selected",
43546
43866
  "selectedColor",
@@ -43797,7 +44117,7 @@ i0.ɵɵngDeclareClassMetadata({
43797
44117
  </ui-button>
43798
44118
  }
43799
44119
  } @else if (current().kind === 'integrations') {
43800
- <ui-button variant="primary" size="sm" [fullWidth]="narrow()" data-testid="wizard-save" [loading]="busy()" (click)="saveAndContinue()">{{ t().saveAndContinue }} <svg class="sw-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path [attr.d]="iconD('arrow-right')" /></svg></ui-button>
44120
+ <ui-button variant="primary" size="sm" [fullWidth]="narrow()" data-testid="wizard-save" [loading]="busy()" [disabled]="current().canContinue ? !current().canContinue!() : false" (click)="saveAndContinue()">{{ t().saveAndContinue }} <svg class="sw-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path [attr.d]="iconD('arrow-right')" /></svg></ui-button>
43801
44121
  } @else if (current().kind === 'import') {
43802
44122
  <ui-button variant="primary" size="sm" [fullWidth]="narrow()" data-testid="wizard-continue" [disabled]="importBlocked()" [ariaDescribedBy]="importBlocked() ? 'sw-continue-hint' : ''" (click)="continueFromImport()">{{ t().continue }} <svg class="sw-ico" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path [attr.d]="iconD('arrow-right')" /></svg></ui-button>
43803
44123
  }
@@ -43811,6 +44131,7 @@ i0.ɵɵngDeclareClassMetadata({
43811
44131
  [ariaLabel]="current().extraTitle || current().label" headerBackground="rgb(248, 250, 252)" footerBackground="rgb(248, 250, 252)" data-testid="wizard-extra-card">
43812
44132
  <ng-container [ngTemplateOutlet]="left" [ngTemplateOutletContext]="{ $implicit: api }" />
43813
44133
  <p class="sw-extra-status" role="status" data-testid="wizard-extra-status">{{ current().extraFooter?.() }}</p>
44134
+ @if (error()) { <ui-alert variant="danger" data-testid="wizard-save-error">{{ error() }}</ui-alert> }
43814
44135
  @if (importBlocked()) { <p class="sw-hint sw-continue-hint" id="sw-continue-hint" data-testid="wizard-continue-hint">{{ t().importRequired }}</p> }
43815
44136
  <div card-footer class="sw-step-actions"><ng-container [ngTemplateOutlet]="actions" /></div>
43816
44137
  </ui-detail-card>
@@ -43858,7 +44179,7 @@ i0.ɵɵngDeclareClassMetadata({
43858
44179
  }
43859
44180
  }
43860
44181
  @case ('integrations') {
43861
- <div class="sw-provider-grid">
44182
+ <div class="sw-provider-grid" [class.sw-provider-grid--single]="(current().integrations?.length ?? 0) <= 1" data-testid="wizard-provider-grid">
43862
44183
  @for (it of current().integrations ?? []; track it.key) {
43863
44184
  <ui-option-card class="sw-integration" [attr.data-testid]="'integration-' + it.key" [title]="it.title" [description]="detailFor(it)" [letter]="it.letter || ''"
43864
44185
  [icon]="it.letter ? '' : (it.icon ?? 'link')" [color]="it.color || '#64748b'" [actionLabel]="isConnected(it) ? t().reconnect : t().connect"
@@ -43887,7 +44208,7 @@ i0.ɵɵngDeclareClassMetadata({
43887
44208
  </main>
43888
44209
  </div>
43889
44210
  `,
43890
- styles: [".sw-page{display:flex;min-height:100vh;min-height:100dvh;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.sw-sidebar{width:260px;min-width:260px;background:#faf9f7;border-right:1px solid #e5e2dc;display:flex;flex-direction:column;justify-content:space-between;padding:28px 20px}.sw-brand{display:flex;align-items:center;gap:10px;margin-bottom:28px}.sw-brand-text{font-size:1.125rem;font-weight:700;color:#1e293b}.sw-step-nav{display:block}.sw-sidebar-bottom{margin-top:24px}.sw-progress-text{display:block;margin-top:8px;font-size:.75rem;color:#94a3b8}.sw-content{flex:1;min-width:0;background:#fff;display:flex;flex-direction:column;overflow-y:auto}.sw-content-header{display:flex;justify-content:space-between;align-items:center;gap:16px;padding:20px 32px;border-bottom:1px solid #f1f5f9}.sw-step-title{font-size:1.25rem;font-weight:700;color:#111827;margin:0}.sw-step-content{max-width:780px;padding:16px 48px 60px}.sw-intro{margin-bottom:24px}.sw-step-number{font-size:1.25rem;font-weight:800;line-height:1}.sw-split-tabs{display:block;margin-bottom:16px}.sw-hint.sw-continue-hint{margin:14px 0 0}.sw-extra-status{font-size:.8125rem;color:#64748b;margin:12px 0 0}.sw-extra-status:empty{display:none}.sw-hint{font-size:.8125rem;line-height:1.5;color:#64748b;margin:0 0 16px}.sw-import-tags{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:28px}.sw-tag{font-size:.6875rem;font-weight:700;letter-spacing:1px;color:#6b7280}.sw-provider-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:10px;margin-bottom:8px}@media(max-width:768px){.sw-provider-grid{grid-template-columns:1fr}}.sw-provider-grid .sw-integration{margin-bottom:0}.sw-area-code{display:flex;align-items:flex-end;gap:12px;margin-top:12px;max-width:320px}.sw-area-code ui-text-input{flex:1}.sw-content ui-alert{display:block;margin-bottom:16px}.sw-card{display:block}.sw-header-action{margin-left:auto;min-width:0;max-width:100%}.sw-step-actions{display:flex;justify-content:space-between;align-items:center}.sw-ico{flex-shrink:0;vertical-align:-2px}.sw-actions-right{display:flex;align-items:center;gap:16px;margin-left:auto}.sw-summary{display:block}.sw-done-hint{margin:16px 0 0}@media(max-width:768px){.sw-step-actions{flex-direction:column-reverse;align-items:stretch;gap:10px}.sw-actions-right{flex-direction:column-reverse;align-items:stretch;gap:10px;margin-left:0}.sw-page{flex-direction:column}.sw-sidebar{width:100%;min-width:unset;padding:16px}.sw-brand{margin-bottom:12px}.sw-sidebar-bottom{display:none}.sw-step-content{padding:12px 20px 40px}}\n"]
44211
+ styles: [".sw-page{display:flex;min-height:100vh;min-height:100dvh;font-family:Inter,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.sw-sidebar{width:260px;min-width:260px;background:#faf9f7;border-right:1px solid #e5e2dc;display:flex;flex-direction:column;justify-content:space-between;padding:28px 20px}.sw-brand{display:flex;align-items:center;gap:10px;margin-bottom:28px}.sw-brand-text{font-size:1.125rem;font-weight:700;color:#1e293b}.sw-step-nav{display:block}.sw-sidebar-bottom{margin-top:24px}.sw-progress-text{display:block;margin-top:8px;font-size:.75rem;color:#64748b}.sw-content{flex:1;min-width:0;background:#fff;display:flex;flex-direction:column;overflow-y:auto}.sw-content-header{display:flex;justify-content:space-between;align-items:center;gap:16px;padding:20px 32px;border-bottom:1px solid #f1f5f9}.sw-step-title{font-size:1.25rem;font-weight:700;color:#111827;margin:0}.sw-step-content{max-width:780px;padding:16px 48px 60px}.sw-intro{margin-bottom:24px}.sw-step-number{font-size:1.25rem;font-weight:800;line-height:1}.sw-split-tabs{display:block;margin-bottom:16px}.sw-hint.sw-continue-hint{margin:14px 0 0}.sw-extra-status{font-size:.8125rem;color:#64748b;margin:12px 0 0}.sw-extra-status:empty{display:none}.sw-hint{font-size:.8125rem;line-height:1.5;color:#64748b;margin:0 0 16px}.sw-import-tags{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:28px}.sw-tag{font-size:.6875rem;font-weight:700;letter-spacing:1px;color:#6b7280}.sw-provider-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:10px;margin-bottom:8px}.sw-provider-grid--single{grid-template-columns:1fr}@media(max-width:768px){.sw-provider-grid{grid-template-columns:1fr}}.sw-provider-grid .sw-integration{margin-bottom:0}.sw-area-code{display:flex;align-items:flex-end;gap:12px;margin-top:12px;max-width:320px}.sw-area-code ui-text-input{flex:1}.sw-content ui-alert{display:block;margin-bottom:16px}.sw-card{display:block}.sw-header-action{margin-left:auto;min-width:0;max-width:100%}.sw-step-actions{display:flex;justify-content:space-between;align-items:center}.sw-ico{flex-shrink:0;vertical-align:-2px}.sw-actions-right{display:flex;align-items:center;gap:16px;margin-left:auto}.sw-summary{display:block}.sw-done-hint{margin:16px 0 0}@media(max-width:768px){.sw-step-actions{flex-direction:column-reverse;align-items:stretch;gap:10px}.sw-actions-right{flex-direction:column-reverse;align-items:stretch;gap:10px;margin-left:0}.sw-page{flex-direction:column}.sw-sidebar{width:100%;min-width:unset;padding:16px}.sw-brand{margin-bottom:12px}.sw-sidebar-bottom{display:none}.sw-step-content{padding:12px 20px 40px}}\n"]
43891
44212
  }]
43892
44213
  }],
43893
44214
  ctorParameters: () => [],
@@ -43953,6 +44274,14 @@ function isWebUrl(raw) {
43953
44274
  return false;
43954
44275
  }
43955
44276
  }
44277
+ function serializedTextLength(text) {
44278
+ let n = 0;
44279
+ for (let i = 0; i < text.length; i++) {
44280
+ const ch = text[i];
44281
+ n += ch === "&" ? 5 : ch === "<" || ch === ">" ? 4 : ch === "\xA0" ? 6 : 1;
44282
+ }
44283
+ return n;
44284
+ }
43956
44285
  function escapeHtml(value) {
43957
44286
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
43958
44287
  }
@@ -44367,7 +44696,7 @@ var EmailTemplateEditorComponent = class EmailTemplateEditorComponent {
44367
44696
  if (this.subjectError()) missing.push(this.subject().trim() ? "a shorter subject" : "a subject");
44368
44697
  if (this.bodyError()) missing.push(this.bodyHasContent() ? "a shorter message" : "a message");
44369
44698
  if (this.assignmentError()) missing.push("at least one assignment");
44370
- return missing.length ? `Add ${missing.join(", ")} to save this template.` : this.datesError() || "Fix the highlighted fields to save this template.";
44699
+ return missing.length ? `Add ${missing.join(", ")} to save this template.` : this.datesError();
44371
44700
  }, ...ngDevMode ? [{ debugName: "saveHint" }] : /* istanbul ignore next */ []);
44372
44701
  this.normalizedLink = computed(() => {
44373
44702
  const url = this.linkUrl().trim();
@@ -44640,8 +44969,8 @@ var EmailTemplateEditorComponent = class EmailTemplateEditorComponent {
44640
44969
  const incoming = event.data ?? event.dataTransfer?.getData("text/plain") ?? "";
44641
44970
  if (!incoming) return;
44642
44971
  const sel = window.getSelection();
44643
- const replaced = sel && !sel.isCollapsed && insideEditorBlock(sel.anchorNode) ? sel.toString().length : 0;
44644
- if (this.bodyLength() - replaced + escapeHtml(incoming).length > this.maxBodyLength()) event.preventDefault();
44972
+ const replaced = sel && !sel.isCollapsed && insideEditorBlock(sel.anchorNode) ? serializedTextLength(sel.toString()) : 0;
44973
+ if (this.bodyLength() - replaced + serializedTextLength(incoming) > this.maxBodyLength()) event.preventDefault();
44645
44974
  }
44646
44975
  onBlockFocus(block) {
44647
44976
  this.selectedBlockId.set(block.id);
@@ -44821,8 +45150,8 @@ var EmailTemplateEditorComponent = class EmailTemplateEditorComponent {
44821
45150
  const sel = window.getSelection();
44822
45151
  if (sel && sel.rangeCount > 0 && !sel.isCollapsed) {
44823
45152
  document.execCommand("createLink", false, url);
44824
- const range = sel.getRangeAt(0);
44825
- (range.commonAncestorContainer instanceof Element ? range.commonAncestorContainer : range.commonAncestorContainer.parentElement)?.querySelectorAll("a").forEach((l) => {
45153
+ const node = sel.getRangeAt(0).commonAncestorContainer;
45154
+ (this.editorFor(this.selectedBlockId())?.nativeElement ?? (node instanceof Element ? node : node.parentElement)?.closest(".block-content") ?? null)?.querySelectorAll("a").forEach((l) => {
44826
45155
  if (l.getAttribute("href") === url) l.setAttribute("target", "_blank");
44827
45156
  });
44828
45157
  } else {
@@ -45159,7 +45488,7 @@ var EmailTemplateEditorComponent = class EmailTemplateEditorComponent {
45159
45488
  <!-- Actions (outside the white panels) -->
45160
45489
  <div class="editor-actions">
45161
45490
  @if (heading()) {
45162
- <h1 class="editor-heading" data-testid="editor-heading">{{ heading() }}</h1>
45491
+ <h2 class="editor-heading" data-testid="editor-heading">{{ heading() }}</h2>
45163
45492
  }
45164
45493
  <div class="editor-actions-buttons">
45165
45494
  @if (canWrite() && testSendEnabled()) {
@@ -45251,7 +45580,7 @@ var EmailTemplateEditorComponent = class EmailTemplateEditorComponent {
45251
45580
  </aside>
45252
45581
 
45253
45582
  <!-- CENTER: Editor -->
45254
- <main class="panel-center">
45583
+ <section class="panel-center" aria-label="Email design">
45255
45584
  <div class="editor-tabs" role="tablist" aria-label="Editor view">
45256
45585
  <button type="button" class="editor-tab" role="tab" [class.active]="activeEditorTab() === 'edit'" [attr.aria-selected]="activeEditorTab() === 'edit'" (click)="setEditorTab('edit')">
45257
45586
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z"/></svg>
@@ -45447,7 +45776,7 @@ var EmailTemplateEditorComponent = class EmailTemplateEditorComponent {
45447
45776
  </div>
45448
45777
  </div>
45449
45778
  }
45450
- </main>
45779
+ </section>
45451
45780
 
45452
45781
  <!-- RIGHT PANEL: Blocks, Variables, Video -->
45453
45782
  <aside class="panel-right" aria-label="Blocks, variables and video">
@@ -45621,7 +45950,7 @@ var EmailTemplateEditorComponent = class EmailTemplateEditorComponent {
45621
45950
 
45622
45951
  `,
45623
45952
  isInline: true,
45624
- styles: [":host{display:flex;flex-direction:column;flex:1;min-height:0}.required{color:#ef4444;font-weight:400}.editor-page{display:flex;flex-direction:column;flex:1;min-height:0;background:#f8fafc;overflow:hidden}.editor-actions{display:flex;justify-content:space-between;align-items:center;gap:8px 16px;flex-wrap:wrap;padding:0 0 12px;flex-shrink:0}.editor-heading{margin:0;font-size:1.25rem;font-weight:700;color:#1e293b;line-height:1.3;min-width:0}.save-wrap{display:inline-flex}.editor-actions-buttons{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:wrap;margin-left:auto}.readonly-note{margin:0;padding:8px 16px;font-size:.8125rem;color:#92400e;background:#fffbeb;border-bottom:1px solid #fde68a}.readonly-note.system-note{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.editor-body{display:grid;grid-template-columns:340px minmax(0,1fr) 300px;grid-template-rows:minmax(0,1fr);flex:1;min-height:0;overflow:hidden}.panel-left{background:#fff;border-right:1px solid #e2e8f0;padding:24px;overflow-y:auto}.panel-title{font-size:1.0625rem;font-weight:700;color:#1e293b;margin:0 0 20px}.form-field{display:flex;flex-direction:column;gap:6px;margin-bottom:16px}.form-row-dates{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:16px}.assignments-section{border-top:1px solid #e2e8f0;padding-top:20px;margin-top:16px}.section-title{font-size:.9375rem;font-weight:700;color:#1e293b;margin:0 0 6px}.section-desc{font-size:.8125rem;color:#64748b;line-height:1.5;margin:0 0 12px}.assignment-list{max-height:340px;overflow-y:auto;margin-top:12px;border:1px solid #e2e8f0;border-radius:8px}.assignment-item{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:12px 14px;cursor:pointer;border-bottom:1px solid #f1f5f9;transition:background .1s}.assignment-item:last-child{border-bottom:none}.assignment-item:hover{background:#f8fafc}.assignment-item:focus-visible{outline:2px solid #3b82f6;outline-offset:-2px}.assignment-item.assignment-readonly{cursor:default}.assignment-item.assignment-readonly:hover{background:transparent}.assignment-text{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}.assignment-text strong{font-size:.8125rem;color:#1e293b}.assignment-text span{font-size:.75rem;color:#64748b}.assignment-channels{display:flex;gap:4px;flex-shrink:0}.channel-badge{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:4px;background:#f1f5f9;color:#94a3b8;transition:all .15s}.channel-badge.active{background:#3b82f6;color:#fff}.channel-badge.disabled{opacity:.4;cursor:not-allowed}.config-desc{font-size:.875rem;color:#64748b;line-height:1.5;margin:0}.config-divider{border:none;border-top:1px solid #e2e8f0;margin:20px 0}.config-section{display:flex;flex-direction:column;gap:12px}.config-section-label{font-size:.6875rem;font-weight:700;color:#3b82f6;text-transform:uppercase;letter-spacing:.04em}.config-field{display:flex;flex-direction:column;gap:6px}.config-field-mt{margin-top:16px}.config-label{font-size:.8125rem;font-weight:600;color:#1e293b}.config-readonly{padding:9px 12px;border:1px solid #e2e8f0;border-radius:8px;background:#f8fafc;font-size:.875rem;color:#1e293b}.config-hint{font-size:.75rem;color:#64748b}.test-save-note{display:flex;align-items:center;gap:8px;padding:10px 14px;background:#fef3c7;border:1px solid #fcd34d;border-radius:8px;font-size:.8125rem;color:#92400e;margin-top:12px}.test-save-note svg{flex-shrink:0}.panel-center{display:flex;flex-direction:column;min-height:0;min-width:0;overflow:hidden}.editor-tabs{display:flex;gap:2px;padding:12px 20px 0;background:#fff;border-bottom:1px solid #e2e8f0;overflow-x:auto;-webkit-overflow-scrolling:touch}.editor-tab{display:flex;align-items:center;gap:6px;padding:10px 16px;border:1px solid #e2e8f0;border-bottom:none;border-radius:8px 8px 0 0;background:#f8fafc;font-size:.8125rem;font-weight:600;color:#64748b;cursor:pointer;font-family:inherit;white-space:nowrap}.editor-tab:hover{background:#fff;color:#1e293b}.editor-tab.active{background:#eff6ff;color:#3b82f6;border-color:#93c5fd}.editor-tab:focus-visible,.right-tab:focus-visible,.block-group-header:focus-visible,.variable-item:focus-visible,.add-block-btn:focus-visible,.block-item:focus-visible,.video-lib-item:focus-visible{outline:2px solid #3b82f6;outline-offset:-2px}.editor-toolbar{display:flex;align-items:center;gap:8px;padding:10px 20px;background:#fff;border-bottom:1px solid #e2e8f0;flex-wrap:wrap}.toolbar-row-2{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:8px 20px;background:#fff;border-bottom:1px solid #e2e8f0}.toolbar-group{display:flex;align-items:center;gap:2px;padding-right:8px;border-right:1px solid #e2e8f0}.toolbar-group:last-child{border-right:none;padding-right:0}.toolbar-select{display:block;min-width:96px}.toolbar-label{font-size:.75rem;font-weight:600;color:#64748b;white-space:nowrap;margin-right:6px}.hidden-color-input{position:absolute;width:0;height:0;opacity:0;pointer-events:none;border:none;padding:0}.color-indicator{display:flex;flex-direction:column;align-items:center;gap:2px;color:#475569}.color-bar{display:block;width:16px;height:3px;border-radius:2px;border:1px solid rgba(0,0,0,.1)}.link-dialog{background:#fff;border-bottom:1px solid #e2e8f0;padding:12px 20px}.link-dialog-inner{display:flex;align-items:flex-start;gap:8px;flex-wrap:wrap}.link-dialog-title{font-size:.8125rem;font-weight:700;color:#1e293b;white-space:nowrap;padding-top:10px}.link-dialog-input{display:block;width:240px;max-width:100%}.link-dialog-actions{display:flex;gap:6px}.preview-frame{flex:1;display:flex;justify-content:center;align-items:stretch;padding:24px;overflow-y:auto;background:#f1f5f9}.preview-frame.mobile{align-items:flex-start}.preview-content{display:block;flex:1 1 auto;max-width:600px;width:100%;box-sizing:border-box;min-height:400px;background:#fff;border:1px solid #e2e8f0;border-radius:8px}.mobile-shell{width:375px;max-width:100%;box-sizing:border-box;min-height:600px;border:2px solid #1e293b;border-radius:24px;padding:40px 12px 24px;background:#fff;box-shadow:0 8px 32px #0000001f}.mobile-shell .preview-content{border:none;border-radius:0;max-width:100%;height:520px;min-height:0}.blocks-canvas{flex:1 1 0;min-height:0;padding:16px 20px 24px 56px;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;background:#f1f5f9;display:flex;flex-direction:column}.block-wrapper{flex-shrink:0;position:relative;display:flex;align-items:flex-start;background:#fff;border:2px solid transparent;border-radius:8px;transition:border-color .15s,box-shadow .15s}.block-wrapper:hover,.block-wrapper.selected,.block-wrapper:focus-within{border-color:#3b82f6;box-shadow:0 0 0 3px #3b82f61a}.block-wrapper:hover .block-drag-handle,.block-wrapper.selected .block-drag-handle,.block-wrapper:focus-within .block-drag-handle{opacity:1}.block-wrapper:hover .block-controls,.block-wrapper.selected .block-controls,.block-wrapper:focus-within .block-controls{opacity:1;pointer-events:all}.block-wrapper.dragging{opacity:.4}.block-drag-handle{flex-shrink:0;width:28px;display:flex;align-items:flex-start;justify-content:center;padding-top:14px;opacity:0;transition:opacity .15s;cursor:grab;color:#94a3b8}.block-drag-handle:active{cursor:grabbing}.block-content{flex:1;min-width:0;padding:12px 16px;outline:none;font-size:.9375rem;color:#1e293b;line-height:1.6;min-height:48px;overflow-wrap:anywhere}.block-content:empty:before{content:attr(data-placeholder);color:#94a3b8;font-style:italic;pointer-events:none}.block-content p{margin:0 0 8px}.block-content p:last-child{margin:0}.block-content h1,.block-content h2,.block-content h3{margin:8px 0 6px;color:#1e293b}.block-content a{color:#3b82f6}.block-content img{max-width:100%;border-radius:6px}.block-content ul,.block-content ol{padding-left:20px;margin:0 0 8px}.block-controls{position:absolute;top:0;left:-48px;flex-direction:column;display:flex;gap:2px;opacity:0;pointer-events:none;transition:opacity .15s;background:#fff;border:1px solid #e2e8f0;border-radius:6px;padding:3px;box-shadow:0 2px 6px #00000014}@media(hover:none){.block-wrapper.selected .block-controls{opacity:1;pointer-events:all}}.drop-zone{flex-shrink:0;height:8px;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:height .15s,background .15s;position:relative}.drop-zone-label{font-size:.75rem;font-weight:600;color:#3b82f6;opacity:0;transition:opacity .1s}.drop-zone.active{height:40px;background:#3b82f614;border:2px dashed #3b82f6}.drop-zone.active .drop-zone-label{opacity:1}.add-block-btn{flex-shrink:0;display:flex;align-items:center;justify-content:center;gap:8px;width:100%;min-height:44px;padding:12px;margin-top:8px;border:2px dashed #e2e8f0;border-radius:8px;background:none;font-size:.875rem;font-weight:600;color:#64748b;cursor:pointer;font-family:inherit;transition:border-color .15s,color .15s,background .15s}.add-block-btn:hover{border-color:#3b82f6;color:#3b82f6;background:#3b82f60a}.panel-right{background:#fff;border-left:1px solid #e2e8f0;display:flex;flex-direction:column;min-height:0;min-width:0}.right-tabs{display:flex;border-bottom:1px solid #e2e8f0}.right-tab{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;padding:12px;min-height:44px;border:none;background:none;font-size:.8125rem;font-weight:600;color:#64748b;cursor:pointer;font-family:inherit;border-bottom:2px solid transparent}.right-tab:hover{color:#1e293b}.right-tab.active{color:#3b82f6;background:#eff6ff;border-bottom-color:#3b82f6}.right-panel-body{flex:1;padding:20px;overflow-y:auto}.right-section-title{font-size:.9375rem;font-weight:700;color:#1e293b;margin:0 0 12px}.right-section-desc{font-size:.8125rem;color:#64748b;margin:0 0 16px;line-height:1.5}.right-section-hint{font-size:.8125rem;color:#64748b;margin:0 0 12px;line-height:1.5}.block-group{border-bottom:1px solid #f1f5f9}.block-group-header{display:flex;align-items:center;gap:8px;width:100%;min-height:44px;padding:12px 0;border:none;background:none;font-size:.875rem;font-weight:600;color:#1e293b;cursor:pointer;font-family:inherit;text-align:left}.block-group-header .chevron{transition:transform .2s;color:#94a3b8}.block-group-header .chevron.expanded{transform:rotate(90deg)}.block-group-header span:nth-of-type(1){flex:1}.block-count{font-size:.75rem;font-weight:600;color:#64748b;background:#f1f5f9;padding:2px 8px;border-radius:10px;margin-left:auto}.block-list{display:flex;flex-direction:column;gap:2px;padding-bottom:8px}.variable-list{display:flex;flex-direction:column;padding-bottom:8px}.variable-item+.variable-item{border-top:1px solid #f1f5f9;border-radius:0}.block-item{display:flex;align-items:center;gap:6px;box-sizing:border-box;width:100%;min-height:44px;padding:8px 0;font-size:.8125rem;color:#64748b;cursor:pointer;text-align:left;border-radius:4px;font-family:inherit;-webkit-user-select:none;user-select:none}.block-item:hover{background:#f8fafc;color:#1e293b}.block-item-drag{color:#cbd5e1;flex-shrink:0;cursor:grab}.variable-item{display:flex;flex-direction:column;align-items:flex-start;gap:4px;min-height:44px;padding:8px 0;min-width:0;width:100%;box-sizing:border-box;border:none;background:none;cursor:pointer;text-align:left;border-radius:4px;font-family:inherit}.variable-item:hover{background:#f8fafc}.var-tag{max-width:100%;overflow-wrap:anywhere;font-size:.75rem;font-weight:600;color:#3b82f6;background:#3b82f614;padding:2px 6px;border-radius:4px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.var-example{font-size:.75rem;color:#64748b;overflow-wrap:anywhere}.var-description{font-size:.75rem;color:#64748b;line-height:1.4;overflow-wrap:anywhere}.video-library-panel{padding:0}.video-library-panel .right-section-title,.video-library-panel .right-section-hint{padding:0 16px}.video-library-panel .right-section-title{padding-top:16px}.video-config-section{display:flex;flex-direction:column;gap:8px;padding:12px 16px;border-bottom:1px solid #f1f5f9}.video-config-section:last-of-type{border-bottom:none}.video-lib-list{display:flex;flex-direction:column;gap:2px;padding:8px 8px 16px}.video-lib-item{display:flex;align-items:center;gap:10px;width:100%;min-height:44px;padding:8px;border:1px solid transparent;border-radius:8px;background:none;cursor:pointer;text-align:left;font-family:inherit;transition:background .15s,border-color .15s}.video-lib-item:hover{background:#f8fafc;border-color:#e2e8f0}.video-lib-item.selected{background:#eff6ff;border-color:#93c5fd}.vli-thumb{position:relative;flex-shrink:0;width:56px;height:40px;border-radius:6px;background:linear-gradient(135deg,#f1f5f9,#e2e8f0);display:flex;align-items:center;justify-content:center}.vli-dur{position:absolute;bottom:2px;right:3px;font-size:.625rem;font-weight:600;color:#fff;background:#000000a6;padding:1px 4px;border-radius:3px}.vli-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.vli-title{font-size:.8125rem;font-weight:600;color:#1e293b;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vli-cat{font-size:.75rem;color:#64748b}.vli-check{flex-shrink:0}.video-lib-loading{padding:8px}.video-lib-skeleton{display:flex;align-items:center;gap:10px;padding:8px;margin-bottom:4px}.vls-info{flex:1;display:flex;flex-direction:column;gap:6px}@media(max-width:1200px){.editor-body{grid-template-columns:280px minmax(0,1fr) 260px}}@media(max-width:900px){:host{flex:none}.editor-page{flex:none;min-height:100vh;min-height:100dvh;overflow:visible}.editor-body{display:flex;flex-direction:column;flex:none;overflow:visible}.panel-left{border-right:none;border-bottom:1px solid #e2e8f0;overflow:visible;padding:16px}.panel-center{overflow:visible;min-height:480px}.blocks-canvas{overflow:visible;min-height:320px}.panel-right{border-left:none;border-top:1px solid #e2e8f0}.right-panel-body{overflow:visible}.editor-toolbar,.toolbar-row-2,.link-dialog,.editor-tabs{padding-left:12px;padding-right:12px}.preview-frame{padding:12px}.form-row-dates{grid-template-columns:1fr}}@media(prefers-reduced-motion:reduce){*{transition:none!important}}\n"],
45953
+ styles: [":host{display:flex;flex-direction:column;flex:1;min-height:0}.required{color:#ef4444;font-weight:400}.editor-page{display:flex;flex-direction:column;flex:1;min-height:0;background:#f8fafc;overflow:hidden}.editor-actions{display:flex;justify-content:space-between;align-items:center;gap:8px 16px;flex-wrap:wrap;padding:0 0 12px;flex-shrink:0}.editor-heading{margin:0;font-size:1.25rem;font-weight:700;color:#1e293b;line-height:1.3;min-width:0}.save-wrap{display:inline-flex}.editor-actions-buttons{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:wrap;margin-left:auto}.readonly-note{margin:0;padding:8px 16px;font-size:.8125rem;color:#92400e;background:#fffbeb;border-bottom:1px solid #fde68a}.readonly-note.system-note{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.editor-body{display:grid;grid-template-columns:340px minmax(0,1fr) 300px;grid-template-rows:minmax(0,1fr);flex:1;min-height:0;overflow:hidden}.panel-left{background:#fff;border-right:1px solid #e2e8f0;padding:24px;overflow-y:auto}.panel-title{font-size:1.0625rem;font-weight:700;color:#1e293b;margin:0 0 20px}.form-field{display:flex;flex-direction:column;gap:6px;margin-bottom:16px}.form-row-dates{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:16px}.assignments-section{border-top:1px solid #e2e8f0;padding-top:20px;margin-top:16px}.section-title{font-size:.9375rem;font-weight:700;color:#1e293b;margin:0 0 6px}.section-desc{font-size:.8125rem;color:#64748b;line-height:1.5;margin:0 0 12px}.assignment-list{max-height:340px;overflow-y:auto;margin-top:12px;border:1px solid #e2e8f0;border-radius:8px}.assignment-item{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:12px 14px;cursor:pointer;border-bottom:1px solid #f1f5f9;transition:background .1s}.assignment-item:last-child{border-bottom:none}.assignment-item:hover{background:#f8fafc}.assignment-item:focus-visible{outline:2px solid #3b82f6;outline-offset:-2px}.assignment-item.assignment-readonly{cursor:default}.assignment-item.assignment-readonly:hover{background:transparent}.assignment-text{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}.assignment-text strong{font-size:.8125rem;color:#1e293b}.assignment-text span{font-size:.75rem;color:#64748b}.assignment-channels{display:flex;gap:4px;flex-shrink:0}.channel-badge{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:4px;background:#f1f5f9;color:#64748b;transition:all .15s}.channel-badge.active{background:#3b82f6;color:#fff}.channel-badge.disabled{opacity:.4;cursor:not-allowed}.config-desc{font-size:.875rem;color:#64748b;line-height:1.5;margin:0}.config-divider{border:none;border-top:1px solid #e2e8f0;margin:20px 0}.config-section{display:flex;flex-direction:column;gap:12px}.config-section-label{font-size:.6875rem;font-weight:700;color:#3b82f6;text-transform:uppercase;letter-spacing:.04em}.config-field{display:flex;flex-direction:column;gap:6px}.config-field-mt{margin-top:16px}.config-label{font-size:.8125rem;font-weight:600;color:#1e293b}.config-readonly{padding:9px 12px;border:1px solid #e2e8f0;border-radius:8px;background:#f8fafc;font-size:.875rem;color:#1e293b}.config-hint{font-size:.75rem;color:#64748b}.test-save-note{display:flex;align-items:center;gap:8px;padding:10px 14px;background:#fef3c7;border:1px solid #fcd34d;border-radius:8px;font-size:.8125rem;color:#92400e;margin-top:12px}.test-save-note svg{flex-shrink:0}.panel-center{display:flex;flex-direction:column;min-height:0;min-width:0;overflow:hidden}.editor-tabs{display:flex;gap:2px;padding:12px 20px 0;background:#fff;border-bottom:1px solid #e2e8f0;overflow-x:auto;-webkit-overflow-scrolling:touch}.editor-tab{display:flex;align-items:center;gap:6px;padding:10px 16px;border:1px solid #e2e8f0;border-bottom:none;border-radius:8px 8px 0 0;background:#f8fafc;font-size:.8125rem;font-weight:600;color:#64748b;cursor:pointer;font-family:inherit;white-space:nowrap}.editor-tab:hover{background:#fff;color:#1e293b}.editor-tab.active{background:#eff6ff;color:#1d4ed8;border-color:#93c5fd}.editor-tab:focus-visible,.right-tab:focus-visible,.block-group-header:focus-visible,.variable-item:focus-visible,.add-block-btn:focus-visible,.block-item:focus-visible,.video-lib-item:focus-visible{outline:2px solid #3b82f6;outline-offset:-2px}.editor-toolbar{display:flex;align-items:center;gap:8px;padding:10px 20px;background:#fff;border-bottom:1px solid #e2e8f0;flex-wrap:wrap}.toolbar-row-2{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:8px 20px;background:#fff;border-bottom:1px solid #e2e8f0}.toolbar-group{display:flex;align-items:center;gap:2px;padding-right:8px;border-right:1px solid #e2e8f0}.toolbar-group:last-child{border-right:none;padding-right:0}.toolbar-select{display:block;min-width:96px}.toolbar-label{font-size:.75rem;font-weight:600;color:#64748b;white-space:nowrap;margin-right:6px}.hidden-color-input{position:absolute;width:0;height:0;opacity:0;pointer-events:none;border:none;padding:0}.color-indicator{display:flex;flex-direction:column;align-items:center;gap:2px;color:#475569}.color-bar{display:block;width:16px;height:3px;border-radius:2px;border:1px solid rgba(0,0,0,.1)}.link-dialog{background:#fff;border-bottom:1px solid #e2e8f0;padding:12px 20px}.link-dialog-inner{display:flex;align-items:flex-start;gap:8px;flex-wrap:wrap}.link-dialog-title{font-size:.8125rem;font-weight:700;color:#1e293b;white-space:nowrap;padding-top:10px}.link-dialog-input{display:block;width:240px;max-width:100%}.link-dialog-actions{display:flex;gap:6px}.preview-frame{flex:1;display:flex;justify-content:center;align-items:stretch;padding:24px;overflow-y:auto;background:#f1f5f9}.preview-frame.mobile{align-items:flex-start}.preview-content{display:block;flex:1 1 auto;max-width:600px;width:100%;box-sizing:border-box;min-height:400px;background:#fff;border:1px solid #e2e8f0;border-radius:8px}.mobile-shell{width:375px;max-width:100%;box-sizing:border-box;min-height:600px;border:2px solid #1e293b;border-radius:24px;padding:40px 12px 24px;background:#fff;box-shadow:0 8px 32px #0000001f}.mobile-shell .preview-content{border:none;border-radius:0;max-width:100%;height:520px;min-height:0}.blocks-canvas{flex:1 1 0;min-height:0;padding:16px 20px 24px 56px;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;background:#f1f5f9;display:flex;flex-direction:column}.block-wrapper{flex-shrink:0;position:relative;display:flex;align-items:flex-start;background:#fff;border:2px solid transparent;border-radius:8px;transition:border-color .15s,box-shadow .15s}.block-wrapper:hover,.block-wrapper.selected,.block-wrapper:focus-within{border-color:#3b82f6;box-shadow:0 0 0 3px #3b82f61a}.block-wrapper:hover .block-drag-handle,.block-wrapper.selected .block-drag-handle,.block-wrapper:focus-within .block-drag-handle{opacity:1}.block-wrapper:hover .block-controls,.block-wrapper.selected .block-controls,.block-wrapper:focus-within .block-controls{opacity:1;pointer-events:all}.block-wrapper.dragging{opacity:.4}.block-drag-handle{flex-shrink:0;width:28px;display:flex;align-items:flex-start;justify-content:center;padding-top:14px;opacity:0;transition:opacity .15s;cursor:grab;color:#94a3b8}.block-drag-handle:active{cursor:grabbing}.block-content{flex:1;min-width:0;padding:12px 16px;outline:none;font-size:.9375rem;color:#1e293b;line-height:1.6;min-height:48px;overflow-wrap:anywhere}.block-content:empty:before{content:attr(data-placeholder);color:#64748b;font-style:italic;pointer-events:none}.block-content p{margin:0 0 8px}.block-content p:last-child{margin:0}.block-content h1,.block-content h2,.block-content h3{margin:8px 0 6px;color:#1e293b}.block-content a{color:#3b82f6}.block-content img{max-width:100%;border-radius:6px}.block-content ul,.block-content ol{padding-left:20px;margin:0 0 8px}.block-controls{position:absolute;top:0;left:-48px;flex-direction:column;display:flex;gap:2px;opacity:0;pointer-events:none;transition:opacity .15s;background:#fff;border:1px solid #e2e8f0;border-radius:6px;padding:3px;box-shadow:0 2px 6px #00000014}@media(pointer:coarse){.editor-tab,.right-tab,.block-group-header{min-height:44px}}@media(hover:none){.block-wrapper.selected .block-controls{opacity:1;pointer-events:all}}.drop-zone{flex-shrink:0;height:8px;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:height .15s,background .15s;position:relative}.drop-zone-label{font-size:.75rem;font-weight:600;color:#1d4ed8;opacity:0;transition:opacity .1s}.drop-zone.active{height:40px;background:#3b82f614;border:2px dashed #3b82f6}.drop-zone.active .drop-zone-label{opacity:1}.add-block-btn{flex-shrink:0;display:flex;align-items:center;justify-content:center;gap:8px;width:100%;min-height:44px;padding:12px;margin-top:8px;border:2px dashed #e2e8f0;border-radius:8px;background:none;font-size:.875rem;font-weight:600;color:#475569;cursor:pointer;font-family:inherit;transition:border-color .15s,color .15s,background .15s}.add-block-btn:hover{border-color:#3b82f6;color:#3b82f6;background:#3b82f60a}.panel-right{background:#fff;border-left:1px solid #e2e8f0;display:flex;flex-direction:column;min-height:0;min-width:0}.right-tabs{display:flex;border-bottom:1px solid #e2e8f0}.right-tab{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;padding:12px;min-height:44px;border:none;background:none;font-size:.8125rem;font-weight:600;color:#64748b;cursor:pointer;font-family:inherit;border-bottom:2px solid transparent}.right-tab:hover{color:#1e293b}.right-tab.active{color:#1d4ed8;background:#eff6ff;border-bottom-color:#3b82f6}.right-panel-body{flex:1;padding:20px;overflow-y:auto}.right-section-title{font-size:.9375rem;font-weight:700;color:#1e293b;margin:0 0 12px}.right-section-desc{font-size:.8125rem;color:#64748b;margin:0 0 16px;line-height:1.5}.right-section-hint{font-size:.8125rem;color:#64748b;margin:0 0 12px;line-height:1.5}.block-group{border-bottom:1px solid #f1f5f9}.block-group-header{display:flex;align-items:center;gap:8px;width:100%;min-height:44px;padding:12px 0;border:none;background:none;font-size:.875rem;font-weight:600;color:#1e293b;cursor:pointer;font-family:inherit;text-align:left}.block-group-header .chevron{transition:transform .2s;color:#94a3b8}.block-group-header .chevron.expanded{transform:rotate(90deg)}.block-group-header span:nth-of-type(1){flex:1}.block-count{font-size:.75rem;font-weight:600;color:#475569;background:#f1f5f9;padding:2px 8px;border-radius:10px;margin-left:auto}.block-list{display:flex;flex-direction:column;gap:2px;padding-bottom:8px}.variable-list{display:flex;flex-direction:column;padding-bottom:8px}.variable-item+.variable-item{border-top:1px solid #f1f5f9;border-radius:0}.block-item{display:flex;align-items:center;gap:6px;box-sizing:border-box;width:100%;min-height:44px;padding:8px 0;font-size:.8125rem;color:#64748b;cursor:pointer;text-align:left;border-radius:4px;font-family:inherit;-webkit-user-select:none;user-select:none}.block-item:hover{background:#f8fafc;color:#1e293b}.block-item-drag{color:#cbd5e1;flex-shrink:0;cursor:grab}.variable-item{display:flex;flex-direction:column;align-items:flex-start;gap:4px;min-height:44px;padding:8px 0;min-width:0;width:100%;box-sizing:border-box;border:none;background:none;cursor:pointer;text-align:left;border-radius:4px;font-family:inherit}.variable-item:hover{background:#f8fafc}.var-tag{max-width:100%;overflow-wrap:anywhere;font-size:.75rem;font-weight:600;color:#1d4ed8;background:#3b82f614;padding:2px 6px;border-radius:4px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.var-example{font-size:.75rem;color:#64748b;overflow-wrap:anywhere}.var-description{font-size:.75rem;color:#64748b;line-height:1.4;overflow-wrap:anywhere}.video-library-panel{padding:0}.video-library-panel .right-section-title,.video-library-panel .right-section-hint{padding:0 16px}.video-library-panel .right-section-title{padding-top:16px}.video-config-section{display:flex;flex-direction:column;gap:8px;padding:12px 16px;border-bottom:1px solid #f1f5f9}.video-config-section:last-of-type{border-bottom:none}.video-lib-list{display:flex;flex-direction:column;gap:2px;padding:8px 8px 16px}.video-lib-item{display:flex;align-items:center;gap:10px;width:100%;min-height:44px;padding:8px;border:1px solid transparent;border-radius:8px;background:none;cursor:pointer;text-align:left;font-family:inherit;transition:background .15s,border-color .15s}.video-lib-item:hover{background:#f8fafc;border-color:#e2e8f0}.video-lib-item.selected{background:#eff6ff;border-color:#93c5fd}.vli-thumb{position:relative;flex-shrink:0;width:56px;height:40px;border-radius:6px;background:linear-gradient(135deg,#f1f5f9,#e2e8f0);display:flex;align-items:center;justify-content:center}.vli-dur{position:absolute;bottom:2px;right:3px;font-size:.625rem;font-weight:600;color:#fff;background:#000000a6;padding:1px 4px;border-radius:3px}.vli-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.vli-title{font-size:.8125rem;font-weight:600;color:#1e293b;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vli-cat{font-size:.75rem;color:#475569}.vli-check{flex-shrink:0}.video-lib-loading{padding:8px}.video-lib-skeleton{display:flex;align-items:center;gap:10px;padding:8px;margin-bottom:4px}.vls-info{flex:1;display:flex;flex-direction:column;gap:6px}@media(max-width:1200px){.editor-body{grid-template-columns:280px minmax(0,1fr) 260px}}@media(max-width:900px){:host{flex:none}.editor-page{flex:none;min-height:100vh;min-height:100dvh;overflow:visible}.editor-body{display:flex;flex-direction:column;flex:none;overflow:visible}.panel-left{border-right:none;border-bottom:1px solid #e2e8f0;overflow:visible;padding:16px}.panel-center{overflow:visible;min-height:480px}.blocks-canvas{overflow:visible;min-height:320px}.panel-right{border-left:none;border-top:1px solid #e2e8f0}.right-panel-body{overflow:visible}.editor-toolbar,.toolbar-row-2,.link-dialog,.editor-tabs{padding-left:12px;padding-right:12px}.preview-frame{padding:12px}.form-row-dates{grid-template-columns:1fr}}@media(prefers-reduced-motion:reduce){*{transition:none!important}}\n"],
45625
45954
  dependencies: [
45626
45955
  {
45627
45956
  kind: "ngmodule",
@@ -45942,7 +46271,7 @@ i0.ɵɵngDeclareClassMetadata({
45942
46271
  <!-- Actions (outside the white panels) -->
45943
46272
  <div class="editor-actions">
45944
46273
  @if (heading()) {
45945
- <h1 class="editor-heading" data-testid="editor-heading">{{ heading() }}</h1>
46274
+ <h2 class="editor-heading" data-testid="editor-heading">{{ heading() }}</h2>
45946
46275
  }
45947
46276
  <div class="editor-actions-buttons">
45948
46277
  @if (canWrite() && testSendEnabled()) {
@@ -46034,7 +46363,7 @@ i0.ɵɵngDeclareClassMetadata({
46034
46363
  </aside>
46035
46364
 
46036
46365
  <!-- CENTER: Editor -->
46037
- <main class="panel-center">
46366
+ <section class="panel-center" aria-label="Email design">
46038
46367
  <div class="editor-tabs" role="tablist" aria-label="Editor view">
46039
46368
  <button type="button" class="editor-tab" role="tab" [class.active]="activeEditorTab() === 'edit'" [attr.aria-selected]="activeEditorTab() === 'edit'" (click)="setEditorTab('edit')">
46040
46369
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z"/></svg>
@@ -46230,7 +46559,7 @@ i0.ɵɵngDeclareClassMetadata({
46230
46559
  </div>
46231
46560
  </div>
46232
46561
  }
46233
- </main>
46562
+ </section>
46234
46563
 
46235
46564
  <!-- RIGHT PANEL: Blocks, Variables, Video -->
46236
46565
  <aside class="panel-right" aria-label="Blocks, variables and video">
@@ -46403,7 +46732,7 @@ i0.ɵɵngDeclareClassMetadata({
46403
46732
 
46404
46733
 
46405
46734
  `,
46406
- styles: [":host{display:flex;flex-direction:column;flex:1;min-height:0}.required{color:#ef4444;font-weight:400}.editor-page{display:flex;flex-direction:column;flex:1;min-height:0;background:#f8fafc;overflow:hidden}.editor-actions{display:flex;justify-content:space-between;align-items:center;gap:8px 16px;flex-wrap:wrap;padding:0 0 12px;flex-shrink:0}.editor-heading{margin:0;font-size:1.25rem;font-weight:700;color:#1e293b;line-height:1.3;min-width:0}.save-wrap{display:inline-flex}.editor-actions-buttons{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:wrap;margin-left:auto}.readonly-note{margin:0;padding:8px 16px;font-size:.8125rem;color:#92400e;background:#fffbeb;border-bottom:1px solid #fde68a}.readonly-note.system-note{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.editor-body{display:grid;grid-template-columns:340px minmax(0,1fr) 300px;grid-template-rows:minmax(0,1fr);flex:1;min-height:0;overflow:hidden}.panel-left{background:#fff;border-right:1px solid #e2e8f0;padding:24px;overflow-y:auto}.panel-title{font-size:1.0625rem;font-weight:700;color:#1e293b;margin:0 0 20px}.form-field{display:flex;flex-direction:column;gap:6px;margin-bottom:16px}.form-row-dates{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:16px}.assignments-section{border-top:1px solid #e2e8f0;padding-top:20px;margin-top:16px}.section-title{font-size:.9375rem;font-weight:700;color:#1e293b;margin:0 0 6px}.section-desc{font-size:.8125rem;color:#64748b;line-height:1.5;margin:0 0 12px}.assignment-list{max-height:340px;overflow-y:auto;margin-top:12px;border:1px solid #e2e8f0;border-radius:8px}.assignment-item{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:12px 14px;cursor:pointer;border-bottom:1px solid #f1f5f9;transition:background .1s}.assignment-item:last-child{border-bottom:none}.assignment-item:hover{background:#f8fafc}.assignment-item:focus-visible{outline:2px solid #3b82f6;outline-offset:-2px}.assignment-item.assignment-readonly{cursor:default}.assignment-item.assignment-readonly:hover{background:transparent}.assignment-text{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}.assignment-text strong{font-size:.8125rem;color:#1e293b}.assignment-text span{font-size:.75rem;color:#64748b}.assignment-channels{display:flex;gap:4px;flex-shrink:0}.channel-badge{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:4px;background:#f1f5f9;color:#94a3b8;transition:all .15s}.channel-badge.active{background:#3b82f6;color:#fff}.channel-badge.disabled{opacity:.4;cursor:not-allowed}.config-desc{font-size:.875rem;color:#64748b;line-height:1.5;margin:0}.config-divider{border:none;border-top:1px solid #e2e8f0;margin:20px 0}.config-section{display:flex;flex-direction:column;gap:12px}.config-section-label{font-size:.6875rem;font-weight:700;color:#3b82f6;text-transform:uppercase;letter-spacing:.04em}.config-field{display:flex;flex-direction:column;gap:6px}.config-field-mt{margin-top:16px}.config-label{font-size:.8125rem;font-weight:600;color:#1e293b}.config-readonly{padding:9px 12px;border:1px solid #e2e8f0;border-radius:8px;background:#f8fafc;font-size:.875rem;color:#1e293b}.config-hint{font-size:.75rem;color:#64748b}.test-save-note{display:flex;align-items:center;gap:8px;padding:10px 14px;background:#fef3c7;border:1px solid #fcd34d;border-radius:8px;font-size:.8125rem;color:#92400e;margin-top:12px}.test-save-note svg{flex-shrink:0}.panel-center{display:flex;flex-direction:column;min-height:0;min-width:0;overflow:hidden}.editor-tabs{display:flex;gap:2px;padding:12px 20px 0;background:#fff;border-bottom:1px solid #e2e8f0;overflow-x:auto;-webkit-overflow-scrolling:touch}.editor-tab{display:flex;align-items:center;gap:6px;padding:10px 16px;border:1px solid #e2e8f0;border-bottom:none;border-radius:8px 8px 0 0;background:#f8fafc;font-size:.8125rem;font-weight:600;color:#64748b;cursor:pointer;font-family:inherit;white-space:nowrap}.editor-tab:hover{background:#fff;color:#1e293b}.editor-tab.active{background:#eff6ff;color:#3b82f6;border-color:#93c5fd}.editor-tab:focus-visible,.right-tab:focus-visible,.block-group-header:focus-visible,.variable-item:focus-visible,.add-block-btn:focus-visible,.block-item:focus-visible,.video-lib-item:focus-visible{outline:2px solid #3b82f6;outline-offset:-2px}.editor-toolbar{display:flex;align-items:center;gap:8px;padding:10px 20px;background:#fff;border-bottom:1px solid #e2e8f0;flex-wrap:wrap}.toolbar-row-2{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:8px 20px;background:#fff;border-bottom:1px solid #e2e8f0}.toolbar-group{display:flex;align-items:center;gap:2px;padding-right:8px;border-right:1px solid #e2e8f0}.toolbar-group:last-child{border-right:none;padding-right:0}.toolbar-select{display:block;min-width:96px}.toolbar-label{font-size:.75rem;font-weight:600;color:#64748b;white-space:nowrap;margin-right:6px}.hidden-color-input{position:absolute;width:0;height:0;opacity:0;pointer-events:none;border:none;padding:0}.color-indicator{display:flex;flex-direction:column;align-items:center;gap:2px;color:#475569}.color-bar{display:block;width:16px;height:3px;border-radius:2px;border:1px solid rgba(0,0,0,.1)}.link-dialog{background:#fff;border-bottom:1px solid #e2e8f0;padding:12px 20px}.link-dialog-inner{display:flex;align-items:flex-start;gap:8px;flex-wrap:wrap}.link-dialog-title{font-size:.8125rem;font-weight:700;color:#1e293b;white-space:nowrap;padding-top:10px}.link-dialog-input{display:block;width:240px;max-width:100%}.link-dialog-actions{display:flex;gap:6px}.preview-frame{flex:1;display:flex;justify-content:center;align-items:stretch;padding:24px;overflow-y:auto;background:#f1f5f9}.preview-frame.mobile{align-items:flex-start}.preview-content{display:block;flex:1 1 auto;max-width:600px;width:100%;box-sizing:border-box;min-height:400px;background:#fff;border:1px solid #e2e8f0;border-radius:8px}.mobile-shell{width:375px;max-width:100%;box-sizing:border-box;min-height:600px;border:2px solid #1e293b;border-radius:24px;padding:40px 12px 24px;background:#fff;box-shadow:0 8px 32px #0000001f}.mobile-shell .preview-content{border:none;border-radius:0;max-width:100%;height:520px;min-height:0}.blocks-canvas{flex:1 1 0;min-height:0;padding:16px 20px 24px 56px;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;background:#f1f5f9;display:flex;flex-direction:column}.block-wrapper{flex-shrink:0;position:relative;display:flex;align-items:flex-start;background:#fff;border:2px solid transparent;border-radius:8px;transition:border-color .15s,box-shadow .15s}.block-wrapper:hover,.block-wrapper.selected,.block-wrapper:focus-within{border-color:#3b82f6;box-shadow:0 0 0 3px #3b82f61a}.block-wrapper:hover .block-drag-handle,.block-wrapper.selected .block-drag-handle,.block-wrapper:focus-within .block-drag-handle{opacity:1}.block-wrapper:hover .block-controls,.block-wrapper.selected .block-controls,.block-wrapper:focus-within .block-controls{opacity:1;pointer-events:all}.block-wrapper.dragging{opacity:.4}.block-drag-handle{flex-shrink:0;width:28px;display:flex;align-items:flex-start;justify-content:center;padding-top:14px;opacity:0;transition:opacity .15s;cursor:grab;color:#94a3b8}.block-drag-handle:active{cursor:grabbing}.block-content{flex:1;min-width:0;padding:12px 16px;outline:none;font-size:.9375rem;color:#1e293b;line-height:1.6;min-height:48px;overflow-wrap:anywhere}.block-content:empty:before{content:attr(data-placeholder);color:#94a3b8;font-style:italic;pointer-events:none}.block-content p{margin:0 0 8px}.block-content p:last-child{margin:0}.block-content h1,.block-content h2,.block-content h3{margin:8px 0 6px;color:#1e293b}.block-content a{color:#3b82f6}.block-content img{max-width:100%;border-radius:6px}.block-content ul,.block-content ol{padding-left:20px;margin:0 0 8px}.block-controls{position:absolute;top:0;left:-48px;flex-direction:column;display:flex;gap:2px;opacity:0;pointer-events:none;transition:opacity .15s;background:#fff;border:1px solid #e2e8f0;border-radius:6px;padding:3px;box-shadow:0 2px 6px #00000014}@media(hover:none){.block-wrapper.selected .block-controls{opacity:1;pointer-events:all}}.drop-zone{flex-shrink:0;height:8px;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:height .15s,background .15s;position:relative}.drop-zone-label{font-size:.75rem;font-weight:600;color:#3b82f6;opacity:0;transition:opacity .1s}.drop-zone.active{height:40px;background:#3b82f614;border:2px dashed #3b82f6}.drop-zone.active .drop-zone-label{opacity:1}.add-block-btn{flex-shrink:0;display:flex;align-items:center;justify-content:center;gap:8px;width:100%;min-height:44px;padding:12px;margin-top:8px;border:2px dashed #e2e8f0;border-radius:8px;background:none;font-size:.875rem;font-weight:600;color:#64748b;cursor:pointer;font-family:inherit;transition:border-color .15s,color .15s,background .15s}.add-block-btn:hover{border-color:#3b82f6;color:#3b82f6;background:#3b82f60a}.panel-right{background:#fff;border-left:1px solid #e2e8f0;display:flex;flex-direction:column;min-height:0;min-width:0}.right-tabs{display:flex;border-bottom:1px solid #e2e8f0}.right-tab{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;padding:12px;min-height:44px;border:none;background:none;font-size:.8125rem;font-weight:600;color:#64748b;cursor:pointer;font-family:inherit;border-bottom:2px solid transparent}.right-tab:hover{color:#1e293b}.right-tab.active{color:#3b82f6;background:#eff6ff;border-bottom-color:#3b82f6}.right-panel-body{flex:1;padding:20px;overflow-y:auto}.right-section-title{font-size:.9375rem;font-weight:700;color:#1e293b;margin:0 0 12px}.right-section-desc{font-size:.8125rem;color:#64748b;margin:0 0 16px;line-height:1.5}.right-section-hint{font-size:.8125rem;color:#64748b;margin:0 0 12px;line-height:1.5}.block-group{border-bottom:1px solid #f1f5f9}.block-group-header{display:flex;align-items:center;gap:8px;width:100%;min-height:44px;padding:12px 0;border:none;background:none;font-size:.875rem;font-weight:600;color:#1e293b;cursor:pointer;font-family:inherit;text-align:left}.block-group-header .chevron{transition:transform .2s;color:#94a3b8}.block-group-header .chevron.expanded{transform:rotate(90deg)}.block-group-header span:nth-of-type(1){flex:1}.block-count{font-size:.75rem;font-weight:600;color:#64748b;background:#f1f5f9;padding:2px 8px;border-radius:10px;margin-left:auto}.block-list{display:flex;flex-direction:column;gap:2px;padding-bottom:8px}.variable-list{display:flex;flex-direction:column;padding-bottom:8px}.variable-item+.variable-item{border-top:1px solid #f1f5f9;border-radius:0}.block-item{display:flex;align-items:center;gap:6px;box-sizing:border-box;width:100%;min-height:44px;padding:8px 0;font-size:.8125rem;color:#64748b;cursor:pointer;text-align:left;border-radius:4px;font-family:inherit;-webkit-user-select:none;user-select:none}.block-item:hover{background:#f8fafc;color:#1e293b}.block-item-drag{color:#cbd5e1;flex-shrink:0;cursor:grab}.variable-item{display:flex;flex-direction:column;align-items:flex-start;gap:4px;min-height:44px;padding:8px 0;min-width:0;width:100%;box-sizing:border-box;border:none;background:none;cursor:pointer;text-align:left;border-radius:4px;font-family:inherit}.variable-item:hover{background:#f8fafc}.var-tag{max-width:100%;overflow-wrap:anywhere;font-size:.75rem;font-weight:600;color:#3b82f6;background:#3b82f614;padding:2px 6px;border-radius:4px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.var-example{font-size:.75rem;color:#64748b;overflow-wrap:anywhere}.var-description{font-size:.75rem;color:#64748b;line-height:1.4;overflow-wrap:anywhere}.video-library-panel{padding:0}.video-library-panel .right-section-title,.video-library-panel .right-section-hint{padding:0 16px}.video-library-panel .right-section-title{padding-top:16px}.video-config-section{display:flex;flex-direction:column;gap:8px;padding:12px 16px;border-bottom:1px solid #f1f5f9}.video-config-section:last-of-type{border-bottom:none}.video-lib-list{display:flex;flex-direction:column;gap:2px;padding:8px 8px 16px}.video-lib-item{display:flex;align-items:center;gap:10px;width:100%;min-height:44px;padding:8px;border:1px solid transparent;border-radius:8px;background:none;cursor:pointer;text-align:left;font-family:inherit;transition:background .15s,border-color .15s}.video-lib-item:hover{background:#f8fafc;border-color:#e2e8f0}.video-lib-item.selected{background:#eff6ff;border-color:#93c5fd}.vli-thumb{position:relative;flex-shrink:0;width:56px;height:40px;border-radius:6px;background:linear-gradient(135deg,#f1f5f9,#e2e8f0);display:flex;align-items:center;justify-content:center}.vli-dur{position:absolute;bottom:2px;right:3px;font-size:.625rem;font-weight:600;color:#fff;background:#000000a6;padding:1px 4px;border-radius:3px}.vli-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.vli-title{font-size:.8125rem;font-weight:600;color:#1e293b;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vli-cat{font-size:.75rem;color:#64748b}.vli-check{flex-shrink:0}.video-lib-loading{padding:8px}.video-lib-skeleton{display:flex;align-items:center;gap:10px;padding:8px;margin-bottom:4px}.vls-info{flex:1;display:flex;flex-direction:column;gap:6px}@media(max-width:1200px){.editor-body{grid-template-columns:280px minmax(0,1fr) 260px}}@media(max-width:900px){:host{flex:none}.editor-page{flex:none;min-height:100vh;min-height:100dvh;overflow:visible}.editor-body{display:flex;flex-direction:column;flex:none;overflow:visible}.panel-left{border-right:none;border-bottom:1px solid #e2e8f0;overflow:visible;padding:16px}.panel-center{overflow:visible;min-height:480px}.blocks-canvas{overflow:visible;min-height:320px}.panel-right{border-left:none;border-top:1px solid #e2e8f0}.right-panel-body{overflow:visible}.editor-toolbar,.toolbar-row-2,.link-dialog,.editor-tabs{padding-left:12px;padding-right:12px}.preview-frame{padding:12px}.form-row-dates{grid-template-columns:1fr}}@media(prefers-reduced-motion:reduce){*{transition:none!important}}\n"]
46735
+ styles: [":host{display:flex;flex-direction:column;flex:1;min-height:0}.required{color:#ef4444;font-weight:400}.editor-page{display:flex;flex-direction:column;flex:1;min-height:0;background:#f8fafc;overflow:hidden}.editor-actions{display:flex;justify-content:space-between;align-items:center;gap:8px 16px;flex-wrap:wrap;padding:0 0 12px;flex-shrink:0}.editor-heading{margin:0;font-size:1.25rem;font-weight:700;color:#1e293b;line-height:1.3;min-width:0}.save-wrap{display:inline-flex}.editor-actions-buttons{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:wrap;margin-left:auto}.readonly-note{margin:0;padding:8px 16px;font-size:.8125rem;color:#92400e;background:#fffbeb;border-bottom:1px solid #fde68a}.readonly-note.system-note{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.editor-body{display:grid;grid-template-columns:340px minmax(0,1fr) 300px;grid-template-rows:minmax(0,1fr);flex:1;min-height:0;overflow:hidden}.panel-left{background:#fff;border-right:1px solid #e2e8f0;padding:24px;overflow-y:auto}.panel-title{font-size:1.0625rem;font-weight:700;color:#1e293b;margin:0 0 20px}.form-field{display:flex;flex-direction:column;gap:6px;margin-bottom:16px}.form-row-dates{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:16px}.assignments-section{border-top:1px solid #e2e8f0;padding-top:20px;margin-top:16px}.section-title{font-size:.9375rem;font-weight:700;color:#1e293b;margin:0 0 6px}.section-desc{font-size:.8125rem;color:#64748b;line-height:1.5;margin:0 0 12px}.assignment-list{max-height:340px;overflow-y:auto;margin-top:12px;border:1px solid #e2e8f0;border-radius:8px}.assignment-item{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:12px 14px;cursor:pointer;border-bottom:1px solid #f1f5f9;transition:background .1s}.assignment-item:last-child{border-bottom:none}.assignment-item:hover{background:#f8fafc}.assignment-item:focus-visible{outline:2px solid #3b82f6;outline-offset:-2px}.assignment-item.assignment-readonly{cursor:default}.assignment-item.assignment-readonly:hover{background:transparent}.assignment-text{display:flex;flex-direction:column;gap:2px;flex:1;min-width:0}.assignment-text strong{font-size:.8125rem;color:#1e293b}.assignment-text span{font-size:.75rem;color:#64748b}.assignment-channels{display:flex;gap:4px;flex-shrink:0}.channel-badge{display:flex;align-items:center;justify-content:center;width:24px;height:24px;border-radius:4px;background:#f1f5f9;color:#64748b;transition:all .15s}.channel-badge.active{background:#3b82f6;color:#fff}.channel-badge.disabled{opacity:.4;cursor:not-allowed}.config-desc{font-size:.875rem;color:#64748b;line-height:1.5;margin:0}.config-divider{border:none;border-top:1px solid #e2e8f0;margin:20px 0}.config-section{display:flex;flex-direction:column;gap:12px}.config-section-label{font-size:.6875rem;font-weight:700;color:#3b82f6;text-transform:uppercase;letter-spacing:.04em}.config-field{display:flex;flex-direction:column;gap:6px}.config-field-mt{margin-top:16px}.config-label{font-size:.8125rem;font-weight:600;color:#1e293b}.config-readonly{padding:9px 12px;border:1px solid #e2e8f0;border-radius:8px;background:#f8fafc;font-size:.875rem;color:#1e293b}.config-hint{font-size:.75rem;color:#64748b}.test-save-note{display:flex;align-items:center;gap:8px;padding:10px 14px;background:#fef3c7;border:1px solid #fcd34d;border-radius:8px;font-size:.8125rem;color:#92400e;margin-top:12px}.test-save-note svg{flex-shrink:0}.panel-center{display:flex;flex-direction:column;min-height:0;min-width:0;overflow:hidden}.editor-tabs{display:flex;gap:2px;padding:12px 20px 0;background:#fff;border-bottom:1px solid #e2e8f0;overflow-x:auto;-webkit-overflow-scrolling:touch}.editor-tab{display:flex;align-items:center;gap:6px;padding:10px 16px;border:1px solid #e2e8f0;border-bottom:none;border-radius:8px 8px 0 0;background:#f8fafc;font-size:.8125rem;font-weight:600;color:#64748b;cursor:pointer;font-family:inherit;white-space:nowrap}.editor-tab:hover{background:#fff;color:#1e293b}.editor-tab.active{background:#eff6ff;color:#1d4ed8;border-color:#93c5fd}.editor-tab:focus-visible,.right-tab:focus-visible,.block-group-header:focus-visible,.variable-item:focus-visible,.add-block-btn:focus-visible,.block-item:focus-visible,.video-lib-item:focus-visible{outline:2px solid #3b82f6;outline-offset:-2px}.editor-toolbar{display:flex;align-items:center;gap:8px;padding:10px 20px;background:#fff;border-bottom:1px solid #e2e8f0;flex-wrap:wrap}.toolbar-row-2{display:flex;align-items:center;gap:8px;flex-wrap:wrap;padding:8px 20px;background:#fff;border-bottom:1px solid #e2e8f0}.toolbar-group{display:flex;align-items:center;gap:2px;padding-right:8px;border-right:1px solid #e2e8f0}.toolbar-group:last-child{border-right:none;padding-right:0}.toolbar-select{display:block;min-width:96px}.toolbar-label{font-size:.75rem;font-weight:600;color:#64748b;white-space:nowrap;margin-right:6px}.hidden-color-input{position:absolute;width:0;height:0;opacity:0;pointer-events:none;border:none;padding:0}.color-indicator{display:flex;flex-direction:column;align-items:center;gap:2px;color:#475569}.color-bar{display:block;width:16px;height:3px;border-radius:2px;border:1px solid rgba(0,0,0,.1)}.link-dialog{background:#fff;border-bottom:1px solid #e2e8f0;padding:12px 20px}.link-dialog-inner{display:flex;align-items:flex-start;gap:8px;flex-wrap:wrap}.link-dialog-title{font-size:.8125rem;font-weight:700;color:#1e293b;white-space:nowrap;padding-top:10px}.link-dialog-input{display:block;width:240px;max-width:100%}.link-dialog-actions{display:flex;gap:6px}.preview-frame{flex:1;display:flex;justify-content:center;align-items:stretch;padding:24px;overflow-y:auto;background:#f1f5f9}.preview-frame.mobile{align-items:flex-start}.preview-content{display:block;flex:1 1 auto;max-width:600px;width:100%;box-sizing:border-box;min-height:400px;background:#fff;border:1px solid #e2e8f0;border-radius:8px}.mobile-shell{width:375px;max-width:100%;box-sizing:border-box;min-height:600px;border:2px solid #1e293b;border-radius:24px;padding:40px 12px 24px;background:#fff;box-shadow:0 8px 32px #0000001f}.mobile-shell .preview-content{border:none;border-radius:0;max-width:100%;height:520px;min-height:0}.blocks-canvas{flex:1 1 0;min-height:0;padding:16px 20px 24px 56px;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;background:#f1f5f9;display:flex;flex-direction:column}.block-wrapper{flex-shrink:0;position:relative;display:flex;align-items:flex-start;background:#fff;border:2px solid transparent;border-radius:8px;transition:border-color .15s,box-shadow .15s}.block-wrapper:hover,.block-wrapper.selected,.block-wrapper:focus-within{border-color:#3b82f6;box-shadow:0 0 0 3px #3b82f61a}.block-wrapper:hover .block-drag-handle,.block-wrapper.selected .block-drag-handle,.block-wrapper:focus-within .block-drag-handle{opacity:1}.block-wrapper:hover .block-controls,.block-wrapper.selected .block-controls,.block-wrapper:focus-within .block-controls{opacity:1;pointer-events:all}.block-wrapper.dragging{opacity:.4}.block-drag-handle{flex-shrink:0;width:28px;display:flex;align-items:flex-start;justify-content:center;padding-top:14px;opacity:0;transition:opacity .15s;cursor:grab;color:#94a3b8}.block-drag-handle:active{cursor:grabbing}.block-content{flex:1;min-width:0;padding:12px 16px;outline:none;font-size:.9375rem;color:#1e293b;line-height:1.6;min-height:48px;overflow-wrap:anywhere}.block-content:empty:before{content:attr(data-placeholder);color:#64748b;font-style:italic;pointer-events:none}.block-content p{margin:0 0 8px}.block-content p:last-child{margin:0}.block-content h1,.block-content h2,.block-content h3{margin:8px 0 6px;color:#1e293b}.block-content a{color:#3b82f6}.block-content img{max-width:100%;border-radius:6px}.block-content ul,.block-content ol{padding-left:20px;margin:0 0 8px}.block-controls{position:absolute;top:0;left:-48px;flex-direction:column;display:flex;gap:2px;opacity:0;pointer-events:none;transition:opacity .15s;background:#fff;border:1px solid #e2e8f0;border-radius:6px;padding:3px;box-shadow:0 2px 6px #00000014}@media(pointer:coarse){.editor-tab,.right-tab,.block-group-header{min-height:44px}}@media(hover:none){.block-wrapper.selected .block-controls{opacity:1;pointer-events:all}}.drop-zone{flex-shrink:0;height:8px;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:height .15s,background .15s;position:relative}.drop-zone-label{font-size:.75rem;font-weight:600;color:#1d4ed8;opacity:0;transition:opacity .1s}.drop-zone.active{height:40px;background:#3b82f614;border:2px dashed #3b82f6}.drop-zone.active .drop-zone-label{opacity:1}.add-block-btn{flex-shrink:0;display:flex;align-items:center;justify-content:center;gap:8px;width:100%;min-height:44px;padding:12px;margin-top:8px;border:2px dashed #e2e8f0;border-radius:8px;background:none;font-size:.875rem;font-weight:600;color:#475569;cursor:pointer;font-family:inherit;transition:border-color .15s,color .15s,background .15s}.add-block-btn:hover{border-color:#3b82f6;color:#3b82f6;background:#3b82f60a}.panel-right{background:#fff;border-left:1px solid #e2e8f0;display:flex;flex-direction:column;min-height:0;min-width:0}.right-tabs{display:flex;border-bottom:1px solid #e2e8f0}.right-tab{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;padding:12px;min-height:44px;border:none;background:none;font-size:.8125rem;font-weight:600;color:#64748b;cursor:pointer;font-family:inherit;border-bottom:2px solid transparent}.right-tab:hover{color:#1e293b}.right-tab.active{color:#1d4ed8;background:#eff6ff;border-bottom-color:#3b82f6}.right-panel-body{flex:1;padding:20px;overflow-y:auto}.right-section-title{font-size:.9375rem;font-weight:700;color:#1e293b;margin:0 0 12px}.right-section-desc{font-size:.8125rem;color:#64748b;margin:0 0 16px;line-height:1.5}.right-section-hint{font-size:.8125rem;color:#64748b;margin:0 0 12px;line-height:1.5}.block-group{border-bottom:1px solid #f1f5f9}.block-group-header{display:flex;align-items:center;gap:8px;width:100%;min-height:44px;padding:12px 0;border:none;background:none;font-size:.875rem;font-weight:600;color:#1e293b;cursor:pointer;font-family:inherit;text-align:left}.block-group-header .chevron{transition:transform .2s;color:#94a3b8}.block-group-header .chevron.expanded{transform:rotate(90deg)}.block-group-header span:nth-of-type(1){flex:1}.block-count{font-size:.75rem;font-weight:600;color:#475569;background:#f1f5f9;padding:2px 8px;border-radius:10px;margin-left:auto}.block-list{display:flex;flex-direction:column;gap:2px;padding-bottom:8px}.variable-list{display:flex;flex-direction:column;padding-bottom:8px}.variable-item+.variable-item{border-top:1px solid #f1f5f9;border-radius:0}.block-item{display:flex;align-items:center;gap:6px;box-sizing:border-box;width:100%;min-height:44px;padding:8px 0;font-size:.8125rem;color:#64748b;cursor:pointer;text-align:left;border-radius:4px;font-family:inherit;-webkit-user-select:none;user-select:none}.block-item:hover{background:#f8fafc;color:#1e293b}.block-item-drag{color:#cbd5e1;flex-shrink:0;cursor:grab}.variable-item{display:flex;flex-direction:column;align-items:flex-start;gap:4px;min-height:44px;padding:8px 0;min-width:0;width:100%;box-sizing:border-box;border:none;background:none;cursor:pointer;text-align:left;border-radius:4px;font-family:inherit}.variable-item:hover{background:#f8fafc}.var-tag{max-width:100%;overflow-wrap:anywhere;font-size:.75rem;font-weight:600;color:#1d4ed8;background:#3b82f614;padding:2px 6px;border-radius:4px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.var-example{font-size:.75rem;color:#64748b;overflow-wrap:anywhere}.var-description{font-size:.75rem;color:#64748b;line-height:1.4;overflow-wrap:anywhere}.video-library-panel{padding:0}.video-library-panel .right-section-title,.video-library-panel .right-section-hint{padding:0 16px}.video-library-panel .right-section-title{padding-top:16px}.video-config-section{display:flex;flex-direction:column;gap:8px;padding:12px 16px;border-bottom:1px solid #f1f5f9}.video-config-section:last-of-type{border-bottom:none}.video-lib-list{display:flex;flex-direction:column;gap:2px;padding:8px 8px 16px}.video-lib-item{display:flex;align-items:center;gap:10px;width:100%;min-height:44px;padding:8px;border:1px solid transparent;border-radius:8px;background:none;cursor:pointer;text-align:left;font-family:inherit;transition:background .15s,border-color .15s}.video-lib-item:hover{background:#f8fafc;border-color:#e2e8f0}.video-lib-item.selected{background:#eff6ff;border-color:#93c5fd}.vli-thumb{position:relative;flex-shrink:0;width:56px;height:40px;border-radius:6px;background:linear-gradient(135deg,#f1f5f9,#e2e8f0);display:flex;align-items:center;justify-content:center}.vli-dur{position:absolute;bottom:2px;right:3px;font-size:.625rem;font-weight:600;color:#fff;background:#000000a6;padding:1px 4px;border-radius:3px}.vli-info{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}.vli-title{font-size:.8125rem;font-weight:600;color:#1e293b;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vli-cat{font-size:.75rem;color:#475569}.vli-check{flex-shrink:0}.video-lib-loading{padding:8px}.video-lib-skeleton{display:flex;align-items:center;gap:10px;padding:8px;margin-bottom:4px}.vls-info{flex:1;display:flex;flex-direction:column;gap:6px}@media(max-width:1200px){.editor-body{grid-template-columns:280px minmax(0,1fr) 260px}}@media(max-width:900px){:host{flex:none}.editor-page{flex:none;min-height:100vh;min-height:100dvh;overflow:visible}.editor-body{display:flex;flex-direction:column;flex:none;overflow:visible}.panel-left{border-right:none;border-bottom:1px solid #e2e8f0;overflow:visible;padding:16px}.panel-center{overflow:visible;min-height:480px}.blocks-canvas{overflow:visible;min-height:320px}.panel-right{border-left:none;border-top:1px solid #e2e8f0}.right-panel-body{overflow:visible}.editor-toolbar,.toolbar-row-2,.link-dialog,.editor-tabs{padding-left:12px;padding-right:12px}.preview-frame{padding:12px}.form-row-dates{grid-template-columns:1fr}}@media(prefers-reduced-motion:reduce){*{transition:none!important}}\n"]
46407
46736
  }]
46408
46737
  }],
46409
46738
  ctorParameters: () => [],
@@ -46650,6 +46979,6 @@ i0.ɵɵngDeclareClassMetadata({
46650
46979
  }]
46651
46980
  }
46652
46981
  });
46653
- export { ActionBarComponent, AlertComponent, AppShellComponent, AudioPlayerComponent, AvatarComponent, BADGE_VARIANT_STYLES, BadgeComponent, BarRowComponent, BoardCardDirective, BoardComponent, BulkMessagePanelComponent, BusinessHoursComponent, ButtonComponent, CalendarComponent, CardListItemComponent, CheckboxComponent, CheckboxTrailingDirective, ChipComponent, ChipGroupComponent, ColorInputComponent, ColumnFilterComponent, ColumnsComponent, ComboboxComponent, ConfirmOutletComponent, ConfirmService, ContactPickerComponent, DEFAULT_EMAIL_TEMPLATE_BLOCK_GROUPS, DEFAULT_EMAIL_TEMPLATE_VARIABLE_GROUPS, DEFAULT_PASSWORD_RULES, DROPDOWN_GROUP, DataTableComponent, DateInputComponent, DateRangePickerComponent, DatetimeInputComponent, DetailCardComponent, DiscountSelectorComponent, DividerComponent, DropdownComponent, EMAIL_BLOCK_DRAG_TYPE, EMAIL_TEMPLATE_MAX_BODY, EMAIL_TEMPLATE_MAX_TEXT, EMAIL_TEMPLATE_START_TEXT, EmailTemplateEditorComponent, EmptyStateComponent, ErrorComponent, ErrorStateComponent, FORGEROUTE_CONFIG, FadeComponent, FileInputComponent, FormColSpanDirective, FormComponent, FormRowComponent, FormSectionComponent, HintComponent, IDX_BEHAVIOR_SIGNALS, INTEGRATION_CATALOG, INTEGRATION_FIELD_DEFS, INTEGRATION_GROUP_ORDER, INTEGRATION_OPTION_DEFS, IconComponent, ImageComponent, ImageGalleryComponent, ImageGalleryDialogComponent, ImagePreviewComponent, ImageRepositionComponent, ImportExportComponent, ImportSourceGridComponent, KpiCardComponent, LAYOUT_GAP_PX, ListComponent, ListItemComponent, LiveRegionComponent, MAX_EMAIL_ADDRESS_LENGTH, ModalComponent, MoreMenuComponent, MultiSelectComponent, NOTES_MAX_LENGTH, NotesComponent, OptionCardComponent, PHONE_NUMBER_AREA_CODE_LENGTH, PHONE_NUMBER_ZIP_LENGTH, PageComponent, PaginationComponent, PanelInfoListComponent, PasswordRequirementsComponent, PdfHotspotComponent, PhoneNumberPickerComponent, PillTabsComponent, ProgressBarComponent, ProgressRingComponent, QrCodeComponent, QuickFilterTabsComponent, RICH_TEXT_OPTIONS, RadioComponent, RadioGroupComponent, RadioMatrixComponent, RadioOptionContentDirective, ReadonlyFieldsetComponent, RichTextEditorComponent, SEMANTIC_BADGE_MAP, SETUP_DONE_KEY, SETUP_INTEGRATIONS, SearchInputComponent, SectionHeaderComponent, SelectComponent, SetupWizardComponent, SetupWizardStepDirective, SetupWizardStepHeaderDirective, ShowComponent, SidePanelComponent, SidebarLayoutComponent, SignaturePadComponent, SkeletonComponent, StackComponent, StarComponent, StepperComponent, TableToolbarComponent, TextComponent, TextInputComponent, TextareaInputComponent, TimeInputComponent, TimelineComponent, ToastComponent, ToastService, ToggleBadgeComponent, ToggleComponent, TooltipDirective, US_STATES, UiFieldValidity, acceptLabel, appShellGroupsStorageKey, autoVariant, blockHasContent, buildDefaultSchedule, buildPreviewDocument, buildVideoBlockHtml, builtInActionIcon, businessTimeOptions, createFieldReveal, decodeFilterValues, digitsOnly, downloadBlob, encodeFilterValue, encodeFilterValues, errorContextInterceptor, escapeHtml, fileMatchesAccept, formatAddress, formatCategoryLabel, formatCurrency, formatFileSize, formatIsoDate, formatIsoDateTime, formatTime, formatVideoDuration, getEligibleIds, getEligibleOptions, getErrorMessage, humanReadableMessage, iconPath, insideEditorBlock, integrationPlainText, integrationsForApp, integrationsOnlyFor, isBlockDrag, isValidEmailAddress, isWebUrl, isoKey, issueToErrors, maxLengthIssue, normalizeHexColor, parseBizHours, parseIso, parseTime, passwordMeetsRules, pluralize, provideForgerouteToast, reorderItems, requiredIssue, resolveLayoutGap, resolvePreset, serializeBizHours, setupIntegrations, splitIntro };
46982
+ export { ActionBarComponent, AlertComponent, AppShellComponent, AudioPlayerComponent, AvatarComponent, BADGE_VARIANT_STYLES, BadgeComponent, BarRowComponent, BoardCardDirective, BoardComponent, BulkMessagePanelComponent, BusinessHoursComponent, ButtonComponent, CalendarComponent, CardListItemComponent, CheckboxComponent, CheckboxTrailingDirective, ChipComponent, ChipGroupComponent, ColorInputComponent, ColumnFilterComponent, ColumnsComponent, ComboboxComponent, ConfirmOutletComponent, ConfirmService, ContactPickerComponent, DEFAULT_EMAIL_TEMPLATE_BLOCK_GROUPS, DEFAULT_EMAIL_TEMPLATE_VARIABLE_GROUPS, DEFAULT_PASSWORD_RULES, DROPDOWN_GROUP, DataTableComponent, DateInputComponent, DateRangePickerComponent, DatetimeInputComponent, DetailCardComponent, DiscountSelectorComponent, DividerComponent, DropdownComponent, EMAIL_BLOCK_DRAG_TYPE, EMAIL_TEMPLATE_MAX_BODY, EMAIL_TEMPLATE_MAX_TEXT, EMAIL_TEMPLATE_START_TEXT, EmailTemplateEditorComponent, EmptyStateComponent, ErrorComponent, ErrorStateComponent, FORGEROUTE_CONFIG, FadeComponent, FileInputComponent, FormColSpanDirective, FormComponent, FormRowComponent, FormSectionComponent, HintComponent, IDX_BEHAVIOR_SIGNALS, INTEGRATION_CATALOG, INTEGRATION_FIELD_DEFS, INTEGRATION_GROUP_ORDER, INTEGRATION_OPTION_DEFS, IconComponent, ImageComponent, ImageGalleryComponent, ImageGalleryDialogComponent, ImagePreviewComponent, ImageRepositionComponent, ImportExportComponent, ImportSourceGridComponent, KpiCardComponent, LAYOUT_GAP_PX, ListComponent, ListItemComponent, LiveRegionComponent, MAX_EMAIL_ADDRESS_LENGTH, ModalComponent, MoreMenuComponent, MultiSelectComponent, NOTES_MAX_LENGTH, NotesComponent, OptionCardComponent, PHONE_NUMBER_AREA_CODE_LENGTH, PHONE_NUMBER_ZIP_LENGTH, PageComponent, PaginationComponent, PanelInfoListComponent, PasswordRequirementsComponent, PdfHotspotComponent, PhoneNumberPickerComponent, PillTabsComponent, ProgressBarComponent, ProgressRingComponent, QrCodeComponent, QuickFilterTabsComponent, RICH_TEXT_OPTIONS, RadioComponent, RadioGroupComponent, RadioMatrixComponent, RadioOptionContentDirective, ReadonlyFieldsetComponent, RichTextEditorComponent, SEMANTIC_BADGE_MAP, SETUP_DONE_KEY, SETUP_INTEGRATIONS, SearchInputComponent, SectionHeaderComponent, SelectComponent, SetupWizardComponent, SetupWizardStepDirective, SetupWizardStepHeaderDirective, ShowComponent, SidePanelComponent, SidebarLayoutComponent, SignaturePadComponent, SkeletonComponent, StackComponent, StarComponent, StepperComponent, TableToolbarComponent, TextComponent, TextInputComponent, TextareaInputComponent, TimeInputComponent, TimelineComponent, ToastComponent, ToastService, ToggleBadgeComponent, ToggleComponent, TooltipDirective, US_STATES, UiFieldValidity, acceptLabel, appShellGroupsStorageKey, autoVariant, blockHasContent, buildDefaultSchedule, buildPreviewDocument, buildVideoBlockHtml, builtInActionIcon, businessTimeOptions, createFieldReveal, decodeFilterValues, digitsOnly, downloadBlob, encodeFilterValue, encodeFilterValues, errorContextInterceptor, escapeHtml, fileMatchesAccept, formatAddress, formatCategoryLabel, formatCurrency, formatFileSize, formatIsoDate, formatIsoDateTime, formatTime, formatVideoDuration, getEligibleIds, getEligibleOptions, getErrorMessage, humanReadableMessage, iconPath, insideEditorBlock, integrationPlainText, integrationsForApp, integrationsOnlyFor, isBlockDrag, isValidEmailAddress, isWebUrl, isoKey, issueToErrors, maxLengthIssue, normalizeHexColor, parseBizHours, parseIso, parseTime, passwordMeetsRules, pluralize, provideForgerouteToast, reorderItems, requiredIssue, resolveLayoutGap, resolvePreset, serializeBizHours, serializedTextLength, setupIntegrations, splitIntro };
46654
46983
 
46655
46984
  //# sourceMappingURL=forgeroute-ui.mjs.map