@cqa-lib/cqa-ui 1.1.513 → 1.1.515

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.
@@ -1192,6 +1192,15 @@ class DynamicTableComponent {
1192
1192
  this.computedData = [];
1193
1193
  this.computedGridTemplate = '';
1194
1194
  this.computedColumnWidths = [];
1195
+ /**
1196
+ * Hard floor on the `<table>` width = sum of every visible column's minimum (fixed
1197
+ * widths + dynamic columns' `minWidth`). With `table-layout: fixed; width: 100%;` set
1198
+ * by consumers, `<col>` widths become advisory once the container is narrower than
1199
+ * the column-width sum — the browser silently shrinks them. Pinning `min-width` on
1200
+ * the table itself prevents that compaction; the consumer's `overflow-x: auto`
1201
+ * wrapper then scrolls horizontally instead of squishing columns to zero width.
1202
+ */
1203
+ this.computedTableMinWidth = 0;
1195
1204
  // Cache for sanitized HTML to avoid redundant innerHTML renders
1196
1205
  this.htmlCache = new Map();
1197
1206
  this._sortDirection = null;
@@ -1444,13 +1453,32 @@ class DynamicTableComponent {
1444
1453
  const fixedPx = cols.reduce((sum, c) => sum + (c.fixedPx || 0), 0);
1445
1454
  const dynamicCols = cols.filter(c => !c.fixedPx);
1446
1455
  const totalWeight = dynamicCols.reduce((sum, c) => sum + (c.weight || 1), 0) || 1;
1456
+ // Floor on the table itself = every column's minimum width contribution. Fixed
1457
+ // columns contribute `fixedPx`; dynamic columns contribute `minWidth` (or 0 when
1458
+ // unset). The template binds this via `[style.min-width.px]` on the <table> so
1459
+ // narrow containers force horizontal scroll instead of squishing columns to 0.
1460
+ this.computedTableMinWidth = cols.reduce((sum, c) => sum + (c.fixedPx && c.fixedPx > 0 ? c.fixedPx : (c.minWidth || 0)), 0);
1447
1461
  this.computedColumnWidths = cols.map(c => {
1448
1462
  if (c.fixedPx && c.fixedPx > 0) {
1449
- return `${c.fixedPx}px`;
1463
+ // Fixed-width columns still respect minWidth in the (rare) case the consumer
1464
+ // configured both — `max(fixed, min)` picks whichever is larger.
1465
+ return c.minWidth && c.minWidth > c.fixedPx
1466
+ ? `max(${c.fixedPx}px, ${c.minWidth}px)`
1467
+ : `${c.fixedPx}px`;
1450
1468
  }
1451
1469
  const share = (c.weight || 1) / totalWeight;
1452
- return `calc((100% - ${fixedPx}px) * ${share.toFixed(4)})`;
1470
+ const dynamicWidth = `calc((100% - ${fixedPx}px) * ${share.toFixed(4)})`;
1471
+ // Dynamic-share columns can collapse to ~0 when the table is narrower than
1472
+ // (sum of fixed widths) + the share they'd compute to. Using CSS `max()` floors
1473
+ // the column at `minWidth` so it grows naturally as the table widens but never
1474
+ // shrinks below the configured pixel minimum. With `table-layout: fixed`, the
1475
+ // <col> width is authoritative — without max() here the cell-level `min-width`
1476
+ // we set on <th>/<td> is ignored and columns visually disappear at narrow widths.
1477
+ return c.minWidth && c.minWidth > 0
1478
+ ? `max(${c.minWidth}px, ${dynamicWidth})`
1479
+ : dynamicWidth;
1453
1480
  });
1481
+ console.log('computedColumnWidths', this.computedColumnWidths);
1454
1482
  if (this.gridTemplateColumns) {
1455
1483
  this.computedGridTemplate = this.gridTemplateColumns;
1456
1484
  }
@@ -1598,10 +1626,10 @@ class DynamicTableComponent {
1598
1626
  }
1599
1627
  }
1600
1628
  DynamicTableComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DynamicTableComponent, deps: [{ token: i1$2.DomSanitizer }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
1601
- DynamicTableComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: DynamicTableComponent, selector: "app-dynamic-table", inputs: { data: "data", columns: "columns", emptyState: "emptyState", gridTemplateColumns: "gridTemplateColumns", screenWidth: "screenWidth", enableSelectAll: "enableSelectAll", rowSelectable: "rowSelectable", enableLocalSort: "enableLocalSort", isTableLoading: "isTableLoading", isTableDataLoading: "isTableDataLoading", cellJsonPathGetter: "cellJsonPathGetter", onJsonPathCopiedHandler: "onJsonPathCopiedHandler" }, outputs: { sortChange: "sortChange" }, host: { classAttribute: "cqa-ui-root" }, queries: [{ propertyName: "emptyTableTpl", first: true, predicate: ["emptyTableTpl"], descendants: true, read: TemplateRef }, { propertyName: "cellTemplates", predicate: DynamicCellTemplateDirective }, { propertyName: "headerTemplates", predicate: DynamicHeaderTemplateDirective }], usesOnChanges: true, ngImport: i0, template: "<div class=\"cqa-ui-root\">\n <div class=\"cqa-relative\" [class.cqa-max-h-[100px]]=\"showTableLoading\">\n <cqa-full-table-loader *ngIf=\"showTableLoading\"></cqa-full-table-loader>\n <cqa-table-data-loader *ngIf=\"showTableDataLoading\"></cqa-table-data-loader>\n <table class=\"table-inner cqa-w-full cqa-border cqa-border-solid cqa-border-gray-200\" [class.is-loading]=\"true\" *ngIf=\"!showTableLoading\">\n <colgroup>\n <ng-container *ngFor=\"let width of computedColumnWidths; trackBy: trackByIndex\">\n <col [style.width]=\"width\" />\n </ng-container>\n </colgroup>\n\n <thead *ngIf=\"data?.length\">\n <tr class=\"table-header cqa-items-center\">\n <ng-container *ngFor=\"let col of visibleColumns; trackBy: trackByIndex\">\n <th\n class=\"header-cell cqa-py-[13.25px] cqa-px-[10.5px] cqa-text-xs cqa-font-semibold cqa-text-[#374151] cqa-bg-[#eff0f7]\"\n [ngClass]=\"[\n col.fieldId + '-cell',\n col.align === 'center' ? 'cqa-text-center' : col.align === 'right' ? 'cqa-text-right' : 'cqa-text-left'\n ]\">\n <!-- Built-in select-all for checkbox column when enabled and no custom header template -->\n <ng-container\n *ngIf=\"col.fieldId === 'checkbox' && enableSelectAll && !getHeaderTemplate(col.fieldId); else headerTplOrDefault\">\n <div class=\"custom-checkbox\">\n <input type=\"checkbox\" id=\"checkbox\" aria-label=\"Select all rows\" [checked]=\"allSelected\"\n [indeterminate]=\"someSelected\" (change)=\"onSelectAllChange($event)\" class=\"hidden-checkbox\" />\n <label for=\"checkbox\" class=\"custom-checkbox-label\"></label>\n </div>\n </ng-container>\n <ng-template #headerTplOrDefault>\n <ng-container *ngIf=\"getHeaderTemplate(col.fieldId) as headerTpl; else defaultHeader\">\n <ng-container *ngTemplateOutlet=\"headerTpl\"></ng-container>\n </ng-container>\n <ng-template #defaultHeader>\n <ng-container *ngIf=\"col.sortable; else plainHeader\">\n <button type=\"button\" class=\"header-text cqa-inline-flex cqa-items-center cqa-gap-1 cqa-px-0\"\n (click)=\"toggleSort(col)\" [attr.aria-label]=\"'Sort by ' + (col.fieldName || col.fieldId)\">\n <span class=\"cqa-text-[12.3px] cqa-leading-[17.5px] cqa-text-[#0A0A0A] cqa-font-medium\">{{\n col.fieldName }}</span>\n <span *ngIf=\"isSortedAsc(col.fieldId)\">\u25B2</span>\n <span *ngIf=\"isSortedDesc(col.fieldId)\">\u25BC</span>\n </button>\n </ng-container>\n <ng-template #plainHeader>\n <span class=\"header-text\">{{ col.fieldName }}</span>\n </ng-template>\n </ng-template>\n </ng-template>\n </th>\n </ng-container>\n </tr>\n </thead>\n\n <tbody class=\"table-body cqa-w-full\">\n <tr class=\"table-row cqa-bg-surface-default hover:cqa-bg-surface-hover\" *ngFor=\"let row of computedData; let rowIndex = index; trackBy: trackByIndex\"\n [class.selected]=\"row?.isSelected\">\n <ng-container *ngFor=\"let col of visibleColumns; trackBy: trackByIndex\">\n <td class=\"cell cqa-px-[10.5px] cqa-py-[11px]\" \n [ngClass]=\"[\n col.fieldId + '-cell',\n col.align === 'center' ? 'cqa-text-center' : col.align === 'right' ? 'cqa-text-right' : 'cqa-text-left',\n cellJsonPathGetter && col.fieldId !== 'checkbox' && col.fieldId !== 'actions' ? 'cqa-cursor-pointer' : ''\n ]\"\n (dblclick)=\"cellJsonPathGetter && col?.fieldId !== 'checkbox' && col?.fieldId !== 'actions' && copyCellJsonPath(cellJsonPathGetter(rowIndex, col?.fieldId, row), $event, 'dbQueryResponse')\">\n <!-- Built-in checkbox cell when no custom template is provided -->\n <ng-container *ngIf=\"col.fieldId === 'checkbox' && !getCellTemplate(col.fieldId); else regularCell\">\n <div class=\"custom-checkbox\" [ngClass]=\"[\n col.align === 'center' ? 'cqa-flex cqa-justify-center' : col.align === 'right' ? 'cqa-flex cqa-justify-end' : ''\n ]\">\n <input\n type=\"checkbox\"\n class=\"hidden-checkbox\"\n [attr.id]=\"'row-checkbox-' + rowIndex\"\n aria-label=\"Select row\"\n [checked]=\"row?.isSelected\"\n [disabled]=\"!isRowSelectable(row)\"\n (change)=\"onRowSelectChange($event, row)\" />\n <label\n class=\"custom-checkbox-label\"\n [style.opacity]=\"!isRowSelectable(row) ? '0.45' : null\"\n [style.cursor]=\"!isRowSelectable(row) ? 'not-allowed' : null\"\n [style.pointer-events]=\"!isRowSelectable(row) ? 'none' : null\"\n [attr.for]=\"'row-checkbox-' + rowIndex\">\n </label>\n </div>\n </ng-container>\n <ng-template #regularCell>\n <ng-container *ngIf=\"getCellTemplate(col.fieldId) as cellTpl; else defaultCell\">\n <ng-container\n *ngTemplateOutlet=\"cellTpl; context: {$implicit: row, row: row, value: getCellValue(row, col.fieldValue)}\"></ng-container>\n </ng-container>\n <ng-template #defaultCell>\n <ng-container *ngIf=\"getRenderedValue(row, col) as renderedValue\">\n <ng-container *ngIf=\"isComponent(renderedValue); else nonComponentCell\">\n <ng-container \n cqaCellContainer \n #cellContainer=\"cqaCellContainer\"\n [componentConfig]=\"renderedValue\"\n [row]=\"row\"\n [column]=\"col\"\n [rowIndex]=\"rowIndex\"\n [colId]=\"col.fieldId\">\n </ng-container>\n </ng-container>\n <ng-template #nonComponentCell>\n <ng-container *ngIf=\"isHtmlString(renderedValue); else textCell\">\n <div class=\"cqa-text-xs cqa-leading-[17px]\" [innerHTML]=\"getSanitizedHtml(renderedValue)\" [ngClass]=\"[\n col.align === 'center' ? 'cqa-flex cqa-justify-center' : col.align === 'right' ? 'cqa-flex cqa-justify-end' : ''\n ]\"></div>\n </ng-container>\n <ng-template #textCell>\n <span class=\"cqa-text-xs cqa-text-[#374151]\" [ngClass]=\"[\n col.align === 'center' ? 'cqa-flex cqa-justify-center' : col.align === 'right' ? 'cqa-flex cqa-justify-end' : ''\n ]\">{{ renderedValue }}</span>\n </ng-template>\n </ng-template>\n </ng-container>\n </ng-template>\n </ng-template>\n </td>\n </ng-container>\n </tr>\n </tbody>\n </table>\n </div>\n</div>", components: [{ type: FullTableLoaderComponent, selector: "cqa-full-table-loader", inputs: ["label"] }, { type: TableDataLoaderComponent, selector: "cqa-table-data-loader", inputs: ["label", "size"] }], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }, { type: DynamicCellContainerDirective, selector: "[cqaCellContainer]", inputs: ["componentConfig", "row", "column", "rowIndex", "colId"], exportAs: ["cqaCellContainer"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1629
+ DynamicTableComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: DynamicTableComponent, selector: "app-dynamic-table", inputs: { data: "data", columns: "columns", emptyState: "emptyState", gridTemplateColumns: "gridTemplateColumns", screenWidth: "screenWidth", enableSelectAll: "enableSelectAll", rowSelectable: "rowSelectable", enableLocalSort: "enableLocalSort", isTableLoading: "isTableLoading", isTableDataLoading: "isTableDataLoading", cellJsonPathGetter: "cellJsonPathGetter", onJsonPathCopiedHandler: "onJsonPathCopiedHandler" }, outputs: { sortChange: "sortChange" }, host: { classAttribute: "cqa-ui-root" }, queries: [{ propertyName: "emptyTableTpl", first: true, predicate: ["emptyTableTpl"], descendants: true, read: TemplateRef }, { propertyName: "cellTemplates", predicate: DynamicCellTemplateDirective }, { propertyName: "headerTemplates", predicate: DynamicHeaderTemplateDirective }], usesOnChanges: true, ngImport: i0, template: "<div class=\"cqa-ui-root\">\n <div class=\"cqa-relative\" [class.cqa-max-h-[100px]]=\"showTableLoading\">\n <cqa-full-table-loader *ngIf=\"showTableLoading\"></cqa-full-table-loader>\n <cqa-table-data-loader *ngIf=\"showTableDataLoading\"></cqa-table-data-loader>\n <table class=\"table-inner cqa-w-full cqa-border cqa-border-solid cqa-border-gray-200\"\n [class.is-loading]=\"true\"\n [style.min-width.px]=\"computedTableMinWidth || null\"\n *ngIf=\"!showTableLoading\">\n <colgroup>\n <ng-container *ngFor=\"let width of computedColumnWidths; trackBy: trackByIndex\">\n <col [style.width]=\"width\" />\n </ng-container>\n </colgroup>\n\n <thead *ngIf=\"data?.length\">\n <tr class=\"table-header cqa-items-center\">\n <ng-container *ngFor=\"let col of visibleColumns; trackBy: trackByIndex\">\n <th\n class=\"header-cell cqa-py-[13.25px] cqa-px-[10.5px] cqa-text-xs cqa-font-semibold cqa-text-[#374151] cqa-bg-[#eff0f7]\"\n [style.min-width.px]=\"col.minWidth\"\n [ngClass]=\"[\n col.fieldId + '-cell',\n col.align === 'center' ? 'cqa-text-center' : col.align === 'right' ? 'cqa-text-right' : 'cqa-text-left'\n ]\">\n <!-- Built-in select-all for checkbox column when enabled and no custom header template -->\n <ng-container\n *ngIf=\"col.fieldId === 'checkbox' && enableSelectAll && !getHeaderTemplate(col.fieldId); else headerTplOrDefault\">\n <div class=\"custom-checkbox\">\n <input type=\"checkbox\" id=\"checkbox\" aria-label=\"Select all rows\" [checked]=\"allSelected\"\n [indeterminate]=\"someSelected\" (change)=\"onSelectAllChange($event)\" class=\"hidden-checkbox\" />\n <label for=\"checkbox\" class=\"custom-checkbox-label\"></label>\n </div>\n </ng-container>\n <ng-template #headerTplOrDefault>\n <ng-container *ngIf=\"getHeaderTemplate(col.fieldId) as headerTpl; else defaultHeader\">\n <ng-container *ngTemplateOutlet=\"headerTpl\"></ng-container>\n </ng-container>\n <ng-template #defaultHeader>\n <ng-container *ngIf=\"col.sortable; else plainHeader\">\n <button type=\"button\" class=\"header-text cqa-inline-flex cqa-items-center cqa-gap-1 cqa-px-0\"\n (click)=\"toggleSort(col)\" [attr.aria-label]=\"'Sort by ' + (col.fieldName || col.fieldId)\">\n <span class=\"cqa-text-[12.3px] cqa-leading-[17.5px] cqa-text-[#0A0A0A] cqa-font-medium\">{{\n col.fieldName }}</span>\n <span *ngIf=\"isSortedAsc(col.fieldId)\">\u25B2</span>\n <span *ngIf=\"isSortedDesc(col.fieldId)\">\u25BC</span>\n </button>\n </ng-container>\n <ng-template #plainHeader>\n <span class=\"header-text\">{{ col.fieldName }}</span>\n </ng-template>\n </ng-template>\n </ng-template>\n </th>\n </ng-container>\n </tr>\n </thead>\n\n <tbody class=\"table-body cqa-w-full\">\n <tr class=\"table-row cqa-bg-surface-default hover:cqa-bg-surface-hover\" *ngFor=\"let row of computedData; let rowIndex = index; trackBy: trackByIndex\"\n [class.selected]=\"row?.isSelected\">\n <ng-container *ngFor=\"let col of visibleColumns; trackBy: trackByIndex\">\n <td class=\"cell cqa-px-[10.5px] cqa-py-[11px]\"\n [style.min-width.px]=\"col.minWidth\"\n [ngClass]=\"[\n col.fieldId + '-cell',\n col.align === 'center' ? 'cqa-text-center' : col.align === 'right' ? 'cqa-text-right' : 'cqa-text-left',\n cellJsonPathGetter && col.fieldId !== 'checkbox' && col.fieldId !== 'actions' ? 'cqa-cursor-pointer' : ''\n ]\"\n (dblclick)=\"cellJsonPathGetter && col?.fieldId !== 'checkbox' && col?.fieldId !== 'actions' && copyCellJsonPath(cellJsonPathGetter(rowIndex, col?.fieldId, row), $event, 'dbQueryResponse')\">\n <!-- Built-in checkbox cell when no custom template is provided -->\n <ng-container *ngIf=\"col.fieldId === 'checkbox' && !getCellTemplate(col.fieldId); else regularCell\">\n <div class=\"custom-checkbox\" [ngClass]=\"[\n col.align === 'center' ? 'cqa-flex cqa-justify-center' : col.align === 'right' ? 'cqa-flex cqa-justify-end' : ''\n ]\">\n <input\n type=\"checkbox\"\n class=\"hidden-checkbox\"\n [attr.id]=\"'row-checkbox-' + rowIndex\"\n aria-label=\"Select row\"\n [checked]=\"row?.isSelected\"\n [disabled]=\"!isRowSelectable(row)\"\n (change)=\"onRowSelectChange($event, row)\" />\n <label\n class=\"custom-checkbox-label\"\n [style.opacity]=\"!isRowSelectable(row) ? '0.45' : null\"\n [style.cursor]=\"!isRowSelectable(row) ? 'not-allowed' : null\"\n [style.pointer-events]=\"!isRowSelectable(row) ? 'none' : null\"\n [attr.for]=\"'row-checkbox-' + rowIndex\">\n </label>\n </div>\n </ng-container>\n <ng-template #regularCell>\n <ng-container *ngIf=\"getCellTemplate(col.fieldId) as cellTpl; else defaultCell\">\n <ng-container\n *ngTemplateOutlet=\"cellTpl; context: {$implicit: row, row: row, value: getCellValue(row, col.fieldValue)}\"></ng-container>\n </ng-container>\n <ng-template #defaultCell>\n <ng-container *ngIf=\"getRenderedValue(row, col) as renderedValue\">\n <ng-container *ngIf=\"isComponent(renderedValue); else nonComponentCell\">\n <ng-container \n cqaCellContainer \n #cellContainer=\"cqaCellContainer\"\n [componentConfig]=\"renderedValue\"\n [row]=\"row\"\n [column]=\"col\"\n [rowIndex]=\"rowIndex\"\n [colId]=\"col.fieldId\">\n </ng-container>\n </ng-container>\n <ng-template #nonComponentCell>\n <ng-container *ngIf=\"isHtmlString(renderedValue); else textCell\">\n <div class=\"cqa-text-xs cqa-leading-[17px]\" [innerHTML]=\"getSanitizedHtml(renderedValue)\" [ngClass]=\"[\n col.align === 'center' ? 'cqa-flex cqa-justify-center' : col.align === 'right' ? 'cqa-flex cqa-justify-end' : ''\n ]\"></div>\n </ng-container>\n <ng-template #textCell>\n <span class=\"cqa-text-xs cqa-text-[#374151]\" [ngClass]=\"[\n col.align === 'center' ? 'cqa-flex cqa-justify-center' : col.align === 'right' ? 'cqa-flex cqa-justify-end' : ''\n ]\">{{ renderedValue }}</span>\n </ng-template>\n </ng-template>\n </ng-container>\n </ng-template>\n </ng-template>\n </td>\n </ng-container>\n </tr>\n </tbody>\n </table>\n </div>\n</div>", components: [{ type: FullTableLoaderComponent, selector: "cqa-full-table-loader", inputs: ["label"] }, { type: TableDataLoaderComponent, selector: "cqa-table-data-loader", inputs: ["label", "size"] }], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet"] }, { type: DynamicCellContainerDirective, selector: "[cqaCellContainer]", inputs: ["componentConfig", "row", "column", "rowIndex", "colId"], exportAs: ["cqaCellContainer"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1602
1630
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: DynamicTableComponent, decorators: [{
1603
1631
  type: Component,
1604
- args: [{ selector: "app-dynamic-table", host: { class: 'cqa-ui-root' }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"cqa-ui-root\">\n <div class=\"cqa-relative\" [class.cqa-max-h-[100px]]=\"showTableLoading\">\n <cqa-full-table-loader *ngIf=\"showTableLoading\"></cqa-full-table-loader>\n <cqa-table-data-loader *ngIf=\"showTableDataLoading\"></cqa-table-data-loader>\n <table class=\"table-inner cqa-w-full cqa-border cqa-border-solid cqa-border-gray-200\" [class.is-loading]=\"true\" *ngIf=\"!showTableLoading\">\n <colgroup>\n <ng-container *ngFor=\"let width of computedColumnWidths; trackBy: trackByIndex\">\n <col [style.width]=\"width\" />\n </ng-container>\n </colgroup>\n\n <thead *ngIf=\"data?.length\">\n <tr class=\"table-header cqa-items-center\">\n <ng-container *ngFor=\"let col of visibleColumns; trackBy: trackByIndex\">\n <th\n class=\"header-cell cqa-py-[13.25px] cqa-px-[10.5px] cqa-text-xs cqa-font-semibold cqa-text-[#374151] cqa-bg-[#eff0f7]\"\n [ngClass]=\"[\n col.fieldId + '-cell',\n col.align === 'center' ? 'cqa-text-center' : col.align === 'right' ? 'cqa-text-right' : 'cqa-text-left'\n ]\">\n <!-- Built-in select-all for checkbox column when enabled and no custom header template -->\n <ng-container\n *ngIf=\"col.fieldId === 'checkbox' && enableSelectAll && !getHeaderTemplate(col.fieldId); else headerTplOrDefault\">\n <div class=\"custom-checkbox\">\n <input type=\"checkbox\" id=\"checkbox\" aria-label=\"Select all rows\" [checked]=\"allSelected\"\n [indeterminate]=\"someSelected\" (change)=\"onSelectAllChange($event)\" class=\"hidden-checkbox\" />\n <label for=\"checkbox\" class=\"custom-checkbox-label\"></label>\n </div>\n </ng-container>\n <ng-template #headerTplOrDefault>\n <ng-container *ngIf=\"getHeaderTemplate(col.fieldId) as headerTpl; else defaultHeader\">\n <ng-container *ngTemplateOutlet=\"headerTpl\"></ng-container>\n </ng-container>\n <ng-template #defaultHeader>\n <ng-container *ngIf=\"col.sortable; else plainHeader\">\n <button type=\"button\" class=\"header-text cqa-inline-flex cqa-items-center cqa-gap-1 cqa-px-0\"\n (click)=\"toggleSort(col)\" [attr.aria-label]=\"'Sort by ' + (col.fieldName || col.fieldId)\">\n <span class=\"cqa-text-[12.3px] cqa-leading-[17.5px] cqa-text-[#0A0A0A] cqa-font-medium\">{{\n col.fieldName }}</span>\n <span *ngIf=\"isSortedAsc(col.fieldId)\">\u25B2</span>\n <span *ngIf=\"isSortedDesc(col.fieldId)\">\u25BC</span>\n </button>\n </ng-container>\n <ng-template #plainHeader>\n <span class=\"header-text\">{{ col.fieldName }}</span>\n </ng-template>\n </ng-template>\n </ng-template>\n </th>\n </ng-container>\n </tr>\n </thead>\n\n <tbody class=\"table-body cqa-w-full\">\n <tr class=\"table-row cqa-bg-surface-default hover:cqa-bg-surface-hover\" *ngFor=\"let row of computedData; let rowIndex = index; trackBy: trackByIndex\"\n [class.selected]=\"row?.isSelected\">\n <ng-container *ngFor=\"let col of visibleColumns; trackBy: trackByIndex\">\n <td class=\"cell cqa-px-[10.5px] cqa-py-[11px]\" \n [ngClass]=\"[\n col.fieldId + '-cell',\n col.align === 'center' ? 'cqa-text-center' : col.align === 'right' ? 'cqa-text-right' : 'cqa-text-left',\n cellJsonPathGetter && col.fieldId !== 'checkbox' && col.fieldId !== 'actions' ? 'cqa-cursor-pointer' : ''\n ]\"\n (dblclick)=\"cellJsonPathGetter && col?.fieldId !== 'checkbox' && col?.fieldId !== 'actions' && copyCellJsonPath(cellJsonPathGetter(rowIndex, col?.fieldId, row), $event, 'dbQueryResponse')\">\n <!-- Built-in checkbox cell when no custom template is provided -->\n <ng-container *ngIf=\"col.fieldId === 'checkbox' && !getCellTemplate(col.fieldId); else regularCell\">\n <div class=\"custom-checkbox\" [ngClass]=\"[\n col.align === 'center' ? 'cqa-flex cqa-justify-center' : col.align === 'right' ? 'cqa-flex cqa-justify-end' : ''\n ]\">\n <input\n type=\"checkbox\"\n class=\"hidden-checkbox\"\n [attr.id]=\"'row-checkbox-' + rowIndex\"\n aria-label=\"Select row\"\n [checked]=\"row?.isSelected\"\n [disabled]=\"!isRowSelectable(row)\"\n (change)=\"onRowSelectChange($event, row)\" />\n <label\n class=\"custom-checkbox-label\"\n [style.opacity]=\"!isRowSelectable(row) ? '0.45' : null\"\n [style.cursor]=\"!isRowSelectable(row) ? 'not-allowed' : null\"\n [style.pointer-events]=\"!isRowSelectable(row) ? 'none' : null\"\n [attr.for]=\"'row-checkbox-' + rowIndex\">\n </label>\n </div>\n </ng-container>\n <ng-template #regularCell>\n <ng-container *ngIf=\"getCellTemplate(col.fieldId) as cellTpl; else defaultCell\">\n <ng-container\n *ngTemplateOutlet=\"cellTpl; context: {$implicit: row, row: row, value: getCellValue(row, col.fieldValue)}\"></ng-container>\n </ng-container>\n <ng-template #defaultCell>\n <ng-container *ngIf=\"getRenderedValue(row, col) as renderedValue\">\n <ng-container *ngIf=\"isComponent(renderedValue); else nonComponentCell\">\n <ng-container \n cqaCellContainer \n #cellContainer=\"cqaCellContainer\"\n [componentConfig]=\"renderedValue\"\n [row]=\"row\"\n [column]=\"col\"\n [rowIndex]=\"rowIndex\"\n [colId]=\"col.fieldId\">\n </ng-container>\n </ng-container>\n <ng-template #nonComponentCell>\n <ng-container *ngIf=\"isHtmlString(renderedValue); else textCell\">\n <div class=\"cqa-text-xs cqa-leading-[17px]\" [innerHTML]=\"getSanitizedHtml(renderedValue)\" [ngClass]=\"[\n col.align === 'center' ? 'cqa-flex cqa-justify-center' : col.align === 'right' ? 'cqa-flex cqa-justify-end' : ''\n ]\"></div>\n </ng-container>\n <ng-template #textCell>\n <span class=\"cqa-text-xs cqa-text-[#374151]\" [ngClass]=\"[\n col.align === 'center' ? 'cqa-flex cqa-justify-center' : col.align === 'right' ? 'cqa-flex cqa-justify-end' : ''\n ]\">{{ renderedValue }}</span>\n </ng-template>\n </ng-template>\n </ng-container>\n </ng-template>\n </ng-template>\n </td>\n </ng-container>\n </tr>\n </tbody>\n </table>\n </div>\n</div>", styles: [] }]
1632
+ args: [{ selector: "app-dynamic-table", host: { class: 'cqa-ui-root' }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"cqa-ui-root\">\n <div class=\"cqa-relative\" [class.cqa-max-h-[100px]]=\"showTableLoading\">\n <cqa-full-table-loader *ngIf=\"showTableLoading\"></cqa-full-table-loader>\n <cqa-table-data-loader *ngIf=\"showTableDataLoading\"></cqa-table-data-loader>\n <table class=\"table-inner cqa-w-full cqa-border cqa-border-solid cqa-border-gray-200\"\n [class.is-loading]=\"true\"\n [style.min-width.px]=\"computedTableMinWidth || null\"\n *ngIf=\"!showTableLoading\">\n <colgroup>\n <ng-container *ngFor=\"let width of computedColumnWidths; trackBy: trackByIndex\">\n <col [style.width]=\"width\" />\n </ng-container>\n </colgroup>\n\n <thead *ngIf=\"data?.length\">\n <tr class=\"table-header cqa-items-center\">\n <ng-container *ngFor=\"let col of visibleColumns; trackBy: trackByIndex\">\n <th\n class=\"header-cell cqa-py-[13.25px] cqa-px-[10.5px] cqa-text-xs cqa-font-semibold cqa-text-[#374151] cqa-bg-[#eff0f7]\"\n [style.min-width.px]=\"col.minWidth\"\n [ngClass]=\"[\n col.fieldId + '-cell',\n col.align === 'center' ? 'cqa-text-center' : col.align === 'right' ? 'cqa-text-right' : 'cqa-text-left'\n ]\">\n <!-- Built-in select-all for checkbox column when enabled and no custom header template -->\n <ng-container\n *ngIf=\"col.fieldId === 'checkbox' && enableSelectAll && !getHeaderTemplate(col.fieldId); else headerTplOrDefault\">\n <div class=\"custom-checkbox\">\n <input type=\"checkbox\" id=\"checkbox\" aria-label=\"Select all rows\" [checked]=\"allSelected\"\n [indeterminate]=\"someSelected\" (change)=\"onSelectAllChange($event)\" class=\"hidden-checkbox\" />\n <label for=\"checkbox\" class=\"custom-checkbox-label\"></label>\n </div>\n </ng-container>\n <ng-template #headerTplOrDefault>\n <ng-container *ngIf=\"getHeaderTemplate(col.fieldId) as headerTpl; else defaultHeader\">\n <ng-container *ngTemplateOutlet=\"headerTpl\"></ng-container>\n </ng-container>\n <ng-template #defaultHeader>\n <ng-container *ngIf=\"col.sortable; else plainHeader\">\n <button type=\"button\" class=\"header-text cqa-inline-flex cqa-items-center cqa-gap-1 cqa-px-0\"\n (click)=\"toggleSort(col)\" [attr.aria-label]=\"'Sort by ' + (col.fieldName || col.fieldId)\">\n <span class=\"cqa-text-[12.3px] cqa-leading-[17.5px] cqa-text-[#0A0A0A] cqa-font-medium\">{{\n col.fieldName }}</span>\n <span *ngIf=\"isSortedAsc(col.fieldId)\">\u25B2</span>\n <span *ngIf=\"isSortedDesc(col.fieldId)\">\u25BC</span>\n </button>\n </ng-container>\n <ng-template #plainHeader>\n <span class=\"header-text\">{{ col.fieldName }}</span>\n </ng-template>\n </ng-template>\n </ng-template>\n </th>\n </ng-container>\n </tr>\n </thead>\n\n <tbody class=\"table-body cqa-w-full\">\n <tr class=\"table-row cqa-bg-surface-default hover:cqa-bg-surface-hover\" *ngFor=\"let row of computedData; let rowIndex = index; trackBy: trackByIndex\"\n [class.selected]=\"row?.isSelected\">\n <ng-container *ngFor=\"let col of visibleColumns; trackBy: trackByIndex\">\n <td class=\"cell cqa-px-[10.5px] cqa-py-[11px]\"\n [style.min-width.px]=\"col.minWidth\"\n [ngClass]=\"[\n col.fieldId + '-cell',\n col.align === 'center' ? 'cqa-text-center' : col.align === 'right' ? 'cqa-text-right' : 'cqa-text-left',\n cellJsonPathGetter && col.fieldId !== 'checkbox' && col.fieldId !== 'actions' ? 'cqa-cursor-pointer' : ''\n ]\"\n (dblclick)=\"cellJsonPathGetter && col?.fieldId !== 'checkbox' && col?.fieldId !== 'actions' && copyCellJsonPath(cellJsonPathGetter(rowIndex, col?.fieldId, row), $event, 'dbQueryResponse')\">\n <!-- Built-in checkbox cell when no custom template is provided -->\n <ng-container *ngIf=\"col.fieldId === 'checkbox' && !getCellTemplate(col.fieldId); else regularCell\">\n <div class=\"custom-checkbox\" [ngClass]=\"[\n col.align === 'center' ? 'cqa-flex cqa-justify-center' : col.align === 'right' ? 'cqa-flex cqa-justify-end' : ''\n ]\">\n <input\n type=\"checkbox\"\n class=\"hidden-checkbox\"\n [attr.id]=\"'row-checkbox-' + rowIndex\"\n aria-label=\"Select row\"\n [checked]=\"row?.isSelected\"\n [disabled]=\"!isRowSelectable(row)\"\n (change)=\"onRowSelectChange($event, row)\" />\n <label\n class=\"custom-checkbox-label\"\n [style.opacity]=\"!isRowSelectable(row) ? '0.45' : null\"\n [style.cursor]=\"!isRowSelectable(row) ? 'not-allowed' : null\"\n [style.pointer-events]=\"!isRowSelectable(row) ? 'none' : null\"\n [attr.for]=\"'row-checkbox-' + rowIndex\">\n </label>\n </div>\n </ng-container>\n <ng-template #regularCell>\n <ng-container *ngIf=\"getCellTemplate(col.fieldId) as cellTpl; else defaultCell\">\n <ng-container\n *ngTemplateOutlet=\"cellTpl; context: {$implicit: row, row: row, value: getCellValue(row, col.fieldValue)}\"></ng-container>\n </ng-container>\n <ng-template #defaultCell>\n <ng-container *ngIf=\"getRenderedValue(row, col) as renderedValue\">\n <ng-container *ngIf=\"isComponent(renderedValue); else nonComponentCell\">\n <ng-container \n cqaCellContainer \n #cellContainer=\"cqaCellContainer\"\n [componentConfig]=\"renderedValue\"\n [row]=\"row\"\n [column]=\"col\"\n [rowIndex]=\"rowIndex\"\n [colId]=\"col.fieldId\">\n </ng-container>\n </ng-container>\n <ng-template #nonComponentCell>\n <ng-container *ngIf=\"isHtmlString(renderedValue); else textCell\">\n <div class=\"cqa-text-xs cqa-leading-[17px]\" [innerHTML]=\"getSanitizedHtml(renderedValue)\" [ngClass]=\"[\n col.align === 'center' ? 'cqa-flex cqa-justify-center' : col.align === 'right' ? 'cqa-flex cqa-justify-end' : ''\n ]\"></div>\n </ng-container>\n <ng-template #textCell>\n <span class=\"cqa-text-xs cqa-text-[#374151]\" [ngClass]=\"[\n col.align === 'center' ? 'cqa-flex cqa-justify-center' : col.align === 'right' ? 'cqa-flex cqa-justify-end' : ''\n ]\">{{ renderedValue }}</span>\n </ng-template>\n </ng-template>\n </ng-container>\n </ng-template>\n </ng-template>\n </td>\n </ng-container>\n </tr>\n </tbody>\n </table>\n </div>\n</div>", styles: [] }]
1605
1633
  }], ctorParameters: function () { return [{ type: i1$2.DomSanitizer }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { data: [{
1606
1634
  type: Input
1607
1635
  }], columns: [{
@@ -16199,10 +16227,10 @@ class BreakpointsModalComponent {
16199
16227
  }
16200
16228
  }
16201
16229
  BreakpointsModalComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: BreakpointsModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
16202
- BreakpointsModalComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: BreakpointsModalComponent, selector: "cqa-breakpoints-modal", inputs: { isOpen: "isOpen", title: "title", items: "items", buttonLabel: "buttonLabel", emptyStateLabel: "emptyStateLabel" }, outputs: { onClose: "onClose", onRemove: "onRemove", onRemoveAll: "onRemoveAll" }, host: { classAttribute: "cqa-ui-root" }, usesOnChanges: true, ngImport: i0, template: "<div *ngIf=\"isOpen\"\n class=\"modal-backdrop cqa-fixed cqa-inset-0 cqa-bg-black cqa-bg-opacity-50 cqa-z-50 cqa-flex cqa-items-center cqa-justify-center cqa-p-4\"\n (click)=\"onBackdropClick($event)\">\n <div\n class=\"cqa-rounded-xl cqa-bg-white cqa-shadow-lg cqa-w-full cqa-max-w-[420px] cqa-overflow-hidden cqa-flex cqa-flex-col\"\n style=\"box-shadow: 0px 8px 8px -4px #10182808; max-height: 90vh;\"\n (click)=\"$event.stopPropagation()\">\n\n <!-- Header -->\n <div class=\"cqa-px-6 cqa-pt-6 cqa-pb-2\">\n <div class=\"cqa-flex cqa-items-start cqa-justify-between cqa-gap-3\">\n <h2 class=\"cqa-text-lg cqa-font-semibold cqa-text-[#0B0B0C] cqa-text-base cqa-font-inter\">\n {{ title }}\n </h2>\n <button\n type=\"button\"\n class=\"cqa-flex cqa-items-center cqa-justify-center cqa-w-6 cqa-h-6 cqa-p-0 cqa-border-0 cqa-bg-transparent cqa-cursor-pointer cqa-text-gray-500 hover:cqa-text-gray-700 cqa-transition-colors cqa-rounded cqa--mt-[2px] cqa--mr-[2px]\"\n (click)=\"handleClose()\"\n aria-label=\"Close modal\">\n <mat-icon class=\"!cqa-w-5 !cqa-h-5 !cqa-text-[20px] !cqa-block !cqa-leading-none\">close</mat-icon>\n </button>\n </div>\n <!-- Summary text -->\n <p *ngIf=\"hasItems\" class=\"cqa-text-sm cqa-font-normal cqa-text-[#666666] cqa-text-sm cqa-leading-[1.4] cqa-font-inter cqa-m-0 cqa-mt-1\">\n {{ summaryText }}\n </p>\n </div>\n\n <!-- Divider -->\n <div class=\"cqa--mx-2 cqa-w-[calc(100%+1rem)] cqa-flex-shrink-0\">\n <div class=\"cqa-h-px cqa-w-full cqa-bg-[#E5E7EB]\" role=\"presentation\"></div>\n </div>\n\n <!-- Content -->\n <div class=\"cqa-flex-1 cqa-overflow-y-auto cqa-overflow-x-hidden cqa-px-6 cqa-pt-4 cqa-pb-2\" style=\"scrollbar-width: thin; max-height: 50vh;\">\n <!-- Empty State -->\n <div *ngIf=\"!hasItems\" class=\"cqa-flex cqa-flex-col cqa-items-center cqa-justify-center cqa-py-8 cqa-text-center\">\n <p class=\"cqa-text-sm cqa-text-[#666666] cqa-font-inter cqa-m-0\">\n {{ emptyStateLabel || 'No Data Found.' }}\n </p>\n </div>\n\n <!-- Breakpoints List -->\n <div *ngIf=\"hasItems\" class=\"cqa-flex cqa-flex-col cqa-gap-4\">\n <div\n *ngFor=\"let item of displayItems; trackBy: trackByItemId\"\n class=\"cqa-flex cqa-items-start cqa-gap-3 cqa-px-4 cqa-py-3 cqa-bg-[#e5e7eb2e] cqa-rounded-lg cqa-min-w-0 cqa-border cqa-border-solid cqa-border-[#E5E7EB]\">\n\n <!-- Red Breakpoint Dot -->\n <span class=\"cqa-flex-shrink-0 cqa-w-[10px] cqa-h-[10px] cqa-mt-[5px] cqa-rounded-full cqa-bg-[#DC2626]\" aria-hidden=\"true\"></span>\n\n <!-- Text Content -->\n <div class=\"cqa-flex cqa-flex-col cqa-gap-[2px] cqa-min-w-0 cqa-flex-1\">\n <span class=\"cqa-text-sm cqa-font-normal cqa-text-[#6B7280] cqa-leading-[1.3] cqa-font-inter\">\n {{ item.primaryLabel }}\n </span>\n <span class=\"cqa-text-sm cqa-font-normal cqa-text-[#111827] cqa-leading-[1.4] cqa-font-inter cqa-break-words\">\n {{ item.secondaryLabel }}\n </span>\n </div>\n <div (click)=\"$event.stopPropagation()\">\n <cqa-button\n type=\"button\"\n variant=\"text\"\n [customClass]=\"'cqa-api-edit-step-verification-delete'\"\n [tooltip]=\"'Remove breakpoint'\"\n (clicked)=\"removeItem(item)\">\n <svg class=\"cqa-api-edit-step-verification-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\" fill=\"#99999E\" />\n </svg>\n </cqa-button>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Footer: Remove All Button -->\n <div *ngIf=\"hasItems\" class=\"cqa-px-6 cqa-pb-6 cqa-pt-4\">\n \n <cqa-button\n [fullWidth]=\"true\"\n (click)=\"removeAll()\"\n [label]=\"buttonLabel\"\n variant=\"outlined\"\n color=\"black\"\n [text]=\"buttonLabel\"\n >\n </cqa-button>\n <!-- <button\n type=\"button\"\n class=\"cqa-w-full cqa-py-[10px] cqa-px-5 cqa-text-sm cqa-font-medium cqa-text-[#212121] cqa-bg-white cqa-border cqa-border-solid cqa-border-[#D9D9D9] cqa-rounded-lg cqa-cursor-pointer cqa-transition-colors hover:cqa-bg-[#F9FAFB] hover:cqa-border-[#9CA3AF] cqa-font-inter\"\n (click)=\"removeAll()\">\n {{ buttonLabel }}\n </button> -->\n </div>\n </div>\n</div>\n\n", components: [{ type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { type: ButtonComponent, selector: "cqa-button", inputs: ["variant", "btnSize", "disabled", "loading", "icon", "iconPosition", "fullWidth", "iconColor", "type", "text", "customClass", "inlineStyles", "tooltip", "tooltipPosition"], outputs: ["clicked"] }], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
16230
+ BreakpointsModalComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: BreakpointsModalComponent, selector: "cqa-breakpoints-modal", inputs: { isOpen: "isOpen", title: "title", items: "items", buttonLabel: "buttonLabel", emptyStateLabel: "emptyStateLabel" }, outputs: { onClose: "onClose", onRemove: "onRemove", onRemoveAll: "onRemoveAll" }, host: { classAttribute: "cqa-ui-root" }, usesOnChanges: true, ngImport: i0, template: "<div *ngIf=\"isOpen\"\n class=\"modal-backdrop cqa-fixed cqa-inset-0 cqa-bg-black cqa-bg-opacity-50 cqa-z-[100] cqa-flex cqa-items-center cqa-justify-center cqa-p-4\"\n (click)=\"onBackdropClick($event)\">\n <div\n class=\"cqa-rounded-xl cqa-bg-white cqa-shadow-lg cqa-w-full cqa-max-w-[420px] cqa-overflow-hidden cqa-flex cqa-flex-col\"\n style=\"box-shadow: 0px 8px 8px -4px #10182808; max-height: 90vh;\"\n (click)=\"$event.stopPropagation()\">\n\n <!-- Header -->\n <div class=\"cqa-px-6 cqa-pt-6 cqa-pb-2\">\n <div class=\"cqa-flex cqa-items-start cqa-justify-between cqa-gap-3\">\n <h2 class=\"cqa-text-lg cqa-font-semibold cqa-text-[#0B0B0C] cqa-text-base cqa-font-inter\">\n {{ title }}\n </h2>\n <button\n type=\"button\"\n class=\"cqa-flex cqa-items-center cqa-justify-center cqa-w-6 cqa-h-6 cqa-p-0 cqa-border-0 cqa-bg-transparent cqa-cursor-pointer cqa-text-gray-500 hover:cqa-text-gray-700 cqa-transition-colors cqa-rounded cqa--mt-[2px] cqa--mr-[2px]\"\n (click)=\"handleClose()\"\n aria-label=\"Close modal\">\n <mat-icon class=\"!cqa-w-5 !cqa-h-5 !cqa-text-[20px] !cqa-block !cqa-leading-none\">close</mat-icon>\n </button>\n </div>\n <!-- Summary text -->\n <p *ngIf=\"hasItems\" class=\"cqa-text-sm cqa-font-normal cqa-text-[#666666] cqa-text-sm cqa-leading-[1.4] cqa-font-inter cqa-m-0 cqa-mt-1\">\n {{ summaryText }}\n </p>\n </div>\n\n <!-- Divider -->\n <div class=\"cqa--mx-2 cqa-w-[calc(100%+1rem)] cqa-flex-shrink-0\">\n <div class=\"cqa-h-px cqa-w-full cqa-bg-[#E5E7EB]\" role=\"presentation\"></div>\n </div>\n\n <!-- Content -->\n <div class=\"cqa-flex-1 cqa-overflow-y-auto cqa-overflow-x-hidden cqa-px-6 cqa-pt-4 cqa-pb-2\" style=\"scrollbar-width: thin; max-height: 50vh;\">\n <!-- Empty State -->\n <div *ngIf=\"!hasItems\" class=\"cqa-flex cqa-flex-col cqa-items-center cqa-justify-center cqa-py-8 cqa-text-center\">\n <p class=\"cqa-text-sm cqa-text-[#666666] cqa-font-inter cqa-m-0\">\n {{ emptyStateLabel || 'No Data Found.' }}\n </p>\n </div>\n\n <!-- Breakpoints List -->\n <div *ngIf=\"hasItems\" class=\"cqa-flex cqa-flex-col cqa-gap-4\">\n <div\n *ngFor=\"let item of displayItems; trackBy: trackByItemId\"\n class=\"cqa-flex cqa-items-start cqa-gap-3 cqa-px-4 cqa-py-3 cqa-bg-[#e5e7eb2e] cqa-rounded-lg cqa-min-w-0 cqa-border cqa-border-solid cqa-border-[#E5E7EB]\">\n\n <!-- Red Breakpoint Dot -->\n <span class=\"cqa-flex-shrink-0 cqa-w-[10px] cqa-h-[10px] cqa-mt-[5px] cqa-rounded-full cqa-bg-[#DC2626]\" aria-hidden=\"true\"></span>\n\n <!-- Text Content -->\n <div class=\"cqa-flex cqa-flex-col cqa-gap-[2px] cqa-min-w-0 cqa-flex-1\">\n <span class=\"cqa-text-sm cqa-font-normal cqa-text-[#6B7280] cqa-leading-[1.3] cqa-font-inter\">\n {{ item.primaryLabel }}\n </span>\n <span class=\"cqa-text-sm cqa-font-normal cqa-text-[#111827] cqa-leading-[1.4] cqa-font-inter cqa-break-words\">\n {{ item.secondaryLabel }}\n </span>\n </div>\n <div (click)=\"$event.stopPropagation()\">\n <cqa-button\n type=\"button\"\n variant=\"text\"\n [customClass]=\"'cqa-api-edit-step-verification-delete'\"\n [tooltip]=\"'Remove breakpoint'\"\n (clicked)=\"removeItem(item)\">\n <svg class=\"cqa-api-edit-step-verification-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\" fill=\"#99999E\" />\n </svg>\n </cqa-button>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Footer: Remove All Button -->\n <div *ngIf=\"hasItems\" class=\"cqa-px-6 cqa-pb-6 cqa-pt-4\">\n \n <cqa-button\n [fullWidth]=\"true\"\n (click)=\"removeAll()\"\n [label]=\"buttonLabel\"\n variant=\"outlined\"\n color=\"black\"\n [text]=\"buttonLabel\"\n >\n </cqa-button>\n <!-- <button\n type=\"button\"\n class=\"cqa-w-full cqa-py-[10px] cqa-px-5 cqa-text-sm cqa-font-medium cqa-text-[#212121] cqa-bg-white cqa-border cqa-border-solid cqa-border-[#D9D9D9] cqa-rounded-lg cqa-cursor-pointer cqa-transition-colors hover:cqa-bg-[#F9FAFB] hover:cqa-border-[#9CA3AF] cqa-font-inter\"\n (click)=\"removeAll()\">\n {{ buttonLabel }}\n </button> -->\n </div>\n </div>\n</div>\n\n", components: [{ type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { type: ButtonComponent, selector: "cqa-button", inputs: ["variant", "btnSize", "disabled", "loading", "icon", "iconPosition", "fullWidth", "iconColor", "type", "text", "customClass", "inlineStyles", "tooltip", "tooltipPosition"], outputs: ["clicked"] }], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
16203
16231
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: BreakpointsModalComponent, decorators: [{
16204
16232
  type: Component,
16205
- args: [{ selector: 'cqa-breakpoints-modal', host: { class: 'cqa-ui-root' }, template: "<div *ngIf=\"isOpen\"\n class=\"modal-backdrop cqa-fixed cqa-inset-0 cqa-bg-black cqa-bg-opacity-50 cqa-z-50 cqa-flex cqa-items-center cqa-justify-center cqa-p-4\"\n (click)=\"onBackdropClick($event)\">\n <div\n class=\"cqa-rounded-xl cqa-bg-white cqa-shadow-lg cqa-w-full cqa-max-w-[420px] cqa-overflow-hidden cqa-flex cqa-flex-col\"\n style=\"box-shadow: 0px 8px 8px -4px #10182808; max-height: 90vh;\"\n (click)=\"$event.stopPropagation()\">\n\n <!-- Header -->\n <div class=\"cqa-px-6 cqa-pt-6 cqa-pb-2\">\n <div class=\"cqa-flex cqa-items-start cqa-justify-between cqa-gap-3\">\n <h2 class=\"cqa-text-lg cqa-font-semibold cqa-text-[#0B0B0C] cqa-text-base cqa-font-inter\">\n {{ title }}\n </h2>\n <button\n type=\"button\"\n class=\"cqa-flex cqa-items-center cqa-justify-center cqa-w-6 cqa-h-6 cqa-p-0 cqa-border-0 cqa-bg-transparent cqa-cursor-pointer cqa-text-gray-500 hover:cqa-text-gray-700 cqa-transition-colors cqa-rounded cqa--mt-[2px] cqa--mr-[2px]\"\n (click)=\"handleClose()\"\n aria-label=\"Close modal\">\n <mat-icon class=\"!cqa-w-5 !cqa-h-5 !cqa-text-[20px] !cqa-block !cqa-leading-none\">close</mat-icon>\n </button>\n </div>\n <!-- Summary text -->\n <p *ngIf=\"hasItems\" class=\"cqa-text-sm cqa-font-normal cqa-text-[#666666] cqa-text-sm cqa-leading-[1.4] cqa-font-inter cqa-m-0 cqa-mt-1\">\n {{ summaryText }}\n </p>\n </div>\n\n <!-- Divider -->\n <div class=\"cqa--mx-2 cqa-w-[calc(100%+1rem)] cqa-flex-shrink-0\">\n <div class=\"cqa-h-px cqa-w-full cqa-bg-[#E5E7EB]\" role=\"presentation\"></div>\n </div>\n\n <!-- Content -->\n <div class=\"cqa-flex-1 cqa-overflow-y-auto cqa-overflow-x-hidden cqa-px-6 cqa-pt-4 cqa-pb-2\" style=\"scrollbar-width: thin; max-height: 50vh;\">\n <!-- Empty State -->\n <div *ngIf=\"!hasItems\" class=\"cqa-flex cqa-flex-col cqa-items-center cqa-justify-center cqa-py-8 cqa-text-center\">\n <p class=\"cqa-text-sm cqa-text-[#666666] cqa-font-inter cqa-m-0\">\n {{ emptyStateLabel || 'No Data Found.' }}\n </p>\n </div>\n\n <!-- Breakpoints List -->\n <div *ngIf=\"hasItems\" class=\"cqa-flex cqa-flex-col cqa-gap-4\">\n <div\n *ngFor=\"let item of displayItems; trackBy: trackByItemId\"\n class=\"cqa-flex cqa-items-start cqa-gap-3 cqa-px-4 cqa-py-3 cqa-bg-[#e5e7eb2e] cqa-rounded-lg cqa-min-w-0 cqa-border cqa-border-solid cqa-border-[#E5E7EB]\">\n\n <!-- Red Breakpoint Dot -->\n <span class=\"cqa-flex-shrink-0 cqa-w-[10px] cqa-h-[10px] cqa-mt-[5px] cqa-rounded-full cqa-bg-[#DC2626]\" aria-hidden=\"true\"></span>\n\n <!-- Text Content -->\n <div class=\"cqa-flex cqa-flex-col cqa-gap-[2px] cqa-min-w-0 cqa-flex-1\">\n <span class=\"cqa-text-sm cqa-font-normal cqa-text-[#6B7280] cqa-leading-[1.3] cqa-font-inter\">\n {{ item.primaryLabel }}\n </span>\n <span class=\"cqa-text-sm cqa-font-normal cqa-text-[#111827] cqa-leading-[1.4] cqa-font-inter cqa-break-words\">\n {{ item.secondaryLabel }}\n </span>\n </div>\n <div (click)=\"$event.stopPropagation()\">\n <cqa-button\n type=\"button\"\n variant=\"text\"\n [customClass]=\"'cqa-api-edit-step-verification-delete'\"\n [tooltip]=\"'Remove breakpoint'\"\n (clicked)=\"removeItem(item)\">\n <svg class=\"cqa-api-edit-step-verification-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\" fill=\"#99999E\" />\n </svg>\n </cqa-button>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Footer: Remove All Button -->\n <div *ngIf=\"hasItems\" class=\"cqa-px-6 cqa-pb-6 cqa-pt-4\">\n \n <cqa-button\n [fullWidth]=\"true\"\n (click)=\"removeAll()\"\n [label]=\"buttonLabel\"\n variant=\"outlined\"\n color=\"black\"\n [text]=\"buttonLabel\"\n >\n </cqa-button>\n <!-- <button\n type=\"button\"\n class=\"cqa-w-full cqa-py-[10px] cqa-px-5 cqa-text-sm cqa-font-medium cqa-text-[#212121] cqa-bg-white cqa-border cqa-border-solid cqa-border-[#D9D9D9] cqa-rounded-lg cqa-cursor-pointer cqa-transition-colors hover:cqa-bg-[#F9FAFB] hover:cqa-border-[#9CA3AF] cqa-font-inter\"\n (click)=\"removeAll()\">\n {{ buttonLabel }}\n </button> -->\n </div>\n </div>\n</div>\n\n", styles: [] }]
16233
+ args: [{ selector: 'cqa-breakpoints-modal', host: { class: 'cqa-ui-root' }, template: "<div *ngIf=\"isOpen\"\n class=\"modal-backdrop cqa-fixed cqa-inset-0 cqa-bg-black cqa-bg-opacity-50 cqa-z-[100] cqa-flex cqa-items-center cqa-justify-center cqa-p-4\"\n (click)=\"onBackdropClick($event)\">\n <div\n class=\"cqa-rounded-xl cqa-bg-white cqa-shadow-lg cqa-w-full cqa-max-w-[420px] cqa-overflow-hidden cqa-flex cqa-flex-col\"\n style=\"box-shadow: 0px 8px 8px -4px #10182808; max-height: 90vh;\"\n (click)=\"$event.stopPropagation()\">\n\n <!-- Header -->\n <div class=\"cqa-px-6 cqa-pt-6 cqa-pb-2\">\n <div class=\"cqa-flex cqa-items-start cqa-justify-between cqa-gap-3\">\n <h2 class=\"cqa-text-lg cqa-font-semibold cqa-text-[#0B0B0C] cqa-text-base cqa-font-inter\">\n {{ title }}\n </h2>\n <button\n type=\"button\"\n class=\"cqa-flex cqa-items-center cqa-justify-center cqa-w-6 cqa-h-6 cqa-p-0 cqa-border-0 cqa-bg-transparent cqa-cursor-pointer cqa-text-gray-500 hover:cqa-text-gray-700 cqa-transition-colors cqa-rounded cqa--mt-[2px] cqa--mr-[2px]\"\n (click)=\"handleClose()\"\n aria-label=\"Close modal\">\n <mat-icon class=\"!cqa-w-5 !cqa-h-5 !cqa-text-[20px] !cqa-block !cqa-leading-none\">close</mat-icon>\n </button>\n </div>\n <!-- Summary text -->\n <p *ngIf=\"hasItems\" class=\"cqa-text-sm cqa-font-normal cqa-text-[#666666] cqa-text-sm cqa-leading-[1.4] cqa-font-inter cqa-m-0 cqa-mt-1\">\n {{ summaryText }}\n </p>\n </div>\n\n <!-- Divider -->\n <div class=\"cqa--mx-2 cqa-w-[calc(100%+1rem)] cqa-flex-shrink-0\">\n <div class=\"cqa-h-px cqa-w-full cqa-bg-[#E5E7EB]\" role=\"presentation\"></div>\n </div>\n\n <!-- Content -->\n <div class=\"cqa-flex-1 cqa-overflow-y-auto cqa-overflow-x-hidden cqa-px-6 cqa-pt-4 cqa-pb-2\" style=\"scrollbar-width: thin; max-height: 50vh;\">\n <!-- Empty State -->\n <div *ngIf=\"!hasItems\" class=\"cqa-flex cqa-flex-col cqa-items-center cqa-justify-center cqa-py-8 cqa-text-center\">\n <p class=\"cqa-text-sm cqa-text-[#666666] cqa-font-inter cqa-m-0\">\n {{ emptyStateLabel || 'No Data Found.' }}\n </p>\n </div>\n\n <!-- Breakpoints List -->\n <div *ngIf=\"hasItems\" class=\"cqa-flex cqa-flex-col cqa-gap-4\">\n <div\n *ngFor=\"let item of displayItems; trackBy: trackByItemId\"\n class=\"cqa-flex cqa-items-start cqa-gap-3 cqa-px-4 cqa-py-3 cqa-bg-[#e5e7eb2e] cqa-rounded-lg cqa-min-w-0 cqa-border cqa-border-solid cqa-border-[#E5E7EB]\">\n\n <!-- Red Breakpoint Dot -->\n <span class=\"cqa-flex-shrink-0 cqa-w-[10px] cqa-h-[10px] cqa-mt-[5px] cqa-rounded-full cqa-bg-[#DC2626]\" aria-hidden=\"true\"></span>\n\n <!-- Text Content -->\n <div class=\"cqa-flex cqa-flex-col cqa-gap-[2px] cqa-min-w-0 cqa-flex-1\">\n <span class=\"cqa-text-sm cqa-font-normal cqa-text-[#6B7280] cqa-leading-[1.3] cqa-font-inter\">\n {{ item.primaryLabel }}\n </span>\n <span class=\"cqa-text-sm cqa-font-normal cqa-text-[#111827] cqa-leading-[1.4] cqa-font-inter cqa-break-words\">\n {{ item.secondaryLabel }}\n </span>\n </div>\n <div (click)=\"$event.stopPropagation()\">\n <cqa-button\n type=\"button\"\n variant=\"text\"\n [customClass]=\"'cqa-api-edit-step-verification-delete'\"\n [tooltip]=\"'Remove breakpoint'\"\n (clicked)=\"removeItem(item)\">\n <svg class=\"cqa-api-edit-step-verification-delete-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M10.6663 6V12.6667H5.33301V6H10.6663ZM9.66634 2H6.33301L5.66634 2.66667H3.33301V4H12.6663V2.66667H10.333L9.66634 2ZM11.9997 4.66667H3.99967V12.6667C3.99967 13.4 4.59967 14 5.33301 14H10.6663C11.3997 14 11.9997 13.4 11.9997 12.6667V4.66667Z\" fill=\"#99999E\" />\n </svg>\n </cqa-button>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Footer: Remove All Button -->\n <div *ngIf=\"hasItems\" class=\"cqa-px-6 cqa-pb-6 cqa-pt-4\">\n \n <cqa-button\n [fullWidth]=\"true\"\n (click)=\"removeAll()\"\n [label]=\"buttonLabel\"\n variant=\"outlined\"\n color=\"black\"\n [text]=\"buttonLabel\"\n >\n </cqa-button>\n <!-- <button\n type=\"button\"\n class=\"cqa-w-full cqa-py-[10px] cqa-px-5 cqa-text-sm cqa-font-medium cqa-text-[#212121] cqa-bg-white cqa-border cqa-border-solid cqa-border-[#D9D9D9] cqa-rounded-lg cqa-cursor-pointer cqa-transition-colors hover:cqa-bg-[#F9FAFB] hover:cqa-border-[#9CA3AF] cqa-font-inter\"\n (click)=\"removeAll()\">\n {{ buttonLabel }}\n </button> -->\n </div>\n </div>\n</div>\n\n", styles: [] }]
16206
16234
  }], propDecorators: { isOpen: [{
16207
16235
  type: Input
16208
16236
  }], title: [{
@@ -16578,12 +16606,18 @@ class SimulatorComponent {
16578
16606
  this.showVideoLibrary = false;
16579
16607
  this.showCaptureVideo = false;
16580
16608
  this.isCapturingVideo = false;
16609
+ // When `isLive` and at least one clip is present in `videoUrls`, the simulator
16610
+ // header shows a Live/Captured segment control. Parent can also drive the view
16611
+ // imperatively (e.g. "Watch Now" button in the capture dialog) by pushing a
16612
+ // new value through this input.
16613
+ this.liveSessionView = 'live';
16581
16614
  this.videoTimeUpdate = new EventEmitter();
16582
16615
  this.videoPlay = new EventEmitter();
16583
16616
  this.videoPause = new EventEmitter();
16584
16617
  this.markerHit = new EventEmitter();
16585
16618
  this.isVideoPlayingChange = new EventEmitter();
16586
16619
  this.captureVideoRequested = new EventEmitter();
16620
+ this.liveSessionViewChange = new EventEmitter();
16587
16621
  this.progress = 0;
16588
16622
  this.dragging = false;
16589
16623
  this.isPlaying = false;
@@ -16646,6 +16680,10 @@ class SimulatorComponent {
16646
16680
  { label: '2x', value: '2x' },
16647
16681
  { label: '5x', value: '5x' },
16648
16682
  ];
16683
+ this.liveSessionSegments = [
16684
+ { label: 'Live', value: 'live' },
16685
+ { label: 'Captured', value: 'captured' },
16686
+ ];
16649
16687
  this.videoEventListenerCleanup = null;
16650
16688
  this.lastSetDuration = -1;
16651
16689
  this.dragMouseMoveHandler = null;
@@ -16869,8 +16907,13 @@ class SimulatorComponent {
16869
16907
  return Math.min(100, (c.currentStep / c.totalSteps) * 100);
16870
16908
  }
16871
16909
  set vplayerRef(ref) {
16872
- if (!ref)
16910
+ if (!ref) {
16911
+ // Element is being unmounted (e.g. user toggled from Captured → Live during a
16912
+ // mid-flight switchToVideoAndResetInternal). Drop the stale reference so any
16913
+ // follow-up attempt to operate on .nativeElement short-circuits out cleanly.
16914
+ this._vplayer = undefined;
16873
16915
  return;
16916
+ }
16874
16917
  this._vplayer = ref;
16875
16918
  this.onVideoElementReady();
16876
16919
  }
@@ -16926,6 +16969,52 @@ class SimulatorComponent {
16926
16969
  }
16927
16970
  return {};
16928
16971
  }
16972
+ onLiveSessionViewChange(next) {
16973
+ const resolved = (next === 'captured' ? 'captured' : 'live');
16974
+ if (resolved !== this.liveSessionView) {
16975
+ this.liveSessionView = resolved;
16976
+ this.resetLiveSessionPlaybackState();
16977
+ this.liveSessionViewChange.emit(resolved);
16978
+ }
16979
+ }
16980
+ /** Reset the video player to a clean slate whenever the Live/Captured view toggles
16981
+ * during an active debug session. Pauses playback, exits Full Video mode, rewinds
16982
+ * to clip 0, and clears drag/scroll/marker state.
16983
+ *
16984
+ * Applied synchronously (no operationQueue) because the toggle usually also
16985
+ * unmounts/remounts the <video> element — any in-flight operation on the old
16986
+ * element is already dead, and a fresh element naturally mounts with
16987
+ * `src = videoUrls[0]` from `currentVideoUrl`. Enqueueing here would race with
16988
+ * the element lifecycle and either get discarded or block the queue. */
16989
+ resetLiveSessionPlaybackState() {
16990
+ this.isVideoFullMode = false;
16991
+ this.isVideoLibraryCollapsed = true;
16992
+ this.progress = 0;
16993
+ this.globalDragProgress = 0;
16994
+ this.dragging = false;
16995
+ this.lastScrolledClipIndex = -1;
16996
+ this.currentVideoIndex = 0;
16997
+ this.isPlaying = false;
16998
+ this.playPromise = null;
16999
+ this.hitMarkers.clear();
17000
+ this.lastSetDuration = -1;
17001
+ const video = this._vplayer?.nativeElement;
17002
+ if (video) {
17003
+ try {
17004
+ video.pause();
17005
+ }
17006
+ catch { /* detached element — ignore */ }
17007
+ try {
17008
+ video.currentTime = 0;
17009
+ }
17010
+ catch { /* detached element — ignore */ }
17011
+ this.playerState = 'paused';
17012
+ }
17013
+ else {
17014
+ this.playerState = 'idle';
17015
+ }
17016
+ this.isVideoPlayingChange.emit(false);
17017
+ }
16929
17018
  get effectiveBrowserViewPort() {
16930
17019
  const defaultViewport = { width: 1280, height: 720 };
16931
17020
  if (!this.browserViewPort) {
@@ -17025,11 +17114,53 @@ class SimulatorComponent {
17025
17114
  }
17026
17115
  ngOnChanges(changes) {
17027
17116
  if (changes['videoUrls']) {
17028
- const arr = changes['videoUrls'].currentValue;
17029
- if (arr && arr.length > 0) {
17117
+ const prev = changes['videoUrls'].previousValue ?? [];
17118
+ const curr = changes['videoUrls'].currentValue ?? [];
17119
+ // Mid-session captures append to videoUrls. Treat append-only changes as
17120
+ // additive so the currently-playing clip isn't yanked back to index 0,
17121
+ // and mark the new tail clips with NEW badges.
17122
+ const isAppendOnly = curr.length > prev.length &&
17123
+ prev.every((u, i) => curr[i] === u);
17124
+ if (curr.length > 0) {
17125
+ if (isAppendOnly) {
17126
+ for (let i = prev.length; i < curr.length; i++) {
17127
+ this.newVideoIndexes.add(i);
17128
+ }
17129
+ }
17130
+ else {
17131
+ this.currentVideoIndex = 0;
17132
+ this.progress = 0;
17133
+ this.newVideoIndexes.clear();
17134
+ // URLs replaced (e.g. execution_completed swapping captured → screen_recordings).
17135
+ // Discard stale duration + library caches so the new clips are re-probed.
17136
+ this.detectedVideoDurations = [];
17137
+ this.libraryVideoDurations.clear();
17138
+ }
17139
+ this.detectVideoDurations(curr);
17140
+ this.schedulePreloadAllSegments(curr);
17141
+ }
17142
+ else {
17030
17143
  this.currentVideoIndex = 0;
17031
- this.detectVideoDurations(arr);
17032
- this.schedulePreloadAllSegments(arr);
17144
+ this.progress = 0;
17145
+ this.newVideoIndexes.clear();
17146
+ this.detectedVideoDurations = [];
17147
+ this.libraryVideoDurations.clear();
17148
+ }
17149
+ }
17150
+ if (changes['isLive'] && changes['isLive'].currentValue === false) {
17151
+ // New post-execution session: default back to 'live' so the next live
17152
+ // run starts with the live view, not whatever the user was last on.
17153
+ this.liveSessionView = 'live';
17154
+ }
17155
+ if (changes['liveSessionView'] && !changes['liveSessionView'].firstChange) {
17156
+ const next = changes['liveSessionView'].currentValue;
17157
+ const resolved = (next === 'captured' ? 'captured' : 'live');
17158
+ const prev = changes['liveSessionView'].previousValue;
17159
+ this.liveSessionView = resolved;
17160
+ if (resolved !== prev) {
17161
+ // Parent pushed a new view (e.g. "Watch Now" or resume-action returning to Live).
17162
+ // Reset the simulator the same way the segment control would.
17163
+ this.resetLiveSessionPlaybackState();
17033
17164
  }
17034
17165
  }
17035
17166
  if (changes['videoCurrentDuration'] && !changes['videoCurrentDuration'].firstChange) {
@@ -17414,10 +17545,32 @@ class SimulatorComponent {
17414
17545
  return 0;
17415
17546
  return durations.reduce((a, b) => a + b, 0);
17416
17547
  }
17417
- /** Video durations in ms (detected or from single video element) */
17548
+ /** Video durations in ms (detected or from single video element).
17549
+ *
17550
+ * Merges two independent probes: `detectedVideoDurations` (off-DOM temp videos)
17551
+ * and `libraryVideoDurations` (the rendered library thumbnails). Either can
17552
+ * resolve first depending on browser quirks with WebM/Infinity-duration clips;
17553
+ * we take the first finite value per index. */
17418
17554
  get videoDurations() {
17419
- if (this.hasMultipleVideos && this.detectedVideoDurations.length > 0) {
17420
- return this.detectedVideoDurations;
17555
+ if (this.hasMultipleVideos) {
17556
+ const urls = this.videoUrls || [];
17557
+ if (urls.length === 0)
17558
+ return [];
17559
+ const merged = new Array(urls.length);
17560
+ for (let i = 0; i < urls.length; i++) {
17561
+ const detected = this.detectedVideoDurations[i];
17562
+ if (typeof detected === 'number' && isFinite(detected) && detected > 0) {
17563
+ merged[i] = detected;
17564
+ continue;
17565
+ }
17566
+ const fromLibraryS = this.libraryVideoDurations.get(i);
17567
+ if (typeof fromLibraryS === 'number' && isFinite(fromLibraryS) && fromLibraryS > 0) {
17568
+ merged[i] = fromLibraryS * 1000;
17569
+ continue;
17570
+ }
17571
+ // Unknown yet — leave the slot as a hole so callers can detect partial state.
17572
+ }
17573
+ return merged;
17421
17574
  }
17422
17575
  const video = this.vplayer?.nativeElement;
17423
17576
  if (video?.duration && isFinite(video.duration)) {
@@ -17979,43 +18132,92 @@ class SimulatorComponent {
17979
18132
  }, { once: true });
17980
18133
  }
17981
18134
  /**
17982
- * Detect actual video durations by loading video metadata
17983
- * This creates temporary video elements to read duration without playing
18135
+ * Detect actual video durations by loading video metadata.
18136
+ *
18137
+ * Browser-recorded WebM clips frequently report `duration === Infinity` on the first
18138
+ * `loadedmetadata` firing; the real duration only resolves after seeking past the end
18139
+ * (same trick `onLibraryClipMetadataLoaded` uses for the library thumbnails). We mirror
18140
+ * that probe here so `detectedVideoDurations` ends up dense and `totalDuration` is
18141
+ * correct in Full Video mode.
18142
+ *
18143
+ * Preserves previously-detected entries on re-invocation (append-only videoUrls
18144
+ * changes shouldn't wipe finished detections from earlier clips).
17984
18145
  */
17985
18146
  detectVideoDurations(videoUrls) {
17986
18147
  if (!videoUrls || videoUrls.length === 0) {
17987
18148
  this.detectedVideoDurations = [];
17988
18149
  return;
17989
18150
  }
17990
- // Reset detected durations
17991
- this.detectedVideoDurations = [];
17992
- let loadedCount = 0;
18151
+ // Preserve previously-detected values for existing indexes; trim/pad to current length.
18152
+ const previous = this.detectedVideoDurations.slice(0, videoUrls.length);
18153
+ this.detectedVideoDurations = previous;
17993
18154
  videoUrls.forEach((url, index) => {
18155
+ const existing = this.detectedVideoDurations[index];
18156
+ if (typeof existing === 'number' && isFinite(existing) && existing > 0) {
18157
+ return; // already resolved for this URL position
18158
+ }
17994
18159
  const tempVideo = document.createElement('video');
17995
18160
  tempVideo.preload = 'metadata';
17996
- const onLoadedMetadata = () => {
17997
- const durationMs = tempVideo.duration * 1000;
17998
- this.detectedVideoDurations[index] = durationMs;
17999
- loadedCount++;
18000
- // Clean up
18001
- tempVideo.removeEventListener('loadedmetadata', onLoadedMetadata);
18161
+ tempVideo.muted = true;
18162
+ tempVideo.playsInline = true;
18163
+ // Chrome ignores `currentTime = MAX_SAFE_INTEGER` on a detached element, so the
18164
+ // `durationchange` event never fires for Infinity-duration WebM clips. Attach
18165
+ // off-screen (1×1 invisible) so the seek probe actually resolves, matching
18166
+ // the library thumbnails' behavior.
18167
+ tempVideo.style.position = 'fixed';
18168
+ tempVideo.style.width = '1px';
18169
+ tempVideo.style.height = '1px';
18170
+ tempVideo.style.opacity = '0';
18171
+ tempVideo.style.pointerEvents = 'none';
18172
+ tempVideo.style.left = '-9999px';
18173
+ tempVideo.style.top = '-9999px';
18174
+ document.body.appendChild(tempVideo);
18175
+ let probed = false;
18176
+ let settled = false;
18177
+ const cleanup = () => {
18178
+ tempVideo.removeEventListener('loadedmetadata', onMetadataOrChange);
18179
+ tempVideo.removeEventListener('durationchange', onMetadataOrChange);
18002
18180
  tempVideo.removeEventListener('error', onError);
18003
18181
  tempVideo.src = '';
18004
18182
  tempVideo.remove();
18005
- if (loadedCount === videoUrls.length) {
18006
- console.log('[Simulator] All video durations detected:', this.detectedVideoDurations);
18183
+ };
18184
+ const storeIfValid = (d) => {
18185
+ if (typeof d === 'number' && isFinite(d) && d > 0) {
18186
+ this.detectedVideoDurations[index] = d * 1000;
18187
+ this.cdr.markForCheck();
18188
+ return true;
18189
+ }
18190
+ return false;
18191
+ };
18192
+ const onMetadataOrChange = () => {
18193
+ if (settled)
18194
+ return;
18195
+ const d = tempVideo.duration;
18196
+ if (d === Infinity && !probed) {
18197
+ // WebM with unknown duration: force the browser to resolve by seeking to the
18198
+ // end. The subsequent `durationchange` event reports the real value.
18199
+ probed = true;
18200
+ try {
18201
+ tempVideo.currentTime = Number.MAX_SAFE_INTEGER;
18202
+ }
18203
+ catch (_) { /* some browsers throw before metadata is ready */ }
18204
+ return;
18205
+ }
18206
+ if (storeIfValid(d)) {
18207
+ settled = true;
18208
+ cleanup();
18007
18209
  }
18008
18210
  };
18009
18211
  const onError = () => {
18212
+ if (settled)
18213
+ return;
18214
+ settled = true;
18010
18215
  console.warn(`[Simulator] Failed to load metadata for video ${index + 1}`);
18011
18216
  this.detectedVideoDurations[index] = 0;
18012
- loadedCount++;
18013
- tempVideo.removeEventListener('loadedmetadata', onLoadedMetadata);
18014
- tempVideo.removeEventListener('error', onError);
18015
- tempVideo.src = '';
18016
- tempVideo.remove();
18217
+ cleanup();
18017
18218
  };
18018
- tempVideo.addEventListener('loadedmetadata', onLoadedMetadata);
18219
+ tempVideo.addEventListener('loadedmetadata', onMetadataOrChange);
18220
+ tempVideo.addEventListener('durationchange', onMetadataOrChange);
18019
18221
  tempVideo.addEventListener('error', onError);
18020
18222
  tempVideo.src = url;
18021
18223
  });
@@ -18550,12 +18752,22 @@ class SimulatorComponent {
18550
18752
  isFullScreen: this.isFullScreen,
18551
18753
  readyState: video.readyState
18552
18754
  });
18553
- // Reset error/loading states when video element is recreated (e.g., during fullscreen toggle)
18554
- if (this.playerState === 'error' || this.playerState === 'loading') {
18555
- console.log('[Simulator] onVideoElementReady: resetting error/loading state');
18755
+ // Any in-flight operation on a prior <video> element is dead once it unmounts
18756
+ // (e.g. liveSessionView toggled mid-switch the detached element never fires
18757
+ // `loadedmetadata` and `switchToVideoAndResetInternal` hangs forever, blocking
18758
+ // the operationQueue behind it). Reset from every transient state and drop the
18759
+ // stuck chain so subsequent togglePlay / seek / switch calls actually run.
18760
+ if (this.playerState === 'error' ||
18761
+ this.playerState === 'loading' ||
18762
+ this.playerState === 'switching' ||
18763
+ this.playerState === 'seeking' ||
18764
+ this.playerState === 'playing') {
18765
+ console.log('[Simulator] onVideoElementReady: resetting stuck state', this.playerState);
18556
18766
  this.playerState = 'idle';
18557
18767
  this.isPlaying = false;
18558
18768
  this.playPromise = null;
18769
+ this.operationQueue = Promise.resolve();
18770
+ this.isVideoPlayingChange.emit(false);
18559
18771
  }
18560
18772
  // Apply current playback speed when the video element is first ready
18561
18773
  this.applyCurrentPlaybackRate();
@@ -18735,10 +18947,10 @@ class SimulatorComponent {
18735
18947
  }
18736
18948
  }
18737
18949
  SimulatorComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: SimulatorComponent, deps: [{ token: i1$2.DomSanitizer }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
18738
- SimulatorComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: SimulatorComponent, selector: "cqa-simulator", inputs: { videoUrl: "videoUrl", videoUrls: "videoUrls", videoCurrentDuration: "videoCurrentDuration", stepMarkers: "stepMarkers", screenShotUrl: "screenShotUrl", traceViewUrl: "traceViewUrl", platformName: "platformName", platformType: "platformType", platform: "platform", deviceName: "deviceName", isLive: "isLive", liveStatus: "liveStatus", liveLoadingLabel: "liveLoadingLabel", isContentVideoLoading: "isContentVideoLoading", failedStatusMessage: "failedStatusMessage", isVNCSessionIntruppted: "isVNCSessionIntruppted", vncSessionIntupptedMessage: "vncSessionIntupptedMessage", selectedView: "selectedView", hideVideoTab: "hideVideoTab", browserViewPort: "browserViewPort", browserDevice: "browserDevice", isFastForwarding: "isFastForwarding", fastForwardConfig: "fastForwardConfig", showVideoLibrary: "showVideoLibrary", showCaptureVideo: "showCaptureVideo", isCapturingVideo: "isCapturingVideo" }, outputs: { videoTimeUpdate: "videoTimeUpdate", videoPlay: "videoPlay", videoPause: "videoPause", markerHit: "markerHit", isVideoPlayingChange: "isVideoPlayingChange", captureVideoRequested: "captureVideoRequested" }, host: { listeners: { "window:resize": "onWindowResize()" } }, viewQueries: [{ propertyName: "vplayerRef", first: true, predicate: ["vplayer"], descendants: true }, { propertyName: "timelineBarRef", first: true, predicate: ["timelineBar"], descendants: true }, { propertyName: "speedControlContainerRef", first: true, predicate: ["speedControlContainer"], descendants: true }, { propertyName: "clipsScrollerRef", first: true, predicate: ["clipsScroller"], descendants: true }, { propertyName: "clipVideos", predicate: ["clipVideo"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"cqa-ui-root\" style=\"background-color: #F3F4F6; height: 100%; display: flex; flex-direction: column;\" [ngStyle]=\"{\n position: isFullScreen ? 'fixed' : null,\n inset: isFullScreen ? '1rem' : null,\n zIndex: isFullScreen ? '50' : null,\n boxShadow: isFullScreen ? '0px 13px 25px -12px rgba(0, 0, 0, 0.25)' : null,\n borderRadius: isFullScreen ? '.5rem' : null,\n border: isFullScreen ? '1px solid #E5E7EB' : null,\n width: isFullScreen ? 'calc(100% - 32px)' : null,\n height: isFullScreen ? 'calc(100% - 32px)' : '100%',\n overflow: isFullScreen ? 'hidden' : null\n}\">\n <div class=\"cqa-w-full cqa-py-1 cqa-px-2 cqa-bg-[#FFFFFF]\" style=\"border-bottom: 1px solid #E5E7EB;box-shadow: 0px 1px 2px 0px #0000000D;\">\n <div class=\"cqa-w-full cqa-flex cqa-items-center cqa-justify-between cqa-flex-wrap\">\n <div class=\"cqa-flex cqa-items-center\">\n <div *ngIf=\"isLive\" class=\"cqa-h-[21px] cqa-inline-flex cqa-items-center cqa-gap-1.5 cqa-mr-2 cqa-px-[9px] cqa-py-[3px] cqa-bg-[#FCD9D9] cqa-rounded-[6px]\" style=\"border: 1px solid #F9BFBF;\">\n <span class=\"cqa-relative cqa-w-2 cqa-h-2 cqa-rounded-full cqa-bg-[#F47F7F]\" style=\"flex-shrink: 0;\">\n <span class=\"cqa-absolute cqa-inset-0 cqa-rounded-full cqa-bg-[#F47F7F] cqa-opacity-75 cqa-animate-ping\"></span>\n </span>\n <span class=\"cqa-text-[10px] cqa-font-medium cqa-text-[#C63535] cqa-leading-[15px]\">Live</span>\n </div>\n <mat-icon *ngIf=\"effectivePlatformType === 'browser'\" style=\"width: 10px; height: 10px;\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"none\">\n <g clip-path=\"url(#clip0_935_15847)\">\n <path\n d=\"M0.625 5C0.625 6.16032 1.08594 7.27312 1.90641 8.09359C2.72688 8.91406 3.83968 9.375 5 9.375C6.16032 9.375 7.27312 8.91406 8.09359 8.09359C8.91406 7.27312 9.375 6.16032 9.375 5C9.375 3.83968 8.91406 2.72688 8.09359 1.90641C7.27312 1.08594 6.16032 0.625 5 0.625C3.83968 0.625 2.72688 1.08594 1.90641 1.90641C1.08594 2.72688 0.625 3.83968 0.625 5Z\"\n stroke=\"#9CA3AF\" stroke-width=\"0.6\" stroke-linejoin=\"round\" />\n <path\n d=\"M3.125 5C3.125 3.83968 3.32254 2.72688 3.67417 1.90641C4.02581 1.08594 4.50272 0.625 5 0.625C5.49728 0.625 5.97419 1.08594 6.32582 1.90641C6.67746 2.72688 6.875 3.83968 6.875 5C6.875 6.16032 6.67746 7.27312 6.32582 8.09359C5.97419 8.91406 5.49728 9.375 5 9.375C4.50272 9.375 4.02581 8.91406 3.67417 8.09359C3.32254 7.27312 3.125 6.16032 3.125 5Z\"\n stroke=\"#9CA3AF\" stroke-width=\"0.6\" stroke-linejoin=\"round\" />\n <path d=\"M0.9375 6.45866H9.0625M0.9375 3.54199H9.0625\" stroke=\"#9CA3AF\" stroke-width=\"0.6\"\n stroke-linecap=\"round\" />\n </g>\n <defs>\n <clipPath id=\"clip0_935_15847\">\n <rect width=\"10\" height=\"10\" fill=\"white\" />\n </clipPath>\n </defs>\n </svg>\n </mat-icon>\n <mat-icon *ngIf=\"effectivePlatformType === 'device'\" style=\"width: 10px; height: 10px;\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"none\">\n <path d=\"M7.08325 0.833008H2.91659C2.45635 0.833008 2.08325 1.2061 2.08325 1.66634V8.33301C2.08325 8.79324 2.45635 9.16634 2.91659 9.16634H7.08325C7.54349 9.16634 7.91658 8.79324 7.91658 8.33301V1.66634C7.91658 1.2061 7.54349 0.833008 7.08325 0.833008Z\" stroke=\"#6B7280\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M5 7.5H5.00417\" stroke=\"#6B7280\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </mat-icon>\n <p class=\"cqa-text-sm !cqa-text-[10px] cqa-text-[#6B7280] cqa-ml-2\">\n {{ platformName }}\n <span\n *ngIf=\"effectivePlatformType === 'browser'\"\n class=\"cqa-ml-1\"\n [matTooltip]=\"'Screen size: ' + effectiveBrowserViewPort.width + 'x' + effectiveBrowserViewPort.height\"\n matTooltipPosition=\"below\"\n >\n \u00B7\n <span class=\"cqa-ml-1\">\n {{ effectiveBrowserViewPort.width }}x{{ effectiveBrowserViewPort.height }}\n </span>\n </span>\n </p>\n <button\n *ngIf=\"showCaptureVideo && isLive\"\n type=\"button\"\n class=\"capture-video-btn\"\n [class.is-loading]=\"isCapturingVideo\"\n [disabled]=\"isCapturingVideo\"\n (click)=\"captureVideo()\">\n <span *ngIf=\"!isCapturingVideo\" class=\"capture-video-btn__dot\"></span>\n <span *ngIf=\"isCapturingVideo\" class=\"capture-video-btn__spinner\" aria-hidden=\"true\"></span>\n <span>{{ isCapturingVideo ? 'Capturing\u2026' : 'Capture Video' }}</span>\n </button>\n </div>\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\">\n <div *ngIf=\"isLive\" [ngClass]=\"getStatusBadgeClass()\">\n <span [ngClass]=\"getStatusTextClass()\">{{ liveStatus }}</span>\n </div>\n\n <ng-container *ngIf=\"!isLive\">\n <cqa-segment-control \n [segments]=\"segments\" \n [value]=\"currentView\"\n (valueChange)=\"onSegmentChange($event)\">\n </cqa-segment-control>\n \n <div *ngIf=\"!isFullScreen\" \n class=\"cqa-p-1 cqa-cursor-pointer hover:cqa-bg-gray-100 cqa-rounded-sm cqa-transition-colors\"\n (click)=\"toggleFullScreen()\"\n title=\"Expand\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"none\">\n <path d=\"M6.25 1.25H8.75V3.75\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M8.74992 1.25L5.83325 4.16667\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M1.25 8.74967L4.16667 5.83301\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M3.75 8.75H1.25V6.25\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </div>\n\n <div *ngIf=\"isFullScreen\" \n class=\"cqa-p-1 cqa-cursor-pointer hover:cqa-bg-gray-100 cqa-rounded-sm cqa-transition-colors\"\n (click)=\"toggleFullScreen()\"\n title=\"Exit full screen\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"none\">\n <path d=\"M8.75 6.25H6.25V8.75\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M6.25008 6.25L9.16675 9.16667\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M0.833252 0.833008L3.74992 3.74967\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M1.25 3.75H3.75V1.25\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </div>\n </ng-container>\n </div>\n </div>\n </div>\n <div class=\"cqa-w-full cqa-bg-[#F3F4F6] cqa-h-[calc(100%-41px)]\">\n <!-- Fast-forward overlay: covers content area but not header -->\n <div *ngIf=\"isFastForwarding && fastForwardConfig\"\n class=\"cqa-h-full cqa-w-full cqa-flex cqa-items-center cqa-justify-center cqa-p-6\"\n style=\"background-color: #F3F4F6;\">\n <div class=\"cqa-bg-white cqa-rounded-xl cqa-w-full\"\n style=\"max-width: 500px; padding: 32px; box-shadow: 0px 25px 50px -12px #00000040; border: 1px solid #E5E5E5\">\n <!-- Sparkle avatar with rotating arc -->\n <div class=\"cqa-flex cqa-justify-center cqa-mb-2\">\n <div class=\"cqa-relative\" style=\"width: 56px; height: 56px;\">\n <div class=\"cqa-absolute cqa-inset-0 cqa-rounded-full cqa-flex cqa-items-center cqa-justify-center\"\n style=\"background-color: rgba(63,67,238,0.12);\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"26\" viewBox=\"0 0 24 26\" fill=\"none\">\n <path d=\"M12.0251 0C12.173 0.0634121 12.2362 0.218264 12.2968 0.36211C12.3272 0.445923 12.355 0.530192 12.3821 0.615133C12.3927 0.647682 12.4033 0.680231 12.4142 0.713766C12.5548 1.15177 12.6801 1.59447 12.8061 2.03686C12.8346 2.13673 12.8632 2.23656 12.8918 2.33638C13.0375 2.84464 13.1821 3.35321 13.3258 3.86201C13.346 3.93347 13.3662 4.00492 13.3864 4.07638C13.4434 4.27783 13.5003 4.4793 13.5569 4.68087C13.6764 5.10678 13.7993 5.53145 13.9301 5.95405C13.9415 5.99102 13.9529 6.02799 13.9647 6.06608C14.3045 7.15318 14.7862 8.12751 15.5757 8.95743C15.5953 8.979 15.6148 9.00058 15.635 9.02282C15.972 9.38246 16.4045 9.67063 16.8368 9.90322C16.8588 9.91526 16.8808 9.92729 16.9035 9.9397C18.1757 10.6231 19.7053 10.9237 21.0832 11.3238C21.6869 11.4992 22.2888 11.6802 22.8885 11.8691C22.9306 11.8823 22.9727 11.8956 23.0149 11.9088C23.1409 11.9483 23.2667 11.9885 23.3923 12.0293C23.4216 12.0385 23.4509 12.0478 23.481 12.0573C23.6541 12.1146 23.8197 12.1821 23.9618 12.2992C24.0051 12.3938 24.0051 12.3938 23.9933 12.4884C23.855 12.6258 23.7179 12.6902 23.5354 12.753C23.5093 12.7623 23.4832 12.7716 23.4564 12.7811C23.1483 12.8892 22.836 12.9838 22.5234 13.0775C22.4592 13.0969 22.395 13.1163 22.3309 13.1357C21.9352 13.2551 21.5389 13.3719 21.1421 13.4874C20.6195 13.6395 20.0977 13.7942 19.5763 13.9506C19.4909 13.9763 19.4055 14.0018 19.32 14.0272C18.8712 14.161 18.4246 14.2995 17.9817 14.4516C17.945 14.4642 17.9082 14.4768 17.8704 14.4897C16.387 15.0059 15.2563 15.9687 14.5486 17.3768C14.167 18.1681 13.9419 19.0239 13.7124 19.8685C13.6503 20.0964 13.5842 20.323 13.5175 20.5496C13.4651 20.7278 13.4139 20.9064 13.364 21.0853C13.358 21.1066 13.3521 21.1279 13.346 21.1499C13.3172 21.253 13.2885 21.3562 13.26 21.4594C13.1872 21.7211 13.1087 21.9807 13.0281 22.2401C12.8851 22.7012 12.7549 23.1656 12.6256 23.6306C12.4305 24.3308 12.4305 24.3308 12.3199 24.6674C12.3126 24.69 12.3052 24.7126 12.2977 24.7359C12.2573 24.8557 12.2158 24.966 12.1394 25.0674C12.0231 25.093 12.0231 25.093 11.9187 25.0989C11.7993 24.9249 11.7202 24.7562 11.6569 24.5553C11.6476 24.5269 11.6384 24.4984 11.6289 24.4691C11.5116 24.1019 11.4067 23.7309 11.3018 23.36C11.2778 23.2752 11.2536 23.1904 11.2295 23.1056C11.0496 22.4737 10.8727 21.8409 10.696 21.2081C9.96019 18.0775 9.96019 18.0775 8.104 15.5464C8.07425 15.5222 8.0445 15.4979 8.01385 15.4729C6.79767 14.4989 5.19556 14.1382 3.7284 13.7155C3.36553 13.6109 3.0028 13.5058 2.64009 13.4007C2.60837 13.3915 2.60837 13.3915 2.57601 13.3821C2.05283 13.2304 1.5298 13.0783 1.0086 12.9199C0.986549 12.9132 0.9645 12.9066 0.941783 12.8997C0.141064 12.6578 0.141064 12.6578 0.0017241 12.4884C-0.0022167 12.4115 -0.0022167 12.4115 0.0332505 12.3307C0.178889 12.188 0.320205 12.1269 0.512426 12.0647C0.540259 12.0554 0.56809 12.046 0.596766 12.0364C0.682008 12.0079 0.767436 11.9801 0.852936 11.9524C0.877335 11.9444 0.901735 11.9364 0.926874 11.9282C1.09943 11.8716 1.2726 11.8171 1.44603 11.7633C1.48864 11.75 1.48864 11.75 1.53212 11.7364C2.27272 11.5061 3.01815 11.2917 3.76336 11.0769C4.31866 10.9168 4.87302 10.754 5.42561 10.5847C5.451 10.577 5.4764 10.5692 5.50256 10.5613C6.78267 10.1705 8.03409 9.61612 8.86063 8.51606C8.873 8.49974 8.88538 8.48342 8.89813 8.46661C9.8186 7.24564 10.1775 5.76082 10.5785 4.31201C10.7105 3.83552 10.844 3.35947 10.9779 2.88356C11.015 2.75177 11.0521 2.61997 11.089 2.48812C11.2372 1.95852 11.3876 1.42964 11.5467 0.903181C11.5616 0.853882 11.5764 0.804561 11.5911 0.755218C11.8138 0.0111205 11.8138 0.0111205 12.0251 0Z\" fill=\"#3F43EE\"/>\n <path d=\"M19.2962 1.54899C19.441 1.7128 19.4699 1.90532 19.515 2.11449C19.6653 2.77161 19.8384 3.37584 20.4394 3.75756C20.8102 3.96263 21.2522 4.05595 21.663 4.14409C21.8364 4.18205 21.9829 4.23242 22.1316 4.33316C22.1651 4.38636 22.1651 4.38636 22.1632 4.47897C22.1287 4.59159 22.0984 4.62791 22.0075 4.70163C21.9081 4.73488 21.9081 4.73488 21.7888 4.76074C21.7437 4.7711 21.6985 4.78158 21.6535 4.79214C21.6293 4.79774 21.6051 4.80335 21.5803 4.80912C20.8456 4.97907 20.8456 4.97907 20.2105 5.36368C20.1899 5.3803 20.1692 5.39692 20.1479 5.41405C19.6941 5.81426 19.5981 6.48 19.4615 7.0377C19.4536 7.06833 19.4458 7.09896 19.4377 7.13051C19.431 7.15762 19.4242 7.18474 19.4172 7.21267C19.3877 7.29546 19.3572 7.34945 19.2962 7.41289C19.1266 7.43444 19.1266 7.43444 19.044 7.41289C18.9128 7.31114 18.8834 7.19524 18.8491 7.0395C18.8433 7.01539 18.8376 6.99127 18.8317 6.96642C18.8134 6.8894 18.7956 6.81226 18.778 6.73508C18.6398 6.13403 18.4837 5.54432 17.9324 5.19201C17.5015 4.95172 16.9776 4.83808 16.496 4.74631C16.364 4.71883 16.2884 4.68427 16.2067 4.57552C16.1889 4.46518 16.1889 4.46518 16.2067 4.35484C16.3322 4.22484 16.4644 4.19311 16.6342 4.15386C16.6877 4.14061 16.7412 4.12726 16.7947 4.11383C16.8223 4.10694 16.85 4.10004 16.8784 4.09294C17.0193 4.05668 17.1588 4.01602 17.2983 3.97455C17.3236 3.9672 17.3489 3.95985 17.3751 3.95227C17.6585 3.86752 17.9032 3.75911 18.1298 3.56668C18.1509 3.54928 18.172 3.53189 18.1938 3.51397C18.5465 3.19658 18.6663 2.71976 18.7751 2.27484C18.7834 2.24106 18.7916 2.20728 18.8002 2.17248C18.8167 2.10463 18.8329 2.0367 18.8488 1.9687C18.8602 1.92139 18.8602 1.92139 18.8717 1.87312C18.8784 1.84486 18.885 1.81659 18.8918 1.78747C18.9209 1.69749 18.959 1.62683 19.0125 1.54899C19.1153 1.49761 19.1874 1.52084 19.2962 1.54899Z\" fill=\"#0F12A8\"/>\n <path d=\"M4.7881 17.4219C4.82561 17.4222 4.82561 17.4222 4.86387 17.4225C5.13296 17.4307 5.31503 17.5169 5.51321 17.6978C5.74381 17.9469 5.85029 18.2279 5.84423 18.5637C5.81833 18.8359 5.65779 19.0736 5.46087 19.2576C5.22908 19.4447 4.99525 19.5065 4.69906 19.4979C4.44933 19.4688 4.21438 19.3502 4.03738 19.1717C3.81726 18.8713 3.75291 18.5958 3.78517 18.2259C3.8518 17.9346 4.0439 17.6981 4.28959 17.5323C4.46027 17.4458 4.59755 17.4203 4.7881 17.4219Z\" fill=\"#1216CC\"/>\n </svg>\n </div>\n <svg class=\"cqa-absolute\"\n width=\"60\" height=\"60\" viewBox=\"0 0 60 60\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\n style=\"top: -2px; left: -2px;\">\n <circle cx=\"30\" cy=\"30\" r=\"28\" stroke=\"#E2E2E3\" stroke-width=\"2\" fill=\"none\"/>\n </svg>\n <svg class=\"cqa-absolute cqa-ff-spin\"\n width=\"60\" height=\"60\" viewBox=\"0 0 60 60\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\n style=\"top: -2px; left: -2px; transform-origin: 30px 30px;\">\n <path d=\"M30 2 A 28 28 0 0 1 54.24 44\"\n stroke=\"#3f43ee\" stroke-width=\"2\" stroke-linecap=\"round\" fill=\"none\"/>\n </svg>\n </div>\n </div>\n\n <p class=\"cqa-text-center cqa-m-0 cqa-mb-2\"\n style=\"font-size: 16px; font-weight: 600; color: #161617;\">\n Fast-forwarding to your step\n </p>\n\n <p class=\"cqa-text-center cqa-m-0\"\n style=\"font-size: 12px; color: #6D6D74;\">\n Steps {{ fastForwardConfig.fromStep }}\u2013{{ fastForwardConfig.toStep }} are running at full speed in the background.\n <strong style=\"color: #6D6D74; font-weight: 600;\">Live view and screenshots are paused</strong>\n until execution reaches your target step \u2014 then you're in automatically.\n </p>\n\n <div class=\"cqa-mt-6 cqa-mb-5\">\n <div class=\"cqa-flex cqa-items-center cqa-justify-between cqa-mb-2\">\n <span style=\"font-size: 12px; color: #4C4C51;\">Steps executing in background</span>\n <span style=\"font-size: 12px; color: #6D6D74;\">\n {{ fastForwardConfig.currentStep }} of {{ fastForwardConfig.totalSteps }} steps\n </span>\n </div>\n <div style=\"height: 6px; background-color: #E2E2E3; border-radius: 9999px; overflow: hidden;\">\n <div [style.width.%]=\"fastForwardProgressPercent\"\n style=\"height: 100%; background: linear-gradient(90deg, #3F43EE 0%, #818CF8 100%); transition: width 300ms cubic-bezier(0.4,0,0.2,1);\"></div>\n </div>\n </div>\n\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\"\n style=\"padding: 10px 14px; border-radius: 10px; background: #6366F11A; border: 1px solid #6366F133;\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" style=\"flex-shrink: 0;\">\n <circle cx=\"8\" cy=\"8\" r=\"6.5\" stroke=\"#3f43ee\" stroke-width=\"1.25\"/>\n <circle cx=\"8\" cy=\"8\" r=\"3\" stroke=\"#3f43ee\" stroke-width=\"1.25\"/>\n <circle cx=\"8\" cy=\"8\" r=\"1\" fill=\"#3f43ee\"/>\n </svg>\n <div style=\"font-size: 12px;\">\n <span style=\"color: #6D6D74;\">Jumping to</span>\n <span style=\"color: #3F43EE; font-weight: 400; display: block;\">\n Step {{ fastForwardConfig.targetStepNumber }} \u2014 {{ fastForwardConfig.targetStepLabel }}\n </span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Live Content View -->\n <div *ngIf=\"!isFastForwarding && isLive\" class=\"cqa-h-full cqa-flex cqa-flex-col cqa-justify-center cqa-relative\">\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-p-4\">\n <div class=\"cqa-relative cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-overflow-hidden\"\n [ngClass]=\"{\n 'cqa-w-auto': hasDeviceFrame,\n 'cqa-w-full cqa-flex-col': !hasDeviceFrame,\n 'cqa-rounded-md cqa-overflow-hidden': effectivePlatformType === 'browser',\n 'cqa-max-h-full cqa-h-full': hasDeviceFrame && (effectivePlatformType !== 'browser' || !isLive),\n 'cqa-min-w-max': hasDeviceFrame && effectivePlatformType === 'device'\n }\"\n [ngStyle]=\"liveContentContainerStyle\">\n <img *ngIf=\"hasDeviceFrame\"\n [src]=\"deviceMockupImage\"\n alt=\"Device mockup\"\n class=\"cqa-h-full cqa-w-auto cqa-max-h-full cqa-object-contain cqa-block cqa-pointer-events-none cqa-z-10\"\n />\n <div [ngClass]=\"{\n 'cqa-absolute cqa-flex cqa-flex-col': hasDeviceFrame,\n 'cqa-w-full cqa-h-full cqa-flex cqa-flex-col cqa-items-center cqa-justify-center cqa-relative': !hasDeviceFrame,\n 'cqa-z-20': hasDeviceFrame && effectivePlatformType === 'browser',\n 'cqa-bg-white': hasDeviceFrame && effectivePlatformType !== 'browser'\n }\"\n [ngStyle]=\"hasDeviceFrame ? deviceScreenStyle : {}\">\n <!-- Loading State -->\n <div *ngIf=\"isContentVideoLoading\" class=\"cqa-p-10 cqa-text-center cqa-h-full cqa-flex cqa-flex-col cqa-items-center cqa-justify-center\">\n <div class=\"cqa-mb-4\">\n <mat-progress-spinner mode=\"indeterminate\" diameter=\"40\"></mat-progress-spinner>\n </div>\n <p class=\"cqa-text-gray-400 cqa-text-sm\">{{ liveLoadingLabel }}</p>\n </div>\n\n <!-- Live Content (when not loading) -->\n <div *ngIf=\"!isContentVideoLoading\" class=\"cqa-w-full cqa-h-full cqa-flex cqa-flex-col cqa-items-center cqa-justify-center cqa-relative\">\n <div *ngIf=\"liveStatus === 'Failed' && failedStatusMessage\" class=\"cqa-p-6 cqa-text-center cqa-w-full\">\n <div class=\"cqa-inline-flex cqa-items-center cqa-gap-2 cqa-px-4 cqa-py-3 cqa-bg-[#FCD9D9] cqa-border cqa-border-[#F9BFBF] cqa-rounded-lg\">\n <mat-icon style=\"width: 18px; height: 18px; color: #C63535; font-size: 18px;\">error</mat-icon>\n <p class=\"cqa-text-[#C63535] cqa-text-sm cqa-font-medium cqa-m-0\">{{ failedStatusMessage }}</p>\n </div>\n </div>\n <ng-content *ngIf=\"liveStatus !== 'Failed' || !failedStatusMessage\"></ng-content>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Normal Video View (when not live) -->\n <div *ngIf=\"!isFastForwarding && !isLive && currentView === 'video'\"\n class=\"cqa-h-full cqa-flex cqa-flex-col\"\n tabindex=\"0\"\n role=\"region\"\n aria-label=\"Video playback\"\n (keydown)=\"onVideoKeydown($event)\">\n <div class=\"cqa-w-full cqa-flex cqa-items-center\" *ngIf=\"currentVideoUrl\" [ngClass]=\"{'!cqa-h-full': effectivePlatformType === 'device', 'cqa-mt-auto': hasDeviceFrame, 'cqa-max-h-[calc(100%-108px)]': showVideoLibrary, 'cqa-max-h-[calc(100%-60px)]': !showVideoLibrary}\">\n <ng-container *ngIf=\"hasDeviceFrame; else videoNoFrame\">\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-p-4\">\n <div class=\"cqa-relative cqa-h-full cqa-w-auto cqa-flex cqa-items-center cqa-justify-center cqa-max-h-full\" [ngClass]=\"{'cqa-rounded-md cqa-overflow-hidden': effectivePlatformType === 'browser', 'cqa-min-w-max': effectivePlatformType === 'device'}\">\n <img\n [src]=\"deviceMockupImage\"\n alt=\"Device mockup\"\n class=\"cqa-h-full cqa-w-auto cqa-max-h-full cqa-object-contain cqa-block cqa-pointer-events-none cqa-z-10\"\n />\n <div class=\"cqa-absolute cqa-flex cqa-flex-col\" [ngStyle]=\"deviceScreenStyle\" [ngClass]=\"{'cqa-bg-white': effectivePlatformType !== 'browser'}\">\n <video\n *ngIf=\"!videoRefreshing\"\n #vplayer\n class=\"cqa-object-cover cqa-w-full cqa-h-full cqa-block cqa-bg-[##F2F2F2] cqa-cursor-pointer\"\n [src]=\"currentVideoUrl\"\n type=\"video/webm\"\n [ngClass]=\"{'cqa-z-20': effectivePlatformType === 'browser'}\"\n (click)=\"onVideoFrameClick()\"\n (loadedmetadata)=\"onVideoMetadataLoaded()\"\n (canplay)=\"onVideoCanPlay()\"\n (ended)=\"onVideoEnded()\"\n (error)=\"onVideoError()\"\n ></video>\n <!-- Play/Pause overlay icon -->\n <div *ngIf=\"showPlayPauseOverlay\"\n class=\"cqa-absolute cqa-inset-0 cqa-flex cqa-items-center cqa-justify-center cqa-pointer-events-none\"\n [ngClass]=\"{'cqa-z-30': effectivePlatformType === 'browser'}\"\n [ngStyle]=\"{animation: 'cqaFadeOut 500ms ease-out forwards'}\">\n <div class=\"cqa-rounded-full cqa-bg-black cqa-bg-opacity-50 cqa-flex cqa-items-center cqa-justify-center\"\n style=\"width: 56px; height: 56px;\">\n <svg *ngIf=\"isPlaying\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"white\">\n <polygon points=\"5,3 19,12 5,21\" />\n </svg>\n <svg *ngIf=\"!isPlaying\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"white\">\n <rect x=\"5\" y=\"3\" width=\"4\" height=\"18\" />\n <rect x=\"15\" y=\"3\" width=\"4\" height=\"18\" />\n </svg>\n </div>\n </div>\n </div>\n </div>\n </div>\n </ng-container>\n <ng-template #videoNoFrame>\n <div class=\"cqa-relative cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-p-4 cqa-cursor-pointer\"\n (click)=\"onVideoFrameClick()\">\n <video\n *ngIf=\"!videoRefreshing\"\n #vplayer\n class=\"cqa-w-full cqa-h-full cqa-block cqa-bg-[##F2F2F2]\"\n [src]=\"currentVideoUrl\"\n type=\"video/webm\"\n (loadedmetadata)=\"onVideoMetadataLoaded()\"\n (canplay)=\"onVideoCanPlay()\"\n (ended)=\"onVideoEnded()\"\n (error)=\"onVideoError()\"\n ></video>\n <!-- Play/Pause overlay icon -->\n <div *ngIf=\"showPlayPauseOverlay\"\n class=\"cqa-absolute cqa-inset-0 cqa-flex cqa-items-center cqa-justify-center cqa-pointer-events-none\"\n [ngStyle]=\"{animation: 'cqaFadeOut 500ms ease-out forwards'}\">\n <div class=\"cqa-rounded-full cqa-bg-black cqa-bg-opacity-50 cqa-flex cqa-items-center cqa-justify-center\"\n style=\"width: 56px; height: 56px;\">\n <svg *ngIf=\"isPlaying\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"white\">\n <polygon points=\"5,3 19,12 5,21\" />\n </svg>\n <svg *ngIf=\"!isPlaying\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"white\">\n <rect x=\"5\" y=\"3\" width=\"4\" height=\"18\" />\n <rect x=\"15\" y=\"3\" width=\"4\" height=\"18\" />\n </svg>\n </div>\n </div>\n </div>\n </ng-template>\n </div>\n \n <div class=\"cqa-p-10 cqa-text-center cqa-text-gray-400 cqa-text-sm cqa-h-full cqa-flex cqa-items-center cqa-justify-center\" *ngIf=\"!currentVideoUrl\">\n <ng-container *ngIf=\"isVNCSessionIntruppted && vncSessionIntupptedMessage; else noVideoDefault\">\n <p class=\"cqa-text-sm cqa-text-gray-600\">\n {{ vncSessionIntupptedMessage }}\n </p>\n </ng-container>\n <ng-template #noVideoDefault>\n <span>No video recording found</span>\n </ng-template>\n </div>\n \n <!-- Video Library Panel \u2014 reserves only header-height in flow; the inner panel is absolutely anchored to its bottom edge, so the body expanding makes the panel grow UPWARD over the video. Timeline stays put. -->\n <div *ngIf=\"showVideoLibrary && videoUrls && videoUrls.length > 0\"\n class=\"video-library\"\n [class.video-library--open]=\"!isVideoLibraryCollapsed\"\n [ngClass]=\"{'cqa-mt-auto': hasDeviceFrame}\">\n <div class=\"video-library__inner\">\n <!-- Header \u2014 always visible, clicking the chevron toggles the body -->\n <div class=\"video-library__header\">\n <div class=\"video-library__title-group\">\n <mat-icon class=\"video-library__icon\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\">\n <path d=\"M9.33398 7.58308L12.3807 9.61425C12.4247 9.64348 12.4757 9.66024 12.5284 9.66276C12.5811 9.66528 12.6335 9.65347 12.68 9.62856C12.7265 9.60366 12.7654 9.56661 12.7925 9.52136C12.8196 9.4761 12.834 9.42434 12.834 9.37158V4.59058C12.834 4.53926 12.8205 4.48885 12.7948 4.44443C12.7691 4.40001 12.7321 4.36315 12.6876 4.33759C12.6431 4.31203 12.5926 4.29866 12.5413 4.29883C12.49 4.299 12.4396 4.31272 12.3953 4.33858L9.33398 6.12475\" stroke=\"#3F43EE\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M8.16602 3.5H2.33268C1.68835 3.5 1.16602 4.02233 1.16602 4.66667V9.33333C1.16602 9.97767 1.68835 10.5 2.33268 10.5H8.16602C8.81035 10.5 9.33268 9.97767 9.33268 9.33333V4.66667C9.33268 4.02233 8.81035 3.5 8.16602 3.5Z\" stroke=\"#3F43EE\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </mat-icon>\n <span class=\"video-library__title\">Video Library</span>\n <cqa-badge\n size=\"small\"\n inlineStyles=\"min-width: max-content;\"\n backgroundColor=\"#EDE9FE\"\n textColor=\"#6D28D9\"\n [label]=\"videoUrls.length + ' clip' + (videoUrls.length === 1 ? '' : 's')\">\n </cqa-badge>\n <span class=\"video-library__subtitle\"\n [matTooltip]=\"isVideoFullMode ? 'Playing all clips as one continuous video' : 'Each clip covers execution since last capture'\"\n matTooltipPosition=\"below\">{{ isVideoFullMode ? '\u00B7 Playing all clips as one continuous video' : '\u00B7 Each clip covers execution since last capture' }}</span>\n </div>\n <div class=\"video-library__actions\">\n <cqa-button\n *ngIf=\"!isVideoFullMode\"\n variant=\"filled\"\n btnSize=\"md\"\n icon=\"play_arrow\"\n iconPosition=\"start\"\n text=\"Show Full Video\"\n (clicked)=\"onShowFullVideo()\">\n </cqa-button>\n <cqa-button\n *ngIf=\"isVideoFullMode\"\n variant=\"outlined\"\n btnSize=\"md\"\n icon=\"close\"\n iconPosition=\"start\"\n text=\"Exit Full Video\"\n (clicked)=\"onExitFullVideo()\">\n </cqa-button>\n <button type=\"button\" class=\"video-library__collapse\" (click)=\"toggleVideoLibraryCollapsed()\"\n [attr.aria-label]=\"isVideoLibraryCollapsed ? 'Show video library' : 'Hide video library'\">\n <svg *ngIf=\"!isVideoLibraryCollapsed\" xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\">\n <path d=\"M3 7.5L6 4.5L9 7.5\" stroke=\"#6B7280\" stroke-width=\"1.25\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <svg *ngIf=\"isVideoLibraryCollapsed\" xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\">\n <path d=\"M3 4.5L6 7.5L9 4.5\" stroke=\"#6B7280\" stroke-width=\"1.25\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </button>\n </div>\n </div>\n\n <!-- Body (cards) \u2014 max-height animates from 0 (collapsed) to a fixed height (open). -->\n <div class=\"video-library__body\"\n [class.video-library__body--open]=\"!isVideoLibraryCollapsed\"\n [attr.aria-hidden]=\"isVideoLibraryCollapsed ? true : null\">\n <div class=\"clips-row\"\n #clipsScroller\n [class.clips-row--dragging]=\"clipsDragActive\"\n (mousedown)=\"onClipsMouseDown($event, clipsScroller)\"\n (mousemove)=\"onClipsMouseMove($event, clipsScroller)\"\n (mouseup)=\"onClipsMouseUp($event)\"\n (mouseleave)=\"onClipsMouseLeave($event)\">\n <div\n *ngFor=\"let url of videoUrls; let i = index; trackBy: trackLibraryClipByIndex\"\n class=\"clip-card\"\n [class.clip-card--playing]=\"i === currentVideoIndex\">\n <div class=\"clip-thumb\"\n [class.clip-thumb--ready]=\"libraryVideoDurations.has(i)\"\n (click)=\"selectLibraryClip(i)\">\n <video\n #clipVideo\n class=\"clip-thumb__video\"\n [src]=\"url\"\n preload=\"metadata\"\n playsinline\n muted\n (loadedmetadata)=\"onLibraryClipMetadataLoaded(i, clipVideo)\"\n (durationchange)=\"onLibraryClipMetadataLoaded(i, clipVideo)\"></video>\n <div class=\"clip-thumb__overlay\">\n <div class=\"clip-thumb__play-circle\">\n <svg *ngIf=\"!(i === currentVideoIndex && isPlaying)\" xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\">\n <path d=\"M3.5 1.75L11.375 7L3.5 12.25V1.75Z\" fill=\"#FFFFFF\"/>\n </svg>\n <svg *ngIf=\"i === currentVideoIndex && isPlaying\" xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\">\n <rect x=\"3.5\" y=\"2.5\" width=\"2.5\" height=\"9\" rx=\"0.5\" fill=\"#FFFFFF\"/>\n <rect x=\"8\" y=\"2.5\" width=\"2.5\" height=\"9\" rx=\"0.5\" fill=\"#FFFFFF\"/>\n </svg>\n </div>\n </div>\n <span *ngIf=\"i === currentVideoIndex && isPlaying\" class=\"clip-thumb__badge clip-thumb__badge--playing\">PLAYING</span>\n <span *ngIf=\"newVideoIndexes.has(i) && i !== currentVideoIndex\" class=\"clip-thumb__badge clip-thumb__badge--new\">NEW</span>\n <span class=\"clip-thumb__duration\">{{ formatTime(libraryVideoDurations.get(i) || 0) }}</span>\n </div>\n <div class=\"clip-meta\">\n <div class=\"clip-meta__title\">Clip {{ i + 1 }}</div>\n <button\n type=\"button\"\n class=\"clip-download-btn\"\n [class.clip-download-btn--downloading]=\"downloadingIndexes.has(i)\"\n [disabled]=\"downloadingIndexes.has(i)\"\n (click)=\"downloadClip(i); $event.stopPropagation()\">\n <ng-container *ngIf=\"!downloadingIndexes.has(i)\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\">\n <!-- Figma icon: upward arrow with a short tray line at the bottom (share/export glyph). -->\n <path d=\"M6 8.5V2M6 2L3 5M6 2L9 5M3 10.5H9\" stroke=\"currentColor\" stroke-width=\"1.1\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <span>Download</span>\n </ng-container>\n <ng-container *ngIf=\"downloadingIndexes.has(i)\">\n <svg class=\"clip-download-btn__ring\" width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\">\n <circle cx=\"7\" cy=\"7\" r=\"6\" stroke=\"#E5E7EB\" stroke-width=\"1.5\" fill=\"none\"/>\n <circle cx=\"7\" cy=\"7\" r=\"6\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"\n stroke-linecap=\"round\"\n [attr.stroke-dasharray]=\"downloadRingCircumference\"\n [attr.stroke-dashoffset]=\"downloadRingDashoffset(i)\"\n transform=\"rotate(-90 7 7)\"/>\n </svg>\n <span>{{ downloadProgress.get(i) || 0 }}%</span>\n </ng-container>\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"cqa-px-2 cqa-py-2 cqa-bg-white\" style=\"border-top: 1px solid #E5E7EB;\" [ngClass]=\"{'cqa-mt-auto': hasDeviceFrame && !(showVideoLibrary && videoUrls && videoUrls.length > 0)}\" *ngIf=\"currentVideoUrl && !isLive\">\n <span *ngIf=\"!isVideoFullMode\"\n class=\"cqa-text-[#6B7280] cqa-text-[12px] cqa-font-medium cqa-mb-2 cqa-whitespace-nowrap cqa-block\">\n Video {{ currentVideoIndex + 1 }} playing out of {{ videoUrls?.length || 0 }}\n </span>\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\">\n <button\n *ngIf=\"hasMultipleVideos && !isVideoFullMode\"\n type=\"button\"\n class=\"cqa-bg-transparent cqa-border-none cqa-cursor-pointer !cqa-px-0 cqa-flex cqa-items-center cqa-justify-center hover:cqa-opacity-70 cqa-transition-opacity cqa-outline-none\"\n style=\"pointer-events: auto;\"\n [disabled]=\"currentVideoIndex === 0\"\n [ngClass]=\"{'cqa-opacity-50 cqa-cursor-not-allowed': currentVideoIndex === 0}\"\n (click)=\"prevVideo()\"\n matTooltip=\"Previous video\"\n matTooltipPosition=\"above\">\n <mat-icon class=\"cqa-w-4 cqa-h-4 !cqa-text-[16px] cqa-text-[#374151]\" [ngClass]=\"{'cqa-opacity-50 cqa-cursor-not-allowed': currentVideoIndex === 0}\">skip_previous</mat-icon>\n </button>\n\n <div class=\"cqa-flex cqa-items-center cqa-justify-center\" style=\"width: 16px; height: 16px;\">\n <mat-progress-spinner\n *ngIf=\"isPlayerSwitching\"\n mode=\"indeterminate\"\n diameter=\"16\"\n class=\"cqa-inline-block\">\n </mat-progress-spinner>\n <button \n *ngIf=\"!isPlayerSwitching\"\n type=\"button\"\n class=\"cqa-bg-transparent cqa-border-none cqa-cursor-pointer !cqa-px-0 cqa-flex cqa-items-center cqa-justify-center hover:cqa-opacity-70 cqa-transition-opacity cqa-outline-none\"\n style=\"pointer-events: auto;\"\n (click)=\"togglePlay()\"\n matTooltip=\"{{ isPlaying ? 'Pause' : 'Play' }}\"\n matTooltipPosition=\"above\">\n <span *ngIf=\"!isPlaying\" class=\"cqa-flex cqa-items-center cqa-justify-center\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M3 2L13 8L3 14V2Z\" fill=\"#374151\"/>\n </svg>\n </span>\n <span *ngIf=\"isPlaying\" class=\"cqa-flex cqa-items-center cqa-justify-center\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"3\" y=\"2\" width=\"3\" height=\"12\" fill=\"#374151\"/>\n <rect x=\"10\" y=\"2\" width=\"3\" height=\"12\" fill=\"#374151\"/>\n </svg>\n </span>\n </button>\n </div>\n\n <button\n *ngIf=\"hasMultipleVideos && !isVideoFullMode\"\n type=\"button\"\n class=\"cqa-bg-transparent cqa-border-none cqa-cursor-pointer !cqa-px-0 cqa-flex cqa-items-center cqa-justify-center hover:cqa-opacity-70 cqa-transition-opacity cqa-outline-none\"\n style=\"pointer-events: auto;\"\n [disabled]=\"videoUrls && (currentVideoIndex >= videoUrls.length - 1)\"\n [ngClass]=\"{'cqa-opacity-50 cqa-cursor-not-allowed': videoUrls && (currentVideoIndex >= videoUrls.length - 1)}\"\n (click)=\"nextVideo()\"\n matTooltip=\"Next video\"\n matTooltipPosition=\"above\">\n <mat-icon class=\"cqa-w-4 cqa-h-4 !cqa-text-[16px] cqa-text-[#374151]\" [ngClass]=\"{'cqa-opacity-50 cqa-cursor-not-allowed': videoUrls && (currentVideoIndex >= videoUrls.length - 1)}\">skip_next</mat-icon>\n </button>\n\n <span\n class=\"cqa-text-[#9CA3AF] cqa-text-[9px] cqa-font-normal cqa-whitespace-nowrap cqa-select-none cqa-inline-block\"\n style=\"width: 40px; text-align: center;\">\n {{ formatTime(isVideoFullMode ? (globalCurrentTimeMs / 1000) : (vplayer?.nativeElement?.currentTime || 0)) }}\n </span>\n\n <div #speedControlContainer class=\"cqa-relative cqa-mr-[8px] cqa-flex cqa-items-center cqa-justify-center\">\n <button\n type=\"button\"\n class=\"cqa-bg-transparent cqa-border-none cqa-cursor-pointer cqa-text-[#9CA3AF] cqa-text-[10px] cqa-leading-[15px] cqa-font-medium cqa-whitespace-nowrap cqa-select-none hover:cqa-opacity-70 cqa-transition-opacity cqa-outline-none cqa-px-1\"\n (click)=\"toggleSpeedControl()\"\n [matTooltip]=\"'Playback Speed'\"\n [matTooltipPosition]=\"'below'\">\n {{ currentSpeed }}\n </button>\n \n <div \n *ngIf=\"isSpeedControlOpen\"\n class=\"cqa-absolute cqa-bottom-full cqa-mb-2 cqa-right-0 cqa-bg-[#F0F0F1] cqa-rounded-lg cqa-overflow-hidden cqa-shadow-lg cqa-z-50\"\n style=\"min-width: max-content; left: 50%; bottom: 0%; transform: translate(-50%, -50%); z-index: 101;\">\n <cqa-segment-control\n [segments]=\"speedSegments\"\n [value]=\"currentSpeed\"\n [containerBgColor]=\"'#F0F0F1'\"\n (valueChange)=\"onSpeedChange($event)\">\n </cqa-segment-control>\n </div>\n </div>\n \n <div class=\"cqa-flex-1 cqa-min-w-0\">\n <div \n #timelineBar\n class=\"cqa-relative cqa-h-1 cqa-bg-gray-200 cqa-rounded-full cqa-cursor-pointer cqa-w-full\"\n (click)=\"onTimelineClick($event)\">\n \n <div\n *ngFor=\"let marker of (isVideoFullMode ? fullVideoMarkers : currentVideoMarkers)\"\n class=\"cqa-absolute cqa-rounded-full\"\n [style.left.%]=\"isVideoFullMode ? getGlobalFullStepLeftPosition(marker) : getStepLeftPosition(marker)\"\n [style.width]=\"'8px'\"\n [style.height]=\"'8px'\"\n [style.background]=\"getGlobalMarkerColor(marker.level)\"\n [style.border]=\"'2px solid ' + getGlobalMarkerResultColor(marker.result)\"\n [style.box-sizing]=\"'border-box'\"\n [attr.title]=\"marker.title || ''\"\n style=\"pointer-events: auto; z-index: 50; cursor: pointer; transform: translate(-50%, -50%); top: 50%;\"\n (click)=\"onMarkerClick($event, marker)\">\n </div>\n \n <div\n class=\"cqa-absolute cqa-left-0 cqa-top-0 cqa-h-full cqa-bg-blue-500 cqa-rounded-full\"\n [style.width.%]=\"isVideoFullMode ? globalScrubberPercent : progress\"\n [style.transition]=\"dragging ? 'none' : 'width 100ms'\"\n style=\"pointer-events: none; z-index: 2;\">\n </div>\n \n <div\n class=\"cqa-absolute cqa-top-1/2 cqa-w-3 cqa-h-3 cqa-bg-blue-600 cqa-rounded-full cqa-cursor-grab active:cqa-cursor-grabbing cqa-shadow-md\"\n [style.left.%]=\"isVideoFullMode ? globalScrubberPercent : progress\"\n style=\"transform: translate(-50%, -50%); z-index: 60;\"\n (mousedown)=\"startDrag($event)\">\n </div>\n </div>\n </div>\n\n <span class=\"cqa-text-[#9CA3AF] cqa-text-[9px] cqa-font-normal cqa-whitespace-nowrap cqa-select-none cqa-mr-2\">\n {{ formatTime(isVideoFullMode ? (totalDuration / 1000) : (vplayer?.nativeElement?.duration || 0)) }}\n </span>\n </div>\n </div>\n </div>\n\n <div *ngIf=\"!isFastForwarding && !isLive && currentView === 'screenshots'\" class=\"cqa-h-full\">\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center\" *ngIf=\"screenShotUrl\">\n <ng-container *ngIf=\"hasDeviceFrame; else screenshotNoFrame\">\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-p-4\">\n <div class=\"cqa-relative cqa-h-full cqa-w-auto cqa-flex cqa-items-center cqa-justify-center cqa-max-h-full\" [ngClass]=\"{'cqa-rounded-md cqa-overflow-hidden': effectivePlatformType === 'browser', 'cqa-min-w-max': effectivePlatformType === 'device'}\">\n <img\n [src]=\"deviceMockupImage\"\n alt=\"Device mockup\"\n class=\"cqa-h-full cqa-w-auto cqa-object-contain cqa-block cqa-pointer-events-none cqa-z-10\"\n [ngClass]=\"{'cqa-max-h-[inherit]': effectivePlatformType === 'browser', 'cqa-max-h-full': effectivePlatformType !== 'browser'}\"\n />\n <div class=\"cqa-absolute cqa-flex cqa-flex-col\" [ngStyle]=\"deviceScreenStyle\" [ngClass]=\"{'cqa-bg-white': effectivePlatformType !== 'browser'}\">\n <img\n [src]=\"screenShotUrl\"\n alt=\"Screenshot\"\n [ngClass]=\"{'cqa-z-20': effectivePlatformType === 'browser'}\"\n class=\"cqa-object-contain cqa-w-full cqa-h-full cqa-block cqa-bg-[##F2F2F2]\"\n />\n </div>\n </div>\n </div>\n </ng-container>\n <ng-template #screenshotNoFrame>\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-p-4\">\n <img\n [src]=\"screenShotUrl\"\n alt=\"Screenshot\"\n class=\"cqa-object-contain cqa-w-full cqa-h-full cqa-block cqa-bg-[##F2F2F2]\"\n />\n </div>\n </ng-template>\n </div>\n \n <div class=\"cqa-p-10 cqa-text-center cqa-text-gray-400 cqa-text-sm cqa-h-full cqa-flex cqa-items-center cqa-justify-center\" *ngIf=\"!screenShotUrl\">\n No screenshot available\n </div>\n </div>\n\n <div *ngIf=\"!isFastForwarding && !isLive && currentView === 'trace'\" class=\"cqa-h-full cqa-flex cqa-flex-col cqa-justify-center\">\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-relative\" *ngIf=\"traceViewUrl\" [ngClass]=\"{'!cqa-h-full': effectivePlatformType === 'device'}\" style=\"padding-top: 48px; padding-bottom: 0px;\">\n <div class=\"cqa-w-full cqa-h-full cqa-overflow-hidden cqa-relative\">\n <iframe \n [src]=\"safeTraceUrl\" \n title=\"Trace Viewer\"\n class=\"cqa-object-contain cqa-w-full cqa-min-h-[250px] cqa-max-h-full cqa-block cqa-bg-[##F2F2F2]\"\n style=\"margin-top: -48px; height: calc(100% + 48px);\"\n frameborder=\"0\"\n allowfullscreen\n width=\"100%\"\n loading=\"lazy\"\n (load)=\"onTraceViewerLoad()\"\n (error)=\"onTraceViewerError()\">\n </iframe>\n </div>\n \n <div *ngIf=\"traceViewerLoading\" class=\"cqa-absolute cqa-inset-0 cqa-bg-[#F3F4F6] cqa-flex cqa-items-center cqa-justify-center cqa-z-10\">\n <div class=\"cqa-text-center cqa-text-gray-400 cqa-text-sm\">\n Loading trace viewer...\n </div>\n </div>\n \n <div *ngIf=\"traceViewerError\" class=\"cqa-absolute cqa-inset-0 cqa-bg-[#F3F4F6] cqa-flex cqa-items-center cqa-justify-center cqa-z-10\">\n <div class=\"cqa-text-center cqa-text-gray-400 cqa-text-sm\">\n Failed to load trace viewer\n </div>\n </div>\n </div>\n \n <div class=\"cqa-p-10 cqa-text-center cqa-text-gray-400 cqa-text-sm cqa-h-full cqa-flex cqa-items-center cqa-justify-center\" *ngIf=\"!traceViewUrl\">\n No trace available\n </div>\n </div> \n </div>\n</div>", components: [{ type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { type: SegmentControlComponent, selector: "cqa-segment-control", inputs: ["segments", "value", "disabled", "containerBgColor", "fullWidth"], outputs: ["valueChange"] }, { type: i4.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "diameter", "strokeWidth", "mode", "value"], exportAs: ["matProgressSpinner"] }, { type: BadgeComponent, selector: "cqa-badge", inputs: ["type", "label", "icon", "iconLibrary", "variant", "size", "backgroundColor", "textColor", "borderColor", "iconBackgroundColor", "iconColor", "iconSize", "inlineStyles", "key", "value", "keyTextColor", "valueTextColor", "isLoading", "fullWidth", "centerContent", "title"] }, { type: ButtonComponent, selector: "cqa-button", inputs: ["variant", "btnSize", "disabled", "loading", "icon", "iconPosition", "fullWidth", "iconColor", "type", "text", "customClass", "inlineStyles", "tooltip", "tooltipPosition"], outputs: ["clicked"] }], directives: [{ type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i6.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
18950
+ SimulatorComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: SimulatorComponent, selector: "cqa-simulator", inputs: { videoUrl: "videoUrl", videoUrls: "videoUrls", videoCurrentDuration: "videoCurrentDuration", stepMarkers: "stepMarkers", screenShotUrl: "screenShotUrl", traceViewUrl: "traceViewUrl", platformName: "platformName", platformType: "platformType", platform: "platform", deviceName: "deviceName", isLive: "isLive", liveStatus: "liveStatus", liveLoadingLabel: "liveLoadingLabel", isContentVideoLoading: "isContentVideoLoading", failedStatusMessage: "failedStatusMessage", isVNCSessionIntruppted: "isVNCSessionIntruppted", vncSessionIntupptedMessage: "vncSessionIntupptedMessage", selectedView: "selectedView", hideVideoTab: "hideVideoTab", browserViewPort: "browserViewPort", browserDevice: "browserDevice", isFastForwarding: "isFastForwarding", fastForwardConfig: "fastForwardConfig", showVideoLibrary: "showVideoLibrary", showCaptureVideo: "showCaptureVideo", isCapturingVideo: "isCapturingVideo", liveSessionView: "liveSessionView" }, outputs: { videoTimeUpdate: "videoTimeUpdate", videoPlay: "videoPlay", videoPause: "videoPause", markerHit: "markerHit", isVideoPlayingChange: "isVideoPlayingChange", captureVideoRequested: "captureVideoRequested", liveSessionViewChange: "liveSessionViewChange" }, host: { listeners: { "window:resize": "onWindowResize()" } }, viewQueries: [{ propertyName: "vplayerRef", first: true, predicate: ["vplayer"], descendants: true }, { propertyName: "timelineBarRef", first: true, predicate: ["timelineBar"], descendants: true }, { propertyName: "speedControlContainerRef", first: true, predicate: ["speedControlContainer"], descendants: true }, { propertyName: "clipsScrollerRef", first: true, predicate: ["clipsScroller"], descendants: true }, { propertyName: "clipVideos", predicate: ["clipVideo"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"cqa-ui-root\" style=\"background-color: #F3F4F6; height: 100%; display: flex; flex-direction: column;\" [ngStyle]=\"{\n position: isFullScreen ? 'fixed' : null,\n inset: isFullScreen ? '1rem' : null,\n zIndex: isFullScreen ? '50' : null,\n boxShadow: isFullScreen ? '0px 13px 25px -12px rgba(0, 0, 0, 0.25)' : null,\n borderRadius: isFullScreen ? '.5rem' : null,\n border: isFullScreen ? '1px solid #E5E7EB' : null,\n width: isFullScreen ? 'calc(100% - 32px)' : null,\n height: isFullScreen ? 'calc(100% - 32px)' : '100%',\n overflow: isFullScreen ? 'hidden' : null\n}\">\n <div class=\"cqa-w-full cqa-py-1 cqa-px-2 cqa-bg-[#FFFFFF]\" style=\"border-bottom: 1px solid #E5E7EB;box-shadow: 0px 1px 2px 0px #0000000D;\">\n <div class=\"cqa-w-full cqa-flex cqa-items-center cqa-justify-between cqa-flex-wrap\">\n <div class=\"cqa-flex cqa-items-center\">\n <div *ngIf=\"isLive\" class=\"cqa-h-[21px] cqa-inline-flex cqa-items-center cqa-gap-1.5 cqa-mr-2 cqa-px-[9px] cqa-py-[3px] cqa-bg-[#FCD9D9] cqa-rounded-[6px]\" style=\"border: 1px solid #F9BFBF;\">\n <span class=\"cqa-relative cqa-w-2 cqa-h-2 cqa-rounded-full cqa-bg-[#F47F7F]\" style=\"flex-shrink: 0;\">\n <span class=\"cqa-absolute cqa-inset-0 cqa-rounded-full cqa-bg-[#F47F7F] cqa-opacity-75 cqa-animate-ping\"></span>\n </span>\n <span class=\"cqa-text-[10px] cqa-font-medium cqa-text-[#C63535] cqa-leading-[15px]\">Live</span>\n </div>\n <mat-icon *ngIf=\"effectivePlatformType === 'browser'\" style=\"width: 10px; height: 10px;\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"none\">\n <g clip-path=\"url(#clip0_935_15847)\">\n <path\n d=\"M0.625 5C0.625 6.16032 1.08594 7.27312 1.90641 8.09359C2.72688 8.91406 3.83968 9.375 5 9.375C6.16032 9.375 7.27312 8.91406 8.09359 8.09359C8.91406 7.27312 9.375 6.16032 9.375 5C9.375 3.83968 8.91406 2.72688 8.09359 1.90641C7.27312 1.08594 6.16032 0.625 5 0.625C3.83968 0.625 2.72688 1.08594 1.90641 1.90641C1.08594 2.72688 0.625 3.83968 0.625 5Z\"\n stroke=\"#9CA3AF\" stroke-width=\"0.6\" stroke-linejoin=\"round\" />\n <path\n d=\"M3.125 5C3.125 3.83968 3.32254 2.72688 3.67417 1.90641C4.02581 1.08594 4.50272 0.625 5 0.625C5.49728 0.625 5.97419 1.08594 6.32582 1.90641C6.67746 2.72688 6.875 3.83968 6.875 5C6.875 6.16032 6.67746 7.27312 6.32582 8.09359C5.97419 8.91406 5.49728 9.375 5 9.375C4.50272 9.375 4.02581 8.91406 3.67417 8.09359C3.32254 7.27312 3.125 6.16032 3.125 5Z\"\n stroke=\"#9CA3AF\" stroke-width=\"0.6\" stroke-linejoin=\"round\" />\n <path d=\"M0.9375 6.45866H9.0625M0.9375 3.54199H9.0625\" stroke=\"#9CA3AF\" stroke-width=\"0.6\"\n stroke-linecap=\"round\" />\n </g>\n <defs>\n <clipPath id=\"clip0_935_15847\">\n <rect width=\"10\" height=\"10\" fill=\"white\" />\n </clipPath>\n </defs>\n </svg>\n </mat-icon>\n <mat-icon *ngIf=\"effectivePlatformType === 'device'\" style=\"width: 10px; height: 10px;\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"none\">\n <path d=\"M7.08325 0.833008H2.91659C2.45635 0.833008 2.08325 1.2061 2.08325 1.66634V8.33301C2.08325 8.79324 2.45635 9.16634 2.91659 9.16634H7.08325C7.54349 9.16634 7.91658 8.79324 7.91658 8.33301V1.66634C7.91658 1.2061 7.54349 0.833008 7.08325 0.833008Z\" stroke=\"#6B7280\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M5 7.5H5.00417\" stroke=\"#6B7280\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </mat-icon>\n <p class=\"cqa-text-sm !cqa-text-[10px] cqa-text-[#6B7280] cqa-ml-2\">\n {{ platformName }}\n <span\n *ngIf=\"effectivePlatformType === 'browser'\"\n class=\"cqa-ml-1\"\n [matTooltip]=\"'Screen size: ' + effectiveBrowserViewPort.width + 'x' + effectiveBrowserViewPort.height\"\n matTooltipPosition=\"below\"\n >\n \u00B7\n <span class=\"cqa-ml-1\">\n {{ effectiveBrowserViewPort.width }}x{{ effectiveBrowserViewPort.height }}\n </span>\n </span>\n </p>\n <button\n *ngIf=\"showCaptureVideo && isLive\"\n type=\"button\"\n class=\"capture-video-btn\"\n [class.is-loading]=\"isCapturingVideo\"\n [disabled]=\"isCapturingVideo\"\n (click)=\"captureVideo()\">\n <span *ngIf=\"!isCapturingVideo\" class=\"capture-video-btn__dot\"></span>\n <span *ngIf=\"isCapturingVideo\" class=\"capture-video-btn__spinner\" aria-hidden=\"true\"></span>\n <span>{{ isCapturingVideo ? 'Capturing\u2026' : 'Capture Video' }}</span>\n </button>\n </div>\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\">\n <div *ngIf=\"isLive\" [ngClass]=\"getStatusBadgeClass()\">\n <span [ngClass]=\"getStatusTextClass()\">{{ liveStatus }}</span>\n </div>\n\n <cqa-segment-control\n *ngIf=\"isLive && videoUrls && videoUrls.length > 0\"\n [segments]=\"liveSessionSegments\"\n [value]=\"liveSessionView\"\n (valueChange)=\"onLiveSessionViewChange($event)\">\n </cqa-segment-control>\n\n <ng-container *ngIf=\"!isLive\">\n <cqa-segment-control\n [segments]=\"segments\"\n [value]=\"currentView\"\n (valueChange)=\"onSegmentChange($event)\">\n </cqa-segment-control>\n\n <div *ngIf=\"!isFullScreen\"\n class=\"cqa-p-1 cqa-cursor-pointer hover:cqa-bg-gray-100 cqa-rounded-sm cqa-transition-colors\"\n (click)=\"toggleFullScreen()\"\n title=\"Expand\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"none\">\n <path d=\"M6.25 1.25H8.75V3.75\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M8.74992 1.25L5.83325 4.16667\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M1.25 8.74967L4.16667 5.83301\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M3.75 8.75H1.25V6.25\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </div>\n\n <div *ngIf=\"isFullScreen\" \n class=\"cqa-p-1 cqa-cursor-pointer hover:cqa-bg-gray-100 cqa-rounded-sm cqa-transition-colors\"\n (click)=\"toggleFullScreen()\"\n title=\"Exit full screen\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"none\">\n <path d=\"M8.75 6.25H6.25V8.75\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M6.25008 6.25L9.16675 9.16667\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M0.833252 0.833008L3.74992 3.74967\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M1.25 3.75H3.75V1.25\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </div>\n </ng-container>\n </div>\n </div>\n </div>\n <div class=\"cqa-w-full cqa-bg-[#F3F4F6] cqa-h-[calc(100%-41px)]\">\n <!-- Fast-forward overlay: covers content area but not header -->\n <div *ngIf=\"isFastForwarding && fastForwardConfig\"\n class=\"cqa-h-full cqa-w-full cqa-flex cqa-items-center cqa-justify-center cqa-p-6\"\n style=\"background-color: #F3F4F6;\">\n <div class=\"cqa-bg-white cqa-rounded-xl cqa-w-full\"\n style=\"max-width: 500px; padding: 32px; box-shadow: 0px 25px 50px -12px #00000040; border: 1px solid #E5E5E5\">\n <!-- Sparkle avatar with rotating arc -->\n <div class=\"cqa-flex cqa-justify-center cqa-mb-2\">\n <div class=\"cqa-relative\" style=\"width: 56px; height: 56px;\">\n <div class=\"cqa-absolute cqa-inset-0 cqa-rounded-full cqa-flex cqa-items-center cqa-justify-center\"\n style=\"background-color: rgba(63,67,238,0.12);\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"26\" viewBox=\"0 0 24 26\" fill=\"none\">\n <path d=\"M12.0251 0C12.173 0.0634121 12.2362 0.218264 12.2968 0.36211C12.3272 0.445923 12.355 0.530192 12.3821 0.615133C12.3927 0.647682 12.4033 0.680231 12.4142 0.713766C12.5548 1.15177 12.6801 1.59447 12.8061 2.03686C12.8346 2.13673 12.8632 2.23656 12.8918 2.33638C13.0375 2.84464 13.1821 3.35321 13.3258 3.86201C13.346 3.93347 13.3662 4.00492 13.3864 4.07638C13.4434 4.27783 13.5003 4.4793 13.5569 4.68087C13.6764 5.10678 13.7993 5.53145 13.9301 5.95405C13.9415 5.99102 13.9529 6.02799 13.9647 6.06608C14.3045 7.15318 14.7862 8.12751 15.5757 8.95743C15.5953 8.979 15.6148 9.00058 15.635 9.02282C15.972 9.38246 16.4045 9.67063 16.8368 9.90322C16.8588 9.91526 16.8808 9.92729 16.9035 9.9397C18.1757 10.6231 19.7053 10.9237 21.0832 11.3238C21.6869 11.4992 22.2888 11.6802 22.8885 11.8691C22.9306 11.8823 22.9727 11.8956 23.0149 11.9088C23.1409 11.9483 23.2667 11.9885 23.3923 12.0293C23.4216 12.0385 23.4509 12.0478 23.481 12.0573C23.6541 12.1146 23.8197 12.1821 23.9618 12.2992C24.0051 12.3938 24.0051 12.3938 23.9933 12.4884C23.855 12.6258 23.7179 12.6902 23.5354 12.753C23.5093 12.7623 23.4832 12.7716 23.4564 12.7811C23.1483 12.8892 22.836 12.9838 22.5234 13.0775C22.4592 13.0969 22.395 13.1163 22.3309 13.1357C21.9352 13.2551 21.5389 13.3719 21.1421 13.4874C20.6195 13.6395 20.0977 13.7942 19.5763 13.9506C19.4909 13.9763 19.4055 14.0018 19.32 14.0272C18.8712 14.161 18.4246 14.2995 17.9817 14.4516C17.945 14.4642 17.9082 14.4768 17.8704 14.4897C16.387 15.0059 15.2563 15.9687 14.5486 17.3768C14.167 18.1681 13.9419 19.0239 13.7124 19.8685C13.6503 20.0964 13.5842 20.323 13.5175 20.5496C13.4651 20.7278 13.4139 20.9064 13.364 21.0853C13.358 21.1066 13.3521 21.1279 13.346 21.1499C13.3172 21.253 13.2885 21.3562 13.26 21.4594C13.1872 21.7211 13.1087 21.9807 13.0281 22.2401C12.8851 22.7012 12.7549 23.1656 12.6256 23.6306C12.4305 24.3308 12.4305 24.3308 12.3199 24.6674C12.3126 24.69 12.3052 24.7126 12.2977 24.7359C12.2573 24.8557 12.2158 24.966 12.1394 25.0674C12.0231 25.093 12.0231 25.093 11.9187 25.0989C11.7993 24.9249 11.7202 24.7562 11.6569 24.5553C11.6476 24.5269 11.6384 24.4984 11.6289 24.4691C11.5116 24.1019 11.4067 23.7309 11.3018 23.36C11.2778 23.2752 11.2536 23.1904 11.2295 23.1056C11.0496 22.4737 10.8727 21.8409 10.696 21.2081C9.96019 18.0775 9.96019 18.0775 8.104 15.5464C8.07425 15.5222 8.0445 15.4979 8.01385 15.4729C6.79767 14.4989 5.19556 14.1382 3.7284 13.7155C3.36553 13.6109 3.0028 13.5058 2.64009 13.4007C2.60837 13.3915 2.60837 13.3915 2.57601 13.3821C2.05283 13.2304 1.5298 13.0783 1.0086 12.9199C0.986549 12.9132 0.9645 12.9066 0.941783 12.8997C0.141064 12.6578 0.141064 12.6578 0.0017241 12.4884C-0.0022167 12.4115 -0.0022167 12.4115 0.0332505 12.3307C0.178889 12.188 0.320205 12.1269 0.512426 12.0647C0.540259 12.0554 0.56809 12.046 0.596766 12.0364C0.682008 12.0079 0.767436 11.9801 0.852936 11.9524C0.877335 11.9444 0.901735 11.9364 0.926874 11.9282C1.09943 11.8716 1.2726 11.8171 1.44603 11.7633C1.48864 11.75 1.48864 11.75 1.53212 11.7364C2.27272 11.5061 3.01815 11.2917 3.76336 11.0769C4.31866 10.9168 4.87302 10.754 5.42561 10.5847C5.451 10.577 5.4764 10.5692 5.50256 10.5613C6.78267 10.1705 8.03409 9.61612 8.86063 8.51606C8.873 8.49974 8.88538 8.48342 8.89813 8.46661C9.8186 7.24564 10.1775 5.76082 10.5785 4.31201C10.7105 3.83552 10.844 3.35947 10.9779 2.88356C11.015 2.75177 11.0521 2.61997 11.089 2.48812C11.2372 1.95852 11.3876 1.42964 11.5467 0.903181C11.5616 0.853882 11.5764 0.804561 11.5911 0.755218C11.8138 0.0111205 11.8138 0.0111205 12.0251 0Z\" fill=\"#3F43EE\"/>\n <path d=\"M19.2962 1.54899C19.441 1.7128 19.4699 1.90532 19.515 2.11449C19.6653 2.77161 19.8384 3.37584 20.4394 3.75756C20.8102 3.96263 21.2522 4.05595 21.663 4.14409C21.8364 4.18205 21.9829 4.23242 22.1316 4.33316C22.1651 4.38636 22.1651 4.38636 22.1632 4.47897C22.1287 4.59159 22.0984 4.62791 22.0075 4.70163C21.9081 4.73488 21.9081 4.73488 21.7888 4.76074C21.7437 4.7711 21.6985 4.78158 21.6535 4.79214C21.6293 4.79774 21.6051 4.80335 21.5803 4.80912C20.8456 4.97907 20.8456 4.97907 20.2105 5.36368C20.1899 5.3803 20.1692 5.39692 20.1479 5.41405C19.6941 5.81426 19.5981 6.48 19.4615 7.0377C19.4536 7.06833 19.4458 7.09896 19.4377 7.13051C19.431 7.15762 19.4242 7.18474 19.4172 7.21267C19.3877 7.29546 19.3572 7.34945 19.2962 7.41289C19.1266 7.43444 19.1266 7.43444 19.044 7.41289C18.9128 7.31114 18.8834 7.19524 18.8491 7.0395C18.8433 7.01539 18.8376 6.99127 18.8317 6.96642C18.8134 6.8894 18.7956 6.81226 18.778 6.73508C18.6398 6.13403 18.4837 5.54432 17.9324 5.19201C17.5015 4.95172 16.9776 4.83808 16.496 4.74631C16.364 4.71883 16.2884 4.68427 16.2067 4.57552C16.1889 4.46518 16.1889 4.46518 16.2067 4.35484C16.3322 4.22484 16.4644 4.19311 16.6342 4.15386C16.6877 4.14061 16.7412 4.12726 16.7947 4.11383C16.8223 4.10694 16.85 4.10004 16.8784 4.09294C17.0193 4.05668 17.1588 4.01602 17.2983 3.97455C17.3236 3.9672 17.3489 3.95985 17.3751 3.95227C17.6585 3.86752 17.9032 3.75911 18.1298 3.56668C18.1509 3.54928 18.172 3.53189 18.1938 3.51397C18.5465 3.19658 18.6663 2.71976 18.7751 2.27484C18.7834 2.24106 18.7916 2.20728 18.8002 2.17248C18.8167 2.10463 18.8329 2.0367 18.8488 1.9687C18.8602 1.92139 18.8602 1.92139 18.8717 1.87312C18.8784 1.84486 18.885 1.81659 18.8918 1.78747C18.9209 1.69749 18.959 1.62683 19.0125 1.54899C19.1153 1.49761 19.1874 1.52084 19.2962 1.54899Z\" fill=\"#0F12A8\"/>\n <path d=\"M4.7881 17.4219C4.82561 17.4222 4.82561 17.4222 4.86387 17.4225C5.13296 17.4307 5.31503 17.5169 5.51321 17.6978C5.74381 17.9469 5.85029 18.2279 5.84423 18.5637C5.81833 18.8359 5.65779 19.0736 5.46087 19.2576C5.22908 19.4447 4.99525 19.5065 4.69906 19.4979C4.44933 19.4688 4.21438 19.3502 4.03738 19.1717C3.81726 18.8713 3.75291 18.5958 3.78517 18.2259C3.8518 17.9346 4.0439 17.6981 4.28959 17.5323C4.46027 17.4458 4.59755 17.4203 4.7881 17.4219Z\" fill=\"#1216CC\"/>\n </svg>\n </div>\n <svg class=\"cqa-absolute\"\n width=\"60\" height=\"60\" viewBox=\"0 0 60 60\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\n style=\"top: -2px; left: -2px;\">\n <circle cx=\"30\" cy=\"30\" r=\"28\" stroke=\"#E2E2E3\" stroke-width=\"2\" fill=\"none\"/>\n </svg>\n <svg class=\"cqa-absolute cqa-ff-spin\"\n width=\"60\" height=\"60\" viewBox=\"0 0 60 60\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\n style=\"top: -2px; left: -2px; transform-origin: 30px 30px;\">\n <path d=\"M30 2 A 28 28 0 0 1 54.24 44\"\n stroke=\"#3f43ee\" stroke-width=\"2\" stroke-linecap=\"round\" fill=\"none\"/>\n </svg>\n </div>\n </div>\n\n <p class=\"cqa-text-center cqa-m-0 cqa-mb-2\"\n style=\"font-size: 16px; font-weight: 600; color: #161617;\">\n Fast-forwarding to your step\n </p>\n\n <p class=\"cqa-text-center cqa-m-0\"\n style=\"font-size: 12px; color: #6D6D74;\">\n Steps {{ fastForwardConfig.fromStep }}\u2013{{ fastForwardConfig.toStep }} are running at full speed in the background.\n <strong style=\"color: #6D6D74; font-weight: 600;\">Live view and screenshots are paused</strong>\n until execution reaches your target step \u2014 then you're in automatically.\n </p>\n\n <div class=\"cqa-mt-6 cqa-mb-5\">\n <div class=\"cqa-flex cqa-items-center cqa-justify-between cqa-mb-2\">\n <span style=\"font-size: 12px; color: #4C4C51;\">Steps executing in background</span>\n <span style=\"font-size: 12px; color: #6D6D74;\">\n {{ fastForwardConfig.currentStep }} of {{ fastForwardConfig.totalSteps }} steps\n </span>\n </div>\n <div style=\"height: 6px; background-color: #E2E2E3; border-radius: 9999px; overflow: hidden;\">\n <div [style.width.%]=\"fastForwardProgressPercent\"\n style=\"height: 100%; background: linear-gradient(90deg, #3F43EE 0%, #818CF8 100%); transition: width 300ms cubic-bezier(0.4,0,0.2,1);\"></div>\n </div>\n </div>\n\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\"\n style=\"padding: 10px 14px; border-radius: 10px; background: #6366F11A; border: 1px solid #6366F133;\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" style=\"flex-shrink: 0;\">\n <circle cx=\"8\" cy=\"8\" r=\"6.5\" stroke=\"#3f43ee\" stroke-width=\"1.25\"/>\n <circle cx=\"8\" cy=\"8\" r=\"3\" stroke=\"#3f43ee\" stroke-width=\"1.25\"/>\n <circle cx=\"8\" cy=\"8\" r=\"1\" fill=\"#3f43ee\"/>\n </svg>\n <div style=\"font-size: 12px;\">\n <span style=\"color: #6D6D74;\">Jumping to</span>\n <span style=\"color: #3F43EE; font-weight: 400; display: block;\">\n Step {{ fastForwardConfig.targetStepNumber }} \u2014 {{ fastForwardConfig.targetStepLabel }}\n </span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Live Content View -->\n <div *ngIf=\"!isFastForwarding && isLive && liveSessionView === 'live'\" class=\"cqa-h-full cqa-flex cqa-flex-col cqa-justify-center cqa-relative\">\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-p-4\">\n <div class=\"cqa-relative cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-overflow-hidden\"\n [ngClass]=\"{\n 'cqa-w-auto': hasDeviceFrame,\n 'cqa-w-full cqa-flex-col': !hasDeviceFrame,\n 'cqa-rounded-md cqa-overflow-hidden': effectivePlatformType === 'browser',\n 'cqa-max-h-full cqa-h-full': hasDeviceFrame && (effectivePlatformType !== 'browser' || !isLive),\n 'cqa-min-w-max': hasDeviceFrame && effectivePlatformType === 'device'\n }\"\n [ngStyle]=\"liveContentContainerStyle\">\n <img *ngIf=\"hasDeviceFrame\"\n [src]=\"deviceMockupImage\"\n alt=\"Device mockup\"\n class=\"cqa-h-full cqa-w-auto cqa-max-h-full cqa-object-contain cqa-block cqa-pointer-events-none cqa-z-10\"\n />\n <div [ngClass]=\"{\n 'cqa-absolute cqa-flex cqa-flex-col': hasDeviceFrame,\n 'cqa-w-full cqa-h-full cqa-flex cqa-flex-col cqa-items-center cqa-justify-center cqa-relative': !hasDeviceFrame,\n 'cqa-z-20': hasDeviceFrame && effectivePlatformType === 'browser',\n 'cqa-bg-white': hasDeviceFrame && effectivePlatformType !== 'browser'\n }\"\n [ngStyle]=\"hasDeviceFrame ? deviceScreenStyle : {}\">\n <!-- Loading State -->\n <div *ngIf=\"isContentVideoLoading\" class=\"cqa-p-10 cqa-text-center cqa-h-full cqa-flex cqa-flex-col cqa-items-center cqa-justify-center\">\n <div class=\"cqa-mb-4\">\n <mat-progress-spinner mode=\"indeterminate\" diameter=\"40\"></mat-progress-spinner>\n </div>\n <p class=\"cqa-text-gray-400 cqa-text-sm\">{{ liveLoadingLabel }}</p>\n </div>\n\n <!-- Live Content (when not loading) -->\n <div *ngIf=\"!isContentVideoLoading\" class=\"cqa-w-full cqa-h-full cqa-flex cqa-flex-col cqa-items-center cqa-justify-center cqa-relative\">\n <div *ngIf=\"liveStatus === 'Failed' && failedStatusMessage\" class=\"cqa-p-6 cqa-text-center cqa-w-full\">\n <div class=\"cqa-inline-flex cqa-items-center cqa-gap-2 cqa-px-4 cqa-py-3 cqa-bg-[#FCD9D9] cqa-border cqa-border-[#F9BFBF] cqa-rounded-lg\">\n <mat-icon style=\"width: 18px; height: 18px; color: #C63535; font-size: 18px;\">error</mat-icon>\n <p class=\"cqa-text-[#C63535] cqa-text-sm cqa-font-medium cqa-m-0\">{{ failedStatusMessage }}</p>\n </div>\n </div>\n <ng-content *ngIf=\"liveStatus !== 'Failed' || !failedStatusMessage\"></ng-content>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Video View: post-execution Video tab, or mid-session Captured view inside a live session -->\n <div *ngIf=\"!isFastForwarding && ((!isLive && currentView === 'video') || (isLive && liveSessionView === 'captured'))\"\n class=\"cqa-h-full cqa-flex cqa-flex-col\"\n tabindex=\"0\"\n role=\"region\"\n aria-label=\"Video playback\"\n (keydown)=\"onVideoKeydown($event)\">\n <div class=\"cqa-w-full cqa-flex cqa-items-center\" *ngIf=\"currentVideoUrl\" [ngClass]=\"{'!cqa-h-full': effectivePlatformType === 'device', 'cqa-mt-auto': hasDeviceFrame, 'cqa-max-h-[calc(100%-108px)]': showVideoLibrary, 'cqa-max-h-[calc(100%-60px)]': !showVideoLibrary}\">\n <ng-container *ngIf=\"hasDeviceFrame; else videoNoFrame\">\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-p-4\">\n <div class=\"cqa-relative cqa-h-full cqa-w-auto cqa-flex cqa-items-center cqa-justify-center cqa-max-h-full\" [ngClass]=\"{'cqa-rounded-md cqa-overflow-hidden': effectivePlatformType === 'browser', 'cqa-min-w-max': effectivePlatformType === 'device'}\">\n <img\n [src]=\"deviceMockupImage\"\n alt=\"Device mockup\"\n class=\"cqa-h-full cqa-w-auto cqa-max-h-full cqa-object-contain cqa-block cqa-pointer-events-none cqa-z-10\"\n />\n <div class=\"cqa-absolute cqa-flex cqa-flex-col\" [ngStyle]=\"deviceScreenStyle\" [ngClass]=\"{'cqa-bg-white': effectivePlatformType !== 'browser'}\">\n <video\n *ngIf=\"!videoRefreshing\"\n #vplayer\n class=\"cqa-object-cover cqa-w-full cqa-h-full cqa-block cqa-bg-[##F2F2F2] cqa-cursor-pointer\"\n [src]=\"currentVideoUrl\"\n type=\"video/webm\"\n [ngClass]=\"{'cqa-z-20': effectivePlatformType === 'browser'}\"\n (click)=\"onVideoFrameClick()\"\n (loadedmetadata)=\"onVideoMetadataLoaded()\"\n (canplay)=\"onVideoCanPlay()\"\n (ended)=\"onVideoEnded()\"\n (error)=\"onVideoError()\"\n ></video>\n <!-- Play/Pause overlay icon -->\n <div *ngIf=\"showPlayPauseOverlay\"\n class=\"cqa-absolute cqa-inset-0 cqa-flex cqa-items-center cqa-justify-center cqa-pointer-events-none\"\n [ngClass]=\"{'cqa-z-30': effectivePlatformType === 'browser'}\"\n [ngStyle]=\"{animation: 'cqaFadeOut 500ms ease-out forwards'}\">\n <div class=\"cqa-rounded-full cqa-bg-black cqa-bg-opacity-50 cqa-flex cqa-items-center cqa-justify-center\"\n style=\"width: 56px; height: 56px;\">\n <svg *ngIf=\"isPlaying\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"white\">\n <polygon points=\"5,3 19,12 5,21\" />\n </svg>\n <svg *ngIf=\"!isPlaying\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"white\">\n <rect x=\"5\" y=\"3\" width=\"4\" height=\"18\" />\n <rect x=\"15\" y=\"3\" width=\"4\" height=\"18\" />\n </svg>\n </div>\n </div>\n </div>\n </div>\n </div>\n </ng-container>\n <ng-template #videoNoFrame>\n <div class=\"cqa-relative cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-p-4 cqa-cursor-pointer\"\n (click)=\"onVideoFrameClick()\">\n <video\n *ngIf=\"!videoRefreshing\"\n #vplayer\n class=\"cqa-w-full cqa-h-full cqa-block cqa-bg-[##F2F2F2]\"\n [src]=\"currentVideoUrl\"\n type=\"video/webm\"\n (loadedmetadata)=\"onVideoMetadataLoaded()\"\n (canplay)=\"onVideoCanPlay()\"\n (ended)=\"onVideoEnded()\"\n (error)=\"onVideoError()\"\n ></video>\n <!-- Play/Pause overlay icon -->\n <div *ngIf=\"showPlayPauseOverlay\"\n class=\"cqa-absolute cqa-inset-0 cqa-flex cqa-items-center cqa-justify-center cqa-pointer-events-none\"\n [ngStyle]=\"{animation: 'cqaFadeOut 500ms ease-out forwards'}\">\n <div class=\"cqa-rounded-full cqa-bg-black cqa-bg-opacity-50 cqa-flex cqa-items-center cqa-justify-center\"\n style=\"width: 56px; height: 56px;\">\n <svg *ngIf=\"isPlaying\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"white\">\n <polygon points=\"5,3 19,12 5,21\" />\n </svg>\n <svg *ngIf=\"!isPlaying\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"white\">\n <rect x=\"5\" y=\"3\" width=\"4\" height=\"18\" />\n <rect x=\"15\" y=\"3\" width=\"4\" height=\"18\" />\n </svg>\n </div>\n </div>\n </div>\n </ng-template>\n </div>\n \n <div class=\"cqa-p-10 cqa-text-center cqa-text-gray-400 cqa-text-sm cqa-h-full cqa-flex cqa-items-center cqa-justify-center\" *ngIf=\"!currentVideoUrl\">\n <ng-container *ngIf=\"isVNCSessionIntruppted && vncSessionIntupptedMessage; else noVideoDefault\">\n <p class=\"cqa-text-sm cqa-text-gray-600\">\n {{ vncSessionIntupptedMessage }}\n </p>\n </ng-container>\n <ng-template #noVideoDefault>\n <span>No video recording found</span>\n </ng-template>\n </div>\n \n <!-- Video Library Panel \u2014 reserves only header-height in flow; the inner panel is absolutely anchored to its bottom edge, so the body expanding makes the panel grow UPWARD over the video. Timeline stays put. -->\n <div *ngIf=\"showVideoLibrary && videoUrls && videoUrls.length > 0\"\n class=\"video-library\"\n [class.video-library--open]=\"!isVideoLibraryCollapsed\"\n [ngClass]=\"{'cqa-mt-auto': hasDeviceFrame}\">\n <div class=\"video-library__inner\">\n <!-- Header \u2014 always visible, clicking the chevron toggles the body -->\n <div class=\"video-library__header\">\n <div class=\"video-library__title-group\">\n <mat-icon class=\"video-library__icon\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\">\n <path d=\"M9.33398 7.58308L12.3807 9.61425C12.4247 9.64348 12.4757 9.66024 12.5284 9.66276C12.5811 9.66528 12.6335 9.65347 12.68 9.62856C12.7265 9.60366 12.7654 9.56661 12.7925 9.52136C12.8196 9.4761 12.834 9.42434 12.834 9.37158V4.59058C12.834 4.53926 12.8205 4.48885 12.7948 4.44443C12.7691 4.40001 12.7321 4.36315 12.6876 4.33759C12.6431 4.31203 12.5926 4.29866 12.5413 4.29883C12.49 4.299 12.4396 4.31272 12.3953 4.33858L9.33398 6.12475\" stroke=\"#3F43EE\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M8.16602 3.5H2.33268C1.68835 3.5 1.16602 4.02233 1.16602 4.66667V9.33333C1.16602 9.97767 1.68835 10.5 2.33268 10.5H8.16602C8.81035 10.5 9.33268 9.97767 9.33268 9.33333V4.66667C9.33268 4.02233 8.81035 3.5 8.16602 3.5Z\" stroke=\"#3F43EE\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </mat-icon>\n <span class=\"video-library__title\">Video Library</span>\n <cqa-badge\n size=\"small\"\n inlineStyles=\"min-width: max-content;\"\n backgroundColor=\"#EDE9FE\"\n textColor=\"#6D28D9\"\n [label]=\"videoUrls.length + ' clip' + (videoUrls.length === 1 ? '' : 's')\">\n </cqa-badge>\n <span class=\"video-library__subtitle\"\n [matTooltip]=\"isVideoFullMode ? 'Playing all clips as one continuous video' : 'Each clip covers execution since last capture'\"\n matTooltipPosition=\"below\">{{ isVideoFullMode ? '\u00B7 Playing all clips as one continuous video' : '\u00B7 Each clip covers execution since last capture' }}</span>\n </div>\n <div class=\"video-library__actions\">\n <cqa-button\n *ngIf=\"!isVideoFullMode\"\n variant=\"filled\"\n btnSize=\"md\"\n icon=\"play_arrow\"\n iconPosition=\"start\"\n text=\"Show Full Video\"\n (clicked)=\"onShowFullVideo()\">\n </cqa-button>\n <cqa-button\n *ngIf=\"isVideoFullMode\"\n variant=\"outlined\"\n btnSize=\"md\"\n icon=\"close\"\n iconPosition=\"start\"\n text=\"Exit Full Video\"\n (clicked)=\"onExitFullVideo()\">\n </cqa-button>\n <button type=\"button\" class=\"video-library__collapse\" (click)=\"toggleVideoLibraryCollapsed()\"\n [attr.aria-label]=\"isVideoLibraryCollapsed ? 'Show video library' : 'Hide video library'\">\n <svg *ngIf=\"!isVideoLibraryCollapsed\" xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\">\n <path d=\"M3 7.5L6 4.5L9 7.5\" stroke=\"#6B7280\" stroke-width=\"1.25\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <svg *ngIf=\"isVideoLibraryCollapsed\" xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\">\n <path d=\"M3 4.5L6 7.5L9 4.5\" stroke=\"#6B7280\" stroke-width=\"1.25\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </button>\n </div>\n </div>\n\n <!-- Body (cards) \u2014 max-height animates from 0 (collapsed) to a fixed height (open). -->\n <div class=\"video-library__body\"\n [class.video-library__body--open]=\"!isVideoLibraryCollapsed\"\n [attr.aria-hidden]=\"isVideoLibraryCollapsed ? true : null\">\n <div class=\"clips-row\"\n #clipsScroller\n [class.clips-row--dragging]=\"clipsDragActive\"\n (mousedown)=\"onClipsMouseDown($event, clipsScroller)\"\n (mousemove)=\"onClipsMouseMove($event, clipsScroller)\"\n (mouseup)=\"onClipsMouseUp($event)\"\n (mouseleave)=\"onClipsMouseLeave($event)\">\n <div\n *ngFor=\"let url of videoUrls; let i = index; trackBy: trackLibraryClipByIndex\"\n class=\"clip-card\"\n [class.clip-card--playing]=\"i === currentVideoIndex\">\n <div class=\"clip-thumb\"\n [class.clip-thumb--ready]=\"libraryVideoDurations.has(i)\"\n (click)=\"selectLibraryClip(i)\">\n <video\n #clipVideo\n class=\"clip-thumb__video\"\n [src]=\"url\"\n preload=\"metadata\"\n playsinline\n muted\n (loadedmetadata)=\"onLibraryClipMetadataLoaded(i, clipVideo)\"\n (durationchange)=\"onLibraryClipMetadataLoaded(i, clipVideo)\"></video>\n <div class=\"clip-thumb__overlay\">\n <div class=\"clip-thumb__play-circle\">\n <svg *ngIf=\"!(i === currentVideoIndex && isPlaying)\" xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\">\n <path d=\"M3.5 1.75L11.375 7L3.5 12.25V1.75Z\" fill=\"#FFFFFF\"/>\n </svg>\n <svg *ngIf=\"i === currentVideoIndex && isPlaying\" xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\">\n <rect x=\"3.5\" y=\"2.5\" width=\"2.5\" height=\"9\" rx=\"0.5\" fill=\"#FFFFFF\"/>\n <rect x=\"8\" y=\"2.5\" width=\"2.5\" height=\"9\" rx=\"0.5\" fill=\"#FFFFFF\"/>\n </svg>\n </div>\n </div>\n <span *ngIf=\"i === currentVideoIndex && isPlaying\" class=\"clip-thumb__badge clip-thumb__badge--playing\">PLAYING</span>\n <span *ngIf=\"newVideoIndexes.has(i) && i !== currentVideoIndex\" class=\"clip-thumb__badge clip-thumb__badge--new\">NEW</span>\n <span class=\"clip-thumb__duration\">{{ formatTime(libraryVideoDurations.get(i) || 0) }}</span>\n </div>\n <div class=\"clip-meta\">\n <div class=\"clip-meta__title\">Clip {{ i + 1 }}</div>\n <button\n type=\"button\"\n class=\"clip-download-btn\"\n [class.clip-download-btn--downloading]=\"downloadingIndexes.has(i)\"\n [disabled]=\"downloadingIndexes.has(i)\"\n (click)=\"downloadClip(i); $event.stopPropagation()\">\n <ng-container *ngIf=\"!downloadingIndexes.has(i)\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\">\n <!-- Figma icon: upward arrow with a short tray line at the bottom (share/export glyph). -->\n <path d=\"M6 8.5V2M6 2L3 5M6 2L9 5M3 10.5H9\" stroke=\"currentColor\" stroke-width=\"1.1\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <span>Download</span>\n </ng-container>\n <ng-container *ngIf=\"downloadingIndexes.has(i)\">\n <svg class=\"clip-download-btn__ring\" width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\">\n <circle cx=\"7\" cy=\"7\" r=\"6\" stroke=\"#E5E7EB\" stroke-width=\"1.5\" fill=\"none\"/>\n <circle cx=\"7\" cy=\"7\" r=\"6\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"\n stroke-linecap=\"round\"\n [attr.stroke-dasharray]=\"downloadRingCircumference\"\n [attr.stroke-dashoffset]=\"downloadRingDashoffset(i)\"\n transform=\"rotate(-90 7 7)\"/>\n </svg>\n <span>{{ downloadProgress.get(i) || 0 }}%</span>\n </ng-container>\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"cqa-px-2 cqa-py-2 cqa-bg-white\" style=\"border-top: 1px solid #E5E7EB;\" [ngClass]=\"{'cqa-mt-auto': hasDeviceFrame && !(showVideoLibrary && videoUrls && videoUrls.length > 0)}\" *ngIf=\"currentVideoUrl && (!isLive || liveSessionView === 'captured')\">\n <span *ngIf=\"!isVideoFullMode\"\n class=\"cqa-text-[#6B7280] cqa-text-[12px] cqa-font-medium cqa-mb-2 cqa-whitespace-nowrap cqa-block\">\n Video {{ currentVideoIndex + 1 }} playing out of {{ videoUrls?.length || 0 }}\n </span>\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\">\n <button\n *ngIf=\"hasMultipleVideos && !isVideoFullMode\"\n type=\"button\"\n class=\"cqa-bg-transparent cqa-border-none cqa-cursor-pointer !cqa-px-0 cqa-flex cqa-items-center cqa-justify-center hover:cqa-opacity-70 cqa-transition-opacity cqa-outline-none\"\n style=\"pointer-events: auto;\"\n [disabled]=\"currentVideoIndex === 0\"\n [ngClass]=\"{'cqa-opacity-50 cqa-cursor-not-allowed': currentVideoIndex === 0}\"\n (click)=\"prevVideo()\"\n matTooltip=\"Previous video\"\n matTooltipPosition=\"above\">\n <mat-icon class=\"cqa-w-4 cqa-h-4 !cqa-text-[16px] cqa-text-[#374151]\" [ngClass]=\"{'cqa-opacity-50 cqa-cursor-not-allowed': currentVideoIndex === 0}\">skip_previous</mat-icon>\n </button>\n\n <div class=\"cqa-flex cqa-items-center cqa-justify-center\" style=\"width: 16px; height: 16px;\">\n <mat-progress-spinner\n *ngIf=\"isPlayerSwitching\"\n mode=\"indeterminate\"\n diameter=\"16\"\n class=\"cqa-inline-block\">\n </mat-progress-spinner>\n <button \n *ngIf=\"!isPlayerSwitching\"\n type=\"button\"\n class=\"cqa-bg-transparent cqa-border-none cqa-cursor-pointer !cqa-px-0 cqa-flex cqa-items-center cqa-justify-center hover:cqa-opacity-70 cqa-transition-opacity cqa-outline-none\"\n style=\"pointer-events: auto;\"\n (click)=\"togglePlay()\"\n matTooltip=\"{{ isPlaying ? 'Pause' : 'Play' }}\"\n matTooltipPosition=\"above\">\n <span *ngIf=\"!isPlaying\" class=\"cqa-flex cqa-items-center cqa-justify-center\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M3 2L13 8L3 14V2Z\" fill=\"#374151\"/>\n </svg>\n </span>\n <span *ngIf=\"isPlaying\" class=\"cqa-flex cqa-items-center cqa-justify-center\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"3\" y=\"2\" width=\"3\" height=\"12\" fill=\"#374151\"/>\n <rect x=\"10\" y=\"2\" width=\"3\" height=\"12\" fill=\"#374151\"/>\n </svg>\n </span>\n </button>\n </div>\n\n <button\n *ngIf=\"hasMultipleVideos && !isVideoFullMode\"\n type=\"button\"\n class=\"cqa-bg-transparent cqa-border-none cqa-cursor-pointer !cqa-px-0 cqa-flex cqa-items-center cqa-justify-center hover:cqa-opacity-70 cqa-transition-opacity cqa-outline-none\"\n style=\"pointer-events: auto;\"\n [disabled]=\"videoUrls && (currentVideoIndex >= videoUrls.length - 1)\"\n [ngClass]=\"{'cqa-opacity-50 cqa-cursor-not-allowed': videoUrls && (currentVideoIndex >= videoUrls.length - 1)}\"\n (click)=\"nextVideo()\"\n matTooltip=\"Next video\"\n matTooltipPosition=\"above\">\n <mat-icon class=\"cqa-w-4 cqa-h-4 !cqa-text-[16px] cqa-text-[#374151]\" [ngClass]=\"{'cqa-opacity-50 cqa-cursor-not-allowed': videoUrls && (currentVideoIndex >= videoUrls.length - 1)}\">skip_next</mat-icon>\n </button>\n\n <span\n class=\"cqa-text-[#9CA3AF] cqa-text-[9px] cqa-font-normal cqa-whitespace-nowrap cqa-select-none cqa-inline-block\"\n style=\"width: 40px; text-align: center;\">\n {{ formatTime(isVideoFullMode ? (globalCurrentTimeMs / 1000) : (vplayer?.nativeElement?.currentTime || 0)) }}\n </span>\n\n <div #speedControlContainer class=\"cqa-relative cqa-mr-[8px] cqa-flex cqa-items-center cqa-justify-center\">\n <button\n type=\"button\"\n class=\"cqa-bg-transparent cqa-border-none cqa-cursor-pointer cqa-text-[#9CA3AF] cqa-text-[10px] cqa-leading-[15px] cqa-font-medium cqa-whitespace-nowrap cqa-select-none hover:cqa-opacity-70 cqa-transition-opacity cqa-outline-none cqa-px-1\"\n (click)=\"toggleSpeedControl()\"\n [matTooltip]=\"'Playback Speed'\"\n [matTooltipPosition]=\"'below'\">\n {{ currentSpeed }}\n </button>\n \n <div \n *ngIf=\"isSpeedControlOpen\"\n class=\"cqa-absolute cqa-bottom-full cqa-mb-2 cqa-right-0 cqa-bg-[#F0F0F1] cqa-rounded-lg cqa-overflow-hidden cqa-shadow-lg cqa-z-50\"\n style=\"min-width: max-content; left: 50%; bottom: 0%; transform: translate(-50%, -50%); z-index: 101;\">\n <cqa-segment-control\n [segments]=\"speedSegments\"\n [value]=\"currentSpeed\"\n [containerBgColor]=\"'#F0F0F1'\"\n (valueChange)=\"onSpeedChange($event)\">\n </cqa-segment-control>\n </div>\n </div>\n \n <div class=\"cqa-flex-1 cqa-min-w-0\">\n <div \n #timelineBar\n class=\"cqa-relative cqa-h-1 cqa-bg-gray-200 cqa-rounded-full cqa-cursor-pointer cqa-w-full\"\n (click)=\"onTimelineClick($event)\">\n \n <div\n *ngFor=\"let marker of (isVideoFullMode ? fullVideoMarkers : currentVideoMarkers)\"\n class=\"cqa-absolute cqa-rounded-full\"\n [style.left.%]=\"isVideoFullMode ? getGlobalFullStepLeftPosition(marker) : getStepLeftPosition(marker)\"\n [style.width]=\"'8px'\"\n [style.height]=\"'8px'\"\n [style.background]=\"getGlobalMarkerColor(marker.level)\"\n [style.border]=\"'2px solid ' + getGlobalMarkerResultColor(marker.result)\"\n [style.box-sizing]=\"'border-box'\"\n [attr.title]=\"marker.title || ''\"\n style=\"pointer-events: auto; z-index: 50; cursor: pointer; transform: translate(-50%, -50%); top: 50%;\"\n (click)=\"onMarkerClick($event, marker)\">\n </div>\n \n <div\n class=\"cqa-absolute cqa-left-0 cqa-top-0 cqa-h-full cqa-bg-blue-500 cqa-rounded-full\"\n [style.width.%]=\"isVideoFullMode ? globalScrubberPercent : progress\"\n [style.transition]=\"dragging ? 'none' : 'width 100ms'\"\n style=\"pointer-events: none; z-index: 2;\">\n </div>\n \n <div\n class=\"cqa-absolute cqa-top-1/2 cqa-w-3 cqa-h-3 cqa-bg-blue-600 cqa-rounded-full cqa-cursor-grab active:cqa-cursor-grabbing cqa-shadow-md\"\n [style.left.%]=\"isVideoFullMode ? globalScrubberPercent : progress\"\n style=\"transform: translate(-50%, -50%); z-index: 60;\"\n (mousedown)=\"startDrag($event)\">\n </div>\n </div>\n </div>\n\n <span class=\"cqa-text-[#9CA3AF] cqa-text-[9px] cqa-font-normal cqa-whitespace-nowrap cqa-select-none cqa-mr-2\">\n {{ formatTime(isVideoFullMode ? (totalDuration / 1000) : (vplayer?.nativeElement?.duration || 0)) }}\n </span>\n </div>\n </div>\n </div>\n\n <div *ngIf=\"!isFastForwarding && !isLive && currentView === 'screenshots'\" class=\"cqa-h-full\">\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center\" *ngIf=\"screenShotUrl\">\n <ng-container *ngIf=\"hasDeviceFrame; else screenshotNoFrame\">\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-p-4\">\n <div class=\"cqa-relative cqa-h-full cqa-w-auto cqa-flex cqa-items-center cqa-justify-center cqa-max-h-full\" [ngClass]=\"{'cqa-rounded-md cqa-overflow-hidden': effectivePlatformType === 'browser', 'cqa-min-w-max': effectivePlatformType === 'device'}\">\n <img\n [src]=\"deviceMockupImage\"\n alt=\"Device mockup\"\n class=\"cqa-h-full cqa-w-auto cqa-object-contain cqa-block cqa-pointer-events-none cqa-z-10\"\n [ngClass]=\"{'cqa-max-h-[inherit]': effectivePlatformType === 'browser', 'cqa-max-h-full': effectivePlatformType !== 'browser'}\"\n />\n <div class=\"cqa-absolute cqa-flex cqa-flex-col\" [ngStyle]=\"deviceScreenStyle\" [ngClass]=\"{'cqa-bg-white': effectivePlatformType !== 'browser'}\">\n <img\n [src]=\"screenShotUrl\"\n alt=\"Screenshot\"\n [ngClass]=\"{'cqa-z-20': effectivePlatformType === 'browser'}\"\n class=\"cqa-object-contain cqa-w-full cqa-h-full cqa-block cqa-bg-[##F2F2F2]\"\n />\n </div>\n </div>\n </div>\n </ng-container>\n <ng-template #screenshotNoFrame>\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-p-4\">\n <img\n [src]=\"screenShotUrl\"\n alt=\"Screenshot\"\n class=\"cqa-object-contain cqa-w-full cqa-h-full cqa-block cqa-bg-[##F2F2F2]\"\n />\n </div>\n </ng-template>\n </div>\n \n <div class=\"cqa-p-10 cqa-text-center cqa-text-gray-400 cqa-text-sm cqa-h-full cqa-flex cqa-items-center cqa-justify-center\" *ngIf=\"!screenShotUrl\">\n No screenshot available\n </div>\n </div>\n\n <div *ngIf=\"!isFastForwarding && !isLive && currentView === 'trace'\" class=\"cqa-h-full cqa-flex cqa-flex-col cqa-justify-center\">\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-relative\" *ngIf=\"traceViewUrl\" [ngClass]=\"{'!cqa-h-full': effectivePlatformType === 'device'}\" style=\"padding-top: 48px; padding-bottom: 0px;\">\n <div class=\"cqa-w-full cqa-h-full cqa-overflow-hidden cqa-relative\">\n <iframe \n [src]=\"safeTraceUrl\" \n title=\"Trace Viewer\"\n class=\"cqa-object-contain cqa-w-full cqa-min-h-[250px] cqa-max-h-full cqa-block cqa-bg-[##F2F2F2]\"\n style=\"margin-top: -48px; height: calc(100% + 48px);\"\n frameborder=\"0\"\n allowfullscreen\n width=\"100%\"\n loading=\"lazy\"\n (load)=\"onTraceViewerLoad()\"\n (error)=\"onTraceViewerError()\">\n </iframe>\n </div>\n \n <div *ngIf=\"traceViewerLoading\" class=\"cqa-absolute cqa-inset-0 cqa-bg-[#F3F4F6] cqa-flex cqa-items-center cqa-justify-center cqa-z-10\">\n <div class=\"cqa-text-center cqa-text-gray-400 cqa-text-sm\">\n Loading trace viewer...\n </div>\n </div>\n \n <div *ngIf=\"traceViewerError\" class=\"cqa-absolute cqa-inset-0 cqa-bg-[#F3F4F6] cqa-flex cqa-items-center cqa-justify-center cqa-z-10\">\n <div class=\"cqa-text-center cqa-text-gray-400 cqa-text-sm\">\n Failed to load trace viewer\n </div>\n </div>\n </div>\n \n <div class=\"cqa-p-10 cqa-text-center cqa-text-gray-400 cqa-text-sm cqa-h-full cqa-flex cqa-items-center cqa-justify-center\" *ngIf=\"!traceViewUrl\">\n No trace available\n </div>\n </div> \n </div>\n</div>", components: [{ type: i1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { type: SegmentControlComponent, selector: "cqa-segment-control", inputs: ["segments", "value", "disabled", "containerBgColor", "fullWidth"], outputs: ["valueChange"] }, { type: i4.MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "diameter", "strokeWidth", "mode", "value"], exportAs: ["matProgressSpinner"] }, { type: BadgeComponent, selector: "cqa-badge", inputs: ["type", "label", "icon", "iconLibrary", "variant", "size", "backgroundColor", "textColor", "borderColor", "iconBackgroundColor", "iconColor", "iconSize", "inlineStyles", "key", "value", "keyTextColor", "valueTextColor", "isLoading", "fullWidth", "centerContent", "title"] }, { type: ButtonComponent, selector: "cqa-button", inputs: ["variant", "btnSize", "disabled", "loading", "icon", "iconPosition", "fullWidth", "iconColor", "type", "text", "customClass", "inlineStyles", "tooltip", "tooltipPosition"], outputs: ["clicked"] }], directives: [{ type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i6.MatTooltip, selector: "[matTooltip]", exportAs: ["matTooltip"] }, { type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
18739
18951
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: SimulatorComponent, decorators: [{
18740
18952
  type: Component,
18741
- args: [{ selector: 'cqa-simulator', template: "<div class=\"cqa-ui-root\" style=\"background-color: #F3F4F6; height: 100%; display: flex; flex-direction: column;\" [ngStyle]=\"{\n position: isFullScreen ? 'fixed' : null,\n inset: isFullScreen ? '1rem' : null,\n zIndex: isFullScreen ? '50' : null,\n boxShadow: isFullScreen ? '0px 13px 25px -12px rgba(0, 0, 0, 0.25)' : null,\n borderRadius: isFullScreen ? '.5rem' : null,\n border: isFullScreen ? '1px solid #E5E7EB' : null,\n width: isFullScreen ? 'calc(100% - 32px)' : null,\n height: isFullScreen ? 'calc(100% - 32px)' : '100%',\n overflow: isFullScreen ? 'hidden' : null\n}\">\n <div class=\"cqa-w-full cqa-py-1 cqa-px-2 cqa-bg-[#FFFFFF]\" style=\"border-bottom: 1px solid #E5E7EB;box-shadow: 0px 1px 2px 0px #0000000D;\">\n <div class=\"cqa-w-full cqa-flex cqa-items-center cqa-justify-between cqa-flex-wrap\">\n <div class=\"cqa-flex cqa-items-center\">\n <div *ngIf=\"isLive\" class=\"cqa-h-[21px] cqa-inline-flex cqa-items-center cqa-gap-1.5 cqa-mr-2 cqa-px-[9px] cqa-py-[3px] cqa-bg-[#FCD9D9] cqa-rounded-[6px]\" style=\"border: 1px solid #F9BFBF;\">\n <span class=\"cqa-relative cqa-w-2 cqa-h-2 cqa-rounded-full cqa-bg-[#F47F7F]\" style=\"flex-shrink: 0;\">\n <span class=\"cqa-absolute cqa-inset-0 cqa-rounded-full cqa-bg-[#F47F7F] cqa-opacity-75 cqa-animate-ping\"></span>\n </span>\n <span class=\"cqa-text-[10px] cqa-font-medium cqa-text-[#C63535] cqa-leading-[15px]\">Live</span>\n </div>\n <mat-icon *ngIf=\"effectivePlatformType === 'browser'\" style=\"width: 10px; height: 10px;\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"none\">\n <g clip-path=\"url(#clip0_935_15847)\">\n <path\n d=\"M0.625 5C0.625 6.16032 1.08594 7.27312 1.90641 8.09359C2.72688 8.91406 3.83968 9.375 5 9.375C6.16032 9.375 7.27312 8.91406 8.09359 8.09359C8.91406 7.27312 9.375 6.16032 9.375 5C9.375 3.83968 8.91406 2.72688 8.09359 1.90641C7.27312 1.08594 6.16032 0.625 5 0.625C3.83968 0.625 2.72688 1.08594 1.90641 1.90641C1.08594 2.72688 0.625 3.83968 0.625 5Z\"\n stroke=\"#9CA3AF\" stroke-width=\"0.6\" stroke-linejoin=\"round\" />\n <path\n d=\"M3.125 5C3.125 3.83968 3.32254 2.72688 3.67417 1.90641C4.02581 1.08594 4.50272 0.625 5 0.625C5.49728 0.625 5.97419 1.08594 6.32582 1.90641C6.67746 2.72688 6.875 3.83968 6.875 5C6.875 6.16032 6.67746 7.27312 6.32582 8.09359C5.97419 8.91406 5.49728 9.375 5 9.375C4.50272 9.375 4.02581 8.91406 3.67417 8.09359C3.32254 7.27312 3.125 6.16032 3.125 5Z\"\n stroke=\"#9CA3AF\" stroke-width=\"0.6\" stroke-linejoin=\"round\" />\n <path d=\"M0.9375 6.45866H9.0625M0.9375 3.54199H9.0625\" stroke=\"#9CA3AF\" stroke-width=\"0.6\"\n stroke-linecap=\"round\" />\n </g>\n <defs>\n <clipPath id=\"clip0_935_15847\">\n <rect width=\"10\" height=\"10\" fill=\"white\" />\n </clipPath>\n </defs>\n </svg>\n </mat-icon>\n <mat-icon *ngIf=\"effectivePlatformType === 'device'\" style=\"width: 10px; height: 10px;\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"none\">\n <path d=\"M7.08325 0.833008H2.91659C2.45635 0.833008 2.08325 1.2061 2.08325 1.66634V8.33301C2.08325 8.79324 2.45635 9.16634 2.91659 9.16634H7.08325C7.54349 9.16634 7.91658 8.79324 7.91658 8.33301V1.66634C7.91658 1.2061 7.54349 0.833008 7.08325 0.833008Z\" stroke=\"#6B7280\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M5 7.5H5.00417\" stroke=\"#6B7280\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </mat-icon>\n <p class=\"cqa-text-sm !cqa-text-[10px] cqa-text-[#6B7280] cqa-ml-2\">\n {{ platformName }}\n <span\n *ngIf=\"effectivePlatformType === 'browser'\"\n class=\"cqa-ml-1\"\n [matTooltip]=\"'Screen size: ' + effectiveBrowserViewPort.width + 'x' + effectiveBrowserViewPort.height\"\n matTooltipPosition=\"below\"\n >\n \u00B7\n <span class=\"cqa-ml-1\">\n {{ effectiveBrowserViewPort.width }}x{{ effectiveBrowserViewPort.height }}\n </span>\n </span>\n </p>\n <button\n *ngIf=\"showCaptureVideo && isLive\"\n type=\"button\"\n class=\"capture-video-btn\"\n [class.is-loading]=\"isCapturingVideo\"\n [disabled]=\"isCapturingVideo\"\n (click)=\"captureVideo()\">\n <span *ngIf=\"!isCapturingVideo\" class=\"capture-video-btn__dot\"></span>\n <span *ngIf=\"isCapturingVideo\" class=\"capture-video-btn__spinner\" aria-hidden=\"true\"></span>\n <span>{{ isCapturingVideo ? 'Capturing\u2026' : 'Capture Video' }}</span>\n </button>\n </div>\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\">\n <div *ngIf=\"isLive\" [ngClass]=\"getStatusBadgeClass()\">\n <span [ngClass]=\"getStatusTextClass()\">{{ liveStatus }}</span>\n </div>\n\n <ng-container *ngIf=\"!isLive\">\n <cqa-segment-control \n [segments]=\"segments\" \n [value]=\"currentView\"\n (valueChange)=\"onSegmentChange($event)\">\n </cqa-segment-control>\n \n <div *ngIf=\"!isFullScreen\" \n class=\"cqa-p-1 cqa-cursor-pointer hover:cqa-bg-gray-100 cqa-rounded-sm cqa-transition-colors\"\n (click)=\"toggleFullScreen()\"\n title=\"Expand\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"none\">\n <path d=\"M6.25 1.25H8.75V3.75\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M8.74992 1.25L5.83325 4.16667\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M1.25 8.74967L4.16667 5.83301\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M3.75 8.75H1.25V6.25\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </div>\n\n <div *ngIf=\"isFullScreen\" \n class=\"cqa-p-1 cqa-cursor-pointer hover:cqa-bg-gray-100 cqa-rounded-sm cqa-transition-colors\"\n (click)=\"toggleFullScreen()\"\n title=\"Exit full screen\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"none\">\n <path d=\"M8.75 6.25H6.25V8.75\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M6.25008 6.25L9.16675 9.16667\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M0.833252 0.833008L3.74992 3.74967\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M1.25 3.75H3.75V1.25\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </div>\n </ng-container>\n </div>\n </div>\n </div>\n <div class=\"cqa-w-full cqa-bg-[#F3F4F6] cqa-h-[calc(100%-41px)]\">\n <!-- Fast-forward overlay: covers content area but not header -->\n <div *ngIf=\"isFastForwarding && fastForwardConfig\"\n class=\"cqa-h-full cqa-w-full cqa-flex cqa-items-center cqa-justify-center cqa-p-6\"\n style=\"background-color: #F3F4F6;\">\n <div class=\"cqa-bg-white cqa-rounded-xl cqa-w-full\"\n style=\"max-width: 500px; padding: 32px; box-shadow: 0px 25px 50px -12px #00000040; border: 1px solid #E5E5E5\">\n <!-- Sparkle avatar with rotating arc -->\n <div class=\"cqa-flex cqa-justify-center cqa-mb-2\">\n <div class=\"cqa-relative\" style=\"width: 56px; height: 56px;\">\n <div class=\"cqa-absolute cqa-inset-0 cqa-rounded-full cqa-flex cqa-items-center cqa-justify-center\"\n style=\"background-color: rgba(63,67,238,0.12);\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"26\" viewBox=\"0 0 24 26\" fill=\"none\">\n <path d=\"M12.0251 0C12.173 0.0634121 12.2362 0.218264 12.2968 0.36211C12.3272 0.445923 12.355 0.530192 12.3821 0.615133C12.3927 0.647682 12.4033 0.680231 12.4142 0.713766C12.5548 1.15177 12.6801 1.59447 12.8061 2.03686C12.8346 2.13673 12.8632 2.23656 12.8918 2.33638C13.0375 2.84464 13.1821 3.35321 13.3258 3.86201C13.346 3.93347 13.3662 4.00492 13.3864 4.07638C13.4434 4.27783 13.5003 4.4793 13.5569 4.68087C13.6764 5.10678 13.7993 5.53145 13.9301 5.95405C13.9415 5.99102 13.9529 6.02799 13.9647 6.06608C14.3045 7.15318 14.7862 8.12751 15.5757 8.95743C15.5953 8.979 15.6148 9.00058 15.635 9.02282C15.972 9.38246 16.4045 9.67063 16.8368 9.90322C16.8588 9.91526 16.8808 9.92729 16.9035 9.9397C18.1757 10.6231 19.7053 10.9237 21.0832 11.3238C21.6869 11.4992 22.2888 11.6802 22.8885 11.8691C22.9306 11.8823 22.9727 11.8956 23.0149 11.9088C23.1409 11.9483 23.2667 11.9885 23.3923 12.0293C23.4216 12.0385 23.4509 12.0478 23.481 12.0573C23.6541 12.1146 23.8197 12.1821 23.9618 12.2992C24.0051 12.3938 24.0051 12.3938 23.9933 12.4884C23.855 12.6258 23.7179 12.6902 23.5354 12.753C23.5093 12.7623 23.4832 12.7716 23.4564 12.7811C23.1483 12.8892 22.836 12.9838 22.5234 13.0775C22.4592 13.0969 22.395 13.1163 22.3309 13.1357C21.9352 13.2551 21.5389 13.3719 21.1421 13.4874C20.6195 13.6395 20.0977 13.7942 19.5763 13.9506C19.4909 13.9763 19.4055 14.0018 19.32 14.0272C18.8712 14.161 18.4246 14.2995 17.9817 14.4516C17.945 14.4642 17.9082 14.4768 17.8704 14.4897C16.387 15.0059 15.2563 15.9687 14.5486 17.3768C14.167 18.1681 13.9419 19.0239 13.7124 19.8685C13.6503 20.0964 13.5842 20.323 13.5175 20.5496C13.4651 20.7278 13.4139 20.9064 13.364 21.0853C13.358 21.1066 13.3521 21.1279 13.346 21.1499C13.3172 21.253 13.2885 21.3562 13.26 21.4594C13.1872 21.7211 13.1087 21.9807 13.0281 22.2401C12.8851 22.7012 12.7549 23.1656 12.6256 23.6306C12.4305 24.3308 12.4305 24.3308 12.3199 24.6674C12.3126 24.69 12.3052 24.7126 12.2977 24.7359C12.2573 24.8557 12.2158 24.966 12.1394 25.0674C12.0231 25.093 12.0231 25.093 11.9187 25.0989C11.7993 24.9249 11.7202 24.7562 11.6569 24.5553C11.6476 24.5269 11.6384 24.4984 11.6289 24.4691C11.5116 24.1019 11.4067 23.7309 11.3018 23.36C11.2778 23.2752 11.2536 23.1904 11.2295 23.1056C11.0496 22.4737 10.8727 21.8409 10.696 21.2081C9.96019 18.0775 9.96019 18.0775 8.104 15.5464C8.07425 15.5222 8.0445 15.4979 8.01385 15.4729C6.79767 14.4989 5.19556 14.1382 3.7284 13.7155C3.36553 13.6109 3.0028 13.5058 2.64009 13.4007C2.60837 13.3915 2.60837 13.3915 2.57601 13.3821C2.05283 13.2304 1.5298 13.0783 1.0086 12.9199C0.986549 12.9132 0.9645 12.9066 0.941783 12.8997C0.141064 12.6578 0.141064 12.6578 0.0017241 12.4884C-0.0022167 12.4115 -0.0022167 12.4115 0.0332505 12.3307C0.178889 12.188 0.320205 12.1269 0.512426 12.0647C0.540259 12.0554 0.56809 12.046 0.596766 12.0364C0.682008 12.0079 0.767436 11.9801 0.852936 11.9524C0.877335 11.9444 0.901735 11.9364 0.926874 11.9282C1.09943 11.8716 1.2726 11.8171 1.44603 11.7633C1.48864 11.75 1.48864 11.75 1.53212 11.7364C2.27272 11.5061 3.01815 11.2917 3.76336 11.0769C4.31866 10.9168 4.87302 10.754 5.42561 10.5847C5.451 10.577 5.4764 10.5692 5.50256 10.5613C6.78267 10.1705 8.03409 9.61612 8.86063 8.51606C8.873 8.49974 8.88538 8.48342 8.89813 8.46661C9.8186 7.24564 10.1775 5.76082 10.5785 4.31201C10.7105 3.83552 10.844 3.35947 10.9779 2.88356C11.015 2.75177 11.0521 2.61997 11.089 2.48812C11.2372 1.95852 11.3876 1.42964 11.5467 0.903181C11.5616 0.853882 11.5764 0.804561 11.5911 0.755218C11.8138 0.0111205 11.8138 0.0111205 12.0251 0Z\" fill=\"#3F43EE\"/>\n <path d=\"M19.2962 1.54899C19.441 1.7128 19.4699 1.90532 19.515 2.11449C19.6653 2.77161 19.8384 3.37584 20.4394 3.75756C20.8102 3.96263 21.2522 4.05595 21.663 4.14409C21.8364 4.18205 21.9829 4.23242 22.1316 4.33316C22.1651 4.38636 22.1651 4.38636 22.1632 4.47897C22.1287 4.59159 22.0984 4.62791 22.0075 4.70163C21.9081 4.73488 21.9081 4.73488 21.7888 4.76074C21.7437 4.7711 21.6985 4.78158 21.6535 4.79214C21.6293 4.79774 21.6051 4.80335 21.5803 4.80912C20.8456 4.97907 20.8456 4.97907 20.2105 5.36368C20.1899 5.3803 20.1692 5.39692 20.1479 5.41405C19.6941 5.81426 19.5981 6.48 19.4615 7.0377C19.4536 7.06833 19.4458 7.09896 19.4377 7.13051C19.431 7.15762 19.4242 7.18474 19.4172 7.21267C19.3877 7.29546 19.3572 7.34945 19.2962 7.41289C19.1266 7.43444 19.1266 7.43444 19.044 7.41289C18.9128 7.31114 18.8834 7.19524 18.8491 7.0395C18.8433 7.01539 18.8376 6.99127 18.8317 6.96642C18.8134 6.8894 18.7956 6.81226 18.778 6.73508C18.6398 6.13403 18.4837 5.54432 17.9324 5.19201C17.5015 4.95172 16.9776 4.83808 16.496 4.74631C16.364 4.71883 16.2884 4.68427 16.2067 4.57552C16.1889 4.46518 16.1889 4.46518 16.2067 4.35484C16.3322 4.22484 16.4644 4.19311 16.6342 4.15386C16.6877 4.14061 16.7412 4.12726 16.7947 4.11383C16.8223 4.10694 16.85 4.10004 16.8784 4.09294C17.0193 4.05668 17.1588 4.01602 17.2983 3.97455C17.3236 3.9672 17.3489 3.95985 17.3751 3.95227C17.6585 3.86752 17.9032 3.75911 18.1298 3.56668C18.1509 3.54928 18.172 3.53189 18.1938 3.51397C18.5465 3.19658 18.6663 2.71976 18.7751 2.27484C18.7834 2.24106 18.7916 2.20728 18.8002 2.17248C18.8167 2.10463 18.8329 2.0367 18.8488 1.9687C18.8602 1.92139 18.8602 1.92139 18.8717 1.87312C18.8784 1.84486 18.885 1.81659 18.8918 1.78747C18.9209 1.69749 18.959 1.62683 19.0125 1.54899C19.1153 1.49761 19.1874 1.52084 19.2962 1.54899Z\" fill=\"#0F12A8\"/>\n <path d=\"M4.7881 17.4219C4.82561 17.4222 4.82561 17.4222 4.86387 17.4225C5.13296 17.4307 5.31503 17.5169 5.51321 17.6978C5.74381 17.9469 5.85029 18.2279 5.84423 18.5637C5.81833 18.8359 5.65779 19.0736 5.46087 19.2576C5.22908 19.4447 4.99525 19.5065 4.69906 19.4979C4.44933 19.4688 4.21438 19.3502 4.03738 19.1717C3.81726 18.8713 3.75291 18.5958 3.78517 18.2259C3.8518 17.9346 4.0439 17.6981 4.28959 17.5323C4.46027 17.4458 4.59755 17.4203 4.7881 17.4219Z\" fill=\"#1216CC\"/>\n </svg>\n </div>\n <svg class=\"cqa-absolute\"\n width=\"60\" height=\"60\" viewBox=\"0 0 60 60\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\n style=\"top: -2px; left: -2px;\">\n <circle cx=\"30\" cy=\"30\" r=\"28\" stroke=\"#E2E2E3\" stroke-width=\"2\" fill=\"none\"/>\n </svg>\n <svg class=\"cqa-absolute cqa-ff-spin\"\n width=\"60\" height=\"60\" viewBox=\"0 0 60 60\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\n style=\"top: -2px; left: -2px; transform-origin: 30px 30px;\">\n <path d=\"M30 2 A 28 28 0 0 1 54.24 44\"\n stroke=\"#3f43ee\" stroke-width=\"2\" stroke-linecap=\"round\" fill=\"none\"/>\n </svg>\n </div>\n </div>\n\n <p class=\"cqa-text-center cqa-m-0 cqa-mb-2\"\n style=\"font-size: 16px; font-weight: 600; color: #161617;\">\n Fast-forwarding to your step\n </p>\n\n <p class=\"cqa-text-center cqa-m-0\"\n style=\"font-size: 12px; color: #6D6D74;\">\n Steps {{ fastForwardConfig.fromStep }}\u2013{{ fastForwardConfig.toStep }} are running at full speed in the background.\n <strong style=\"color: #6D6D74; font-weight: 600;\">Live view and screenshots are paused</strong>\n until execution reaches your target step \u2014 then you're in automatically.\n </p>\n\n <div class=\"cqa-mt-6 cqa-mb-5\">\n <div class=\"cqa-flex cqa-items-center cqa-justify-between cqa-mb-2\">\n <span style=\"font-size: 12px; color: #4C4C51;\">Steps executing in background</span>\n <span style=\"font-size: 12px; color: #6D6D74;\">\n {{ fastForwardConfig.currentStep }} of {{ fastForwardConfig.totalSteps }} steps\n </span>\n </div>\n <div style=\"height: 6px; background-color: #E2E2E3; border-radius: 9999px; overflow: hidden;\">\n <div [style.width.%]=\"fastForwardProgressPercent\"\n style=\"height: 100%; background: linear-gradient(90deg, #3F43EE 0%, #818CF8 100%); transition: width 300ms cubic-bezier(0.4,0,0.2,1);\"></div>\n </div>\n </div>\n\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\"\n style=\"padding: 10px 14px; border-radius: 10px; background: #6366F11A; border: 1px solid #6366F133;\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" style=\"flex-shrink: 0;\">\n <circle cx=\"8\" cy=\"8\" r=\"6.5\" stroke=\"#3f43ee\" stroke-width=\"1.25\"/>\n <circle cx=\"8\" cy=\"8\" r=\"3\" stroke=\"#3f43ee\" stroke-width=\"1.25\"/>\n <circle cx=\"8\" cy=\"8\" r=\"1\" fill=\"#3f43ee\"/>\n </svg>\n <div style=\"font-size: 12px;\">\n <span style=\"color: #6D6D74;\">Jumping to</span>\n <span style=\"color: #3F43EE; font-weight: 400; display: block;\">\n Step {{ fastForwardConfig.targetStepNumber }} \u2014 {{ fastForwardConfig.targetStepLabel }}\n </span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Live Content View -->\n <div *ngIf=\"!isFastForwarding && isLive\" class=\"cqa-h-full cqa-flex cqa-flex-col cqa-justify-center cqa-relative\">\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-p-4\">\n <div class=\"cqa-relative cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-overflow-hidden\"\n [ngClass]=\"{\n 'cqa-w-auto': hasDeviceFrame,\n 'cqa-w-full cqa-flex-col': !hasDeviceFrame,\n 'cqa-rounded-md cqa-overflow-hidden': effectivePlatformType === 'browser',\n 'cqa-max-h-full cqa-h-full': hasDeviceFrame && (effectivePlatformType !== 'browser' || !isLive),\n 'cqa-min-w-max': hasDeviceFrame && effectivePlatformType === 'device'\n }\"\n [ngStyle]=\"liveContentContainerStyle\">\n <img *ngIf=\"hasDeviceFrame\"\n [src]=\"deviceMockupImage\"\n alt=\"Device mockup\"\n class=\"cqa-h-full cqa-w-auto cqa-max-h-full cqa-object-contain cqa-block cqa-pointer-events-none cqa-z-10\"\n />\n <div [ngClass]=\"{\n 'cqa-absolute cqa-flex cqa-flex-col': hasDeviceFrame,\n 'cqa-w-full cqa-h-full cqa-flex cqa-flex-col cqa-items-center cqa-justify-center cqa-relative': !hasDeviceFrame,\n 'cqa-z-20': hasDeviceFrame && effectivePlatformType === 'browser',\n 'cqa-bg-white': hasDeviceFrame && effectivePlatformType !== 'browser'\n }\"\n [ngStyle]=\"hasDeviceFrame ? deviceScreenStyle : {}\">\n <!-- Loading State -->\n <div *ngIf=\"isContentVideoLoading\" class=\"cqa-p-10 cqa-text-center cqa-h-full cqa-flex cqa-flex-col cqa-items-center cqa-justify-center\">\n <div class=\"cqa-mb-4\">\n <mat-progress-spinner mode=\"indeterminate\" diameter=\"40\"></mat-progress-spinner>\n </div>\n <p class=\"cqa-text-gray-400 cqa-text-sm\">{{ liveLoadingLabel }}</p>\n </div>\n\n <!-- Live Content (when not loading) -->\n <div *ngIf=\"!isContentVideoLoading\" class=\"cqa-w-full cqa-h-full cqa-flex cqa-flex-col cqa-items-center cqa-justify-center cqa-relative\">\n <div *ngIf=\"liveStatus === 'Failed' && failedStatusMessage\" class=\"cqa-p-6 cqa-text-center cqa-w-full\">\n <div class=\"cqa-inline-flex cqa-items-center cqa-gap-2 cqa-px-4 cqa-py-3 cqa-bg-[#FCD9D9] cqa-border cqa-border-[#F9BFBF] cqa-rounded-lg\">\n <mat-icon style=\"width: 18px; height: 18px; color: #C63535; font-size: 18px;\">error</mat-icon>\n <p class=\"cqa-text-[#C63535] cqa-text-sm cqa-font-medium cqa-m-0\">{{ failedStatusMessage }}</p>\n </div>\n </div>\n <ng-content *ngIf=\"liveStatus !== 'Failed' || !failedStatusMessage\"></ng-content>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Normal Video View (when not live) -->\n <div *ngIf=\"!isFastForwarding && !isLive && currentView === 'video'\"\n class=\"cqa-h-full cqa-flex cqa-flex-col\"\n tabindex=\"0\"\n role=\"region\"\n aria-label=\"Video playback\"\n (keydown)=\"onVideoKeydown($event)\">\n <div class=\"cqa-w-full cqa-flex cqa-items-center\" *ngIf=\"currentVideoUrl\" [ngClass]=\"{'!cqa-h-full': effectivePlatformType === 'device', 'cqa-mt-auto': hasDeviceFrame, 'cqa-max-h-[calc(100%-108px)]': showVideoLibrary, 'cqa-max-h-[calc(100%-60px)]': !showVideoLibrary}\">\n <ng-container *ngIf=\"hasDeviceFrame; else videoNoFrame\">\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-p-4\">\n <div class=\"cqa-relative cqa-h-full cqa-w-auto cqa-flex cqa-items-center cqa-justify-center cqa-max-h-full\" [ngClass]=\"{'cqa-rounded-md cqa-overflow-hidden': effectivePlatformType === 'browser', 'cqa-min-w-max': effectivePlatformType === 'device'}\">\n <img\n [src]=\"deviceMockupImage\"\n alt=\"Device mockup\"\n class=\"cqa-h-full cqa-w-auto cqa-max-h-full cqa-object-contain cqa-block cqa-pointer-events-none cqa-z-10\"\n />\n <div class=\"cqa-absolute cqa-flex cqa-flex-col\" [ngStyle]=\"deviceScreenStyle\" [ngClass]=\"{'cqa-bg-white': effectivePlatformType !== 'browser'}\">\n <video\n *ngIf=\"!videoRefreshing\"\n #vplayer\n class=\"cqa-object-cover cqa-w-full cqa-h-full cqa-block cqa-bg-[##F2F2F2] cqa-cursor-pointer\"\n [src]=\"currentVideoUrl\"\n type=\"video/webm\"\n [ngClass]=\"{'cqa-z-20': effectivePlatformType === 'browser'}\"\n (click)=\"onVideoFrameClick()\"\n (loadedmetadata)=\"onVideoMetadataLoaded()\"\n (canplay)=\"onVideoCanPlay()\"\n (ended)=\"onVideoEnded()\"\n (error)=\"onVideoError()\"\n ></video>\n <!-- Play/Pause overlay icon -->\n <div *ngIf=\"showPlayPauseOverlay\"\n class=\"cqa-absolute cqa-inset-0 cqa-flex cqa-items-center cqa-justify-center cqa-pointer-events-none\"\n [ngClass]=\"{'cqa-z-30': effectivePlatformType === 'browser'}\"\n [ngStyle]=\"{animation: 'cqaFadeOut 500ms ease-out forwards'}\">\n <div class=\"cqa-rounded-full cqa-bg-black cqa-bg-opacity-50 cqa-flex cqa-items-center cqa-justify-center\"\n style=\"width: 56px; height: 56px;\">\n <svg *ngIf=\"isPlaying\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"white\">\n <polygon points=\"5,3 19,12 5,21\" />\n </svg>\n <svg *ngIf=\"!isPlaying\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"white\">\n <rect x=\"5\" y=\"3\" width=\"4\" height=\"18\" />\n <rect x=\"15\" y=\"3\" width=\"4\" height=\"18\" />\n </svg>\n </div>\n </div>\n </div>\n </div>\n </div>\n </ng-container>\n <ng-template #videoNoFrame>\n <div class=\"cqa-relative cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-p-4 cqa-cursor-pointer\"\n (click)=\"onVideoFrameClick()\">\n <video\n *ngIf=\"!videoRefreshing\"\n #vplayer\n class=\"cqa-w-full cqa-h-full cqa-block cqa-bg-[##F2F2F2]\"\n [src]=\"currentVideoUrl\"\n type=\"video/webm\"\n (loadedmetadata)=\"onVideoMetadataLoaded()\"\n (canplay)=\"onVideoCanPlay()\"\n (ended)=\"onVideoEnded()\"\n (error)=\"onVideoError()\"\n ></video>\n <!-- Play/Pause overlay icon -->\n <div *ngIf=\"showPlayPauseOverlay\"\n class=\"cqa-absolute cqa-inset-0 cqa-flex cqa-items-center cqa-justify-center cqa-pointer-events-none\"\n [ngStyle]=\"{animation: 'cqaFadeOut 500ms ease-out forwards'}\">\n <div class=\"cqa-rounded-full cqa-bg-black cqa-bg-opacity-50 cqa-flex cqa-items-center cqa-justify-center\"\n style=\"width: 56px; height: 56px;\">\n <svg *ngIf=\"isPlaying\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"white\">\n <polygon points=\"5,3 19,12 5,21\" />\n </svg>\n <svg *ngIf=\"!isPlaying\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"white\">\n <rect x=\"5\" y=\"3\" width=\"4\" height=\"18\" />\n <rect x=\"15\" y=\"3\" width=\"4\" height=\"18\" />\n </svg>\n </div>\n </div>\n </div>\n </ng-template>\n </div>\n \n <div class=\"cqa-p-10 cqa-text-center cqa-text-gray-400 cqa-text-sm cqa-h-full cqa-flex cqa-items-center cqa-justify-center\" *ngIf=\"!currentVideoUrl\">\n <ng-container *ngIf=\"isVNCSessionIntruppted && vncSessionIntupptedMessage; else noVideoDefault\">\n <p class=\"cqa-text-sm cqa-text-gray-600\">\n {{ vncSessionIntupptedMessage }}\n </p>\n </ng-container>\n <ng-template #noVideoDefault>\n <span>No video recording found</span>\n </ng-template>\n </div>\n \n <!-- Video Library Panel \u2014 reserves only header-height in flow; the inner panel is absolutely anchored to its bottom edge, so the body expanding makes the panel grow UPWARD over the video. Timeline stays put. -->\n <div *ngIf=\"showVideoLibrary && videoUrls && videoUrls.length > 0\"\n class=\"video-library\"\n [class.video-library--open]=\"!isVideoLibraryCollapsed\"\n [ngClass]=\"{'cqa-mt-auto': hasDeviceFrame}\">\n <div class=\"video-library__inner\">\n <!-- Header \u2014 always visible, clicking the chevron toggles the body -->\n <div class=\"video-library__header\">\n <div class=\"video-library__title-group\">\n <mat-icon class=\"video-library__icon\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\">\n <path d=\"M9.33398 7.58308L12.3807 9.61425C12.4247 9.64348 12.4757 9.66024 12.5284 9.66276C12.5811 9.66528 12.6335 9.65347 12.68 9.62856C12.7265 9.60366 12.7654 9.56661 12.7925 9.52136C12.8196 9.4761 12.834 9.42434 12.834 9.37158V4.59058C12.834 4.53926 12.8205 4.48885 12.7948 4.44443C12.7691 4.40001 12.7321 4.36315 12.6876 4.33759C12.6431 4.31203 12.5926 4.29866 12.5413 4.29883C12.49 4.299 12.4396 4.31272 12.3953 4.33858L9.33398 6.12475\" stroke=\"#3F43EE\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M8.16602 3.5H2.33268C1.68835 3.5 1.16602 4.02233 1.16602 4.66667V9.33333C1.16602 9.97767 1.68835 10.5 2.33268 10.5H8.16602C8.81035 10.5 9.33268 9.97767 9.33268 9.33333V4.66667C9.33268 4.02233 8.81035 3.5 8.16602 3.5Z\" stroke=\"#3F43EE\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </mat-icon>\n <span class=\"video-library__title\">Video Library</span>\n <cqa-badge\n size=\"small\"\n inlineStyles=\"min-width: max-content;\"\n backgroundColor=\"#EDE9FE\"\n textColor=\"#6D28D9\"\n [label]=\"videoUrls.length + ' clip' + (videoUrls.length === 1 ? '' : 's')\">\n </cqa-badge>\n <span class=\"video-library__subtitle\"\n [matTooltip]=\"isVideoFullMode ? 'Playing all clips as one continuous video' : 'Each clip covers execution since last capture'\"\n matTooltipPosition=\"below\">{{ isVideoFullMode ? '\u00B7 Playing all clips as one continuous video' : '\u00B7 Each clip covers execution since last capture' }}</span>\n </div>\n <div class=\"video-library__actions\">\n <cqa-button\n *ngIf=\"!isVideoFullMode\"\n variant=\"filled\"\n btnSize=\"md\"\n icon=\"play_arrow\"\n iconPosition=\"start\"\n text=\"Show Full Video\"\n (clicked)=\"onShowFullVideo()\">\n </cqa-button>\n <cqa-button\n *ngIf=\"isVideoFullMode\"\n variant=\"outlined\"\n btnSize=\"md\"\n icon=\"close\"\n iconPosition=\"start\"\n text=\"Exit Full Video\"\n (clicked)=\"onExitFullVideo()\">\n </cqa-button>\n <button type=\"button\" class=\"video-library__collapse\" (click)=\"toggleVideoLibraryCollapsed()\"\n [attr.aria-label]=\"isVideoLibraryCollapsed ? 'Show video library' : 'Hide video library'\">\n <svg *ngIf=\"!isVideoLibraryCollapsed\" xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\">\n <path d=\"M3 7.5L6 4.5L9 7.5\" stroke=\"#6B7280\" stroke-width=\"1.25\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <svg *ngIf=\"isVideoLibraryCollapsed\" xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\">\n <path d=\"M3 4.5L6 7.5L9 4.5\" stroke=\"#6B7280\" stroke-width=\"1.25\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </button>\n </div>\n </div>\n\n <!-- Body (cards) \u2014 max-height animates from 0 (collapsed) to a fixed height (open). -->\n <div class=\"video-library__body\"\n [class.video-library__body--open]=\"!isVideoLibraryCollapsed\"\n [attr.aria-hidden]=\"isVideoLibraryCollapsed ? true : null\">\n <div class=\"clips-row\"\n #clipsScroller\n [class.clips-row--dragging]=\"clipsDragActive\"\n (mousedown)=\"onClipsMouseDown($event, clipsScroller)\"\n (mousemove)=\"onClipsMouseMove($event, clipsScroller)\"\n (mouseup)=\"onClipsMouseUp($event)\"\n (mouseleave)=\"onClipsMouseLeave($event)\">\n <div\n *ngFor=\"let url of videoUrls; let i = index; trackBy: trackLibraryClipByIndex\"\n class=\"clip-card\"\n [class.clip-card--playing]=\"i === currentVideoIndex\">\n <div class=\"clip-thumb\"\n [class.clip-thumb--ready]=\"libraryVideoDurations.has(i)\"\n (click)=\"selectLibraryClip(i)\">\n <video\n #clipVideo\n class=\"clip-thumb__video\"\n [src]=\"url\"\n preload=\"metadata\"\n playsinline\n muted\n (loadedmetadata)=\"onLibraryClipMetadataLoaded(i, clipVideo)\"\n (durationchange)=\"onLibraryClipMetadataLoaded(i, clipVideo)\"></video>\n <div class=\"clip-thumb__overlay\">\n <div class=\"clip-thumb__play-circle\">\n <svg *ngIf=\"!(i === currentVideoIndex && isPlaying)\" xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\">\n <path d=\"M3.5 1.75L11.375 7L3.5 12.25V1.75Z\" fill=\"#FFFFFF\"/>\n </svg>\n <svg *ngIf=\"i === currentVideoIndex && isPlaying\" xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\">\n <rect x=\"3.5\" y=\"2.5\" width=\"2.5\" height=\"9\" rx=\"0.5\" fill=\"#FFFFFF\"/>\n <rect x=\"8\" y=\"2.5\" width=\"2.5\" height=\"9\" rx=\"0.5\" fill=\"#FFFFFF\"/>\n </svg>\n </div>\n </div>\n <span *ngIf=\"i === currentVideoIndex && isPlaying\" class=\"clip-thumb__badge clip-thumb__badge--playing\">PLAYING</span>\n <span *ngIf=\"newVideoIndexes.has(i) && i !== currentVideoIndex\" class=\"clip-thumb__badge clip-thumb__badge--new\">NEW</span>\n <span class=\"clip-thumb__duration\">{{ formatTime(libraryVideoDurations.get(i) || 0) }}</span>\n </div>\n <div class=\"clip-meta\">\n <div class=\"clip-meta__title\">Clip {{ i + 1 }}</div>\n <button\n type=\"button\"\n class=\"clip-download-btn\"\n [class.clip-download-btn--downloading]=\"downloadingIndexes.has(i)\"\n [disabled]=\"downloadingIndexes.has(i)\"\n (click)=\"downloadClip(i); $event.stopPropagation()\">\n <ng-container *ngIf=\"!downloadingIndexes.has(i)\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\">\n <!-- Figma icon: upward arrow with a short tray line at the bottom (share/export glyph). -->\n <path d=\"M6 8.5V2M6 2L3 5M6 2L9 5M3 10.5H9\" stroke=\"currentColor\" stroke-width=\"1.1\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <span>Download</span>\n </ng-container>\n <ng-container *ngIf=\"downloadingIndexes.has(i)\">\n <svg class=\"clip-download-btn__ring\" width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\">\n <circle cx=\"7\" cy=\"7\" r=\"6\" stroke=\"#E5E7EB\" stroke-width=\"1.5\" fill=\"none\"/>\n <circle cx=\"7\" cy=\"7\" r=\"6\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"\n stroke-linecap=\"round\"\n [attr.stroke-dasharray]=\"downloadRingCircumference\"\n [attr.stroke-dashoffset]=\"downloadRingDashoffset(i)\"\n transform=\"rotate(-90 7 7)\"/>\n </svg>\n <span>{{ downloadProgress.get(i) || 0 }}%</span>\n </ng-container>\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"cqa-px-2 cqa-py-2 cqa-bg-white\" style=\"border-top: 1px solid #E5E7EB;\" [ngClass]=\"{'cqa-mt-auto': hasDeviceFrame && !(showVideoLibrary && videoUrls && videoUrls.length > 0)}\" *ngIf=\"currentVideoUrl && !isLive\">\n <span *ngIf=\"!isVideoFullMode\"\n class=\"cqa-text-[#6B7280] cqa-text-[12px] cqa-font-medium cqa-mb-2 cqa-whitespace-nowrap cqa-block\">\n Video {{ currentVideoIndex + 1 }} playing out of {{ videoUrls?.length || 0 }}\n </span>\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\">\n <button\n *ngIf=\"hasMultipleVideos && !isVideoFullMode\"\n type=\"button\"\n class=\"cqa-bg-transparent cqa-border-none cqa-cursor-pointer !cqa-px-0 cqa-flex cqa-items-center cqa-justify-center hover:cqa-opacity-70 cqa-transition-opacity cqa-outline-none\"\n style=\"pointer-events: auto;\"\n [disabled]=\"currentVideoIndex === 0\"\n [ngClass]=\"{'cqa-opacity-50 cqa-cursor-not-allowed': currentVideoIndex === 0}\"\n (click)=\"prevVideo()\"\n matTooltip=\"Previous video\"\n matTooltipPosition=\"above\">\n <mat-icon class=\"cqa-w-4 cqa-h-4 !cqa-text-[16px] cqa-text-[#374151]\" [ngClass]=\"{'cqa-opacity-50 cqa-cursor-not-allowed': currentVideoIndex === 0}\">skip_previous</mat-icon>\n </button>\n\n <div class=\"cqa-flex cqa-items-center cqa-justify-center\" style=\"width: 16px; height: 16px;\">\n <mat-progress-spinner\n *ngIf=\"isPlayerSwitching\"\n mode=\"indeterminate\"\n diameter=\"16\"\n class=\"cqa-inline-block\">\n </mat-progress-spinner>\n <button \n *ngIf=\"!isPlayerSwitching\"\n type=\"button\"\n class=\"cqa-bg-transparent cqa-border-none cqa-cursor-pointer !cqa-px-0 cqa-flex cqa-items-center cqa-justify-center hover:cqa-opacity-70 cqa-transition-opacity cqa-outline-none\"\n style=\"pointer-events: auto;\"\n (click)=\"togglePlay()\"\n matTooltip=\"{{ isPlaying ? 'Pause' : 'Play' }}\"\n matTooltipPosition=\"above\">\n <span *ngIf=\"!isPlaying\" class=\"cqa-flex cqa-items-center cqa-justify-center\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M3 2L13 8L3 14V2Z\" fill=\"#374151\"/>\n </svg>\n </span>\n <span *ngIf=\"isPlaying\" class=\"cqa-flex cqa-items-center cqa-justify-center\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"3\" y=\"2\" width=\"3\" height=\"12\" fill=\"#374151\"/>\n <rect x=\"10\" y=\"2\" width=\"3\" height=\"12\" fill=\"#374151\"/>\n </svg>\n </span>\n </button>\n </div>\n\n <button\n *ngIf=\"hasMultipleVideos && !isVideoFullMode\"\n type=\"button\"\n class=\"cqa-bg-transparent cqa-border-none cqa-cursor-pointer !cqa-px-0 cqa-flex cqa-items-center cqa-justify-center hover:cqa-opacity-70 cqa-transition-opacity cqa-outline-none\"\n style=\"pointer-events: auto;\"\n [disabled]=\"videoUrls && (currentVideoIndex >= videoUrls.length - 1)\"\n [ngClass]=\"{'cqa-opacity-50 cqa-cursor-not-allowed': videoUrls && (currentVideoIndex >= videoUrls.length - 1)}\"\n (click)=\"nextVideo()\"\n matTooltip=\"Next video\"\n matTooltipPosition=\"above\">\n <mat-icon class=\"cqa-w-4 cqa-h-4 !cqa-text-[16px] cqa-text-[#374151]\" [ngClass]=\"{'cqa-opacity-50 cqa-cursor-not-allowed': videoUrls && (currentVideoIndex >= videoUrls.length - 1)}\">skip_next</mat-icon>\n </button>\n\n <span\n class=\"cqa-text-[#9CA3AF] cqa-text-[9px] cqa-font-normal cqa-whitespace-nowrap cqa-select-none cqa-inline-block\"\n style=\"width: 40px; text-align: center;\">\n {{ formatTime(isVideoFullMode ? (globalCurrentTimeMs / 1000) : (vplayer?.nativeElement?.currentTime || 0)) }}\n </span>\n\n <div #speedControlContainer class=\"cqa-relative cqa-mr-[8px] cqa-flex cqa-items-center cqa-justify-center\">\n <button\n type=\"button\"\n class=\"cqa-bg-transparent cqa-border-none cqa-cursor-pointer cqa-text-[#9CA3AF] cqa-text-[10px] cqa-leading-[15px] cqa-font-medium cqa-whitespace-nowrap cqa-select-none hover:cqa-opacity-70 cqa-transition-opacity cqa-outline-none cqa-px-1\"\n (click)=\"toggleSpeedControl()\"\n [matTooltip]=\"'Playback Speed'\"\n [matTooltipPosition]=\"'below'\">\n {{ currentSpeed }}\n </button>\n \n <div \n *ngIf=\"isSpeedControlOpen\"\n class=\"cqa-absolute cqa-bottom-full cqa-mb-2 cqa-right-0 cqa-bg-[#F0F0F1] cqa-rounded-lg cqa-overflow-hidden cqa-shadow-lg cqa-z-50\"\n style=\"min-width: max-content; left: 50%; bottom: 0%; transform: translate(-50%, -50%); z-index: 101;\">\n <cqa-segment-control\n [segments]=\"speedSegments\"\n [value]=\"currentSpeed\"\n [containerBgColor]=\"'#F0F0F1'\"\n (valueChange)=\"onSpeedChange($event)\">\n </cqa-segment-control>\n </div>\n </div>\n \n <div class=\"cqa-flex-1 cqa-min-w-0\">\n <div \n #timelineBar\n class=\"cqa-relative cqa-h-1 cqa-bg-gray-200 cqa-rounded-full cqa-cursor-pointer cqa-w-full\"\n (click)=\"onTimelineClick($event)\">\n \n <div\n *ngFor=\"let marker of (isVideoFullMode ? fullVideoMarkers : currentVideoMarkers)\"\n class=\"cqa-absolute cqa-rounded-full\"\n [style.left.%]=\"isVideoFullMode ? getGlobalFullStepLeftPosition(marker) : getStepLeftPosition(marker)\"\n [style.width]=\"'8px'\"\n [style.height]=\"'8px'\"\n [style.background]=\"getGlobalMarkerColor(marker.level)\"\n [style.border]=\"'2px solid ' + getGlobalMarkerResultColor(marker.result)\"\n [style.box-sizing]=\"'border-box'\"\n [attr.title]=\"marker.title || ''\"\n style=\"pointer-events: auto; z-index: 50; cursor: pointer; transform: translate(-50%, -50%); top: 50%;\"\n (click)=\"onMarkerClick($event, marker)\">\n </div>\n \n <div\n class=\"cqa-absolute cqa-left-0 cqa-top-0 cqa-h-full cqa-bg-blue-500 cqa-rounded-full\"\n [style.width.%]=\"isVideoFullMode ? globalScrubberPercent : progress\"\n [style.transition]=\"dragging ? 'none' : 'width 100ms'\"\n style=\"pointer-events: none; z-index: 2;\">\n </div>\n \n <div\n class=\"cqa-absolute cqa-top-1/2 cqa-w-3 cqa-h-3 cqa-bg-blue-600 cqa-rounded-full cqa-cursor-grab active:cqa-cursor-grabbing cqa-shadow-md\"\n [style.left.%]=\"isVideoFullMode ? globalScrubberPercent : progress\"\n style=\"transform: translate(-50%, -50%); z-index: 60;\"\n (mousedown)=\"startDrag($event)\">\n </div>\n </div>\n </div>\n\n <span class=\"cqa-text-[#9CA3AF] cqa-text-[9px] cqa-font-normal cqa-whitespace-nowrap cqa-select-none cqa-mr-2\">\n {{ formatTime(isVideoFullMode ? (totalDuration / 1000) : (vplayer?.nativeElement?.duration || 0)) }}\n </span>\n </div>\n </div>\n </div>\n\n <div *ngIf=\"!isFastForwarding && !isLive && currentView === 'screenshots'\" class=\"cqa-h-full\">\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center\" *ngIf=\"screenShotUrl\">\n <ng-container *ngIf=\"hasDeviceFrame; else screenshotNoFrame\">\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-p-4\">\n <div class=\"cqa-relative cqa-h-full cqa-w-auto cqa-flex cqa-items-center cqa-justify-center cqa-max-h-full\" [ngClass]=\"{'cqa-rounded-md cqa-overflow-hidden': effectivePlatformType === 'browser', 'cqa-min-w-max': effectivePlatformType === 'device'}\">\n <img\n [src]=\"deviceMockupImage\"\n alt=\"Device mockup\"\n class=\"cqa-h-full cqa-w-auto cqa-object-contain cqa-block cqa-pointer-events-none cqa-z-10\"\n [ngClass]=\"{'cqa-max-h-[inherit]': effectivePlatformType === 'browser', 'cqa-max-h-full': effectivePlatformType !== 'browser'}\"\n />\n <div class=\"cqa-absolute cqa-flex cqa-flex-col\" [ngStyle]=\"deviceScreenStyle\" [ngClass]=\"{'cqa-bg-white': effectivePlatformType !== 'browser'}\">\n <img\n [src]=\"screenShotUrl\"\n alt=\"Screenshot\"\n [ngClass]=\"{'cqa-z-20': effectivePlatformType === 'browser'}\"\n class=\"cqa-object-contain cqa-w-full cqa-h-full cqa-block cqa-bg-[##F2F2F2]\"\n />\n </div>\n </div>\n </div>\n </ng-container>\n <ng-template #screenshotNoFrame>\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-p-4\">\n <img\n [src]=\"screenShotUrl\"\n alt=\"Screenshot\"\n class=\"cqa-object-contain cqa-w-full cqa-h-full cqa-block cqa-bg-[##F2F2F2]\"\n />\n </div>\n </ng-template>\n </div>\n \n <div class=\"cqa-p-10 cqa-text-center cqa-text-gray-400 cqa-text-sm cqa-h-full cqa-flex cqa-items-center cqa-justify-center\" *ngIf=\"!screenShotUrl\">\n No screenshot available\n </div>\n </div>\n\n <div *ngIf=\"!isFastForwarding && !isLive && currentView === 'trace'\" class=\"cqa-h-full cqa-flex cqa-flex-col cqa-justify-center\">\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-relative\" *ngIf=\"traceViewUrl\" [ngClass]=\"{'!cqa-h-full': effectivePlatformType === 'device'}\" style=\"padding-top: 48px; padding-bottom: 0px;\">\n <div class=\"cqa-w-full cqa-h-full cqa-overflow-hidden cqa-relative\">\n <iframe \n [src]=\"safeTraceUrl\" \n title=\"Trace Viewer\"\n class=\"cqa-object-contain cqa-w-full cqa-min-h-[250px] cqa-max-h-full cqa-block cqa-bg-[##F2F2F2]\"\n style=\"margin-top: -48px; height: calc(100% + 48px);\"\n frameborder=\"0\"\n allowfullscreen\n width=\"100%\"\n loading=\"lazy\"\n (load)=\"onTraceViewerLoad()\"\n (error)=\"onTraceViewerError()\">\n </iframe>\n </div>\n \n <div *ngIf=\"traceViewerLoading\" class=\"cqa-absolute cqa-inset-0 cqa-bg-[#F3F4F6] cqa-flex cqa-items-center cqa-justify-center cqa-z-10\">\n <div class=\"cqa-text-center cqa-text-gray-400 cqa-text-sm\">\n Loading trace viewer...\n </div>\n </div>\n \n <div *ngIf=\"traceViewerError\" class=\"cqa-absolute cqa-inset-0 cqa-bg-[#F3F4F6] cqa-flex cqa-items-center cqa-justify-center cqa-z-10\">\n <div class=\"cqa-text-center cqa-text-gray-400 cqa-text-sm\">\n Failed to load trace viewer\n </div>\n </div>\n </div>\n \n <div class=\"cqa-p-10 cqa-text-center cqa-text-gray-400 cqa-text-sm cqa-h-full cqa-flex cqa-items-center cqa-justify-center\" *ngIf=\"!traceViewUrl\">\n No trace available\n </div>\n </div> \n </div>\n</div>", styles: [] }]
18953
+ args: [{ selector: 'cqa-simulator', template: "<div class=\"cqa-ui-root\" style=\"background-color: #F3F4F6; height: 100%; display: flex; flex-direction: column;\" [ngStyle]=\"{\n position: isFullScreen ? 'fixed' : null,\n inset: isFullScreen ? '1rem' : null,\n zIndex: isFullScreen ? '50' : null,\n boxShadow: isFullScreen ? '0px 13px 25px -12px rgba(0, 0, 0, 0.25)' : null,\n borderRadius: isFullScreen ? '.5rem' : null,\n border: isFullScreen ? '1px solid #E5E7EB' : null,\n width: isFullScreen ? 'calc(100% - 32px)' : null,\n height: isFullScreen ? 'calc(100% - 32px)' : '100%',\n overflow: isFullScreen ? 'hidden' : null\n}\">\n <div class=\"cqa-w-full cqa-py-1 cqa-px-2 cqa-bg-[#FFFFFF]\" style=\"border-bottom: 1px solid #E5E7EB;box-shadow: 0px 1px 2px 0px #0000000D;\">\n <div class=\"cqa-w-full cqa-flex cqa-items-center cqa-justify-between cqa-flex-wrap\">\n <div class=\"cqa-flex cqa-items-center\">\n <div *ngIf=\"isLive\" class=\"cqa-h-[21px] cqa-inline-flex cqa-items-center cqa-gap-1.5 cqa-mr-2 cqa-px-[9px] cqa-py-[3px] cqa-bg-[#FCD9D9] cqa-rounded-[6px]\" style=\"border: 1px solid #F9BFBF;\">\n <span class=\"cqa-relative cqa-w-2 cqa-h-2 cqa-rounded-full cqa-bg-[#F47F7F]\" style=\"flex-shrink: 0;\">\n <span class=\"cqa-absolute cqa-inset-0 cqa-rounded-full cqa-bg-[#F47F7F] cqa-opacity-75 cqa-animate-ping\"></span>\n </span>\n <span class=\"cqa-text-[10px] cqa-font-medium cqa-text-[#C63535] cqa-leading-[15px]\">Live</span>\n </div>\n <mat-icon *ngIf=\"effectivePlatformType === 'browser'\" style=\"width: 10px; height: 10px;\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"none\">\n <g clip-path=\"url(#clip0_935_15847)\">\n <path\n d=\"M0.625 5C0.625 6.16032 1.08594 7.27312 1.90641 8.09359C2.72688 8.91406 3.83968 9.375 5 9.375C6.16032 9.375 7.27312 8.91406 8.09359 8.09359C8.91406 7.27312 9.375 6.16032 9.375 5C9.375 3.83968 8.91406 2.72688 8.09359 1.90641C7.27312 1.08594 6.16032 0.625 5 0.625C3.83968 0.625 2.72688 1.08594 1.90641 1.90641C1.08594 2.72688 0.625 3.83968 0.625 5Z\"\n stroke=\"#9CA3AF\" stroke-width=\"0.6\" stroke-linejoin=\"round\" />\n <path\n d=\"M3.125 5C3.125 3.83968 3.32254 2.72688 3.67417 1.90641C4.02581 1.08594 4.50272 0.625 5 0.625C5.49728 0.625 5.97419 1.08594 6.32582 1.90641C6.67746 2.72688 6.875 3.83968 6.875 5C6.875 6.16032 6.67746 7.27312 6.32582 8.09359C5.97419 8.91406 5.49728 9.375 5 9.375C4.50272 9.375 4.02581 8.91406 3.67417 8.09359C3.32254 7.27312 3.125 6.16032 3.125 5Z\"\n stroke=\"#9CA3AF\" stroke-width=\"0.6\" stroke-linejoin=\"round\" />\n <path d=\"M0.9375 6.45866H9.0625M0.9375 3.54199H9.0625\" stroke=\"#9CA3AF\" stroke-width=\"0.6\"\n stroke-linecap=\"round\" />\n </g>\n <defs>\n <clipPath id=\"clip0_935_15847\">\n <rect width=\"10\" height=\"10\" fill=\"white\" />\n </clipPath>\n </defs>\n </svg>\n </mat-icon>\n <mat-icon *ngIf=\"effectivePlatformType === 'device'\" style=\"width: 10px; height: 10px;\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"none\">\n <path d=\"M7.08325 0.833008H2.91659C2.45635 0.833008 2.08325 1.2061 2.08325 1.66634V8.33301C2.08325 8.79324 2.45635 9.16634 2.91659 9.16634H7.08325C7.54349 9.16634 7.91658 8.79324 7.91658 8.33301V1.66634C7.91658 1.2061 7.54349 0.833008 7.08325 0.833008Z\" stroke=\"#6B7280\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M5 7.5H5.00417\" stroke=\"#6B7280\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </mat-icon>\n <p class=\"cqa-text-sm !cqa-text-[10px] cqa-text-[#6B7280] cqa-ml-2\">\n {{ platformName }}\n <span\n *ngIf=\"effectivePlatformType === 'browser'\"\n class=\"cqa-ml-1\"\n [matTooltip]=\"'Screen size: ' + effectiveBrowserViewPort.width + 'x' + effectiveBrowserViewPort.height\"\n matTooltipPosition=\"below\"\n >\n \u00B7\n <span class=\"cqa-ml-1\">\n {{ effectiveBrowserViewPort.width }}x{{ effectiveBrowserViewPort.height }}\n </span>\n </span>\n </p>\n <button\n *ngIf=\"showCaptureVideo && isLive\"\n type=\"button\"\n class=\"capture-video-btn\"\n [class.is-loading]=\"isCapturingVideo\"\n [disabled]=\"isCapturingVideo\"\n (click)=\"captureVideo()\">\n <span *ngIf=\"!isCapturingVideo\" class=\"capture-video-btn__dot\"></span>\n <span *ngIf=\"isCapturingVideo\" class=\"capture-video-btn__spinner\" aria-hidden=\"true\"></span>\n <span>{{ isCapturingVideo ? 'Capturing\u2026' : 'Capture Video' }}</span>\n </button>\n </div>\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\">\n <div *ngIf=\"isLive\" [ngClass]=\"getStatusBadgeClass()\">\n <span [ngClass]=\"getStatusTextClass()\">{{ liveStatus }}</span>\n </div>\n\n <cqa-segment-control\n *ngIf=\"isLive && videoUrls && videoUrls.length > 0\"\n [segments]=\"liveSessionSegments\"\n [value]=\"liveSessionView\"\n (valueChange)=\"onLiveSessionViewChange($event)\">\n </cqa-segment-control>\n\n <ng-container *ngIf=\"!isLive\">\n <cqa-segment-control\n [segments]=\"segments\"\n [value]=\"currentView\"\n (valueChange)=\"onSegmentChange($event)\">\n </cqa-segment-control>\n\n <div *ngIf=\"!isFullScreen\"\n class=\"cqa-p-1 cqa-cursor-pointer hover:cqa-bg-gray-100 cqa-rounded-sm cqa-transition-colors\"\n (click)=\"toggleFullScreen()\"\n title=\"Expand\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"none\">\n <path d=\"M6.25 1.25H8.75V3.75\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M8.74992 1.25L5.83325 4.16667\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M1.25 8.74967L4.16667 5.83301\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M3.75 8.75H1.25V6.25\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </div>\n\n <div *ngIf=\"isFullScreen\" \n class=\"cqa-p-1 cqa-cursor-pointer hover:cqa-bg-gray-100 cqa-rounded-sm cqa-transition-colors\"\n (click)=\"toggleFullScreen()\"\n title=\"Exit full screen\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" fill=\"none\">\n <path d=\"M8.75 6.25H6.25V8.75\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M6.25008 6.25L9.16675 9.16667\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M0.833252 0.833008L3.74992 3.74967\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M1.25 3.75H3.75V1.25\" stroke=\"#9CA3AF\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </div>\n </ng-container>\n </div>\n </div>\n </div>\n <div class=\"cqa-w-full cqa-bg-[#F3F4F6] cqa-h-[calc(100%-41px)]\">\n <!-- Fast-forward overlay: covers content area but not header -->\n <div *ngIf=\"isFastForwarding && fastForwardConfig\"\n class=\"cqa-h-full cqa-w-full cqa-flex cqa-items-center cqa-justify-center cqa-p-6\"\n style=\"background-color: #F3F4F6;\">\n <div class=\"cqa-bg-white cqa-rounded-xl cqa-w-full\"\n style=\"max-width: 500px; padding: 32px; box-shadow: 0px 25px 50px -12px #00000040; border: 1px solid #E5E5E5\">\n <!-- Sparkle avatar with rotating arc -->\n <div class=\"cqa-flex cqa-justify-center cqa-mb-2\">\n <div class=\"cqa-relative\" style=\"width: 56px; height: 56px;\">\n <div class=\"cqa-absolute cqa-inset-0 cqa-rounded-full cqa-flex cqa-items-center cqa-justify-center\"\n style=\"background-color: rgba(63,67,238,0.12);\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"26\" viewBox=\"0 0 24 26\" fill=\"none\">\n <path d=\"M12.0251 0C12.173 0.0634121 12.2362 0.218264 12.2968 0.36211C12.3272 0.445923 12.355 0.530192 12.3821 0.615133C12.3927 0.647682 12.4033 0.680231 12.4142 0.713766C12.5548 1.15177 12.6801 1.59447 12.8061 2.03686C12.8346 2.13673 12.8632 2.23656 12.8918 2.33638C13.0375 2.84464 13.1821 3.35321 13.3258 3.86201C13.346 3.93347 13.3662 4.00492 13.3864 4.07638C13.4434 4.27783 13.5003 4.4793 13.5569 4.68087C13.6764 5.10678 13.7993 5.53145 13.9301 5.95405C13.9415 5.99102 13.9529 6.02799 13.9647 6.06608C14.3045 7.15318 14.7862 8.12751 15.5757 8.95743C15.5953 8.979 15.6148 9.00058 15.635 9.02282C15.972 9.38246 16.4045 9.67063 16.8368 9.90322C16.8588 9.91526 16.8808 9.92729 16.9035 9.9397C18.1757 10.6231 19.7053 10.9237 21.0832 11.3238C21.6869 11.4992 22.2888 11.6802 22.8885 11.8691C22.9306 11.8823 22.9727 11.8956 23.0149 11.9088C23.1409 11.9483 23.2667 11.9885 23.3923 12.0293C23.4216 12.0385 23.4509 12.0478 23.481 12.0573C23.6541 12.1146 23.8197 12.1821 23.9618 12.2992C24.0051 12.3938 24.0051 12.3938 23.9933 12.4884C23.855 12.6258 23.7179 12.6902 23.5354 12.753C23.5093 12.7623 23.4832 12.7716 23.4564 12.7811C23.1483 12.8892 22.836 12.9838 22.5234 13.0775C22.4592 13.0969 22.395 13.1163 22.3309 13.1357C21.9352 13.2551 21.5389 13.3719 21.1421 13.4874C20.6195 13.6395 20.0977 13.7942 19.5763 13.9506C19.4909 13.9763 19.4055 14.0018 19.32 14.0272C18.8712 14.161 18.4246 14.2995 17.9817 14.4516C17.945 14.4642 17.9082 14.4768 17.8704 14.4897C16.387 15.0059 15.2563 15.9687 14.5486 17.3768C14.167 18.1681 13.9419 19.0239 13.7124 19.8685C13.6503 20.0964 13.5842 20.323 13.5175 20.5496C13.4651 20.7278 13.4139 20.9064 13.364 21.0853C13.358 21.1066 13.3521 21.1279 13.346 21.1499C13.3172 21.253 13.2885 21.3562 13.26 21.4594C13.1872 21.7211 13.1087 21.9807 13.0281 22.2401C12.8851 22.7012 12.7549 23.1656 12.6256 23.6306C12.4305 24.3308 12.4305 24.3308 12.3199 24.6674C12.3126 24.69 12.3052 24.7126 12.2977 24.7359C12.2573 24.8557 12.2158 24.966 12.1394 25.0674C12.0231 25.093 12.0231 25.093 11.9187 25.0989C11.7993 24.9249 11.7202 24.7562 11.6569 24.5553C11.6476 24.5269 11.6384 24.4984 11.6289 24.4691C11.5116 24.1019 11.4067 23.7309 11.3018 23.36C11.2778 23.2752 11.2536 23.1904 11.2295 23.1056C11.0496 22.4737 10.8727 21.8409 10.696 21.2081C9.96019 18.0775 9.96019 18.0775 8.104 15.5464C8.07425 15.5222 8.0445 15.4979 8.01385 15.4729C6.79767 14.4989 5.19556 14.1382 3.7284 13.7155C3.36553 13.6109 3.0028 13.5058 2.64009 13.4007C2.60837 13.3915 2.60837 13.3915 2.57601 13.3821C2.05283 13.2304 1.5298 13.0783 1.0086 12.9199C0.986549 12.9132 0.9645 12.9066 0.941783 12.8997C0.141064 12.6578 0.141064 12.6578 0.0017241 12.4884C-0.0022167 12.4115 -0.0022167 12.4115 0.0332505 12.3307C0.178889 12.188 0.320205 12.1269 0.512426 12.0647C0.540259 12.0554 0.56809 12.046 0.596766 12.0364C0.682008 12.0079 0.767436 11.9801 0.852936 11.9524C0.877335 11.9444 0.901735 11.9364 0.926874 11.9282C1.09943 11.8716 1.2726 11.8171 1.44603 11.7633C1.48864 11.75 1.48864 11.75 1.53212 11.7364C2.27272 11.5061 3.01815 11.2917 3.76336 11.0769C4.31866 10.9168 4.87302 10.754 5.42561 10.5847C5.451 10.577 5.4764 10.5692 5.50256 10.5613C6.78267 10.1705 8.03409 9.61612 8.86063 8.51606C8.873 8.49974 8.88538 8.48342 8.89813 8.46661C9.8186 7.24564 10.1775 5.76082 10.5785 4.31201C10.7105 3.83552 10.844 3.35947 10.9779 2.88356C11.015 2.75177 11.0521 2.61997 11.089 2.48812C11.2372 1.95852 11.3876 1.42964 11.5467 0.903181C11.5616 0.853882 11.5764 0.804561 11.5911 0.755218C11.8138 0.0111205 11.8138 0.0111205 12.0251 0Z\" fill=\"#3F43EE\"/>\n <path d=\"M19.2962 1.54899C19.441 1.7128 19.4699 1.90532 19.515 2.11449C19.6653 2.77161 19.8384 3.37584 20.4394 3.75756C20.8102 3.96263 21.2522 4.05595 21.663 4.14409C21.8364 4.18205 21.9829 4.23242 22.1316 4.33316C22.1651 4.38636 22.1651 4.38636 22.1632 4.47897C22.1287 4.59159 22.0984 4.62791 22.0075 4.70163C21.9081 4.73488 21.9081 4.73488 21.7888 4.76074C21.7437 4.7711 21.6985 4.78158 21.6535 4.79214C21.6293 4.79774 21.6051 4.80335 21.5803 4.80912C20.8456 4.97907 20.8456 4.97907 20.2105 5.36368C20.1899 5.3803 20.1692 5.39692 20.1479 5.41405C19.6941 5.81426 19.5981 6.48 19.4615 7.0377C19.4536 7.06833 19.4458 7.09896 19.4377 7.13051C19.431 7.15762 19.4242 7.18474 19.4172 7.21267C19.3877 7.29546 19.3572 7.34945 19.2962 7.41289C19.1266 7.43444 19.1266 7.43444 19.044 7.41289C18.9128 7.31114 18.8834 7.19524 18.8491 7.0395C18.8433 7.01539 18.8376 6.99127 18.8317 6.96642C18.8134 6.8894 18.7956 6.81226 18.778 6.73508C18.6398 6.13403 18.4837 5.54432 17.9324 5.19201C17.5015 4.95172 16.9776 4.83808 16.496 4.74631C16.364 4.71883 16.2884 4.68427 16.2067 4.57552C16.1889 4.46518 16.1889 4.46518 16.2067 4.35484C16.3322 4.22484 16.4644 4.19311 16.6342 4.15386C16.6877 4.14061 16.7412 4.12726 16.7947 4.11383C16.8223 4.10694 16.85 4.10004 16.8784 4.09294C17.0193 4.05668 17.1588 4.01602 17.2983 3.97455C17.3236 3.9672 17.3489 3.95985 17.3751 3.95227C17.6585 3.86752 17.9032 3.75911 18.1298 3.56668C18.1509 3.54928 18.172 3.53189 18.1938 3.51397C18.5465 3.19658 18.6663 2.71976 18.7751 2.27484C18.7834 2.24106 18.7916 2.20728 18.8002 2.17248C18.8167 2.10463 18.8329 2.0367 18.8488 1.9687C18.8602 1.92139 18.8602 1.92139 18.8717 1.87312C18.8784 1.84486 18.885 1.81659 18.8918 1.78747C18.9209 1.69749 18.959 1.62683 19.0125 1.54899C19.1153 1.49761 19.1874 1.52084 19.2962 1.54899Z\" fill=\"#0F12A8\"/>\n <path d=\"M4.7881 17.4219C4.82561 17.4222 4.82561 17.4222 4.86387 17.4225C5.13296 17.4307 5.31503 17.5169 5.51321 17.6978C5.74381 17.9469 5.85029 18.2279 5.84423 18.5637C5.81833 18.8359 5.65779 19.0736 5.46087 19.2576C5.22908 19.4447 4.99525 19.5065 4.69906 19.4979C4.44933 19.4688 4.21438 19.3502 4.03738 19.1717C3.81726 18.8713 3.75291 18.5958 3.78517 18.2259C3.8518 17.9346 4.0439 17.6981 4.28959 17.5323C4.46027 17.4458 4.59755 17.4203 4.7881 17.4219Z\" fill=\"#1216CC\"/>\n </svg>\n </div>\n <svg class=\"cqa-absolute\"\n width=\"60\" height=\"60\" viewBox=\"0 0 60 60\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\n style=\"top: -2px; left: -2px;\">\n <circle cx=\"30\" cy=\"30\" r=\"28\" stroke=\"#E2E2E3\" stroke-width=\"2\" fill=\"none\"/>\n </svg>\n <svg class=\"cqa-absolute cqa-ff-spin\"\n width=\"60\" height=\"60\" viewBox=\"0 0 60 60\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"\n style=\"top: -2px; left: -2px; transform-origin: 30px 30px;\">\n <path d=\"M30 2 A 28 28 0 0 1 54.24 44\"\n stroke=\"#3f43ee\" stroke-width=\"2\" stroke-linecap=\"round\" fill=\"none\"/>\n </svg>\n </div>\n </div>\n\n <p class=\"cqa-text-center cqa-m-0 cqa-mb-2\"\n style=\"font-size: 16px; font-weight: 600; color: #161617;\">\n Fast-forwarding to your step\n </p>\n\n <p class=\"cqa-text-center cqa-m-0\"\n style=\"font-size: 12px; color: #6D6D74;\">\n Steps {{ fastForwardConfig.fromStep }}\u2013{{ fastForwardConfig.toStep }} are running at full speed in the background.\n <strong style=\"color: #6D6D74; font-weight: 600;\">Live view and screenshots are paused</strong>\n until execution reaches your target step \u2014 then you're in automatically.\n </p>\n\n <div class=\"cqa-mt-6 cqa-mb-5\">\n <div class=\"cqa-flex cqa-items-center cqa-justify-between cqa-mb-2\">\n <span style=\"font-size: 12px; color: #4C4C51;\">Steps executing in background</span>\n <span style=\"font-size: 12px; color: #6D6D74;\">\n {{ fastForwardConfig.currentStep }} of {{ fastForwardConfig.totalSteps }} steps\n </span>\n </div>\n <div style=\"height: 6px; background-color: #E2E2E3; border-radius: 9999px; overflow: hidden;\">\n <div [style.width.%]=\"fastForwardProgressPercent\"\n style=\"height: 100%; background: linear-gradient(90deg, #3F43EE 0%, #818CF8 100%); transition: width 300ms cubic-bezier(0.4,0,0.2,1);\"></div>\n </div>\n </div>\n\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\"\n style=\"padding: 10px 14px; border-radius: 10px; background: #6366F11A; border: 1px solid #6366F133;\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" style=\"flex-shrink: 0;\">\n <circle cx=\"8\" cy=\"8\" r=\"6.5\" stroke=\"#3f43ee\" stroke-width=\"1.25\"/>\n <circle cx=\"8\" cy=\"8\" r=\"3\" stroke=\"#3f43ee\" stroke-width=\"1.25\"/>\n <circle cx=\"8\" cy=\"8\" r=\"1\" fill=\"#3f43ee\"/>\n </svg>\n <div style=\"font-size: 12px;\">\n <span style=\"color: #6D6D74;\">Jumping to</span>\n <span style=\"color: #3F43EE; font-weight: 400; display: block;\">\n Step {{ fastForwardConfig.targetStepNumber }} \u2014 {{ fastForwardConfig.targetStepLabel }}\n </span>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Live Content View -->\n <div *ngIf=\"!isFastForwarding && isLive && liveSessionView === 'live'\" class=\"cqa-h-full cqa-flex cqa-flex-col cqa-justify-center cqa-relative\">\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-p-4\">\n <div class=\"cqa-relative cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-overflow-hidden\"\n [ngClass]=\"{\n 'cqa-w-auto': hasDeviceFrame,\n 'cqa-w-full cqa-flex-col': !hasDeviceFrame,\n 'cqa-rounded-md cqa-overflow-hidden': effectivePlatformType === 'browser',\n 'cqa-max-h-full cqa-h-full': hasDeviceFrame && (effectivePlatformType !== 'browser' || !isLive),\n 'cqa-min-w-max': hasDeviceFrame && effectivePlatformType === 'device'\n }\"\n [ngStyle]=\"liveContentContainerStyle\">\n <img *ngIf=\"hasDeviceFrame\"\n [src]=\"deviceMockupImage\"\n alt=\"Device mockup\"\n class=\"cqa-h-full cqa-w-auto cqa-max-h-full cqa-object-contain cqa-block cqa-pointer-events-none cqa-z-10\"\n />\n <div [ngClass]=\"{\n 'cqa-absolute cqa-flex cqa-flex-col': hasDeviceFrame,\n 'cqa-w-full cqa-h-full cqa-flex cqa-flex-col cqa-items-center cqa-justify-center cqa-relative': !hasDeviceFrame,\n 'cqa-z-20': hasDeviceFrame && effectivePlatformType === 'browser',\n 'cqa-bg-white': hasDeviceFrame && effectivePlatformType !== 'browser'\n }\"\n [ngStyle]=\"hasDeviceFrame ? deviceScreenStyle : {}\">\n <!-- Loading State -->\n <div *ngIf=\"isContentVideoLoading\" class=\"cqa-p-10 cqa-text-center cqa-h-full cqa-flex cqa-flex-col cqa-items-center cqa-justify-center\">\n <div class=\"cqa-mb-4\">\n <mat-progress-spinner mode=\"indeterminate\" diameter=\"40\"></mat-progress-spinner>\n </div>\n <p class=\"cqa-text-gray-400 cqa-text-sm\">{{ liveLoadingLabel }}</p>\n </div>\n\n <!-- Live Content (when not loading) -->\n <div *ngIf=\"!isContentVideoLoading\" class=\"cqa-w-full cqa-h-full cqa-flex cqa-flex-col cqa-items-center cqa-justify-center cqa-relative\">\n <div *ngIf=\"liveStatus === 'Failed' && failedStatusMessage\" class=\"cqa-p-6 cqa-text-center cqa-w-full\">\n <div class=\"cqa-inline-flex cqa-items-center cqa-gap-2 cqa-px-4 cqa-py-3 cqa-bg-[#FCD9D9] cqa-border cqa-border-[#F9BFBF] cqa-rounded-lg\">\n <mat-icon style=\"width: 18px; height: 18px; color: #C63535; font-size: 18px;\">error</mat-icon>\n <p class=\"cqa-text-[#C63535] cqa-text-sm cqa-font-medium cqa-m-0\">{{ failedStatusMessage }}</p>\n </div>\n </div>\n <ng-content *ngIf=\"liveStatus !== 'Failed' || !failedStatusMessage\"></ng-content>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Video View: post-execution Video tab, or mid-session Captured view inside a live session -->\n <div *ngIf=\"!isFastForwarding && ((!isLive && currentView === 'video') || (isLive && liveSessionView === 'captured'))\"\n class=\"cqa-h-full cqa-flex cqa-flex-col\"\n tabindex=\"0\"\n role=\"region\"\n aria-label=\"Video playback\"\n (keydown)=\"onVideoKeydown($event)\">\n <div class=\"cqa-w-full cqa-flex cqa-items-center\" *ngIf=\"currentVideoUrl\" [ngClass]=\"{'!cqa-h-full': effectivePlatformType === 'device', 'cqa-mt-auto': hasDeviceFrame, 'cqa-max-h-[calc(100%-108px)]': showVideoLibrary, 'cqa-max-h-[calc(100%-60px)]': !showVideoLibrary}\">\n <ng-container *ngIf=\"hasDeviceFrame; else videoNoFrame\">\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-p-4\">\n <div class=\"cqa-relative cqa-h-full cqa-w-auto cqa-flex cqa-items-center cqa-justify-center cqa-max-h-full\" [ngClass]=\"{'cqa-rounded-md cqa-overflow-hidden': effectivePlatformType === 'browser', 'cqa-min-w-max': effectivePlatformType === 'device'}\">\n <img\n [src]=\"deviceMockupImage\"\n alt=\"Device mockup\"\n class=\"cqa-h-full cqa-w-auto cqa-max-h-full cqa-object-contain cqa-block cqa-pointer-events-none cqa-z-10\"\n />\n <div class=\"cqa-absolute cqa-flex cqa-flex-col\" [ngStyle]=\"deviceScreenStyle\" [ngClass]=\"{'cqa-bg-white': effectivePlatformType !== 'browser'}\">\n <video\n *ngIf=\"!videoRefreshing\"\n #vplayer\n class=\"cqa-object-cover cqa-w-full cqa-h-full cqa-block cqa-bg-[##F2F2F2] cqa-cursor-pointer\"\n [src]=\"currentVideoUrl\"\n type=\"video/webm\"\n [ngClass]=\"{'cqa-z-20': effectivePlatformType === 'browser'}\"\n (click)=\"onVideoFrameClick()\"\n (loadedmetadata)=\"onVideoMetadataLoaded()\"\n (canplay)=\"onVideoCanPlay()\"\n (ended)=\"onVideoEnded()\"\n (error)=\"onVideoError()\"\n ></video>\n <!-- Play/Pause overlay icon -->\n <div *ngIf=\"showPlayPauseOverlay\"\n class=\"cqa-absolute cqa-inset-0 cqa-flex cqa-items-center cqa-justify-center cqa-pointer-events-none\"\n [ngClass]=\"{'cqa-z-30': effectivePlatformType === 'browser'}\"\n [ngStyle]=\"{animation: 'cqaFadeOut 500ms ease-out forwards'}\">\n <div class=\"cqa-rounded-full cqa-bg-black cqa-bg-opacity-50 cqa-flex cqa-items-center cqa-justify-center\"\n style=\"width: 56px; height: 56px;\">\n <svg *ngIf=\"isPlaying\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"white\">\n <polygon points=\"5,3 19,12 5,21\" />\n </svg>\n <svg *ngIf=\"!isPlaying\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"white\">\n <rect x=\"5\" y=\"3\" width=\"4\" height=\"18\" />\n <rect x=\"15\" y=\"3\" width=\"4\" height=\"18\" />\n </svg>\n </div>\n </div>\n </div>\n </div>\n </div>\n </ng-container>\n <ng-template #videoNoFrame>\n <div class=\"cqa-relative cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-p-4 cqa-cursor-pointer\"\n (click)=\"onVideoFrameClick()\">\n <video\n *ngIf=\"!videoRefreshing\"\n #vplayer\n class=\"cqa-w-full cqa-h-full cqa-block cqa-bg-[##F2F2F2]\"\n [src]=\"currentVideoUrl\"\n type=\"video/webm\"\n (loadedmetadata)=\"onVideoMetadataLoaded()\"\n (canplay)=\"onVideoCanPlay()\"\n (ended)=\"onVideoEnded()\"\n (error)=\"onVideoError()\"\n ></video>\n <!-- Play/Pause overlay icon -->\n <div *ngIf=\"showPlayPauseOverlay\"\n class=\"cqa-absolute cqa-inset-0 cqa-flex cqa-items-center cqa-justify-center cqa-pointer-events-none\"\n [ngStyle]=\"{animation: 'cqaFadeOut 500ms ease-out forwards'}\">\n <div class=\"cqa-rounded-full cqa-bg-black cqa-bg-opacity-50 cqa-flex cqa-items-center cqa-justify-center\"\n style=\"width: 56px; height: 56px;\">\n <svg *ngIf=\"isPlaying\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"white\">\n <polygon points=\"5,3 19,12 5,21\" />\n </svg>\n <svg *ngIf=\"!isPlaying\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"white\">\n <rect x=\"5\" y=\"3\" width=\"4\" height=\"18\" />\n <rect x=\"15\" y=\"3\" width=\"4\" height=\"18\" />\n </svg>\n </div>\n </div>\n </div>\n </ng-template>\n </div>\n \n <div class=\"cqa-p-10 cqa-text-center cqa-text-gray-400 cqa-text-sm cqa-h-full cqa-flex cqa-items-center cqa-justify-center\" *ngIf=\"!currentVideoUrl\">\n <ng-container *ngIf=\"isVNCSessionIntruppted && vncSessionIntupptedMessage; else noVideoDefault\">\n <p class=\"cqa-text-sm cqa-text-gray-600\">\n {{ vncSessionIntupptedMessage }}\n </p>\n </ng-container>\n <ng-template #noVideoDefault>\n <span>No video recording found</span>\n </ng-template>\n </div>\n \n <!-- Video Library Panel \u2014 reserves only header-height in flow; the inner panel is absolutely anchored to its bottom edge, so the body expanding makes the panel grow UPWARD over the video. Timeline stays put. -->\n <div *ngIf=\"showVideoLibrary && videoUrls && videoUrls.length > 0\"\n class=\"video-library\"\n [class.video-library--open]=\"!isVideoLibraryCollapsed\"\n [ngClass]=\"{'cqa-mt-auto': hasDeviceFrame}\">\n <div class=\"video-library__inner\">\n <!-- Header \u2014 always visible, clicking the chevron toggles the body -->\n <div class=\"video-library__header\">\n <div class=\"video-library__title-group\">\n <mat-icon class=\"video-library__icon\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\">\n <path d=\"M9.33398 7.58308L12.3807 9.61425C12.4247 9.64348 12.4757 9.66024 12.5284 9.66276C12.5811 9.66528 12.6335 9.65347 12.68 9.62856C12.7265 9.60366 12.7654 9.56661 12.7925 9.52136C12.8196 9.4761 12.834 9.42434 12.834 9.37158V4.59058C12.834 4.53926 12.8205 4.48885 12.7948 4.44443C12.7691 4.40001 12.7321 4.36315 12.6876 4.33759C12.6431 4.31203 12.5926 4.29866 12.5413 4.29883C12.49 4.299 12.4396 4.31272 12.3953 4.33858L9.33398 6.12475\" stroke=\"#3F43EE\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n <path d=\"M8.16602 3.5H2.33268C1.68835 3.5 1.16602 4.02233 1.16602 4.66667V9.33333C1.16602 9.97767 1.68835 10.5 2.33268 10.5H8.16602C8.81035 10.5 9.33268 9.97767 9.33268 9.33333V4.66667C9.33268 4.02233 8.81035 3.5 8.16602 3.5Z\" stroke=\"#3F43EE\" stroke-width=\"0.833333\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </mat-icon>\n <span class=\"video-library__title\">Video Library</span>\n <cqa-badge\n size=\"small\"\n inlineStyles=\"min-width: max-content;\"\n backgroundColor=\"#EDE9FE\"\n textColor=\"#6D28D9\"\n [label]=\"videoUrls.length + ' clip' + (videoUrls.length === 1 ? '' : 's')\">\n </cqa-badge>\n <span class=\"video-library__subtitle\"\n [matTooltip]=\"isVideoFullMode ? 'Playing all clips as one continuous video' : 'Each clip covers execution since last capture'\"\n matTooltipPosition=\"below\">{{ isVideoFullMode ? '\u00B7 Playing all clips as one continuous video' : '\u00B7 Each clip covers execution since last capture' }}</span>\n </div>\n <div class=\"video-library__actions\">\n <cqa-button\n *ngIf=\"!isVideoFullMode\"\n variant=\"filled\"\n btnSize=\"md\"\n icon=\"play_arrow\"\n iconPosition=\"start\"\n text=\"Show Full Video\"\n (clicked)=\"onShowFullVideo()\">\n </cqa-button>\n <cqa-button\n *ngIf=\"isVideoFullMode\"\n variant=\"outlined\"\n btnSize=\"md\"\n icon=\"close\"\n iconPosition=\"start\"\n text=\"Exit Full Video\"\n (clicked)=\"onExitFullVideo()\">\n </cqa-button>\n <button type=\"button\" class=\"video-library__collapse\" (click)=\"toggleVideoLibraryCollapsed()\"\n [attr.aria-label]=\"isVideoLibraryCollapsed ? 'Show video library' : 'Hide video library'\">\n <svg *ngIf=\"!isVideoLibraryCollapsed\" xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\">\n <path d=\"M3 7.5L6 4.5L9 7.5\" stroke=\"#6B7280\" stroke-width=\"1.25\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <svg *ngIf=\"isVideoLibraryCollapsed\" xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\">\n <path d=\"M3 4.5L6 7.5L9 4.5\" stroke=\"#6B7280\" stroke-width=\"1.25\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </button>\n </div>\n </div>\n\n <!-- Body (cards) \u2014 max-height animates from 0 (collapsed) to a fixed height (open). -->\n <div class=\"video-library__body\"\n [class.video-library__body--open]=\"!isVideoLibraryCollapsed\"\n [attr.aria-hidden]=\"isVideoLibraryCollapsed ? true : null\">\n <div class=\"clips-row\"\n #clipsScroller\n [class.clips-row--dragging]=\"clipsDragActive\"\n (mousedown)=\"onClipsMouseDown($event, clipsScroller)\"\n (mousemove)=\"onClipsMouseMove($event, clipsScroller)\"\n (mouseup)=\"onClipsMouseUp($event)\"\n (mouseleave)=\"onClipsMouseLeave($event)\">\n <div\n *ngFor=\"let url of videoUrls; let i = index; trackBy: trackLibraryClipByIndex\"\n class=\"clip-card\"\n [class.clip-card--playing]=\"i === currentVideoIndex\">\n <div class=\"clip-thumb\"\n [class.clip-thumb--ready]=\"libraryVideoDurations.has(i)\"\n (click)=\"selectLibraryClip(i)\">\n <video\n #clipVideo\n class=\"clip-thumb__video\"\n [src]=\"url\"\n preload=\"metadata\"\n playsinline\n muted\n (loadedmetadata)=\"onLibraryClipMetadataLoaded(i, clipVideo)\"\n (durationchange)=\"onLibraryClipMetadataLoaded(i, clipVideo)\"></video>\n <div class=\"clip-thumb__overlay\">\n <div class=\"clip-thumb__play-circle\">\n <svg *ngIf=\"!(i === currentVideoIndex && isPlaying)\" xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\">\n <path d=\"M3.5 1.75L11.375 7L3.5 12.25V1.75Z\" fill=\"#FFFFFF\"/>\n </svg>\n <svg *ngIf=\"i === currentVideoIndex && isPlaying\" xmlns=\"http://www.w3.org/2000/svg\" width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\">\n <rect x=\"3.5\" y=\"2.5\" width=\"2.5\" height=\"9\" rx=\"0.5\" fill=\"#FFFFFF\"/>\n <rect x=\"8\" y=\"2.5\" width=\"2.5\" height=\"9\" rx=\"0.5\" fill=\"#FFFFFF\"/>\n </svg>\n </div>\n </div>\n <span *ngIf=\"i === currentVideoIndex && isPlaying\" class=\"clip-thumb__badge clip-thumb__badge--playing\">PLAYING</span>\n <span *ngIf=\"newVideoIndexes.has(i) && i !== currentVideoIndex\" class=\"clip-thumb__badge clip-thumb__badge--new\">NEW</span>\n <span class=\"clip-thumb__duration\">{{ formatTime(libraryVideoDurations.get(i) || 0) }}</span>\n </div>\n <div class=\"clip-meta\">\n <div class=\"clip-meta__title\">Clip {{ i + 1 }}</div>\n <button\n type=\"button\"\n class=\"clip-download-btn\"\n [class.clip-download-btn--downloading]=\"downloadingIndexes.has(i)\"\n [disabled]=\"downloadingIndexes.has(i)\"\n (click)=\"downloadClip(i); $event.stopPropagation()\">\n <ng-container *ngIf=\"!downloadingIndexes.has(i)\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"12\" height=\"12\" viewBox=\"0 0 12 12\" fill=\"none\">\n <!-- Figma icon: upward arrow with a short tray line at the bottom (share/export glyph). -->\n <path d=\"M6 8.5V2M6 2L3 5M6 2L9 5M3 10.5H9\" stroke=\"currentColor\" stroke-width=\"1.1\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <span>Download</span>\n </ng-container>\n <ng-container *ngIf=\"downloadingIndexes.has(i)\">\n <svg class=\"clip-download-btn__ring\" width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\">\n <circle cx=\"7\" cy=\"7\" r=\"6\" stroke=\"#E5E7EB\" stroke-width=\"1.5\" fill=\"none\"/>\n <circle cx=\"7\" cy=\"7\" r=\"6\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"\n stroke-linecap=\"round\"\n [attr.stroke-dasharray]=\"downloadRingCircumference\"\n [attr.stroke-dashoffset]=\"downloadRingDashoffset(i)\"\n transform=\"rotate(-90 7 7)\"/>\n </svg>\n <span>{{ downloadProgress.get(i) || 0 }}%</span>\n </ng-container>\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"cqa-px-2 cqa-py-2 cqa-bg-white\" style=\"border-top: 1px solid #E5E7EB;\" [ngClass]=\"{'cqa-mt-auto': hasDeviceFrame && !(showVideoLibrary && videoUrls && videoUrls.length > 0)}\" *ngIf=\"currentVideoUrl && (!isLive || liveSessionView === 'captured')\">\n <span *ngIf=\"!isVideoFullMode\"\n class=\"cqa-text-[#6B7280] cqa-text-[12px] cqa-font-medium cqa-mb-2 cqa-whitespace-nowrap cqa-block\">\n Video {{ currentVideoIndex + 1 }} playing out of {{ videoUrls?.length || 0 }}\n </span>\n <div class=\"cqa-flex cqa-items-center cqa-gap-2\">\n <button\n *ngIf=\"hasMultipleVideos && !isVideoFullMode\"\n type=\"button\"\n class=\"cqa-bg-transparent cqa-border-none cqa-cursor-pointer !cqa-px-0 cqa-flex cqa-items-center cqa-justify-center hover:cqa-opacity-70 cqa-transition-opacity cqa-outline-none\"\n style=\"pointer-events: auto;\"\n [disabled]=\"currentVideoIndex === 0\"\n [ngClass]=\"{'cqa-opacity-50 cqa-cursor-not-allowed': currentVideoIndex === 0}\"\n (click)=\"prevVideo()\"\n matTooltip=\"Previous video\"\n matTooltipPosition=\"above\">\n <mat-icon class=\"cqa-w-4 cqa-h-4 !cqa-text-[16px] cqa-text-[#374151]\" [ngClass]=\"{'cqa-opacity-50 cqa-cursor-not-allowed': currentVideoIndex === 0}\">skip_previous</mat-icon>\n </button>\n\n <div class=\"cqa-flex cqa-items-center cqa-justify-center\" style=\"width: 16px; height: 16px;\">\n <mat-progress-spinner\n *ngIf=\"isPlayerSwitching\"\n mode=\"indeterminate\"\n diameter=\"16\"\n class=\"cqa-inline-block\">\n </mat-progress-spinner>\n <button \n *ngIf=\"!isPlayerSwitching\"\n type=\"button\"\n class=\"cqa-bg-transparent cqa-border-none cqa-cursor-pointer !cqa-px-0 cqa-flex cqa-items-center cqa-justify-center hover:cqa-opacity-70 cqa-transition-opacity cqa-outline-none\"\n style=\"pointer-events: auto;\"\n (click)=\"togglePlay()\"\n matTooltip=\"{{ isPlaying ? 'Pause' : 'Play' }}\"\n matTooltipPosition=\"above\">\n <span *ngIf=\"!isPlaying\" class=\"cqa-flex cqa-items-center cqa-justify-center\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M3 2L13 8L3 14V2Z\" fill=\"#374151\"/>\n </svg>\n </span>\n <span *ngIf=\"isPlaying\" class=\"cqa-flex cqa-items-center cqa-justify-center\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"3\" y=\"2\" width=\"3\" height=\"12\" fill=\"#374151\"/>\n <rect x=\"10\" y=\"2\" width=\"3\" height=\"12\" fill=\"#374151\"/>\n </svg>\n </span>\n </button>\n </div>\n\n <button\n *ngIf=\"hasMultipleVideos && !isVideoFullMode\"\n type=\"button\"\n class=\"cqa-bg-transparent cqa-border-none cqa-cursor-pointer !cqa-px-0 cqa-flex cqa-items-center cqa-justify-center hover:cqa-opacity-70 cqa-transition-opacity cqa-outline-none\"\n style=\"pointer-events: auto;\"\n [disabled]=\"videoUrls && (currentVideoIndex >= videoUrls.length - 1)\"\n [ngClass]=\"{'cqa-opacity-50 cqa-cursor-not-allowed': videoUrls && (currentVideoIndex >= videoUrls.length - 1)}\"\n (click)=\"nextVideo()\"\n matTooltip=\"Next video\"\n matTooltipPosition=\"above\">\n <mat-icon class=\"cqa-w-4 cqa-h-4 !cqa-text-[16px] cqa-text-[#374151]\" [ngClass]=\"{'cqa-opacity-50 cqa-cursor-not-allowed': videoUrls && (currentVideoIndex >= videoUrls.length - 1)}\">skip_next</mat-icon>\n </button>\n\n <span\n class=\"cqa-text-[#9CA3AF] cqa-text-[9px] cqa-font-normal cqa-whitespace-nowrap cqa-select-none cqa-inline-block\"\n style=\"width: 40px; text-align: center;\">\n {{ formatTime(isVideoFullMode ? (globalCurrentTimeMs / 1000) : (vplayer?.nativeElement?.currentTime || 0)) }}\n </span>\n\n <div #speedControlContainer class=\"cqa-relative cqa-mr-[8px] cqa-flex cqa-items-center cqa-justify-center\">\n <button\n type=\"button\"\n class=\"cqa-bg-transparent cqa-border-none cqa-cursor-pointer cqa-text-[#9CA3AF] cqa-text-[10px] cqa-leading-[15px] cqa-font-medium cqa-whitespace-nowrap cqa-select-none hover:cqa-opacity-70 cqa-transition-opacity cqa-outline-none cqa-px-1\"\n (click)=\"toggleSpeedControl()\"\n [matTooltip]=\"'Playback Speed'\"\n [matTooltipPosition]=\"'below'\">\n {{ currentSpeed }}\n </button>\n \n <div \n *ngIf=\"isSpeedControlOpen\"\n class=\"cqa-absolute cqa-bottom-full cqa-mb-2 cqa-right-0 cqa-bg-[#F0F0F1] cqa-rounded-lg cqa-overflow-hidden cqa-shadow-lg cqa-z-50\"\n style=\"min-width: max-content; left: 50%; bottom: 0%; transform: translate(-50%, -50%); z-index: 101;\">\n <cqa-segment-control\n [segments]=\"speedSegments\"\n [value]=\"currentSpeed\"\n [containerBgColor]=\"'#F0F0F1'\"\n (valueChange)=\"onSpeedChange($event)\">\n </cqa-segment-control>\n </div>\n </div>\n \n <div class=\"cqa-flex-1 cqa-min-w-0\">\n <div \n #timelineBar\n class=\"cqa-relative cqa-h-1 cqa-bg-gray-200 cqa-rounded-full cqa-cursor-pointer cqa-w-full\"\n (click)=\"onTimelineClick($event)\">\n \n <div\n *ngFor=\"let marker of (isVideoFullMode ? fullVideoMarkers : currentVideoMarkers)\"\n class=\"cqa-absolute cqa-rounded-full\"\n [style.left.%]=\"isVideoFullMode ? getGlobalFullStepLeftPosition(marker) : getStepLeftPosition(marker)\"\n [style.width]=\"'8px'\"\n [style.height]=\"'8px'\"\n [style.background]=\"getGlobalMarkerColor(marker.level)\"\n [style.border]=\"'2px solid ' + getGlobalMarkerResultColor(marker.result)\"\n [style.box-sizing]=\"'border-box'\"\n [attr.title]=\"marker.title || ''\"\n style=\"pointer-events: auto; z-index: 50; cursor: pointer; transform: translate(-50%, -50%); top: 50%;\"\n (click)=\"onMarkerClick($event, marker)\">\n </div>\n \n <div\n class=\"cqa-absolute cqa-left-0 cqa-top-0 cqa-h-full cqa-bg-blue-500 cqa-rounded-full\"\n [style.width.%]=\"isVideoFullMode ? globalScrubberPercent : progress\"\n [style.transition]=\"dragging ? 'none' : 'width 100ms'\"\n style=\"pointer-events: none; z-index: 2;\">\n </div>\n \n <div\n class=\"cqa-absolute cqa-top-1/2 cqa-w-3 cqa-h-3 cqa-bg-blue-600 cqa-rounded-full cqa-cursor-grab active:cqa-cursor-grabbing cqa-shadow-md\"\n [style.left.%]=\"isVideoFullMode ? globalScrubberPercent : progress\"\n style=\"transform: translate(-50%, -50%); z-index: 60;\"\n (mousedown)=\"startDrag($event)\">\n </div>\n </div>\n </div>\n\n <span class=\"cqa-text-[#9CA3AF] cqa-text-[9px] cqa-font-normal cqa-whitespace-nowrap cqa-select-none cqa-mr-2\">\n {{ formatTime(isVideoFullMode ? (totalDuration / 1000) : (vplayer?.nativeElement?.duration || 0)) }}\n </span>\n </div>\n </div>\n </div>\n\n <div *ngIf=\"!isFastForwarding && !isLive && currentView === 'screenshots'\" class=\"cqa-h-full\">\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center\" *ngIf=\"screenShotUrl\">\n <ng-container *ngIf=\"hasDeviceFrame; else screenshotNoFrame\">\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-p-4\">\n <div class=\"cqa-relative cqa-h-full cqa-w-auto cqa-flex cqa-items-center cqa-justify-center cqa-max-h-full\" [ngClass]=\"{'cqa-rounded-md cqa-overflow-hidden': effectivePlatformType === 'browser', 'cqa-min-w-max': effectivePlatformType === 'device'}\">\n <img\n [src]=\"deviceMockupImage\"\n alt=\"Device mockup\"\n class=\"cqa-h-full cqa-w-auto cqa-object-contain cqa-block cqa-pointer-events-none cqa-z-10\"\n [ngClass]=\"{'cqa-max-h-[inherit]': effectivePlatformType === 'browser', 'cqa-max-h-full': effectivePlatformType !== 'browser'}\"\n />\n <div class=\"cqa-absolute cqa-flex cqa-flex-col\" [ngStyle]=\"deviceScreenStyle\" [ngClass]=\"{'cqa-bg-white': effectivePlatformType !== 'browser'}\">\n <img\n [src]=\"screenShotUrl\"\n alt=\"Screenshot\"\n [ngClass]=\"{'cqa-z-20': effectivePlatformType === 'browser'}\"\n class=\"cqa-object-contain cqa-w-full cqa-h-full cqa-block cqa-bg-[##F2F2F2]\"\n />\n </div>\n </div>\n </div>\n </ng-container>\n <ng-template #screenshotNoFrame>\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-justify-center cqa-p-4\">\n <img\n [src]=\"screenShotUrl\"\n alt=\"Screenshot\"\n class=\"cqa-object-contain cqa-w-full cqa-h-full cqa-block cqa-bg-[##F2F2F2]\"\n />\n </div>\n </ng-template>\n </div>\n \n <div class=\"cqa-p-10 cqa-text-center cqa-text-gray-400 cqa-text-sm cqa-h-full cqa-flex cqa-items-center cqa-justify-center\" *ngIf=\"!screenShotUrl\">\n No screenshot available\n </div>\n </div>\n\n <div *ngIf=\"!isFastForwarding && !isLive && currentView === 'trace'\" class=\"cqa-h-full cqa-flex cqa-flex-col cqa-justify-center\">\n <div class=\"cqa-w-full cqa-h-full cqa-flex cqa-items-center cqa-relative\" *ngIf=\"traceViewUrl\" [ngClass]=\"{'!cqa-h-full': effectivePlatformType === 'device'}\" style=\"padding-top: 48px; padding-bottom: 0px;\">\n <div class=\"cqa-w-full cqa-h-full cqa-overflow-hidden cqa-relative\">\n <iframe \n [src]=\"safeTraceUrl\" \n title=\"Trace Viewer\"\n class=\"cqa-object-contain cqa-w-full cqa-min-h-[250px] cqa-max-h-full cqa-block cqa-bg-[##F2F2F2]\"\n style=\"margin-top: -48px; height: calc(100% + 48px);\"\n frameborder=\"0\"\n allowfullscreen\n width=\"100%\"\n loading=\"lazy\"\n (load)=\"onTraceViewerLoad()\"\n (error)=\"onTraceViewerError()\">\n </iframe>\n </div>\n \n <div *ngIf=\"traceViewerLoading\" class=\"cqa-absolute cqa-inset-0 cqa-bg-[#F3F4F6] cqa-flex cqa-items-center cqa-justify-center cqa-z-10\">\n <div class=\"cqa-text-center cqa-text-gray-400 cqa-text-sm\">\n Loading trace viewer...\n </div>\n </div>\n \n <div *ngIf=\"traceViewerError\" class=\"cqa-absolute cqa-inset-0 cqa-bg-[#F3F4F6] cqa-flex cqa-items-center cqa-justify-center cqa-z-10\">\n <div class=\"cqa-text-center cqa-text-gray-400 cqa-text-sm\">\n Failed to load trace viewer\n </div>\n </div>\n </div>\n \n <div class=\"cqa-p-10 cqa-text-center cqa-text-gray-400 cqa-text-sm cqa-h-full cqa-flex cqa-items-center cqa-justify-center\" *ngIf=\"!traceViewUrl\">\n No trace available\n </div>\n </div> \n </div>\n</div>", styles: [] }]
18742
18954
  }], ctorParameters: function () { return [{ type: i1$2.DomSanitizer }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { videoUrl: [{
18743
18955
  type: Input
18744
18956
  }], videoUrls: [{
@@ -18791,6 +19003,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImpor
18791
19003
  type: Input
18792
19004
  }], isCapturingVideo: [{
18793
19005
  type: Input
19006
+ }], liveSessionView: [{
19007
+ type: Input
18794
19008
  }], videoTimeUpdate: [{
18795
19009
  type: Output
18796
19010
  }], videoPlay: [{
@@ -18803,6 +19017,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImpor
18803
19017
  type: Output
18804
19018
  }], captureVideoRequested: [{
18805
19019
  type: Output
19020
+ }], liveSessionViewChange: [{
19021
+ type: Output
18806
19022
  }], vplayerRef: [{
18807
19023
  type: ViewChild,
18808
19024
  args: ['vplayer']
@@ -22162,6 +22378,82 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImpor
22162
22378
  type: Output
22163
22379
  }] } });
22164
22380
 
22381
+ class CaptureVideoDialogComponent {
22382
+ constructor() {
22383
+ this.isOpen = false;
22384
+ this.state = null;
22385
+ this.buttons = [];
22386
+ this.actionClick = new EventEmitter();
22387
+ this.closeModal = new EventEmitter();
22388
+ }
22389
+ get isStarting() { return this.state === 'starting'; }
22390
+ get isCapturing() { return this.state === 'capturing'; }
22391
+ get isReady() { return this.state === 'ready'; }
22392
+ get isFailed() { return this.state === 'failed'; }
22393
+ get isInProgress() { return this.isStarting || this.isCapturing; }
22394
+ get resolvedTitle() {
22395
+ if (this.title)
22396
+ return this.title;
22397
+ switch (this.state) {
22398
+ case 'starting': return 'Starting Capture';
22399
+ case 'capturing': return 'Capturing Video';
22400
+ case 'ready': return 'Your Video is Ready';
22401
+ case 'failed': return 'Video Capture Failed';
22402
+ default: return '';
22403
+ }
22404
+ }
22405
+ get resolvedMessage() {
22406
+ if (this.message != null)
22407
+ return this.message;
22408
+ switch (this.state) {
22409
+ case 'starting': return 'Preparing the recorder for this segment.';
22410
+ case 'capturing': return 'Recording the current segment. This will only take a few seconds.';
22411
+ case 'ready': return 'Switch to the Captured tab in the simulator to watch it.';
22412
+ case 'failed': return '';
22413
+ default: return '';
22414
+ }
22415
+ }
22416
+ get progressBadgeLabel() {
22417
+ return this.isStarting ? 'Starting…' : 'Capturing…';
22418
+ }
22419
+ onBackdropClick(event) {
22420
+ if (this.isInProgress)
22421
+ return;
22422
+ const target = event.target;
22423
+ const currentTarget = event.currentTarget;
22424
+ if (target === currentTarget || target.classList.contains('modal-backdrop')) {
22425
+ this.closeModal.emit();
22426
+ }
22427
+ }
22428
+ onButtonClick(button) {
22429
+ if (button.disabled)
22430
+ return;
22431
+ this.actionClick.emit(button.action);
22432
+ }
22433
+ }
22434
+ CaptureVideoDialogComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: CaptureVideoDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
22435
+ CaptureVideoDialogComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: CaptureVideoDialogComponent, selector: "cqa-capture-video-dialog", inputs: { isOpen: "isOpen", state: "state", title: "title", message: "message", errorMessage: "errorMessage", buttons: "buttons" }, outputs: { actionClick: "actionClick", closeModal: "closeModal" }, ngImport: i0, template: "<div *ngIf=\"isOpen\"\n class=\"modal-backdrop cqa-fixed cqa-inset-0 cqa-bg-black cqa-bg-opacity-50 cqa-z-[9999] cqa-flex cqa-items-center cqa-justify-center cqa-p-4\"\n (click)=\"onBackdropClick($event)\">\n <div\n class=\"cqa-rounded-lg cqa-bg-white cqa-shadow-xl cqa-w-full cqa-max-w-[460px] cqa-overflow-hidden cqa-p-6 cqa-flex cqa-flex-col cqa-gap-4 cqa-max-h-[85vh] cqa-overflow-y-auto\"\n style=\"box-shadow: 0px 8px 8px -4px #10182808;\" (click)=\"$event.stopPropagation()\">\n\n <div class=\"cqa-flex cqa-flex-col cqa-items-center\">\n <div *ngIf=\"isInProgress\">\n <svg width=\"93\" height=\"93\" viewBox=\"0 0 93 93\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"4\" y=\"4\" width=\"85\" height=\"85\" rx=\"42.5\" fill=\"#DBEAFE\"/>\n <rect x=\"4\" y=\"4\" width=\"85\" height=\"85\" rx=\"42.5\" stroke=\"#EFF6FF\" stroke-width=\"8\"/>\n <path d=\"M46.5 26.5V35.5M46.5 57.5V66.5M28.5 46.5H37.5M55.5 46.5H64.5M33.79 33.79L40.15 40.15M52.85 52.85L59.21 59.21M33.79 59.21L40.15 52.85M52.85 40.15L59.21 33.79\"\n stroke=\"#2563EB\" stroke-width=\"4\" stroke-linecap=\"round\">\n <animateTransform attributeName=\"transform\" type=\"rotate\" from=\"0 46.5 46.5\" to=\"360 46.5 46.5\" dur=\"1.5s\" repeatCount=\"indefinite\"/>\n </path>\n </svg>\n </div>\n\n <div *ngIf=\"isReady\">\n <svg width=\"93\" height=\"93\" viewBox=\"0 0 93 93\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"4\" y=\"4\" width=\"85\" height=\"85\" rx=\"42.5\" fill=\"#10B981\"/>\n <rect x=\"4\" y=\"4\" width=\"85\" height=\"85\" rx=\"42.5\" stroke=\"#D1FAE5\" stroke-width=\"8\"/>\n <path d=\"M30 47L42 59L63 36\" stroke=\"white\" stroke-width=\"5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </div>\n\n <div *ngIf=\"isFailed\">\n <svg width=\"93\" height=\"93\" viewBox=\"0 0 93 93\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"4\" y=\"4\" width=\"85\" height=\"85\" rx=\"42.5\" fill=\"#F15F5F\"/>\n <rect x=\"4\" y=\"4\" width=\"85\" height=\"85\" rx=\"42.5\" stroke=\"#FCD9D9\" stroke-width=\"8\"/>\n <path d=\"M32.3815 62.4437H60.619C63.5065 62.4437 65.3065 59.3125 63.8627 56.8188L49.744 32.425C48.3002 29.9313 44.7002 29.9313 43.2565 32.425L29.1377 56.8188C27.694 59.3125 29.494 62.4437 32.3815 62.4437ZM46.5002 49.3188C45.469 49.3188 44.6252 48.475 44.6252 47.4438V43.6938C44.6252 42.6625 45.469 41.8188 46.5002 41.8188C47.5315 41.8188 48.3752 42.6625 48.3752 43.6938V47.4438C48.3752 48.475 47.5315 49.3188 46.5002 49.3188ZM48.3752 56.8188H44.6252V53.0688H48.3752V56.8188Z\" fill=\"white\"/>\n </svg>\n </div>\n\n <h2 class=\"cqa-text-[22px] cqa-font-semibold cqa-text-[#0B0B0C] cqa-mt-[20px] cqa-leading-[28px]\">\n {{ resolvedTitle }}\n </h2>\n\n <p *ngIf=\"resolvedMessage\"\n class=\"cqa-text-sm cqa-text-[#4A5565] cqa-mt-2 cqa-text-center\">\n {{ resolvedMessage }}\n </p>\n </div>\n\n <div *ngIf=\"isFailed && errorMessage\"\n class=\"cqa-my-1 cqa-p-4 cqa-rounded-lg cqa-bg-white\"\n style=\"border: 1px solid #E5E7EB;\">\n <div class=\"cqa-text-xs cqa-text-[#9CA3AF] cqa-mb-2 cqa-font-medium\">\n Error Message\n </div>\n <div class=\"cqa-text-sm cqa-text-[#111827] cqa-font-semibold cqa-whitespace-normal cqa-break-anywhere\"\n style=\"word-break: break-word; white-space: pre-line;\">\n {{ errorMessage }}\n </div>\n </div>\n\n <div class=\"cqa-flex cqa-flex-col cqa-gap-[20px]\">\n <cqa-badge\n *ngIf=\"isInProgress\"\n [label]=\"progressBadgeLabel\"\n icon=\"autorenew\"\n [isLoading]=\"true\"\n [fullWidth]=\"true\"\n [centerContent]=\"true\"\n variant=\"info\"\n size=\"medium\"\n inlineStyles=\"min-height: 44px;\">\n </cqa-badge>\n\n <ng-container *ngIf=\"!isInProgress\">\n <cqa-button\n *ngFor=\"let button of buttons\"\n [variant]=\"button.variant\"\n [text]=\"button.label\"\n [icon]=\"button.icon\"\n [btnSize]=\"button.btnSize || 'lg'\"\n [fullWidth]=\"button.fullWidth !== undefined ? button.fullWidth : true\"\n [disabled]=\"button.disabled ?? false\"\n (clicked)=\"onButtonClick(button)\">\n </cqa-button>\n </ng-container>\n </div>\n </div>\n</div>\n", components: [{ type: BadgeComponent, selector: "cqa-badge", inputs: ["type", "label", "icon", "iconLibrary", "variant", "size", "backgroundColor", "textColor", "borderColor", "iconBackgroundColor", "iconColor", "iconSize", "inlineStyles", "key", "value", "keyTextColor", "valueTextColor", "isLoading", "fullWidth", "centerContent", "title"] }, { type: ButtonComponent, selector: "cqa-button", inputs: ["variant", "btnSize", "disabled", "loading", "icon", "iconPosition", "fullWidth", "iconColor", "type", "text", "customClass", "inlineStyles", "tooltip", "tooltipPosition"], outputs: ["clicked"] }], directives: [{ type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }] });
22436
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: CaptureVideoDialogComponent, decorators: [{
22437
+ type: Component,
22438
+ args: [{ selector: 'cqa-capture-video-dialog', template: "<div *ngIf=\"isOpen\"\n class=\"modal-backdrop cqa-fixed cqa-inset-0 cqa-bg-black cqa-bg-opacity-50 cqa-z-[9999] cqa-flex cqa-items-center cqa-justify-center cqa-p-4\"\n (click)=\"onBackdropClick($event)\">\n <div\n class=\"cqa-rounded-lg cqa-bg-white cqa-shadow-xl cqa-w-full cqa-max-w-[460px] cqa-overflow-hidden cqa-p-6 cqa-flex cqa-flex-col cqa-gap-4 cqa-max-h-[85vh] cqa-overflow-y-auto\"\n style=\"box-shadow: 0px 8px 8px -4px #10182808;\" (click)=\"$event.stopPropagation()\">\n\n <div class=\"cqa-flex cqa-flex-col cqa-items-center\">\n <div *ngIf=\"isInProgress\">\n <svg width=\"93\" height=\"93\" viewBox=\"0 0 93 93\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"4\" y=\"4\" width=\"85\" height=\"85\" rx=\"42.5\" fill=\"#DBEAFE\"/>\n <rect x=\"4\" y=\"4\" width=\"85\" height=\"85\" rx=\"42.5\" stroke=\"#EFF6FF\" stroke-width=\"8\"/>\n <path d=\"M46.5 26.5V35.5M46.5 57.5V66.5M28.5 46.5H37.5M55.5 46.5H64.5M33.79 33.79L40.15 40.15M52.85 52.85L59.21 59.21M33.79 59.21L40.15 52.85M52.85 40.15L59.21 33.79\"\n stroke=\"#2563EB\" stroke-width=\"4\" stroke-linecap=\"round\">\n <animateTransform attributeName=\"transform\" type=\"rotate\" from=\"0 46.5 46.5\" to=\"360 46.5 46.5\" dur=\"1.5s\" repeatCount=\"indefinite\"/>\n </path>\n </svg>\n </div>\n\n <div *ngIf=\"isReady\">\n <svg width=\"93\" height=\"93\" viewBox=\"0 0 93 93\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"4\" y=\"4\" width=\"85\" height=\"85\" rx=\"42.5\" fill=\"#10B981\"/>\n <rect x=\"4\" y=\"4\" width=\"85\" height=\"85\" rx=\"42.5\" stroke=\"#D1FAE5\" stroke-width=\"8\"/>\n <path d=\"M30 47L42 59L63 36\" stroke=\"white\" stroke-width=\"5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n </div>\n\n <div *ngIf=\"isFailed\">\n <svg width=\"93\" height=\"93\" viewBox=\"0 0 93 93\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"4\" y=\"4\" width=\"85\" height=\"85\" rx=\"42.5\" fill=\"#F15F5F\"/>\n <rect x=\"4\" y=\"4\" width=\"85\" height=\"85\" rx=\"42.5\" stroke=\"#FCD9D9\" stroke-width=\"8\"/>\n <path d=\"M32.3815 62.4437H60.619C63.5065 62.4437 65.3065 59.3125 63.8627 56.8188L49.744 32.425C48.3002 29.9313 44.7002 29.9313 43.2565 32.425L29.1377 56.8188C27.694 59.3125 29.494 62.4437 32.3815 62.4437ZM46.5002 49.3188C45.469 49.3188 44.6252 48.475 44.6252 47.4438V43.6938C44.6252 42.6625 45.469 41.8188 46.5002 41.8188C47.5315 41.8188 48.3752 42.6625 48.3752 43.6938V47.4438C48.3752 48.475 47.5315 49.3188 46.5002 49.3188ZM48.3752 56.8188H44.6252V53.0688H48.3752V56.8188Z\" fill=\"white\"/>\n </svg>\n </div>\n\n <h2 class=\"cqa-text-[22px] cqa-font-semibold cqa-text-[#0B0B0C] cqa-mt-[20px] cqa-leading-[28px]\">\n {{ resolvedTitle }}\n </h2>\n\n <p *ngIf=\"resolvedMessage\"\n class=\"cqa-text-sm cqa-text-[#4A5565] cqa-mt-2 cqa-text-center\">\n {{ resolvedMessage }}\n </p>\n </div>\n\n <div *ngIf=\"isFailed && errorMessage\"\n class=\"cqa-my-1 cqa-p-4 cqa-rounded-lg cqa-bg-white\"\n style=\"border: 1px solid #E5E7EB;\">\n <div class=\"cqa-text-xs cqa-text-[#9CA3AF] cqa-mb-2 cqa-font-medium\">\n Error Message\n </div>\n <div class=\"cqa-text-sm cqa-text-[#111827] cqa-font-semibold cqa-whitespace-normal cqa-break-anywhere\"\n style=\"word-break: break-word; white-space: pre-line;\">\n {{ errorMessage }}\n </div>\n </div>\n\n <div class=\"cqa-flex cqa-flex-col cqa-gap-[20px]\">\n <cqa-badge\n *ngIf=\"isInProgress\"\n [label]=\"progressBadgeLabel\"\n icon=\"autorenew\"\n [isLoading]=\"true\"\n [fullWidth]=\"true\"\n [centerContent]=\"true\"\n variant=\"info\"\n size=\"medium\"\n inlineStyles=\"min-height: 44px;\">\n </cqa-badge>\n\n <ng-container *ngIf=\"!isInProgress\">\n <cqa-button\n *ngFor=\"let button of buttons\"\n [variant]=\"button.variant\"\n [text]=\"button.label\"\n [icon]=\"button.icon\"\n [btnSize]=\"button.btnSize || 'lg'\"\n [fullWidth]=\"button.fullWidth !== undefined ? button.fullWidth : true\"\n [disabled]=\"button.disabled ?? false\"\n (clicked)=\"onButtonClick(button)\">\n </cqa-button>\n </ng-container>\n </div>\n </div>\n</div>\n", styles: [] }]
22439
+ }], propDecorators: { isOpen: [{
22440
+ type: Input
22441
+ }], state: [{
22442
+ type: Input
22443
+ }], title: [{
22444
+ type: Input
22445
+ }], message: [{
22446
+ type: Input
22447
+ }], errorMessage: [{
22448
+ type: Input
22449
+ }], buttons: [{
22450
+ type: Input
22451
+ }], actionClick: [{
22452
+ type: Output
22453
+ }], closeModal: [{
22454
+ type: Output
22455
+ }] } });
22456
+
22165
22457
  class ProgressIndicatorComponent {
22166
22458
  constructor() {
22167
22459
  this.variant = 'progress';
@@ -46354,6 +46646,7 @@ UiKitModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "1
46354
46646
  SessionChangesModalComponent,
46355
46647
  ErrorModalComponent,
46356
46648
  SessionRestorationDialogComponent,
46649
+ CaptureVideoDialogComponent,
46357
46650
  SubStepsConfirmationDialogComponent,
46358
46651
  ExportCodeModalComponent,
46359
46652
  ProgressIndicatorComponent,
@@ -46529,6 +46822,7 @@ UiKitModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "1
46529
46822
  SessionChangesModalComponent,
46530
46823
  ErrorModalComponent,
46531
46824
  SessionRestorationDialogComponent,
46825
+ CaptureVideoDialogComponent,
46532
46826
  SubStepsConfirmationDialogComponent,
46533
46827
  ExportCodeModalComponent,
46534
46828
  ProgressIndicatorComponent,
@@ -46749,6 +47043,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImpor
46749
47043
  SessionChangesModalComponent,
46750
47044
  ErrorModalComponent,
46751
47045
  SessionRestorationDialogComponent,
47046
+ CaptureVideoDialogComponent,
46752
47047
  SubStepsConfirmationDialogComponent,
46753
47048
  ExportCodeModalComponent,
46754
47049
  ProgressIndicatorComponent,
@@ -46930,6 +47225,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImpor
46930
47225
  SessionChangesModalComponent,
46931
47226
  ErrorModalComponent,
46932
47227
  SessionRestorationDialogComponent,
47228
+ CaptureVideoDialogComponent,
46933
47229
  SubStepsConfirmationDialogComponent,
46934
47230
  ExportCodeModalComponent,
46935
47231
  ProgressIndicatorComponent,
@@ -47761,5 +48057,5 @@ function buildTestCaseDetailsFromApi(data, options) {
47761
48057
  * Generated bundle index. Do not edit.
47762
48058
  */
47763
48059
 
47764
- export { ADVANCED_SUBFIELDS_BY_TYPE, ADVANCED_TOGGLE_KEYS, AIActionStepComponent, AIAgentStepComponent, API_EDIT_STEP_LABELS, ActionMenuButtonComponent, AddPrerequisiteCasesSectionComponent, AdvancedVariablesFormComponent, AiDebugAlertComponent, AiLogsWithReasoningComponent, AiPromptCardComponent, AiReasoningComponent, ApiEditStepComponent, ApiMockingCardComponent, ApiStepComponent, AutocompleteComponent, BadgeComponent, BasicStepComponent, BreakpointsModalComponent, ButtonComponent, CUSTOM_EDIT_STEP_DATA, CUSTOM_EDIT_STEP_EDIT_IN_DEPTH, CUSTOM_EDIT_STEP_REF, CUSTOM_ELEMENT_POPUP_REF, ChangeHistoryComponent, ChartCardComponent, CodeEditorComponent, ColumnVisibilityComponent, CompareRunsComponent, ConditionBranchEditorComponent, ConditionDebugStepComponent, ConditionStepComponent, ConfigurationCardComponent, ConsoleAlertComponent, CoverageModuleCardComponent, CreateStepGroupComponent, CustomEditStepComponent, CustomEditStepRef, CustomEditStepService, CustomInputComponent, CustomTextareaComponent, CustomToggleComponent, DEFAULT_METADATA_COLOR, DEFAULT_PRIORITY_COLOR_CONFIG, DEFAULT_STATUS_COLOR_CONFIG, DIALOG_DATA, DIALOG_REF, DashboardHeaderComponent, DaterangepickerComponent, DaterangepickerDirective, DbQueryExecutionItemComponent, DbVerificationStepComponent, DeleteStepsComponent, DetailDrawerComponent, DetailDrawerTabComponent, DetailDrawerTabContentDirective, DetailSidePanelComponent, DialogComponent, DialogRef, DialogService, DocumentVerificationStepComponent, DropdownButtonComponent, DynamicCellContainerDirective, DynamicCellTemplateDirective, DynamicFilterComponent, DynamicHeaderTemplateDirective, DynamicSelectFieldComponent, DynamicTableComponent, ELEMENT_POPUP_DATA, ELEMENT_POPUP_EDIT_IN_DEPTH, EMPTY_STATE_IMAGES, EMPTY_STATE_PRESETS, ElementFormComponent, ElementListComponent, ElementPopupComponent, ElementPopupRef, ElementPopupService, EmptyStateComponent, ErrorModalComponent, ExecutionResultModalComponent, ExportCodeModalComponent, FailedStepCardComponent, FailedStepComponent, FailedTestCasesCardComponent, FileDownloadStepComponent, FileUploadComponent, FullTableLoaderComponent, HeatErrorMapCellComponent, InsightCardComponent, ItemListComponent, IterationsLoopComponent, JumpToStepModalComponent, LiveConversationComponent, LiveExecutionStepComponent, LoopStepComponent, MONACO_LANGUAGE_MAP, MainStepCollapseComponent, MetricsCardComponent, MixedVariableInputComponent, NetworkRequestComponent, NewVersionHistoryDetailComponent, OtherButtonComponent, PRIORITY_COLORS, PaginationComponent, ProgressIndicatorComponent, ProgressTextCardComponent, QuestionnaireListComponent, RESULT_COLORS, RadioCardGroupComponent, RecordingBannerComponent, ReviewRecordedStepsModalComponent, RunExecutionAlertComponent, RunHistoryCardComponent, STATUS_COLORS, STEP_DETAILS_DRAWER_DATA, STEP_DETAILS_DRAWER_REF, STEP_DETAILS_FIELDS_BY_TYPE, STEP_DETAILS_FIELD_META, STEP_DETAILS_MODAL_DATA, STEP_DETAILS_MODAL_REF, SearchBarComponent, SegmentControlComponent, SelectedFiltersComponent, SelfHealAnalysisComponent, SessionChangesModalComponent, SessionRestorationDialogComponent, SimulatorComponent, StepBuilderActionComponent, StepBuilderAiAgentComponent, StepBuilderConditionComponent, StepBuilderCustomCodeComponent, StepBuilderDatabaseComponent, StepBuilderDocumentComponent, StepBuilderDocumentGenerationTemplateStepComponent, StepBuilderGroupComponent, StepBuilderLoopComponent, StepBuilderRecordStepComponent, StepDetailsDrawerComponent, StepDetailsDrawerRef, StepDetailsDrawerService, StepDetailsModalComponent, StepDetailsModalRef, StepDetailsModalService, StepGroupComponent, StepProgressCardComponent, StepRendererComponent, StepStatusCardComponent, StepTypes, StepperComponent, SubStepsConfirmationDialogComponent, TEST_CASE_DETAILS_FIELD_MAP, TEST_CASE_DETAILS_SELECT_KEYS, TEST_DATA_MODAL_DATA, TEST_DATA_MODAL_EDIT_IN_DEPTH, TEST_DATA_MODAL_REF, TableActionToolbarComponent, TableDataLoaderComponent, TableTemplateComponent, TailwindOverlayContainer, TemplateVariablesFormComponent, TestCaseAiAgentStepComponent, TestCaseAiVerifyStepComponent, TestCaseApiStepComponent, TestCaseConditionStepComponent, TestCaseCustomCodeStepComponent, TestCaseDatabaseStepComponent, TestCaseDetailsComponent, TestCaseDetailsEditComponent, TestCaseDetailsRendererComponent, TestCaseLinkCellComponent, TestCaseLoopStepComponent, TestCaseNormalStepComponent, TestCaseRestoreSessionStepComponent, TestCaseScreenshotStepComponent, TestCaseScrollStepComponent, TestCaseStepGroupComponent, TestCaseUploadStepComponent, TestCaseVerifyUrlStepComponent, TestDataModalComponent, TestDataModalRef, TestDataModalService, TestDistributionCardComponent, UiKitModule, UpdatedFailedStepComponent, VersionHistoryCompareComponent, VersionHistoryDetailComponent, VersionHistoryListComponent, VersionHistoryRestoreConfirmComponent, ViewCompareButtonComponent, ViewMoreFailedStepButtonComponent, VisualComparisonComponent, VisualDifferenceModalComponent, WorkspaceSelectorComponent, buildTestCaseDetailsFromApi, getDynamicFieldsFromLegacyConfig, getEmptyStatePreset, getMetadataColor, getMetadataValueStyle, getStepDetailsStepType, humanizeVariableKey, isAiAgentStepConfig, isAiVerifyStepConfig, isApiStepConfig, isConditionStepConfig, isCustomCodeStepConfig, isDatabaseStepConfig, isLoopStepConfig, isNormalStepConfig, isRestoreSessionStepConfig, isScreenshotStepConfig, isScrollStepConfig, isStepGroupConfig, isUploadStepConfig, isVerifyUrlStepConfig, mapApiVariablesToDynamicFields };
48060
+ export { ADVANCED_SUBFIELDS_BY_TYPE, ADVANCED_TOGGLE_KEYS, AIActionStepComponent, AIAgentStepComponent, API_EDIT_STEP_LABELS, ActionMenuButtonComponent, AddPrerequisiteCasesSectionComponent, AdvancedVariablesFormComponent, AiDebugAlertComponent, AiLogsWithReasoningComponent, AiPromptCardComponent, AiReasoningComponent, ApiEditStepComponent, ApiMockingCardComponent, ApiStepComponent, AutocompleteComponent, BadgeComponent, BasicStepComponent, BreakpointsModalComponent, ButtonComponent, CUSTOM_EDIT_STEP_DATA, CUSTOM_EDIT_STEP_EDIT_IN_DEPTH, CUSTOM_EDIT_STEP_REF, CUSTOM_ELEMENT_POPUP_REF, CaptureVideoDialogComponent, ChangeHistoryComponent, ChartCardComponent, CodeEditorComponent, ColumnVisibilityComponent, CompareRunsComponent, ConditionBranchEditorComponent, ConditionDebugStepComponent, ConditionStepComponent, ConfigurationCardComponent, ConsoleAlertComponent, CoverageModuleCardComponent, CreateStepGroupComponent, CustomEditStepComponent, CustomEditStepRef, CustomEditStepService, CustomInputComponent, CustomTextareaComponent, CustomToggleComponent, DEFAULT_METADATA_COLOR, DEFAULT_PRIORITY_COLOR_CONFIG, DEFAULT_STATUS_COLOR_CONFIG, DIALOG_DATA, DIALOG_REF, DashboardHeaderComponent, DaterangepickerComponent, DaterangepickerDirective, DbQueryExecutionItemComponent, DbVerificationStepComponent, DeleteStepsComponent, DetailDrawerComponent, DetailDrawerTabComponent, DetailDrawerTabContentDirective, DetailSidePanelComponent, DialogComponent, DialogRef, DialogService, DocumentVerificationStepComponent, DropdownButtonComponent, DynamicCellContainerDirective, DynamicCellTemplateDirective, DynamicFilterComponent, DynamicHeaderTemplateDirective, DynamicSelectFieldComponent, DynamicTableComponent, ELEMENT_POPUP_DATA, ELEMENT_POPUP_EDIT_IN_DEPTH, EMPTY_STATE_IMAGES, EMPTY_STATE_PRESETS, ElementFormComponent, ElementListComponent, ElementPopupComponent, ElementPopupRef, ElementPopupService, EmptyStateComponent, ErrorModalComponent, ExecutionResultModalComponent, ExportCodeModalComponent, FailedStepCardComponent, FailedStepComponent, FailedTestCasesCardComponent, FileDownloadStepComponent, FileUploadComponent, FullTableLoaderComponent, HeatErrorMapCellComponent, InsightCardComponent, ItemListComponent, IterationsLoopComponent, JumpToStepModalComponent, LiveConversationComponent, LiveExecutionStepComponent, LoopStepComponent, MONACO_LANGUAGE_MAP, MainStepCollapseComponent, MetricsCardComponent, MixedVariableInputComponent, NetworkRequestComponent, NewVersionHistoryDetailComponent, OtherButtonComponent, PRIORITY_COLORS, PaginationComponent, ProgressIndicatorComponent, ProgressTextCardComponent, QuestionnaireListComponent, RESULT_COLORS, RadioCardGroupComponent, RecordingBannerComponent, ReviewRecordedStepsModalComponent, RunExecutionAlertComponent, RunHistoryCardComponent, STATUS_COLORS, STEP_DETAILS_DRAWER_DATA, STEP_DETAILS_DRAWER_REF, STEP_DETAILS_FIELDS_BY_TYPE, STEP_DETAILS_FIELD_META, STEP_DETAILS_MODAL_DATA, STEP_DETAILS_MODAL_REF, SearchBarComponent, SegmentControlComponent, SelectedFiltersComponent, SelfHealAnalysisComponent, SessionChangesModalComponent, SessionRestorationDialogComponent, SimulatorComponent, StepBuilderActionComponent, StepBuilderAiAgentComponent, StepBuilderConditionComponent, StepBuilderCustomCodeComponent, StepBuilderDatabaseComponent, StepBuilderDocumentComponent, StepBuilderDocumentGenerationTemplateStepComponent, StepBuilderGroupComponent, StepBuilderLoopComponent, StepBuilderRecordStepComponent, StepDetailsDrawerComponent, StepDetailsDrawerRef, StepDetailsDrawerService, StepDetailsModalComponent, StepDetailsModalRef, StepDetailsModalService, StepGroupComponent, StepProgressCardComponent, StepRendererComponent, StepStatusCardComponent, StepTypes, StepperComponent, SubStepsConfirmationDialogComponent, TEST_CASE_DETAILS_FIELD_MAP, TEST_CASE_DETAILS_SELECT_KEYS, TEST_DATA_MODAL_DATA, TEST_DATA_MODAL_EDIT_IN_DEPTH, TEST_DATA_MODAL_REF, TableActionToolbarComponent, TableDataLoaderComponent, TableTemplateComponent, TailwindOverlayContainer, TemplateVariablesFormComponent, TestCaseAiAgentStepComponent, TestCaseAiVerifyStepComponent, TestCaseApiStepComponent, TestCaseConditionStepComponent, TestCaseCustomCodeStepComponent, TestCaseDatabaseStepComponent, TestCaseDetailsComponent, TestCaseDetailsEditComponent, TestCaseDetailsRendererComponent, TestCaseLinkCellComponent, TestCaseLoopStepComponent, TestCaseNormalStepComponent, TestCaseRestoreSessionStepComponent, TestCaseScreenshotStepComponent, TestCaseScrollStepComponent, TestCaseStepGroupComponent, TestCaseUploadStepComponent, TestCaseVerifyUrlStepComponent, TestDataModalComponent, TestDataModalRef, TestDataModalService, TestDistributionCardComponent, UiKitModule, UpdatedFailedStepComponent, VersionHistoryCompareComponent, VersionHistoryDetailComponent, VersionHistoryListComponent, VersionHistoryRestoreConfirmComponent, ViewCompareButtonComponent, ViewMoreFailedStepButtonComponent, VisualComparisonComponent, VisualDifferenceModalComponent, WorkspaceSelectorComponent, buildTestCaseDetailsFromApi, getDynamicFieldsFromLegacyConfig, getEmptyStatePreset, getMetadataColor, getMetadataValueStyle, getStepDetailsStepType, humanizeVariableKey, isAiAgentStepConfig, isAiVerifyStepConfig, isApiStepConfig, isConditionStepConfig, isCustomCodeStepConfig, isDatabaseStepConfig, isLoopStepConfig, isNormalStepConfig, isRestoreSessionStepConfig, isScreenshotStepConfig, isScrollStepConfig, isStepGroupConfig, isUploadStepConfig, isVerifyUrlStepConfig, mapApiVariablesToDynamicFields };
47765
48061
  //# sourceMappingURL=cqa-lib-cqa-ui.mjs.map