@sdcorejs/angular 19.0.10 → 19.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/README.md +32 -23
  2. package/assets/STYLE-GUIDE.md +58 -72
  3. package/assets/scss/core/_inline-edit.scss +1 -1
  4. package/assets/scss/core/color.scss +56 -45
  5. package/assets/scss/core/form.scss +21 -9
  6. package/assets/scss/core/utilities/_base.scss +3 -3
  7. package/assets/scss/core/utilities/_border.scss +1 -1
  8. package/assets/scss/themes/default.scss +46 -32
  9. package/assets/scss/themes/material-theme.scss +8 -87
  10. package/components/badge/src/badge.component.d.ts +5 -16
  11. package/components/button/src/button.component.d.ts +6 -4
  12. package/components/form-generic/src/components/form-builder/components/group/attribute/group-attribute.component.d.ts +1 -0
  13. package/components/form-generic/src/components/form-builder/form-builder.component.d.ts +9 -1
  14. package/components/inform/src/inform.component.d.ts +2 -8
  15. package/components/modal/src/modal.component.d.ts +1 -1
  16. package/components/modal-resizable/src/modal-resizable.component.d.ts +1 -1
  17. package/components/query-bar/src/query-bar.component.d.ts +1 -0
  18. package/components/query-builder/src/query-builder.component.d.ts +6 -6
  19. package/components/query-builder/src/query-builder.model.d.ts +3 -3
  20. package/components/section/src/section.component.d.ts +1 -5
  21. package/components/side-drawer/src/side-drawer.component.d.ts +1 -1
  22. package/components/table/src/components/command/desktop-command.component.d.ts +0 -1
  23. package/components/table/src/models/table-command.model.d.ts +3 -3
  24. package/components/table/src/models/table-option-selector.model.d.ts +5 -5
  25. package/components/table/src/services/table-filter/table-filter.model.d.ts +0 -2
  26. package/components/tree/src/tree.model.d.ts +3 -2
  27. package/fesm2022/sdcorejs-angular-components-anchor.mjs +5 -6
  28. package/fesm2022/sdcorejs-angular-components-anchor.mjs.map +1 -1
  29. package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs +10 -11
  30. package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs.map +1 -1
  31. package/fesm2022/sdcorejs-angular-components-badge.mjs +15 -13
  32. package/fesm2022/sdcorejs-angular-components-badge.mjs.map +1 -1
  33. package/fesm2022/sdcorejs-angular-components-button.mjs +10 -11
  34. package/fesm2022/sdcorejs-angular-components-button.mjs.map +1 -1
  35. package/fesm2022/sdcorejs-angular-components-code-editor.mjs +4 -5
  36. package/fesm2022/sdcorejs-angular-components-code-editor.mjs.map +1 -1
  37. package/fesm2022/sdcorejs-angular-components-document-builder.mjs +3 -3
  38. package/fesm2022/sdcorejs-angular-components-document-builder.mjs.map +1 -1
  39. package/fesm2022/sdcorejs-angular-components-editor.mjs +4 -5
  40. package/fesm2022/sdcorejs-angular-components-editor.mjs.map +1 -1
  41. package/fesm2022/sdcorejs-angular-components-form-generic.mjs +117 -46
  42. package/fesm2022/sdcorejs-angular-components-form-generic.mjs.map +1 -1
  43. package/fesm2022/sdcorejs-angular-components-history.mjs +2 -2
  44. package/fesm2022/sdcorejs-angular-components-history.mjs.map +1 -1
  45. package/fesm2022/sdcorejs-angular-components-import-excel.mjs +5 -7
  46. package/fesm2022/sdcorejs-angular-components-import-excel.mjs.map +1 -1
  47. package/fesm2022/sdcorejs-angular-components-inform.mjs +5 -15
  48. package/fesm2022/sdcorejs-angular-components-inform.mjs.map +1 -1
  49. package/fesm2022/sdcorejs-angular-components-mini-editor.mjs +2 -2
  50. package/fesm2022/sdcorejs-angular-components-mini-editor.mjs.map +1 -1
  51. package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs +5 -6
  52. package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs.map +1 -1
  53. package/fesm2022/sdcorejs-angular-components-modal.mjs +25 -7
  54. package/fesm2022/sdcorejs-angular-components-modal.mjs.map +1 -1
  55. package/fesm2022/sdcorejs-angular-components-org-chart.mjs +4 -5
  56. package/fesm2022/sdcorejs-angular-components-org-chart.mjs.map +1 -1
  57. package/fesm2022/sdcorejs-angular-components-preview.mjs +7 -8
  58. package/fesm2022/sdcorejs-angular-components-preview.mjs.map +1 -1
  59. package/fesm2022/sdcorejs-angular-components-query-bar.mjs +27 -28
  60. package/fesm2022/sdcorejs-angular-components-query-bar.mjs.map +1 -1
  61. package/fesm2022/sdcorejs-angular-components-query-builder.mjs +5 -5
  62. package/fesm2022/sdcorejs-angular-components-query-builder.mjs.map +1 -1
  63. package/fesm2022/sdcorejs-angular-components-section.mjs +7 -12
  64. package/fesm2022/sdcorejs-angular-components-section.mjs.map +1 -1
  65. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs +3 -4
  66. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs.map +1 -1
  67. package/fesm2022/sdcorejs-angular-components-splitter.mjs +2 -2
  68. package/fesm2022/sdcorejs-angular-components-splitter.mjs.map +1 -1
  69. package/fesm2022/sdcorejs-angular-components-stepper.mjs +3 -4
  70. package/fesm2022/sdcorejs-angular-components-stepper.mjs.map +1 -1
  71. package/fesm2022/sdcorejs-angular-components-tab-router.mjs +5 -6
  72. package/fesm2022/sdcorejs-angular-components-tab-router.mjs.map +1 -1
  73. package/fesm2022/sdcorejs-angular-components-tab.mjs +3 -4
  74. package/fesm2022/sdcorejs-angular-components-tab.mjs.map +1 -1
  75. package/fesm2022/sdcorejs-angular-components-table.mjs +28 -32
  76. package/fesm2022/sdcorejs-angular-components-table.mjs.map +1 -1
  77. package/fesm2022/sdcorejs-angular-components-tree.mjs +6 -8
  78. package/fesm2022/sdcorejs-angular-components-tree.mjs.map +1 -1
  79. package/fesm2022/sdcorejs-angular-components-upload-file.mjs +8 -9
  80. package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -1
  81. package/fesm2022/sdcorejs-angular-components-view.mjs +2 -2
  82. package/fesm2022/sdcorejs-angular-components-view.mjs.map +1 -1
  83. package/fesm2022/sdcorejs-angular-directives.mjs +1 -1
  84. package/fesm2022/sdcorejs-angular-directives.mjs.map +1 -1
  85. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +5 -7
  86. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs.map +1 -1
  87. package/fesm2022/sdcorejs-angular-forms-checkbox.mjs +2 -2
  88. package/fesm2022/sdcorejs-angular-forms-checkbox.mjs.map +1 -1
  89. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +7 -9
  90. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs.map +1 -1
  91. package/fesm2022/sdcorejs-angular-forms-chip.mjs +5 -7
  92. package/fesm2022/sdcorejs-angular-forms-chip.mjs.map +1 -1
  93. package/fesm2022/sdcorejs-angular-forms-date-range.mjs +7 -9
  94. package/fesm2022/sdcorejs-angular-forms-date-range.mjs.map +1 -1
  95. package/fesm2022/sdcorejs-angular-forms-date.mjs +6 -8
  96. package/fesm2022/sdcorejs-angular-forms-date.mjs.map +1 -1
  97. package/fesm2022/sdcorejs-angular-forms-datetime.mjs +465 -343
  98. package/fesm2022/sdcorejs-angular-forms-datetime.mjs.map +1 -1
  99. package/fesm2022/sdcorejs-angular-forms-inline-text.mjs +4 -5
  100. package/fesm2022/sdcorejs-angular-forms-inline-text.mjs.map +1 -1
  101. package/fesm2022/sdcorejs-angular-forms-input-number.mjs +5 -7
  102. package/fesm2022/sdcorejs-angular-forms-input-number.mjs.map +1 -1
  103. package/fesm2022/sdcorejs-angular-forms-input.mjs +5 -7
  104. package/fesm2022/sdcorejs-angular-forms-input.mjs.map +1 -1
  105. package/fesm2022/sdcorejs-angular-forms-label.mjs +4 -5
  106. package/fesm2022/sdcorejs-angular-forms-label.mjs.map +1 -1
  107. package/fesm2022/sdcorejs-angular-forms-radio.mjs +2 -4
  108. package/fesm2022/sdcorejs-angular-forms-radio.mjs.map +1 -1
  109. package/fesm2022/sdcorejs-angular-forms-select.mjs +6 -8
  110. package/fesm2022/sdcorejs-angular-forms-select.mjs.map +1 -1
  111. package/fesm2022/sdcorejs-angular-forms-switch.mjs +2 -2
  112. package/fesm2022/sdcorejs-angular-forms-switch.mjs.map +1 -1
  113. package/fesm2022/sdcorejs-angular-forms-textarea.mjs +5 -7
  114. package/fesm2022/sdcorejs-angular-forms-textarea.mjs.map +1 -1
  115. package/fesm2022/sdcorejs-angular-modules-icon.mjs +523 -0
  116. package/fesm2022/sdcorejs-angular-modules-icon.mjs.map +1 -0
  117. package/fesm2022/sdcorejs-angular-modules-layout.mjs +29 -29
  118. package/fesm2022/sdcorejs-angular-modules-layout.mjs.map +1 -1
  119. package/fesm2022/sdcorejs-angular-modules.mjs +1 -0
  120. package/fesm2022/sdcorejs-angular-modules.mjs.map +1 -1
  121. package/fesm2022/sdcorejs-angular-services-confirm.mjs +85 -5
  122. package/fesm2022/sdcorejs-angular-services-confirm.mjs.map +1 -1
  123. package/fesm2022/sdcorejs-angular-services-excel.mjs +22 -14
  124. package/fesm2022/sdcorejs-angular-services-excel.mjs.map +1 -1
  125. package/fesm2022/sdcorejs-angular-services-loading.mjs +1 -1
  126. package/fesm2022/sdcorejs-angular-services-loading.mjs.map +1 -1
  127. package/fesm2022/sdcorejs-angular-utilities-models.mjs +3 -2
  128. package/fesm2022/sdcorejs-angular-utilities-models.mjs.map +1 -1
  129. package/forms/datetime/src/datetime.component.d.ts +4 -4
  130. package/forms/datetime/src/material-datetime/index.d.ts +14 -0
  131. package/forms/datetime/src/material-datetime/lib/core/date-adapter.d.ts +10 -0
  132. package/forms/datetime/src/material-datetime/lib/core/date-formats.d.ts +18 -0
  133. package/forms/datetime/src/material-datetime/lib/core/default-options.d.ts +13 -0
  134. package/forms/datetime/src/material-datetime/lib/datetime-picker/datetime-picker-actions.component.d.ts +5 -0
  135. package/forms/datetime/src/material-datetime/lib/datetime-picker/datetime-picker-apply.directive.d.ts +7 -0
  136. package/forms/datetime/src/material-datetime/lib/datetime-picker/datetime-picker-cancel.directive.d.ts +7 -0
  137. package/forms/datetime/src/material-datetime/lib/datetime-picker/datetime-picker-input.directive.d.ts +24 -0
  138. package/forms/datetime/src/material-datetime/lib/datetime-picker/datetime-picker-now.directive.d.ts +7 -0
  139. package/forms/datetime/src/material-datetime/lib/datetime-picker/datetime-picker-toggle.directive.d.ts +8 -0
  140. package/forms/datetime/src/material-datetime/lib/datetime-picker/datetime-picker.component.d.ts +39 -0
  141. package/forms/datetime/src/material-datetime/lib/native/native-date-adapter.d.ts +15 -0
  142. package/forms/datetime/src/material-datetime/lib/native/native-date-formats.d.ts +2 -0
  143. package/forms/datetime/src/material-datetime/lib/native/provide-native.d.ts +3 -0
  144. package/forms/datetime/src/material-datetime/lib/time-spinner/time-spinner.component.d.ts +31 -0
  145. package/modules/icon/index.d.ts +1 -0
  146. package/modules/icon/src/icon.component.d.ts +101 -0
  147. package/modules/icon/src/icon.model.d.ts +59 -0
  148. package/modules/icon/src/icon.provider.d.ts +33 -0
  149. package/modules/icon/src/index.d.ts +3 -0
  150. package/modules/index.d.ts +1 -0
  151. package/package.json +10 -5
  152. package/services/confirm/src/lib/components/dialog-confirm/dialog-confirm.component.d.ts +17 -0
  153. package/services/confirm/src/lib/confirm.service.d.ts +32 -1
  154. package/services/excel/src/lib/spreadsheet-formula-injection.util.d.ts +5 -0
  155. package/utilities/models/src/icon.model.d.ts +4 -2
  156. package/forms/datetime/src/popup/sd-datetime-picker.component.d.ts +0 -37
  157. package/forms/datetime/src/popup/sd-time-spinner.component.d.ts +0 -41
@@ -2,13 +2,12 @@ import * as i1 from '@angular/common';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
4
  import { InjectionToken, Injectable, inject, input, booleanAttribute, signal, computed, HostListener, ChangeDetectionStrategy, Component } from '@angular/core';
5
- import * as i3 from '@angular/material/icon';
6
- import { MatIconModule } from '@angular/material/icon';
7
- import * as i4 from '@angular/material/tooltip';
5
+ import * as i3 from '@angular/material/tooltip';
8
6
  import { MatTooltipModule } from '@angular/material/tooltip';
9
7
  import * as i2 from '@angular/forms';
10
8
  import { FormsModule } from '@angular/forms';
11
9
  import { Router } from '@angular/router';
10
+ import { SdIcon } from '@sdcorejs/angular/modules/icon';
12
11
 
13
12
  /** Cau hinh global cho AutoId Inspector. `[config]` tren component co the override theo instance. */
14
13
  const SD_AUTOID_INSPECTOR_CONFIGURATION = new InjectionToken('sd.autoid-inspector.configuration');
@@ -281,12 +280,12 @@ const SAVED_BORDER_RADIUS = 'data-sd-autoid-prev-radius';
281
280
  // Token màu lấy từ @sdcorejs/angular theme (--sd-*). Fallback hex áp dụng khi
282
281
  // consumer chưa import sd-core.scss. Dùng cùng bộ var với scss component để
283
282
  // nhất quán visual giữa highlight ok / duplicate / missing.
284
- const COLOR_OK = 'var(--sd-success, #4CAF50)';
285
- const COLOR_OK_BG = 'var(--sd-success-light, rgba(76, 175, 80, 0.12))';
286
- const COLOR_DUP = 'var(--sd-error, #F82C13)';
287
- const COLOR_DUP_BG = 'var(--sd-error-light, rgba(248, 44, 19, 0.12))';
288
- const COLOR_MISSING = 'var(--sd-warning, #FF9600)';
289
- const COLOR_MISSING_BG = 'var(--sd-warning-light, rgba(255, 150, 0, 0.12))';
283
+ const COLOR_OK = 'var(--sd-success, #2e7d32)';
284
+ const COLOR_OK_BG = 'var(--sd-success-light, #d8f0d3)';
285
+ const COLOR_DUP = 'var(--sd-error, #ba1a1a)';
286
+ const COLOR_DUP_BG = 'var(--sd-error-light, #ffdad6)';
287
+ const COLOR_MISSING = 'var(--sd-warning, #a66300)';
288
+ const COLOR_MISSING_BG = 'var(--sd-warning-light, #ffddb0)';
290
289
  class SdAutoidHighlightService {
291
290
  /** Apply outline cho mọi [data-autoid]: xanh nếu unique, đỏ nếu duplicate. Idempotent. */
292
291
  apply(root = document.body) {
@@ -962,11 +961,11 @@ class SdAutoidInspector {
962
961
  }, 1800);
963
962
  }
964
963
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdAutoidInspector, deps: [], target: i0.ɵɵFactoryTarget.Component });
965
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdAutoidInspector, isStandalone: true, selector: "sd-autoid-inspector", inputs: { enabled: { classPropertyName: "enabled", publicName: "enabled", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "document:keydown.escape": "onEscape()" } }, ngImport: i0, template: "@let _enabled = enabled();\n@let _open = open();\n@if (_enabled && !dismissed()) {\n <div class=\"sd-autoid-inspector__fab-wrap\">\n <button\n type=\"button\"\n class=\"sd-autoid-inspector__fab\"\n data-autoid=\"sd-autoid-inspector-fab\"\n [attr.data-opened]=\"dataOpened()\"\n [matTooltip]=\"_open ? '\u0110\u00F3ng AutoId Inspector' : 'M\u1EDF AutoId Inspector'\"\n matTooltipPosition=\"left\"\n (click)=\"togglePanel()\">\n <mat-icon>{{ _open ? 'close' : 'tag' }}</mat-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-autoid-inspector__fab-dismiss\"\n matTooltip=\"\u1EA8n t\u1EA1m \u2014 reload trang \u0111\u1EC3 hi\u1EC7n l\u1EA1i\"\n matTooltipPosition=\"left\"\n aria-label=\"\u1EA8n t\u1EA1m FAB\"\n (click)=\"dismissFab($event)\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n}\n\n@if (_enabled && _open) {\n @let _segment = segment();\n <button type=\"button\" class=\"sd-autoid-inspector__backdrop\" aria-label=\"\u0110\u00F3ng AutoId Inspector\" (click)=\"closePanel()\"></button>\n <section\n class=\"sd-autoid-inspector__panel\"\n role=\"dialog\"\n aria-label=\"AutoId Inspector\"\n data-autoid=\"sd-autoid-inspector-panel\"\n [attr.data-segment]=\"dataSegment()\"\n [attr.data-highlight-on]=\"dataHighlightOn()\"\n [attr.data-element-count]=\"dataElementCount()\"\n [attr.data-missing-count]=\"dataMissingCount()\"\n [attr.data-duplicate-count]=\"dataDuplicateCount()\">\n <header class=\"sd-autoid-inspector__header\">\n <div class=\"sd-autoid-inspector__segments\">\n <button type=\"button\" [class.is-active]=\"_segment === 'audit'\" (click)=\"setSegment('audit')\">\n Audit\n @if (audit(); as a) {\n <span class=\"sd-autoid-inspector__badge\" [class.is-warn]=\"a.missingCount + a.duplicateCount > 0\">\n {{ a.missingCount + a.duplicateCount }}\n </span>\n }\n </button>\n <button type=\"button\" [class.is-active]=\"_segment === 'elements'\" (click)=\"setSegment('elements')\">\n Elements\n <span class=\"sd-autoid-inspector__badge\">{{ elements().length }}</span>\n </button>\n <button type=\"button\" [class.is-active]=\"_segment === 'export'\" (click)=\"setSegment('export')\">Export</button>\n </div>\n <div class=\"sd-autoid-inspector__actions\">\n <button type=\"button\" class=\"sd-autoid-inspector__icon-btn\" matTooltip=\"Qu\u00E9t l\u1EA1i\" (click)=\"refresh()\">\n <mat-icon>refresh</mat-icon>\n </button>\n <button type=\"button\" class=\"sd-autoid-inspector__close-btn\" matTooltip=\"\u0110\u00F3ng\" aria-label=\"\u0110\u00F3ng panel\" (click)=\"closePanel()\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </header>\n\n @if (copyOk()) {\n <div class=\"sd-autoid-inspector__toast\">\u0110\u00E3 copy \u2713</div>\n }\n\n <div class=\"sd-autoid-inspector__body\">\n @switch (_segment) {\n @case ('audit') {\n <ng-container *ngTemplateOutlet=\"auditTpl\"></ng-container>\n }\n @case ('elements') {\n <ng-container *ngTemplateOutlet=\"elementsTpl\"></ng-container>\n }\n @case ('export') {\n <ng-container *ngTemplateOutlet=\"exportTpl\"></ng-container>\n }\n }\n </div>\n </section>\n\n <ng-template #auditTpl>\n @if (audit(); as a) {\n @let _highlightOn = highlightOn();\n <div class=\"sd-autoid-inspector__audit-toolbar\">\n <button type=\"button\" class=\"sd-autoid-inspector__toggle\" [class.is-on]=\"_highlightOn\" (click)=\"toggleHighlight()\">\n <mat-icon>{{ _highlightOn ? 'visibility' : 'visibility_off' }}</mat-icon>\n <span>Highlight DOM</span>\n <span class=\"sd-autoid-inspector__toggle-state\">{{ _highlightOn ? 'ON' : 'OFF' }}</span>\n </button>\n </div>\n <div class=\"sd-autoid-inspector__summary\">\n <div class=\"sd-autoid-inspector__stat is-info\">\n <div class=\"sd-autoid-inspector__stat-value\">{{ a.total }}</div>\n <div class=\"sd-autoid-inspector__stat-label\">T\u1ED5ng autoid</div>\n </div>\n <div class=\"sd-autoid-inspector__stat\" [class.is-error]=\"a.duplicateCount > 0\" [class.is-success]=\"a.duplicateCount === 0\">\n <div class=\"sd-autoid-inspector__stat-value\">{{ a.duplicateCount }}</div>\n <div class=\"sd-autoid-inspector__stat-label\">Tr\u00F9ng</div>\n </div>\n <div class=\"sd-autoid-inspector__stat\" [class.is-warning]=\"a.missingCount > 0\" [class.is-success]=\"a.missingCount === 0\">\n <div class=\"sd-autoid-inspector__stat-value\">{{ a.missingCount }}</div>\n <div class=\"sd-autoid-inspector__stat-label\">Thi\u1EBFu</div>\n </div>\n </div>\n\n @if (a.duplicateCount > 0) {\n <h4 class=\"sd-autoid-inspector__section-title\">\n <mat-icon class=\"is-error\">error_outline</mat-icon>\n Autoid b\u1ECB tr\u00F9ng\n </h4>\n <div class=\"sd-autoid-inspector__table-wrap\">\n <table class=\"sd-autoid-inspector__table\">\n <thead>\n <tr>\n <th>Autoid</th>\n <th>S\u1ED1 l\u1EA7n</th>\n </tr>\n </thead>\n <tbody>\n @for (entry of a.duplicates | keyvalue; track entry.key) {\n <tr class=\"is-error\">\n <td>\n <code>{{ entry.key }}</code>\n </td>\n <td>{{ entry.value }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n\n @if (a.missingCount > 0) {\n <h4 class=\"sd-autoid-inspector__section-title\">\n <mat-icon class=\"is-warning\">warning_amber</mat-icon>\n Element thi\u1EBFu autoid\n </h4>\n <div class=\"sd-autoid-inspector__table-wrap\">\n <table class=\"sd-autoid-inspector__table\">\n <thead>\n <tr>\n <th>Tag</th>\n <th>Name hint</th>\n <th>Selector</th>\n </tr>\n </thead>\n <tbody>\n @for (m of a.missing; track $index) {\n <tr class=\"is-warning\">\n <td>\n <code>{{ m.tag }}</code>\n </td>\n <td>{{ m.nameHint }}</td>\n <td>\n <code>{{ m.selector }}</code>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n\n @if (a.duplicateCount === 0 && a.missingCount === 0) {\n <div class=\"sd-autoid-inspector__success-card\">\n <mat-icon>check_circle</mat-icon>\n <div>\n <strong>Kh\u00F4ng c\u00F3 element thi\u1EBFu ho\u1EB7c tr\u00F9ng autoid.</strong>\n <small>Trang \u0111\u00E3 \u0111\u1EA1t chu\u1EA9n convention autoid.</small>\n </div>\n </div>\n }\n }\n </ng-template>\n\n <ng-template #elementsTpl>\n @let _filter = filter();\n <div class=\"sd-autoid-inspector__filter\">\n <mat-icon>search</mat-icon>\n <input type=\"text\" placeholder=\"L\u1ECDc theo autoid / name / tag\" [ngModel]=\"_filter\" (ngModelChange)=\"filter.set($event)\" />\n @if (_filter) {\n <button type=\"button\" class=\"sd-autoid-inspector__icon-btn\" matTooltip=\"Xo\u00E1 filter\" (click)=\"filter.set('')\">\n <mat-icon>close</mat-icon>\n </button>\n }\n </div>\n\n <!-- Top-level elements (not inside any sd-table) -->\n <h4 class=\"sd-autoid-inspector__section-title\">\n Top-level elements\n <span class=\"sd-autoid-inspector__badge\">{{ topLevelElements().length }}</span>\n </h4>\n <div class=\"sd-autoid-inspector__table-wrap\">\n <table class=\"sd-autoid-inspector__table\">\n <thead>\n <tr>\n <th class=\"is-num\">STT</th>\n <th>Name</th>\n <th>Autoid</th>\n <th>Tag</th>\n <th>State</th>\n <th>Value</th>\n </tr>\n </thead>\n <tbody>\n @for (el of topLevelElements(); track el.autoid + $index) {\n <tr [class.is-error]=\"el.duplicate\">\n <td class=\"is-num\">{{ el.stt }}</td>\n <td>{{ el.name || '\u2014' }}</td>\n <td>\n <div class=\"sd-autoid-inspector__autoid-cell\">\n <code>{{ el.autoid }}</code>\n <button\n type=\"button\"\n class=\"sd-autoid-inspector__icon-btn is-primary\"\n matTooltip=\"Copy xpath\"\n (click)=\"copyXpath(el.autoid)\">\n <mat-icon>content_copy</mat-icon>\n </button>\n </div>\n </td>\n <td>\n <span class=\"sd-autoid-inspector__tag-chip\">{{ el.tag }}</span>\n </td>\n <td class=\"sd-autoid-inspector__state-cell\">\n @if (el.state.disabled !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-error]=\"el.state.disabled === 'true'\">\n disabled={{ el.state.disabled }}\n </span>\n }\n @if (el.state.loading !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-warning]=\"el.state.loading === 'true'\">\n loading={{ el.state.loading }}\n </span>\n }\n @if (el.state.empty !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-info]=\"el.state.empty === 'true'\">\n empty={{ el.state.empty }}\n </span>\n }\n @if (el.state.invalid !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-error]=\"el.state.invalid === 'true'\">\n invalid={{ el.state.invalid }}\n </span>\n }\n @if (el.state.opened !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-info]=\"el.state.opened === 'true'\">\n opened={{ el.state.opened }}\n </span>\n }\n @if (el.state.count !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> count={{ el.state.count }} </span>\n }\n @if (el.state.required !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-warning]=\"el.state.required === 'true'\">\n required={{ el.state.required }}\n </span>\n }\n @if (el.state.maxlength !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> maxlength={{ el.state.maxlength }} </span>\n }\n @if (el.state.minlength !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> minlength={{ el.state.minlength }} </span>\n }\n @if (el.state.pattern !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> pattern={{ el.state.pattern }} </span>\n }\n @if (el.state.errorMessage !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-error\"> error: {{ el.state.errorMessage }} </span>\n }\n </td>\n <td class=\"sd-autoid-inspector__value\" [matTooltip]=\"el.state.dataValue || el.text || ''\">\n {{ el.state.dataValue || el.text || '\u2014' }}\n </td>\n </tr>\n } @empty {\n <tr>\n <td colspan=\"6\" class=\"sd-autoid-inspector__empty\">Kh\u00F4ng c\u00F3 k\u1EBFt qu\u1EA3.</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <!-- One section per sd-table group -->\n @for (group of tableGroups(); track group.scope) {\n <h4 class=\"sd-autoid-inspector__section-title\">\n <mat-icon class=\"is-info\">table_chart</mat-icon>\n Inside <code>{{ group.scope }}</code>\n <span class=\"sd-autoid-inspector__badge\">{{ group.items.length }}</span>\n </h4>\n <div class=\"sd-autoid-inspector__table-wrap\">\n <table class=\"sd-autoid-inspector__table\">\n <thead>\n <tr>\n <th class=\"is-num\">STT</th>\n <th>Name</th>\n <th>Autoid</th>\n <th>Tag</th>\n <th>State</th>\n <th>Value</th>\n </tr>\n </thead>\n <tbody>\n @for (el of group.items; track el.autoid + $index) {\n <tr [class.is-error]=\"el.duplicate\">\n <td class=\"is-num\">{{ el.stt }}</td>\n <td>{{ el.name || '\u2014' }}</td>\n <td>\n <div class=\"sd-autoid-inspector__autoid-cell\">\n <code>{{ el.autoid }}</code>\n <button\n type=\"button\"\n class=\"sd-autoid-inspector__icon-btn is-primary\"\n matTooltip=\"Copy xpath\"\n (click)=\"copyXpath(el.autoid)\">\n <mat-icon>content_copy</mat-icon>\n </button>\n </div>\n </td>\n <td>\n <span class=\"sd-autoid-inspector__tag-chip\">{{ el.tag }}</span>\n </td>\n <td class=\"sd-autoid-inspector__state-cell\">\n @if (el.state.disabled !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-error]=\"el.state.disabled === 'true'\">\n disabled={{ el.state.disabled }}\n </span>\n }\n @if (el.state.loading !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-warning]=\"el.state.loading === 'true'\">\n loading={{ el.state.loading }}\n </span>\n }\n @if (el.state.empty !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-info]=\"el.state.empty === 'true'\">\n empty={{ el.state.empty }}\n </span>\n }\n @if (el.state.invalid !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-error]=\"el.state.invalid === 'true'\">\n invalid={{ el.state.invalid }}\n </span>\n }\n @if (el.state.opened !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-info]=\"el.state.opened === 'true'\">\n opened={{ el.state.opened }}\n </span>\n }\n @if (el.state.count !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> count={{ el.state.count }} </span>\n }\n @if (el.state.required !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-warning]=\"el.state.required === 'true'\">\n required={{ el.state.required }}\n </span>\n }\n @if (el.state.maxlength !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> maxlength={{ el.state.maxlength }} </span>\n }\n @if (el.state.minlength !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> minlength={{ el.state.minlength }} </span>\n }\n @if (el.state.pattern !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> pattern={{ el.state.pattern }} </span>\n }\n @if (el.state.errorMessage !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-error\"> error: {{ el.state.errorMessage }} </span>\n }\n </td>\n <td class=\"sd-autoid-inspector__value\" [matTooltip]=\"el.state.dataValue || el.text || ''\">\n {{ el.state.dataValue || el.text || '\u2014' }}\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </ng-template>\n\n <ng-template #exportTpl>\n <div class=\"sd-autoid-inspector__export-grid\">\n <button type=\"button\" matTooltip=\"Copy JSON v\u00E0o clipboard\" (click)=\"copyJson()\">\n <mat-icon>content_copy</mat-icon>\n <span>Copy JSON</span>\n <small>clipboard</small>\n </button>\n <button type=\"button\" matTooltip=\"T\u1EA3i file JSON\" (click)=\"exportJson()\">\n <mat-icon>download</mat-icon>\n <span>JSON</span>\n <small>.json</small>\n </button>\n <button type=\"button\" matTooltip=\"T\u1EA3i file CSV\" (click)=\"exportCsv()\">\n <mat-icon>table_view</mat-icon>\n <span>CSV</span>\n <small>.csv</small>\n </button>\n <button type=\"button\" matTooltip=\"T\u1EA3i MD POM skeleton \u2014 cho AI vi\u1EBFt auto-test\" (click)=\"exportMdPom()\">\n <mat-icon>code</mat-icon>\n <span>MD POM</span>\n <small>AI test</small>\n </button>\n <button type=\"button\" matTooltip=\"T\u1EA3i MD reference table \u2014 group theo tag sd-*\" (click)=\"exportMdTable()\">\n <mat-icon>view_list</mat-icon>\n <span>MD Table</span>\n <small>reference</small>\n </button>\n </div>\n\n @if (e2eGeneratorHost()) {\n @let _e2eExporting = e2eExporting();\n <div class=\"sd-autoid-inspector__export-section\">\n <h4 class=\"sd-autoid-inspector__section-title\">\n <mat-icon class=\"is-info\">auto_awesome</mat-icon>\n T\u1EA1o E2E test\n </h4>\n <div class=\"sd-autoid-inspector__export-grid is-e2e\">\n <button\n type=\"button\"\n matTooltip=\"T\u1EA1o b\u1ED9 test Playwright (.zip)\"\n [disabled]=\"_e2eExporting !== null\"\n (click)=\"exportE2eTest('playwright')\">\n <mat-icon>{{ _e2eExporting === 'playwright' ? 'hourglass_empty' : 'play_arrow' }}</mat-icon>\n <span>Playwright</span>\n <small>.zip</small>\n </button>\n <button\n type=\"button\"\n matTooltip=\"T\u1EA1o b\u1ED9 test Robot Framework (.zip)\"\n [disabled]=\"_e2eExporting !== null\"\n (click)=\"openRobotExportContext()\">\n <mat-icon>{{ _e2eExporting === 'robot' ? 'hourglass_empty' : 'smart_toy' }}</mat-icon>\n <span>Robot Framework</span>\n <small>.zip</small>\n </button>\n </div>\n\n @if (robotContextOpen()) {\n <div class=\"sd-autoid-inspector__context-backdrop\" role=\"presentation\" (click)=\"closeRobotExportContext()\"></div>\n <section\n class=\"sd-autoid-inspector__context-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"sd-autoid-robot-context-title\">\n <header class=\"sd-autoid-inspector__context-header\">\n <div>\n <h4 id=\"sd-autoid-robot-context-title\">Ng\u1EEF c\u1EA3nh QC cho Robot Framework</h4>\n <small>Requirement, test case, test data</small>\n </div>\n <button type=\"button\" class=\"sd-autoid-inspector__icon-btn\" matTooltip=\"\u0110\u00F3ng\" (click)=\"closeRobotExportContext()\">\n <mat-icon>close</mat-icon>\n </button>\n </header>\n\n <div class=\"sd-autoid-inspector__context-form\">\n <label>\n <span>Component</span>\n <input\n type=\"text\"\n placeholder=\"SP-Wallet\"\n [ngModel]=\"robotExportContext().component || ''\"\n (ngModelChange)=\"updateRobotExportContext('component', $event)\" />\n </label>\n <label>\n <span>Story / Trace</span>\n <input\n type=\"text\"\n placeholder=\"US-001, AC-01\"\n [ngModel]=\"robotExportContext().storyLinkages || ''\"\n (ngModelChange)=\"updateRobotExportContext('storyLinkages', $event)\" />\n </label>\n <label class=\"is-full\">\n <span>Requirement / AC / BR</span>\n <textarea\n rows=\"4\"\n placeholder=\"D\u00E1n requirement ho\u1EB7c acceptance criteria...\"\n [ngModel]=\"robotExportContext().requirement || ''\"\n (ngModelChange)=\"updateRobotExportContext('requirement', $event)\"></textarea>\n </label>\n <label class=\"is-full\">\n <span>Test case QC \u0111\u00E3 \u0111\u1ECBnh ngh\u0129a</span>\n <textarea\n rows=\"4\"\n placeholder=\"TC-001: ...\"\n [ngModel]=\"robotExportContext().testCases || ''\"\n (ngModelChange)=\"updateRobotExportContext('testCases', $event)\"></textarea>\n </label>\n <label class=\"is-full\">\n <span>Test data</span>\n <textarea\n rows=\"3\"\n placeholder=\"wallet=V\u00ED A, amount=100000...\"\n [ngModel]=\"robotExportContext().testData || ''\"\n (ngModelChange)=\"updateRobotExportContext('testData', $event)\"></textarea>\n </label>\n <label class=\"is-full\">\n <span>Precondition</span>\n <textarea\n rows=\"2\"\n placeholder=\"User \u0111\u00E3 \u0111\u0103ng nh\u1EADp, d\u1EEF li\u1EC7u v\u00ED \u0111\u00E3 t\u1ED3n t\u1EA1i...\"\n [ngModel]=\"robotExportContext().precondition || ''\"\n (ngModelChange)=\"updateRobotExportContext('precondition', $event)\"></textarea>\n </label>\n <label class=\"is-full\">\n <span>Ghi ch\u00FA format QC</span>\n <textarea\n rows=\"3\"\n placeholder=\"Smoke=Y, Auto=Y, \u01B0u ti\u00EAn happy path...\"\n [ngModel]=\"robotExportContext().qcNotes || ''\"\n (ngModelChange)=\"updateRobotExportContext('qcNotes', $event)\"></textarea>\n </label>\n </div>\n\n <footer class=\"sd-autoid-inspector__context-actions\">\n <button type=\"button\" class=\"is-secondary\" [disabled]=\"_e2eExporting !== null\" (click)=\"closeRobotExportContext()\">\n Hu\u1EF7\n </button>\n <button\n type=\"button\"\n class=\"is-primary\"\n [disabled]=\"_e2eExporting !== null || !hasRobotExportContext()\"\n (click)=\"confirmRobotExport()\">\n <mat-icon>{{ _e2eExporting === 'robot' ? 'hourglass_empty' : 'download' }}</mat-icon>\n <span>\u0110\u1ED3ng \u00FD xu\u1EA5t</span>\n </button>\n </footer>\n </section>\n }\n\n @if (e2eExportOk(); as targetLabel) {\n <div class=\"sd-autoid-inspector__export-message is-success\">\u0110\u00E3 t\u1EA3i b\u1ED9 test {{ targetLabel }}.</div>\n }\n @if (e2eExportError(); as error) {\n <div class=\"sd-autoid-inspector__export-message is-error\">\n {{ error }}\n </div>\n }\n </div>\n }\n </ng-template>\n}\n", styles: [":host{position:fixed;inset:auto 0 0 auto;z-index:100000;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;font-size:13px;color:var(--sd-black500, #212121)}.sd-autoid-inspector__fab-wrap{position:fixed;right:16px;bottom:16px;z-index:100000}.sd-autoid-inspector__fab-wrap:hover .sd-autoid-inspector__fab-dismiss{opacity:1;transform:scale(1);pointer-events:auto}.sd-autoid-inspector__fab{width:36px;height:36px;border-radius:50%;border:none;background:var(--sd-primary, #2a66f4);color:#fff;cursor:pointer;box-shadow:0 3px 10px #00000038;display:inline-flex;align-items:center;justify-content:center;transition:transform .15s ease,background .15s ease,box-shadow .15s ease}.sd-autoid-inspector__fab:hover{background:var(--sd-primary-dark, #1c4ad9);transform:scale(1.08);box-shadow:0 5px 14px #00000047}.sd-autoid-inspector__fab mat-icon{font-size:18px;width:18px;height:18px}.sd-autoid-inspector__fab-dismiss{position:absolute;top:-6px;right:-6px;width:18px;height:18px;border-radius:50%;border:1px solid var(--sd-black200, #e6e6e6);background:#fff;color:var(--sd-black400, #757575);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;padding:0;opacity:0;transform:scale(.7);pointer-events:none;transition:opacity .12s ease,transform .12s ease,color .12s ease,background .12s ease;box-shadow:0 1px 4px #00000026}.sd-autoid-inspector__fab-dismiss:hover{background:var(--sd-error-light, #fed5d0);color:var(--sd-error-dark, #ba200e);border-color:var(--sd-error, #f82c13)}.sd-autoid-inspector__fab-dismiss mat-icon{font-size:12px;width:12px;height:12px;line-height:12px}.sd-autoid-inspector__backdrop{position:fixed;inset:0;padding:0;border:none;background:#00000052;z-index:100001;backdrop-filter:blur(2px)}.sd-autoid-inspector__panel{position:fixed;right:20px;bottom:80px;width:min(860px,100vw - 40px);max-height:calc(100vh - 110px);background:#fff;border-radius:12px;box-shadow:0 16px 48px #00000038;display:flex;flex-direction:column;z-index:100002;overflow:hidden;border:1px solid var(--sd-black200, #e6e6e6)}.sd-autoid-inspector__header{display:flex;align-items:center;gap:8px;padding:8px 10px;border-bottom:1px solid var(--sd-black200, #e6e6e6);background:var(--sd-black100, #f2f2f2);flex-shrink:0}.sd-autoid-inspector__segments{display:inline-flex;border:1px solid var(--sd-black200, #e6e6e6);border-radius:8px;background:#fff;overflow:hidden}.sd-autoid-inspector__segments button{border:none;background:transparent;padding:5px 12px;cursor:pointer;font-size:12px;font-weight:500;color:var(--sd-black400, #757575);display:inline-flex;align-items:center;gap:6px;border-right:1px solid var(--sd-black200, #e6e6e6);transition:background .12s ease,color .12s ease}.sd-autoid-inspector__segments button:last-child{border-right:none}.sd-autoid-inspector__segments button:hover:not(.is-active){background:var(--sd-black100, #f2f2f2);color:var(--sd-black500, #212121)}.sd-autoid-inspector__segments button.is-active{background:var(--sd-primary, #2a66f4);color:#fff}.sd-autoid-inspector__badge{background:var(--sd-black200, #e6e6e6);color:var(--sd-black500, #212121);border-radius:10px;padding:0 7px;font-size:11px;font-weight:600;line-height:16px}.is-active .sd-autoid-inspector__badge{background:#ffffff40;color:#fff}.sd-autoid-inspector__badge.is-warn{background:var(--sd-error, #f82c13);color:#fff}.sd-autoid-inspector__actions{display:inline-flex;gap:4px;margin-left:auto}.sd-autoid-inspector__icon-btn{border:none;background:transparent;cursor:pointer;padding:4px;border-radius:6px;color:var(--sd-black400, #757575);display:inline-flex;align-items:center;justify-content:center;transition:background .12s ease,color .12s ease}.sd-autoid-inspector__icon-btn:hover{background:var(--sd-black100, #f2f2f2);color:var(--sd-black500, #212121)}.sd-autoid-inspector__icon-btn.is-primary{color:var(--sd-primary, #2a66f4)}.sd-autoid-inspector__icon-btn.is-primary:hover{background:var(--sd-primary-light, #eaf1ff);color:var(--sd-primary-dark, #1c4ad9)}.sd-autoid-inspector__icon-btn mat-icon{font-size:18px;width:18px;height:18px}.sd-autoid-inspector__close-btn{border:none;cursor:pointer;padding:4px;border-radius:50%;width:28px;height:28px;background:transparent;color:var(--sd-black400, #757575);display:inline-flex;align-items:center;justify-content:center;transition:background .12s ease,color .12s ease}.sd-autoid-inspector__close-btn:hover{background:var(--sd-error-light, #fed5d0);color:var(--sd-error-dark, #ba200e)}.sd-autoid-inspector__close-btn mat-icon{font-size:18px;width:18px;height:18px}.sd-autoid-inspector__body{padding:14px;overflow:auto;flex:1;background:#fff}.sd-autoid-inspector__toast{position:absolute;top:56px;right:14px;background:var(--sd-success, #4caf50);color:#fff;padding:6px 12px;border-radius:6px;font-size:12px;z-index:2;box-shadow:0 4px 12px #0000002e}.sd-autoid-inspector__audit-toolbar{display:flex;justify-content:flex-end;margin-bottom:12px}.sd-autoid-inspector__toggle{display:inline-flex;align-items:center;gap:8px;border:1px solid var(--sd-black200, #e6e6e6);background:#fff;border-radius:999px;padding:5px 12px 5px 10px;cursor:pointer;font-size:12px;font-weight:500;color:var(--sd-black400, #757575);transition:all .15s ease}.sd-autoid-inspector__toggle:hover{border-color:var(--sd-primary, #2a66f4);color:var(--sd-primary, #2a66f4)}.sd-autoid-inspector__toggle.is-on{border-color:var(--sd-primary, #2a66f4);color:var(--sd-primary, #2a66f4);background:var(--sd-primary-light, #eaf1ff)}.sd-autoid-inspector__toggle mat-icon{font-size:16px;width:16px;height:16px}.sd-autoid-inspector__toggle-state{font-size:10px;font-weight:700;padding:1px 7px;border-radius:999px;background:var(--sd-black200, #e6e6e6);color:var(--sd-black400, #757575)}.is-on .sd-autoid-inspector__toggle-state{background:var(--sd-primary, #2a66f4);color:#fff}.sd-autoid-inspector__summary{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:16px}.sd-autoid-inspector__stat{background:var(--sd-black100, #f2f2f2);border-radius:10px;padding:12px;text-align:center;border:1px solid transparent}.sd-autoid-inspector__stat.is-info{background:var(--sd-info-light, #e7e9ff);border-color:var(--sd-info, #2962ff)}.sd-autoid-inspector__stat.is-info .sd-autoid-inspector__stat-value{color:var(--sd-info, #2962ff)}.sd-autoid-inspector__stat.is-success{background:var(--sd-success-light, #dbefdc);border-color:var(--sd-success, #4caf50)}.sd-autoid-inspector__stat.is-success .sd-autoid-inspector__stat-value{color:var(--sd-success-dark, #39833c)}.sd-autoid-inspector__stat.is-warning{background:var(--sd-warning-light, #ffeacc);border-color:var(--sd-warning, #ff9600)}.sd-autoid-inspector__stat.is-warning .sd-autoid-inspector__stat-value{color:var(--sd-warning-dark, #bf7000)}.sd-autoid-inspector__stat.is-error{background:var(--sd-error-light, #fed5d0);border-color:var(--sd-error, #f82c13)}.sd-autoid-inspector__stat.is-error .sd-autoid-inspector__stat-value{color:var(--sd-error-dark, #ba200e)}.sd-autoid-inspector__stat-value{font-size:22px;font-weight:700;line-height:1;margin-bottom:4px}.sd-autoid-inspector__stat-label{font-size:11px;color:var(--sd-black400, #757575);text-transform:uppercase;letter-spacing:.4px;font-weight:600}.sd-autoid-inspector__section-title{display:flex;align-items:center;gap:6px;margin:14px 0 8px;font-size:13px;font-weight:600;color:var(--sd-black500, #212121)}.sd-autoid-inspector__section-title mat-icon{font-size:18px;width:18px;height:18px}.sd-autoid-inspector__section-title mat-icon.is-error{color:var(--sd-error, #f82c13)}.sd-autoid-inspector__section-title mat-icon.is-warning{color:var(--sd-warning, #ff9600)}.sd-autoid-inspector__section-title mat-icon.is-info{color:var(--sd-info, #2962ff)}.sd-autoid-inspector__success-card{display:flex;align-items:center;gap:12px;padding:14px 16px;background:var(--sd-success-light, #dbefdc);border:1px solid var(--sd-success, #4caf50);border-radius:10px;color:var(--sd-success-dark, #39833c)}.sd-autoid-inspector__success-card mat-icon{font-size:28px;width:28px;height:28px;color:var(--sd-success, #4caf50)}.sd-autoid-inspector__success-card strong{display:block;font-size:13px;margin-bottom:2px}.sd-autoid-inspector__success-card small{font-size:11px;opacity:.85}.sd-autoid-inspector__table-wrap{border:1px solid var(--sd-black200, #e6e6e6);border-radius:8px;overflow:auto;max-height:60vh;background:#fff}.sd-autoid-inspector__table{width:100%;border-collapse:separate;border-spacing:0;font-size:12px}.sd-autoid-inspector__table th,.sd-autoid-inspector__table td{padding:8px 10px;text-align:left;vertical-align:middle;border-bottom:1px solid var(--sd-black200, #e6e6e6)}.sd-autoid-inspector__table thead th{position:sticky;top:0;z-index:1;background:var(--sd-black100, #f2f2f2);font-weight:600;color:var(--sd-black500, #212121);text-transform:none;font-size:11px;letter-spacing:.3px;border-bottom:none;box-shadow:inset 0 -1px 0 var(--sd-black200, #e6e6e6),0 2px 4px #0000000a;white-space:nowrap}.sd-autoid-inspector__table thead th.is-num{text-align:right;width:50px}.sd-autoid-inspector__table tbody tr{transition:background .1s ease}.sd-autoid-inspector__table tbody tr:hover{background:var(--sd-primary-light, #eaf1ff)}.sd-autoid-inspector__table tbody tr:last-child td{border-bottom:none}.sd-autoid-inspector__table tbody tr.is-error{background:var(--sd-error-light, #fed5d0)}.sd-autoid-inspector__table tbody tr.is-error:hover{background:var(--sd-error-light, #fed5d0);filter:brightness(.98)}.sd-autoid-inspector__table tbody tr.is-warning{background:var(--sd-warning-light, #ffeacc)}.sd-autoid-inspector__table tbody tr.is-warning:hover{background:var(--sd-warning-light, #ffeacc);filter:brightness(.98)}.sd-autoid-inspector__table td.is-num{text-align:right;color:var(--sd-black400, #757575);font-variant-numeric:tabular-nums}.sd-autoid-inspector__table code{font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:11px;background:var(--sd-black100, #f2f2f2);padding:2px 6px;border-radius:4px;color:var(--sd-primary-dark, #1c4ad9);border:1px solid var(--sd-black200, #e6e6e6)}.sd-autoid-inspector__autoid-cell{display:inline-flex;align-items:center;gap:6px}.sd-autoid-inspector__tag-chip{display:inline-block;font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:11px;background:var(--sd-info-light, #e7e9ff);color:var(--sd-info, #2962ff);border:1px solid var(--sd-info, #2962ff);padding:1px 8px;border-radius:999px;font-weight:500}.sd-autoid-inspector__value{max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--sd-black400, #757575);font-style:italic}.sd-autoid-inspector__state-cell{display:flex;flex-wrap:wrap;gap:4px;align-items:center;min-width:0}.sd-autoid-inspector__state-chip{display:inline-block;font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:10px;background:var(--sd-black100, #f2f2f2);color:var(--sd-black400, #757575);border:1px solid var(--sd-black200, #e6e6e6);padding:1px 6px;border-radius:999px;font-weight:500;white-space:nowrap}.sd-autoid-inspector__state-chip.is-error{background:var(--sd-error-light, #fed5d0);color:var(--sd-error-dark, #ba200e);border-color:var(--sd-error, #f82c13)}.sd-autoid-inspector__state-chip.is-warning{background:var(--sd-warning-light, #ffeacc);color:var(--sd-warning-dark, #bf7000);border-color:var(--sd-warning, #ff9600)}.sd-autoid-inspector__state-chip.is-info{background:var(--sd-info-light, #e7e9ff);color:var(--sd-info, #2962ff);border-color:var(--sd-info, #2962ff)}.sd-autoid-inspector__empty{text-align:center;color:var(--sd-black400, #757575);padding:24px;font-style:italic}.sd-autoid-inspector__filter{display:flex;align-items:center;gap:6px;background:var(--sd-black100, #f2f2f2);border:1px solid var(--sd-black200, #e6e6e6);border-radius:8px;padding:6px 10px;margin-bottom:12px;transition:border-color .15s ease}.sd-autoid-inspector__filter:focus-within{border-color:var(--sd-primary, #2a66f4);background:#fff}.sd-autoid-inspector__filter input{flex:1;border:none;background:transparent;outline:none;font-size:13px;color:var(--sd-black500, #212121)}.sd-autoid-inspector__filter input::placeholder{color:var(--sd-black300, #bfbfbf)}.sd-autoid-inspector__filter>mat-icon{font-size:18px;width:18px;height:18px;color:var(--sd-black400, #757575)}.sd-autoid-inspector__export-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(112px,1fr));gap:8px}.sd-autoid-inspector__export-grid.is-e2e{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}.sd-autoid-inspector__export-grid button{border:1px solid var(--sd-black200, #e6e6e6);background:#fff;border-radius:10px;padding:12px 10px;cursor:pointer;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;min-width:0;transition:border-color .15s ease,background .15s ease,transform .12s ease;text-align:center}.sd-autoid-inspector__export-grid button:hover{border-color:var(--sd-primary, #2a66f4);background:var(--sd-primary-light, #eaf1ff);transform:translateY(-1px)}.sd-autoid-inspector__export-grid button:disabled{cursor:wait;opacity:.68;transform:none}.sd-autoid-inspector__export-grid button span{font-size:12px;font-weight:600;color:var(--sd-black500, #212121);line-height:1.25}.sd-autoid-inspector__export-grid button small{font-size:10px;color:var(--sd-black400, #757575);line-height:1.2}.sd-autoid-inspector__export-grid button mat-icon{font-size:22px;width:22px;height:22px;color:var(--sd-primary, #2a66f4)}.sd-autoid-inspector__export-section{margin-top:14px;padding-top:12px;border-top:1px solid var(--sd-black200, #e6e6e6)}.sd-autoid-inspector__context-backdrop{position:fixed;inset:0;z-index:10001;background:#141c2652}.sd-autoid-inspector__context-dialog{position:fixed;z-index:10002;top:50%;left:50%;width:min(720px,100vw - 28px);max-height:min(720px,100vh - 28px);transform:translate(-50%,-50%);display:flex;flex-direction:column;border:1px solid var(--sd-black200, #e6e6e6);border-radius:8px;background:#fff;box-shadow:0 18px 48px #0f172a38;overflow:hidden}.sd-autoid-inspector__context-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:14px 16px;border-bottom:1px solid var(--sd-black200, #e6e6e6)}.sd-autoid-inspector__context-header h4{margin:0;font-size:15px;font-weight:700;line-height:1.3;color:var(--sd-black500, #212121)}.sd-autoid-inspector__context-header small{display:block;margin-top:2px;font-size:11px;color:var(--sd-black400, #757575);line-height:1.3}.sd-autoid-inspector__context-form{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;padding:14px 16px;overflow:auto}.sd-autoid-inspector__context-form label{display:flex;flex-direction:column;gap:6px;min-width:0}.sd-autoid-inspector__context-form label.is-full{grid-column:1/-1}.sd-autoid-inspector__context-form span{font-size:12px;font-weight:600;color:var(--sd-black500, #212121);line-height:1.25}.sd-autoid-inspector__context-form input,.sd-autoid-inspector__context-form textarea{width:100%;min-width:0;border:1px solid var(--sd-black200, #e6e6e6);border-radius:6px;padding:8px 10px;color:var(--sd-black500, #212121);font:inherit;font-size:12px;line-height:1.4;outline:none;resize:vertical}.sd-autoid-inspector__context-form input:focus,.sd-autoid-inspector__context-form textarea:focus{border-color:var(--sd-primary, #2a66f4);box-shadow:0 0 0 2px #2a66f424}.sd-autoid-inspector__context-actions{display:flex;justify-content:flex-end;gap:8px;padding:12px 16px;border-top:1px solid var(--sd-black200, #e6e6e6)}.sd-autoid-inspector__context-actions button{min-height:34px;border-radius:6px;padding:0 12px;border:1px solid var(--sd-black200, #e6e6e6);background:#fff;color:var(--sd-black500, #212121);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:6px;font-size:12px;font-weight:600}.sd-autoid-inspector__context-actions button.is-primary{border-color:var(--sd-primary, #2a66f4);background:var(--sd-primary, #2a66f4);color:#fff}.sd-autoid-inspector__context-actions button:disabled{cursor:not-allowed;opacity:.62}.sd-autoid-inspector__context-actions button mat-icon{font-size:17px;width:17px;height:17px}.sd-autoid-inspector__export-message{margin-top:10px;border-radius:8px;padding:8px 10px;font-size:12px;line-height:1.35}.sd-autoid-inspector__export-message.is-success{background:var(--sd-success-light, #dbefdc);color:var(--sd-success-dark, #39833c);border:1px solid var(--sd-success, #4caf50)}.sd-autoid-inspector__export-message.is-error{background:var(--sd-error-light, #fed5d0);color:var(--sd-error-dark, #ba200e);border:1px solid var(--sd-error, #f82c13)}@media(max-width:560px){.sd-autoid-inspector__context-form{grid-template-columns:1fr}.sd-autoid-inspector__context-actions{flex-direction:column-reverse}.sd-autoid-inspector__context-actions button{width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1.KeyValuePipe, name: "keyvalue" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i4.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
964
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdAutoidInspector, isStandalone: true, selector: "sd-autoid-inspector", inputs: { enabled: { classPropertyName: "enabled", publicName: "enabled", isSignal: true, isRequired: false, transformFunction: null }, config: { classPropertyName: "config", publicName: "config", isSignal: true, isRequired: false, transformFunction: null } }, host: { listeners: { "document:keydown.escape": "onEscape()" } }, ngImport: i0, template: "@let _enabled = enabled();\n@let _open = open();\n@if (_enabled && !dismissed()) {\n <div class=\"sd-autoid-inspector__fab-wrap\">\n <button\n type=\"button\"\n class=\"sd-autoid-inspector__fab\"\n data-autoid=\"sd-autoid-inspector-fab\"\n [attr.data-opened]=\"dataOpened()\"\n [matTooltip]=\"_open ? '\u0110\u00F3ng AutoId Inspector' : 'M\u1EDF AutoId Inspector'\"\n matTooltipPosition=\"left\"\n (click)=\"togglePanel()\">\n <sd-icon [name]=\"_open ? 'close' : 'tag'\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-autoid-inspector__fab-dismiss\"\n matTooltip=\"\u1EA8n t\u1EA1m \u2014 reload trang \u0111\u1EC3 hi\u1EC7n l\u1EA1i\"\n matTooltipPosition=\"left\"\n aria-label=\"\u1EA8n t\u1EA1m FAB\"\n (click)=\"dismissFab($event)\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n </div>\n}\n\n@if (_enabled && _open) {\n @let _segment = segment();\n <button type=\"button\" class=\"sd-autoid-inspector__backdrop\" aria-label=\"\u0110\u00F3ng AutoId Inspector\" (click)=\"closePanel()\"></button>\n <section\n class=\"sd-autoid-inspector__panel\"\n role=\"dialog\"\n aria-label=\"AutoId Inspector\"\n data-autoid=\"sd-autoid-inspector-panel\"\n [attr.data-segment]=\"dataSegment()\"\n [attr.data-highlight-on]=\"dataHighlightOn()\"\n [attr.data-element-count]=\"dataElementCount()\"\n [attr.data-missing-count]=\"dataMissingCount()\"\n [attr.data-duplicate-count]=\"dataDuplicateCount()\">\n <header class=\"sd-autoid-inspector__header\">\n <div class=\"sd-autoid-inspector__segments\">\n <button type=\"button\" [class.is-active]=\"_segment === 'audit'\" (click)=\"setSegment('audit')\">\n Audit\n @if (audit(); as a) {\n <span class=\"sd-autoid-inspector__badge\" [class.is-warn]=\"a.missingCount + a.duplicateCount > 0\">\n {{ a.missingCount + a.duplicateCount }}\n </span>\n }\n </button>\n <button type=\"button\" [class.is-active]=\"_segment === 'elements'\" (click)=\"setSegment('elements')\">\n Elements\n <span class=\"sd-autoid-inspector__badge\">{{ elements().length }}</span>\n </button>\n <button type=\"button\" [class.is-active]=\"_segment === 'export'\" (click)=\"setSegment('export')\">Export</button>\n </div>\n <div class=\"sd-autoid-inspector__actions\">\n <button type=\"button\" class=\"sd-autoid-inspector__icon-btn\" matTooltip=\"Qu\u00E9t l\u1EA1i\" (click)=\"refresh()\">\n <sd-icon name=\"refresh\"></sd-icon>\n </button>\n <button type=\"button\" class=\"sd-autoid-inspector__close-btn\" matTooltip=\"\u0110\u00F3ng\" aria-label=\"\u0110\u00F3ng panel\" (click)=\"closePanel()\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n </div>\n </header>\n\n @if (copyOk()) {\n <div class=\"sd-autoid-inspector__toast\">\u0110\u00E3 copy \u2713</div>\n }\n\n <div class=\"sd-autoid-inspector__body\">\n @switch (_segment) {\n @case ('audit') {\n <ng-container *ngTemplateOutlet=\"auditTpl\"></ng-container>\n }\n @case ('elements') {\n <ng-container *ngTemplateOutlet=\"elementsTpl\"></ng-container>\n }\n @case ('export') {\n <ng-container *ngTemplateOutlet=\"exportTpl\"></ng-container>\n }\n }\n </div>\n </section>\n\n <ng-template #auditTpl>\n @if (audit(); as a) {\n @let _highlightOn = highlightOn();\n <div class=\"sd-autoid-inspector__audit-toolbar\">\n <button type=\"button\" class=\"sd-autoid-inspector__toggle\" [class.is-on]=\"_highlightOn\" (click)=\"toggleHighlight()\">\n <sd-icon [name]=\"_highlightOn ? 'visibility' : 'visibility_off'\"></sd-icon>\n <span>Highlight DOM</span>\n <span class=\"sd-autoid-inspector__toggle-state\">{{ _highlightOn ? 'ON' : 'OFF' }}</span>\n </button>\n </div>\n <div class=\"sd-autoid-inspector__summary\">\n <div class=\"sd-autoid-inspector__stat is-info\">\n <div class=\"sd-autoid-inspector__stat-value\">{{ a.total }}</div>\n <div class=\"sd-autoid-inspector__stat-label\">T\u1ED5ng autoid</div>\n </div>\n <div class=\"sd-autoid-inspector__stat\" [class.is-error]=\"a.duplicateCount > 0\" [class.is-success]=\"a.duplicateCount === 0\">\n <div class=\"sd-autoid-inspector__stat-value\">{{ a.duplicateCount }}</div>\n <div class=\"sd-autoid-inspector__stat-label\">Tr\u00F9ng</div>\n </div>\n <div class=\"sd-autoid-inspector__stat\" [class.is-warning]=\"a.missingCount > 0\" [class.is-success]=\"a.missingCount === 0\">\n <div class=\"sd-autoid-inspector__stat-value\">{{ a.missingCount }}</div>\n <div class=\"sd-autoid-inspector__stat-label\">Thi\u1EBFu</div>\n </div>\n </div>\n\n @if (a.duplicateCount > 0) {\n <h4 class=\"sd-autoid-inspector__section-title\">\n <sd-icon class=\"is-error\" name=\"error\"></sd-icon>\n Autoid b\u1ECB tr\u00F9ng\n </h4>\n <div class=\"sd-autoid-inspector__table-wrap\">\n <table class=\"sd-autoid-inspector__table\">\n <thead>\n <tr>\n <th>Autoid</th>\n <th>S\u1ED1 l\u1EA7n</th>\n </tr>\n </thead>\n <tbody>\n @for (entry of a.duplicates | keyvalue; track entry.key) {\n <tr class=\"is-error\">\n <td>\n <code>{{ entry.key }}</code>\n </td>\n <td>{{ entry.value }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n\n @if (a.missingCount > 0) {\n <h4 class=\"sd-autoid-inspector__section-title\">\n <sd-icon class=\"is-warning\" name=\"warning_amber\"></sd-icon>\n Element thi\u1EBFu autoid\n </h4>\n <div class=\"sd-autoid-inspector__table-wrap\">\n <table class=\"sd-autoid-inspector__table\">\n <thead>\n <tr>\n <th>Tag</th>\n <th>Name hint</th>\n <th>Selector</th>\n </tr>\n </thead>\n <tbody>\n @for (m of a.missing; track $index) {\n <tr class=\"is-warning\">\n <td>\n <code>{{ m.tag }}</code>\n </td>\n <td>{{ m.nameHint }}</td>\n <td>\n <code>{{ m.selector }}</code>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n\n @if (a.duplicateCount === 0 && a.missingCount === 0) {\n <div class=\"sd-autoid-inspector__success-card\">\n <sd-icon name=\"check_circle\"></sd-icon>\n <div>\n <strong>Kh\u00F4ng c\u00F3 element thi\u1EBFu ho\u1EB7c tr\u00F9ng autoid.</strong>\n <small>Trang \u0111\u00E3 \u0111\u1EA1t chu\u1EA9n convention autoid.</small>\n </div>\n </div>\n }\n }\n </ng-template>\n\n <ng-template #elementsTpl>\n @let _filter = filter();\n <div class=\"sd-autoid-inspector__filter\">\n <sd-icon name=\"search\"></sd-icon>\n <input type=\"text\" placeholder=\"L\u1ECDc theo autoid / name / tag\" [ngModel]=\"_filter\" (ngModelChange)=\"filter.set($event)\" />\n @if (_filter) {\n <button type=\"button\" class=\"sd-autoid-inspector__icon-btn\" matTooltip=\"Xo\u00E1 filter\" (click)=\"filter.set('')\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n }\n </div>\n\n <!-- Top-level elements (not inside any sd-table) -->\n <h4 class=\"sd-autoid-inspector__section-title\">\n Top-level elements\n <span class=\"sd-autoid-inspector__badge\">{{ topLevelElements().length }}</span>\n </h4>\n <div class=\"sd-autoid-inspector__table-wrap\">\n <table class=\"sd-autoid-inspector__table\">\n <thead>\n <tr>\n <th class=\"is-num\">STT</th>\n <th>Name</th>\n <th>Autoid</th>\n <th>Tag</th>\n <th>State</th>\n <th>Value</th>\n </tr>\n </thead>\n <tbody>\n @for (el of topLevelElements(); track el.autoid + $index) {\n <tr [class.is-error]=\"el.duplicate\">\n <td class=\"is-num\">{{ el.stt }}</td>\n <td>{{ el.name || '\u2014' }}</td>\n <td>\n <div class=\"sd-autoid-inspector__autoid-cell\">\n <code>{{ el.autoid }}</code>\n <button\n type=\"button\"\n class=\"sd-autoid-inspector__icon-btn is-primary\"\n matTooltip=\"Copy xpath\"\n (click)=\"copyXpath(el.autoid)\">\n <sd-icon name=\"content_copy\"></sd-icon>\n </button>\n </div>\n </td>\n <td>\n <span class=\"sd-autoid-inspector__tag-chip\">{{ el.tag }}</span>\n </td>\n <td class=\"sd-autoid-inspector__state-cell\">\n @if (el.state.disabled !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-error]=\"el.state.disabled === 'true'\">\n disabled={{ el.state.disabled }}\n </span>\n }\n @if (el.state.loading !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-warning]=\"el.state.loading === 'true'\">\n loading={{ el.state.loading }}\n </span>\n }\n @if (el.state.empty !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-info]=\"el.state.empty === 'true'\">\n empty={{ el.state.empty }}\n </span>\n }\n @if (el.state.invalid !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-error]=\"el.state.invalid === 'true'\">\n invalid={{ el.state.invalid }}\n </span>\n }\n @if (el.state.opened !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-info]=\"el.state.opened === 'true'\">\n opened={{ el.state.opened }}\n </span>\n }\n @if (el.state.count !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> count={{ el.state.count }} </span>\n }\n @if (el.state.required !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-warning]=\"el.state.required === 'true'\">\n required={{ el.state.required }}\n </span>\n }\n @if (el.state.maxlength !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> maxlength={{ el.state.maxlength }} </span>\n }\n @if (el.state.minlength !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> minlength={{ el.state.minlength }} </span>\n }\n @if (el.state.pattern !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> pattern={{ el.state.pattern }} </span>\n }\n @if (el.state.errorMessage !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-error\"> error: {{ el.state.errorMessage }} </span>\n }\n </td>\n <td class=\"sd-autoid-inspector__value\" [matTooltip]=\"el.state.dataValue || el.text || ''\">\n {{ el.state.dataValue || el.text || '\u2014' }}\n </td>\n </tr>\n } @empty {\n <tr>\n <td colspan=\"6\" class=\"sd-autoid-inspector__empty\">Kh\u00F4ng c\u00F3 k\u1EBFt qu\u1EA3.</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <!-- One section per sd-table group -->\n @for (group of tableGroups(); track group.scope) {\n <h4 class=\"sd-autoid-inspector__section-title\">\n <sd-icon class=\"is-info\" name=\"table_chart\"></sd-icon>\n Inside <code>{{ group.scope }}</code>\n <span class=\"sd-autoid-inspector__badge\">{{ group.items.length }}</span>\n </h4>\n <div class=\"sd-autoid-inspector__table-wrap\">\n <table class=\"sd-autoid-inspector__table\">\n <thead>\n <tr>\n <th class=\"is-num\">STT</th>\n <th>Name</th>\n <th>Autoid</th>\n <th>Tag</th>\n <th>State</th>\n <th>Value</th>\n </tr>\n </thead>\n <tbody>\n @for (el of group.items; track el.autoid + $index) {\n <tr [class.is-error]=\"el.duplicate\">\n <td class=\"is-num\">{{ el.stt }}</td>\n <td>{{ el.name || '\u2014' }}</td>\n <td>\n <div class=\"sd-autoid-inspector__autoid-cell\">\n <code>{{ el.autoid }}</code>\n <button\n type=\"button\"\n class=\"sd-autoid-inspector__icon-btn is-primary\"\n matTooltip=\"Copy xpath\"\n (click)=\"copyXpath(el.autoid)\">\n <sd-icon name=\"content_copy\"></sd-icon>\n </button>\n </div>\n </td>\n <td>\n <span class=\"sd-autoid-inspector__tag-chip\">{{ el.tag }}</span>\n </td>\n <td class=\"sd-autoid-inspector__state-cell\">\n @if (el.state.disabled !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-error]=\"el.state.disabled === 'true'\">\n disabled={{ el.state.disabled }}\n </span>\n }\n @if (el.state.loading !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-warning]=\"el.state.loading === 'true'\">\n loading={{ el.state.loading }}\n </span>\n }\n @if (el.state.empty !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-info]=\"el.state.empty === 'true'\">\n empty={{ el.state.empty }}\n </span>\n }\n @if (el.state.invalid !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-error]=\"el.state.invalid === 'true'\">\n invalid={{ el.state.invalid }}\n </span>\n }\n @if (el.state.opened !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-info]=\"el.state.opened === 'true'\">\n opened={{ el.state.opened }}\n </span>\n }\n @if (el.state.count !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> count={{ el.state.count }} </span>\n }\n @if (el.state.required !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-warning]=\"el.state.required === 'true'\">\n required={{ el.state.required }}\n </span>\n }\n @if (el.state.maxlength !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> maxlength={{ el.state.maxlength }} </span>\n }\n @if (el.state.minlength !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> minlength={{ el.state.minlength }} </span>\n }\n @if (el.state.pattern !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> pattern={{ el.state.pattern }} </span>\n }\n @if (el.state.errorMessage !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-error\"> error: {{ el.state.errorMessage }} </span>\n }\n </td>\n <td class=\"sd-autoid-inspector__value\" [matTooltip]=\"el.state.dataValue || el.text || ''\">\n {{ el.state.dataValue || el.text || '\u2014' }}\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </ng-template>\n\n <ng-template #exportTpl>\n <div class=\"sd-autoid-inspector__export-grid\">\n <button type=\"button\" matTooltip=\"Copy JSON v\u00E0o clipboard\" (click)=\"copyJson()\">\n <sd-icon name=\"content_copy\"></sd-icon>\n <span>Copy JSON</span>\n <small>clipboard</small>\n </button>\n <button type=\"button\" matTooltip=\"T\u1EA3i file JSON\" (click)=\"exportJson()\">\n <sd-icon name=\"download\"></sd-icon>\n <span>JSON</span>\n <small>.json</small>\n </button>\n <button type=\"button\" matTooltip=\"T\u1EA3i file CSV\" (click)=\"exportCsv()\">\n <sd-icon name=\"table_view\"></sd-icon>\n <span>CSV</span>\n <small>.csv</small>\n </button>\n <button type=\"button\" matTooltip=\"T\u1EA3i MD POM skeleton \u2014 cho AI vi\u1EBFt auto-test\" (click)=\"exportMdPom()\">\n <sd-icon name=\"code\"></sd-icon>\n <span>MD POM</span>\n <small>AI test</small>\n </button>\n <button type=\"button\" matTooltip=\"T\u1EA3i MD reference table \u2014 group theo tag sd-*\" (click)=\"exportMdTable()\">\n <sd-icon name=\"view_list\"></sd-icon>\n <span>MD Table</span>\n <small>reference</small>\n </button>\n </div>\n\n @if (e2eGeneratorHost()) {\n @let _e2eExporting = e2eExporting();\n <div class=\"sd-autoid-inspector__export-section\">\n <h4 class=\"sd-autoid-inspector__section-title\">\n <sd-icon class=\"is-info\" name=\"auto_awesome\"></sd-icon>\n T\u1EA1o E2E test\n </h4>\n <div class=\"sd-autoid-inspector__export-grid is-e2e\">\n <button\n type=\"button\"\n matTooltip=\"T\u1EA1o b\u1ED9 test Playwright (.zip)\"\n [disabled]=\"_e2eExporting !== null\"\n (click)=\"exportE2eTest('playwright')\">\n <sd-icon [name]=\"_e2eExporting === 'playwright' ? 'hourglass_empty' : 'play_arrow'\"></sd-icon>\n <span>Playwright</span>\n <small>.zip</small>\n </button>\n <button\n type=\"button\"\n matTooltip=\"T\u1EA1o b\u1ED9 test Robot Framework (.zip)\"\n [disabled]=\"_e2eExporting !== null\"\n (click)=\"openRobotExportContext()\">\n <sd-icon [name]=\"_e2eExporting === 'robot' ? 'hourglass_empty' : 'smart_toy'\"></sd-icon>\n <span>Robot Framework</span>\n <small>.zip</small>\n </button>\n </div>\n\n @if (robotContextOpen()) {\n <div class=\"sd-autoid-inspector__context-backdrop\" role=\"presentation\" (click)=\"closeRobotExportContext()\"></div>\n <section\n class=\"sd-autoid-inspector__context-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"sd-autoid-robot-context-title\">\n <header class=\"sd-autoid-inspector__context-header\">\n <div>\n <h4 id=\"sd-autoid-robot-context-title\">Ng\u1EEF c\u1EA3nh QC cho Robot Framework</h4>\n <small>Requirement, test case, test data</small>\n </div>\n <button type=\"button\" class=\"sd-autoid-inspector__icon-btn\" matTooltip=\"\u0110\u00F3ng\" (click)=\"closeRobotExportContext()\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n </header>\n\n <div class=\"sd-autoid-inspector__context-form\">\n <label>\n <span>Component</span>\n <input\n type=\"text\"\n placeholder=\"SP-Wallet\"\n [ngModel]=\"robotExportContext().component || ''\"\n (ngModelChange)=\"updateRobotExportContext('component', $event)\" />\n </label>\n <label>\n <span>Story / Trace</span>\n <input\n type=\"text\"\n placeholder=\"US-001, AC-01\"\n [ngModel]=\"robotExportContext().storyLinkages || ''\"\n (ngModelChange)=\"updateRobotExportContext('storyLinkages', $event)\" />\n </label>\n <label class=\"is-full\">\n <span>Requirement / AC / BR</span>\n <textarea\n rows=\"4\"\n placeholder=\"D\u00E1n requirement ho\u1EB7c acceptance criteria...\"\n [ngModel]=\"robotExportContext().requirement || ''\"\n (ngModelChange)=\"updateRobotExportContext('requirement', $event)\"></textarea>\n </label>\n <label class=\"is-full\">\n <span>Test case QC \u0111\u00E3 \u0111\u1ECBnh ngh\u0129a</span>\n <textarea\n rows=\"4\"\n placeholder=\"TC-001: ...\"\n [ngModel]=\"robotExportContext().testCases || ''\"\n (ngModelChange)=\"updateRobotExportContext('testCases', $event)\"></textarea>\n </label>\n <label class=\"is-full\">\n <span>Test data</span>\n <textarea\n rows=\"3\"\n placeholder=\"wallet=V\u00ED A, amount=100000...\"\n [ngModel]=\"robotExportContext().testData || ''\"\n (ngModelChange)=\"updateRobotExportContext('testData', $event)\"></textarea>\n </label>\n <label class=\"is-full\">\n <span>Precondition</span>\n <textarea\n rows=\"2\"\n placeholder=\"User \u0111\u00E3 \u0111\u0103ng nh\u1EADp, d\u1EEF li\u1EC7u v\u00ED \u0111\u00E3 t\u1ED3n t\u1EA1i...\"\n [ngModel]=\"robotExportContext().precondition || ''\"\n (ngModelChange)=\"updateRobotExportContext('precondition', $event)\"></textarea>\n </label>\n <label class=\"is-full\">\n <span>Ghi ch\u00FA format QC</span>\n <textarea\n rows=\"3\"\n placeholder=\"Smoke=Y, Auto=Y, \u01B0u ti\u00EAn happy path...\"\n [ngModel]=\"robotExportContext().qcNotes || ''\"\n (ngModelChange)=\"updateRobotExportContext('qcNotes', $event)\"></textarea>\n </label>\n </div>\n\n <footer class=\"sd-autoid-inspector__context-actions\">\n <button type=\"button\" class=\"is-secondary\" [disabled]=\"_e2eExporting !== null\" (click)=\"closeRobotExportContext()\">\n Hu\u1EF7\n </button>\n <button\n type=\"button\"\n class=\"is-primary\"\n [disabled]=\"_e2eExporting !== null || !hasRobotExportContext()\"\n (click)=\"confirmRobotExport()\">\n <sd-icon [name]=\"_e2eExporting === 'robot' ? 'hourglass_empty' : 'download'\"></sd-icon>\n <span>\u0110\u1ED3ng \u00FD xu\u1EA5t</span>\n </button>\n </footer>\n </section>\n }\n\n @if (e2eExportOk(); as targetLabel) {\n <div class=\"sd-autoid-inspector__export-message is-success\">\u0110\u00E3 t\u1EA3i b\u1ED9 test {{ targetLabel }}.</div>\n }\n @if (e2eExportError(); as error) {\n <div class=\"sd-autoid-inspector__export-message is-error\">\n {{ error }}\n </div>\n }\n </div>\n }\n </ng-template>\n}\n", styles: [":host{position:fixed;inset:auto 0 0 auto;z-index:100000;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;font-size:13px;color:var(--sd-text, #1a1b1f)}.sd-autoid-inspector__fab-wrap{position:fixed;right:16px;bottom:16px;z-index:100000}.sd-autoid-inspector__fab-wrap:hover .sd-autoid-inspector__fab-dismiss{opacity:1;transform:scale(1);pointer-events:auto}.sd-autoid-inspector__fab{width:36px;height:36px;border-radius:50%;border:none;background:var(--sd-primary, #005cbb);color:#fff;cursor:pointer;box-shadow:0 3px 10px #00000038;display:inline-flex;align-items:center;justify-content:center;transition:transform .15s ease,background .15s ease,box-shadow .15s ease}.sd-autoid-inspector__fab:hover{background:var(--sd-primary-dark, #00458f);transform:scale(1.08);box-shadow:0 5px 14px #00000047}.sd-autoid-inspector__fab sd-icon{font-size:18px;width:18px;height:18px}.sd-autoid-inspector__fab-dismiss{position:absolute;top:-6px;right:-6px;width:18px;height:18px;border-radius:50%;border:1px solid var(--sd-border, #c4c6d0);background:#fff;color:var(--sd-text-secondary, #44474f);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;padding:0;opacity:0;transform:scale(.7);pointer-events:none;transition:opacity .12s ease,transform .12s ease,color .12s ease,background .12s ease;box-shadow:0 1px 4px #00000026}.sd-autoid-inspector__fab-dismiss:hover{background:var(--sd-error-light, #ffdad6);color:var(--sd-error-dark, #93000a);border-color:var(--sd-error, #ba1a1a)}.sd-autoid-inspector__fab-dismiss sd-icon{font-size:12px;width:12px;height:12px;line-height:12px}.sd-autoid-inspector__backdrop{position:fixed;inset:0;padding:0;border:none;background:#00000052;z-index:100001;backdrop-filter:blur(2px)}.sd-autoid-inspector__panel{position:fixed;right:20px;bottom:80px;width:min(860px,100vw - 40px);max-height:calc(100vh - 110px);background:#fff;border-radius:12px;box-shadow:0 16px 48px #00000038;display:flex;flex-direction:column;z-index:100002;overflow:hidden;border:1px solid var(--sd-border, #c4c6d0)}.sd-autoid-inspector__header{display:flex;align-items:center;gap:8px;padding:8px 10px;border-bottom:1px solid var(--sd-border, #c4c6d0);background:var(--sd-surface-muted, #e7e8ed);flex-shrink:0}.sd-autoid-inspector__segments{display:inline-flex;border:1px solid var(--sd-border, #c4c6d0);border-radius:8px;background:#fff;overflow:hidden}.sd-autoid-inspector__segments button{border:none;background:transparent;padding:5px 12px;cursor:pointer;font-size:12px;font-weight:500;color:var(--sd-text-secondary, #44474f);display:inline-flex;align-items:center;gap:6px;border-right:1px solid var(--sd-border, #c4c6d0);transition:background .12s ease,color .12s ease}.sd-autoid-inspector__segments button:last-child{border-right:none}.sd-autoid-inspector__segments button:hover:not(.is-active){background:var(--sd-surface-muted, #e7e8ed);color:var(--sd-text, #1a1b1f)}.sd-autoid-inspector__segments button.is-active{background:var(--sd-primary, #005cbb);color:#fff}.sd-autoid-inspector__badge{background:var(--sd-border, #c4c6d0);color:var(--sd-text, #1a1b1f);border-radius:10px;padding:0 7px;font-size:11px;font-weight:600;line-height:16px}.is-active .sd-autoid-inspector__badge{background:#ffffff40;color:#fff}.sd-autoid-inspector__badge.is-warn{background:var(--sd-error, #ba1a1a);color:#fff}.sd-autoid-inspector__actions{display:inline-flex;gap:4px;margin-left:auto}.sd-autoid-inspector__icon-btn{border:none;background:transparent;cursor:pointer;padding:4px;border-radius:6px;color:var(--sd-text-secondary, #44474f);display:inline-flex;align-items:center;justify-content:center;transition:background .12s ease,color .12s ease}.sd-autoid-inspector__icon-btn:hover{background:var(--sd-surface-muted, #e7e8ed);color:var(--sd-text, #1a1b1f)}.sd-autoid-inspector__icon-btn.is-primary{color:var(--sd-primary, #005cbb)}.sd-autoid-inspector__icon-btn.is-primary:hover{background:var(--sd-primary-light, #d7e3ff);color:var(--sd-primary-dark, #00458f)}.sd-autoid-inspector__icon-btn sd-icon{font-size:18px;width:18px;height:18px}.sd-autoid-inspector__close-btn{border:none;cursor:pointer;padding:4px;border-radius:50%;width:28px;height:28px;background:transparent;color:var(--sd-text-secondary, #44474f);display:inline-flex;align-items:center;justify-content:center;transition:background .12s ease,color .12s ease}.sd-autoid-inspector__close-btn:hover{background:var(--sd-error-light, #ffdad6);color:var(--sd-error-dark, #93000a)}.sd-autoid-inspector__close-btn sd-icon{font-size:18px;width:18px;height:18px}.sd-autoid-inspector__body{padding:14px;overflow:auto;flex:1;background:#fff}.sd-autoid-inspector__toast{position:absolute;top:56px;right:14px;background:var(--sd-success, #2e7d32);color:#fff;padding:6px 12px;border-radius:6px;font-size:12px;z-index:2;box-shadow:0 4px 12px #0000002e}.sd-autoid-inspector__audit-toolbar{display:flex;justify-content:flex-end;margin-bottom:12px}.sd-autoid-inspector__toggle{display:inline-flex;align-items:center;gap:8px;border:1px solid var(--sd-border, #c4c6d0);background:#fff;border-radius:999px;padding:5px 12px 5px 10px;cursor:pointer;font-size:12px;font-weight:500;color:var(--sd-text-secondary, #44474f);transition:all .15s ease}.sd-autoid-inspector__toggle:hover{border-color:var(--sd-primary, #005cbb);color:var(--sd-primary, #005cbb)}.sd-autoid-inspector__toggle.is-on{border-color:var(--sd-primary, #005cbb);color:var(--sd-primary, #005cbb);background:var(--sd-primary-light, #d7e3ff)}.sd-autoid-inspector__toggle sd-icon{font-size:16px;width:16px;height:16px}.sd-autoid-inspector__toggle-state{font-size:10px;font-weight:700;padding:1px 7px;border-radius:999px;background:var(--sd-border, #c4c6d0);color:var(--sd-text-secondary, #44474f)}.is-on .sd-autoid-inspector__toggle-state{background:var(--sd-primary, #005cbb);color:#fff}.sd-autoid-inspector__summary{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:16px}.sd-autoid-inspector__stat{background:var(--sd-surface-muted, #e7e8ed);border-radius:10px;padding:12px;text-align:center;border:1px solid transparent}.sd-autoid-inspector__stat.is-info{background:var(--sd-info-light, #d4f7f6);border-color:var(--sd-info, #006a6a)}.sd-autoid-inspector__stat.is-info .sd-autoid-inspector__stat-value{color:var(--sd-info, #006a6a)}.sd-autoid-inspector__stat.is-success{background:var(--sd-success-light, #d8f0d3);border-color:var(--sd-success, #2e7d32)}.sd-autoid-inspector__stat.is-success .sd-autoid-inspector__stat-value{color:var(--sd-success-dark, #1b5e20)}.sd-autoid-inspector__stat.is-warning{background:var(--sd-warning-light, #ffddb0);border-color:var(--sd-warning, #a66300)}.sd-autoid-inspector__stat.is-warning .sd-autoid-inspector__stat-value{color:var(--sd-warning-dark, #7a4d00)}.sd-autoid-inspector__stat.is-error{background:var(--sd-error-light, #ffdad6);border-color:var(--sd-error, #ba1a1a)}.sd-autoid-inspector__stat.is-error .sd-autoid-inspector__stat-value{color:var(--sd-error-dark, #93000a)}.sd-autoid-inspector__stat-value{font-size:22px;font-weight:700;line-height:1;margin-bottom:4px}.sd-autoid-inspector__stat-label{font-size:11px;color:var(--sd-text-secondary, #44474f);text-transform:uppercase;letter-spacing:.4px;font-weight:600}.sd-autoid-inspector__section-title{display:flex;align-items:center;gap:6px;margin:14px 0 8px;font-size:13px;font-weight:600;color:var(--sd-text, #1a1b1f)}.sd-autoid-inspector__section-title sd-icon{font-size:18px;width:18px;height:18px}.sd-autoid-inspector__section-title sd-icon.is-error{color:var(--sd-error, #ba1a1a)}.sd-autoid-inspector__section-title sd-icon.is-warning{color:var(--sd-warning, #a66300)}.sd-autoid-inspector__section-title sd-icon.is-info{color:var(--sd-info, #006a6a)}.sd-autoid-inspector__success-card{display:flex;align-items:center;gap:12px;padding:14px 16px;background:var(--sd-success-light, #d8f0d3);border:1px solid var(--sd-success, #2e7d32);border-radius:10px;color:var(--sd-success-dark, #1b5e20)}.sd-autoid-inspector__success-card sd-icon{font-size:28px;width:28px;height:28px;color:var(--sd-success, #2e7d32)}.sd-autoid-inspector__success-card strong{display:block;font-size:13px;margin-bottom:2px}.sd-autoid-inspector__success-card small{font-size:11px;opacity:.85}.sd-autoid-inspector__table-wrap{border:1px solid var(--sd-border, #c4c6d0);border-radius:8px;overflow:auto;max-height:60vh;background:#fff}.sd-autoid-inspector__table{width:100%;border-collapse:separate;border-spacing:0;font-size:12px}.sd-autoid-inspector__table th,.sd-autoid-inspector__table td{padding:8px 10px;text-align:left;vertical-align:middle;border-bottom:1px solid var(--sd-border, #c4c6d0)}.sd-autoid-inspector__table thead th{position:sticky;top:0;z-index:1;background:var(--sd-surface-muted, #e7e8ed);font-weight:600;color:var(--sd-text, #1a1b1f);text-transform:none;font-size:11px;letter-spacing:.3px;border-bottom:none;box-shadow:inset 0 -1px 0 var(--sd-border, #c4c6d0),0 2px 4px #0000000a;white-space:nowrap}.sd-autoid-inspector__table thead th.is-num{text-align:right;width:50px}.sd-autoid-inspector__table tbody tr{transition:background .1s ease}.sd-autoid-inspector__table tbody tr:hover{background:var(--sd-primary-light, #d7e3ff)}.sd-autoid-inspector__table tbody tr:last-child td{border-bottom:none}.sd-autoid-inspector__table tbody tr.is-error{background:var(--sd-error-light, #ffdad6)}.sd-autoid-inspector__table tbody tr.is-error:hover{background:var(--sd-error-light, #ffdad6);filter:brightness(.98)}.sd-autoid-inspector__table tbody tr.is-warning{background:var(--sd-warning-light, #ffddb0)}.sd-autoid-inspector__table tbody tr.is-warning:hover{background:var(--sd-warning-light, #ffddb0);filter:brightness(.98)}.sd-autoid-inspector__table td.is-num{text-align:right;color:var(--sd-text-secondary, #44474f);font-variant-numeric:tabular-nums}.sd-autoid-inspector__table code{font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:11px;background:var(--sd-surface-muted, #e7e8ed);padding:2px 6px;border-radius:4px;color:var(--sd-primary-dark, #00458f);border:1px solid var(--sd-border, #c4c6d0)}.sd-autoid-inspector__autoid-cell{display:inline-flex;align-items:center;gap:6px}.sd-autoid-inspector__tag-chip{display:inline-block;font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:11px;background:var(--sd-info-light, #d4f7f6);color:var(--sd-info, #006a6a);border:1px solid var(--sd-info, #006a6a);padding:1px 8px;border-radius:999px;font-weight:500}.sd-autoid-inspector__value{max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--sd-text-secondary, #44474f);font-style:italic}.sd-autoid-inspector__state-cell{display:flex;flex-wrap:wrap;gap:4px;align-items:center;min-width:0}.sd-autoid-inspector__state-chip{display:inline-block;font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:10px;background:var(--sd-surface-muted, #e7e8ed);color:var(--sd-text-secondary, #44474f);border:1px solid var(--sd-border, #c4c6d0);padding:1px 6px;border-radius:999px;font-weight:500;white-space:nowrap}.sd-autoid-inspector__state-chip.is-error{background:var(--sd-error-light, #ffdad6);color:var(--sd-error-dark, #93000a);border-color:var(--sd-error, #ba1a1a)}.sd-autoid-inspector__state-chip.is-warning{background:var(--sd-warning-light, #ffddb0);color:var(--sd-warning-dark, #7a4d00);border-color:var(--sd-warning, #a66300)}.sd-autoid-inspector__state-chip.is-info{background:var(--sd-info-light, #d4f7f6);color:var(--sd-info, #006a6a);border-color:var(--sd-info, #006a6a)}.sd-autoid-inspector__empty{text-align:center;color:var(--sd-text-secondary, #44474f);padding:24px;font-style:italic}.sd-autoid-inspector__filter{display:flex;align-items:center;gap:6px;background:var(--sd-surface-muted, #e7e8ed);border:1px solid var(--sd-border, #c4c6d0);border-radius:8px;padding:6px 10px;margin-bottom:12px;transition:border-color .15s ease}.sd-autoid-inspector__filter:focus-within{border-color:var(--sd-primary, #005cbb);background:#fff}.sd-autoid-inspector__filter input{flex:1;border:none;background:transparent;outline:none;font-size:13px;color:var(--sd-text, #1a1b1f)}.sd-autoid-inspector__filter input::placeholder{color:var(--sd-border-strong, #74777f)}.sd-autoid-inspector__filter>sd-icon{font-size:18px;width:18px;height:18px;color:var(--sd-text-secondary, #44474f)}.sd-autoid-inspector__export-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(112px,1fr));gap:8px}.sd-autoid-inspector__export-grid.is-e2e{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}.sd-autoid-inspector__export-grid button{border:1px solid var(--sd-border, #c4c6d0);background:#fff;border-radius:10px;padding:12px 10px;cursor:pointer;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;min-width:0;transition:border-color .15s ease,background .15s ease,transform .12s ease;text-align:center}.sd-autoid-inspector__export-grid button:hover{border-color:var(--sd-primary, #005cbb);background:var(--sd-primary-light, #d7e3ff);transform:translateY(-1px)}.sd-autoid-inspector__export-grid button:disabled{cursor:wait;opacity:.68;transform:none}.sd-autoid-inspector__export-grid button span{font-size:12px;font-weight:600;color:var(--sd-text, #1a1b1f);line-height:1.25}.sd-autoid-inspector__export-grid button small{font-size:10px;color:var(--sd-text-secondary, #44474f);line-height:1.2}.sd-autoid-inspector__export-grid button sd-icon{font-size:22px;width:22px;height:22px;color:var(--sd-primary, #005cbb)}.sd-autoid-inspector__export-section{margin-top:14px;padding-top:12px;border-top:1px solid var(--sd-border, #c4c6d0)}.sd-autoid-inspector__context-backdrop{position:fixed;inset:0;z-index:10001;background:#141c2652}.sd-autoid-inspector__context-dialog{position:fixed;z-index:10002;top:50%;left:50%;width:min(720px,100vw - 28px);max-height:min(720px,100vh - 28px);transform:translate(-50%,-50%);display:flex;flex-direction:column;border:1px solid var(--sd-border, #c4c6d0);border-radius:8px;background:#fff;box-shadow:0 18px 48px #0f172a38;overflow:hidden}.sd-autoid-inspector__context-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:14px 16px;border-bottom:1px solid var(--sd-border, #c4c6d0)}.sd-autoid-inspector__context-header h4{margin:0;font-size:15px;font-weight:700;line-height:1.3;color:var(--sd-text, #1a1b1f)}.sd-autoid-inspector__context-header small{display:block;margin-top:2px;font-size:11px;color:var(--sd-text-secondary, #44474f);line-height:1.3}.sd-autoid-inspector__context-form{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;padding:14px 16px;overflow:auto}.sd-autoid-inspector__context-form label{display:flex;flex-direction:column;gap:6px;min-width:0}.sd-autoid-inspector__context-form label.is-full{grid-column:1/-1}.sd-autoid-inspector__context-form span{font-size:12px;font-weight:600;color:var(--sd-text, #1a1b1f);line-height:1.25}.sd-autoid-inspector__context-form input,.sd-autoid-inspector__context-form textarea{width:100%;min-width:0;border:1px solid var(--sd-border, #c4c6d0);border-radius:6px;padding:8px 10px;color:var(--sd-text, #1a1b1f);font:inherit;font-size:12px;line-height:1.4;outline:none;resize:vertical}.sd-autoid-inspector__context-form input:focus,.sd-autoid-inspector__context-form textarea:focus{border-color:var(--sd-primary, #005cbb);box-shadow:0 0 0 2px color-mix(in srgb,var(--sd-primary, #005cbb) 18%,transparent)}.sd-autoid-inspector__context-actions{display:flex;justify-content:flex-end;gap:8px;padding:12px 16px;border-top:1px solid var(--sd-border, #c4c6d0)}.sd-autoid-inspector__context-actions button{min-height:34px;border-radius:6px;padding:0 12px;border:1px solid var(--sd-border, #c4c6d0);background:#fff;color:var(--sd-text, #1a1b1f);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:6px;font-size:12px;font-weight:600}.sd-autoid-inspector__context-actions button.is-primary{border-color:var(--sd-primary, #005cbb);background:var(--sd-primary, #005cbb);color:#fff}.sd-autoid-inspector__context-actions button:disabled{cursor:not-allowed;opacity:.62}.sd-autoid-inspector__context-actions button sd-icon{font-size:17px;width:17px;height:17px}.sd-autoid-inspector__export-message{margin-top:10px;border-radius:8px;padding:8px 10px;font-size:12px;line-height:1.35}.sd-autoid-inspector__export-message.is-success{background:var(--sd-success-light, #d8f0d3);color:var(--sd-success-dark, #1b5e20);border:1px solid var(--sd-success, #2e7d32)}.sd-autoid-inspector__export-message.is-error{background:var(--sd-error-light, #ffdad6);color:var(--sd-error-dark, #93000a);border:1px solid var(--sd-error, #ba1a1a)}@media(max-width:560px){.sd-autoid-inspector__context-form{grid-template-columns:1fr}.sd-autoid-inspector__context-actions{flex-direction:column-reverse}.sd-autoid-inspector__context-actions button{width:100%}}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i1.KeyValuePipe, name: "keyvalue" }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i3.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
966
965
  }
967
966
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdAutoidInspector, decorators: [{
968
967
  type: Component,
969
- args: [{ selector: 'sd-autoid-inspector', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, FormsModule, MatIconModule, MatTooltipModule], template: "@let _enabled = enabled();\n@let _open = open();\n@if (_enabled && !dismissed()) {\n <div class=\"sd-autoid-inspector__fab-wrap\">\n <button\n type=\"button\"\n class=\"sd-autoid-inspector__fab\"\n data-autoid=\"sd-autoid-inspector-fab\"\n [attr.data-opened]=\"dataOpened()\"\n [matTooltip]=\"_open ? '\u0110\u00F3ng AutoId Inspector' : 'M\u1EDF AutoId Inspector'\"\n matTooltipPosition=\"left\"\n (click)=\"togglePanel()\">\n <mat-icon>{{ _open ? 'close' : 'tag' }}</mat-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-autoid-inspector__fab-dismiss\"\n matTooltip=\"\u1EA8n t\u1EA1m \u2014 reload trang \u0111\u1EC3 hi\u1EC7n l\u1EA1i\"\n matTooltipPosition=\"left\"\n aria-label=\"\u1EA8n t\u1EA1m FAB\"\n (click)=\"dismissFab($event)\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n}\n\n@if (_enabled && _open) {\n @let _segment = segment();\n <button type=\"button\" class=\"sd-autoid-inspector__backdrop\" aria-label=\"\u0110\u00F3ng AutoId Inspector\" (click)=\"closePanel()\"></button>\n <section\n class=\"sd-autoid-inspector__panel\"\n role=\"dialog\"\n aria-label=\"AutoId Inspector\"\n data-autoid=\"sd-autoid-inspector-panel\"\n [attr.data-segment]=\"dataSegment()\"\n [attr.data-highlight-on]=\"dataHighlightOn()\"\n [attr.data-element-count]=\"dataElementCount()\"\n [attr.data-missing-count]=\"dataMissingCount()\"\n [attr.data-duplicate-count]=\"dataDuplicateCount()\">\n <header class=\"sd-autoid-inspector__header\">\n <div class=\"sd-autoid-inspector__segments\">\n <button type=\"button\" [class.is-active]=\"_segment === 'audit'\" (click)=\"setSegment('audit')\">\n Audit\n @if (audit(); as a) {\n <span class=\"sd-autoid-inspector__badge\" [class.is-warn]=\"a.missingCount + a.duplicateCount > 0\">\n {{ a.missingCount + a.duplicateCount }}\n </span>\n }\n </button>\n <button type=\"button\" [class.is-active]=\"_segment === 'elements'\" (click)=\"setSegment('elements')\">\n Elements\n <span class=\"sd-autoid-inspector__badge\">{{ elements().length }}</span>\n </button>\n <button type=\"button\" [class.is-active]=\"_segment === 'export'\" (click)=\"setSegment('export')\">Export</button>\n </div>\n <div class=\"sd-autoid-inspector__actions\">\n <button type=\"button\" class=\"sd-autoid-inspector__icon-btn\" matTooltip=\"Qu\u00E9t l\u1EA1i\" (click)=\"refresh()\">\n <mat-icon>refresh</mat-icon>\n </button>\n <button type=\"button\" class=\"sd-autoid-inspector__close-btn\" matTooltip=\"\u0110\u00F3ng\" aria-label=\"\u0110\u00F3ng panel\" (click)=\"closePanel()\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </header>\n\n @if (copyOk()) {\n <div class=\"sd-autoid-inspector__toast\">\u0110\u00E3 copy \u2713</div>\n }\n\n <div class=\"sd-autoid-inspector__body\">\n @switch (_segment) {\n @case ('audit') {\n <ng-container *ngTemplateOutlet=\"auditTpl\"></ng-container>\n }\n @case ('elements') {\n <ng-container *ngTemplateOutlet=\"elementsTpl\"></ng-container>\n }\n @case ('export') {\n <ng-container *ngTemplateOutlet=\"exportTpl\"></ng-container>\n }\n }\n </div>\n </section>\n\n <ng-template #auditTpl>\n @if (audit(); as a) {\n @let _highlightOn = highlightOn();\n <div class=\"sd-autoid-inspector__audit-toolbar\">\n <button type=\"button\" class=\"sd-autoid-inspector__toggle\" [class.is-on]=\"_highlightOn\" (click)=\"toggleHighlight()\">\n <mat-icon>{{ _highlightOn ? 'visibility' : 'visibility_off' }}</mat-icon>\n <span>Highlight DOM</span>\n <span class=\"sd-autoid-inspector__toggle-state\">{{ _highlightOn ? 'ON' : 'OFF' }}</span>\n </button>\n </div>\n <div class=\"sd-autoid-inspector__summary\">\n <div class=\"sd-autoid-inspector__stat is-info\">\n <div class=\"sd-autoid-inspector__stat-value\">{{ a.total }}</div>\n <div class=\"sd-autoid-inspector__stat-label\">T\u1ED5ng autoid</div>\n </div>\n <div class=\"sd-autoid-inspector__stat\" [class.is-error]=\"a.duplicateCount > 0\" [class.is-success]=\"a.duplicateCount === 0\">\n <div class=\"sd-autoid-inspector__stat-value\">{{ a.duplicateCount }}</div>\n <div class=\"sd-autoid-inspector__stat-label\">Tr\u00F9ng</div>\n </div>\n <div class=\"sd-autoid-inspector__stat\" [class.is-warning]=\"a.missingCount > 0\" [class.is-success]=\"a.missingCount === 0\">\n <div class=\"sd-autoid-inspector__stat-value\">{{ a.missingCount }}</div>\n <div class=\"sd-autoid-inspector__stat-label\">Thi\u1EBFu</div>\n </div>\n </div>\n\n @if (a.duplicateCount > 0) {\n <h4 class=\"sd-autoid-inspector__section-title\">\n <mat-icon class=\"is-error\">error_outline</mat-icon>\n Autoid b\u1ECB tr\u00F9ng\n </h4>\n <div class=\"sd-autoid-inspector__table-wrap\">\n <table class=\"sd-autoid-inspector__table\">\n <thead>\n <tr>\n <th>Autoid</th>\n <th>S\u1ED1 l\u1EA7n</th>\n </tr>\n </thead>\n <tbody>\n @for (entry of a.duplicates | keyvalue; track entry.key) {\n <tr class=\"is-error\">\n <td>\n <code>{{ entry.key }}</code>\n </td>\n <td>{{ entry.value }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n\n @if (a.missingCount > 0) {\n <h4 class=\"sd-autoid-inspector__section-title\">\n <mat-icon class=\"is-warning\">warning_amber</mat-icon>\n Element thi\u1EBFu autoid\n </h4>\n <div class=\"sd-autoid-inspector__table-wrap\">\n <table class=\"sd-autoid-inspector__table\">\n <thead>\n <tr>\n <th>Tag</th>\n <th>Name hint</th>\n <th>Selector</th>\n </tr>\n </thead>\n <tbody>\n @for (m of a.missing; track $index) {\n <tr class=\"is-warning\">\n <td>\n <code>{{ m.tag }}</code>\n </td>\n <td>{{ m.nameHint }}</td>\n <td>\n <code>{{ m.selector }}</code>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n\n @if (a.duplicateCount === 0 && a.missingCount === 0) {\n <div class=\"sd-autoid-inspector__success-card\">\n <mat-icon>check_circle</mat-icon>\n <div>\n <strong>Kh\u00F4ng c\u00F3 element thi\u1EBFu ho\u1EB7c tr\u00F9ng autoid.</strong>\n <small>Trang \u0111\u00E3 \u0111\u1EA1t chu\u1EA9n convention autoid.</small>\n </div>\n </div>\n }\n }\n </ng-template>\n\n <ng-template #elementsTpl>\n @let _filter = filter();\n <div class=\"sd-autoid-inspector__filter\">\n <mat-icon>search</mat-icon>\n <input type=\"text\" placeholder=\"L\u1ECDc theo autoid / name / tag\" [ngModel]=\"_filter\" (ngModelChange)=\"filter.set($event)\" />\n @if (_filter) {\n <button type=\"button\" class=\"sd-autoid-inspector__icon-btn\" matTooltip=\"Xo\u00E1 filter\" (click)=\"filter.set('')\">\n <mat-icon>close</mat-icon>\n </button>\n }\n </div>\n\n <!-- Top-level elements (not inside any sd-table) -->\n <h4 class=\"sd-autoid-inspector__section-title\">\n Top-level elements\n <span class=\"sd-autoid-inspector__badge\">{{ topLevelElements().length }}</span>\n </h4>\n <div class=\"sd-autoid-inspector__table-wrap\">\n <table class=\"sd-autoid-inspector__table\">\n <thead>\n <tr>\n <th class=\"is-num\">STT</th>\n <th>Name</th>\n <th>Autoid</th>\n <th>Tag</th>\n <th>State</th>\n <th>Value</th>\n </tr>\n </thead>\n <tbody>\n @for (el of topLevelElements(); track el.autoid + $index) {\n <tr [class.is-error]=\"el.duplicate\">\n <td class=\"is-num\">{{ el.stt }}</td>\n <td>{{ el.name || '\u2014' }}</td>\n <td>\n <div class=\"sd-autoid-inspector__autoid-cell\">\n <code>{{ el.autoid }}</code>\n <button\n type=\"button\"\n class=\"sd-autoid-inspector__icon-btn is-primary\"\n matTooltip=\"Copy xpath\"\n (click)=\"copyXpath(el.autoid)\">\n <mat-icon>content_copy</mat-icon>\n </button>\n </div>\n </td>\n <td>\n <span class=\"sd-autoid-inspector__tag-chip\">{{ el.tag }}</span>\n </td>\n <td class=\"sd-autoid-inspector__state-cell\">\n @if (el.state.disabled !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-error]=\"el.state.disabled === 'true'\">\n disabled={{ el.state.disabled }}\n </span>\n }\n @if (el.state.loading !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-warning]=\"el.state.loading === 'true'\">\n loading={{ el.state.loading }}\n </span>\n }\n @if (el.state.empty !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-info]=\"el.state.empty === 'true'\">\n empty={{ el.state.empty }}\n </span>\n }\n @if (el.state.invalid !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-error]=\"el.state.invalid === 'true'\">\n invalid={{ el.state.invalid }}\n </span>\n }\n @if (el.state.opened !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-info]=\"el.state.opened === 'true'\">\n opened={{ el.state.opened }}\n </span>\n }\n @if (el.state.count !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> count={{ el.state.count }} </span>\n }\n @if (el.state.required !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-warning]=\"el.state.required === 'true'\">\n required={{ el.state.required }}\n </span>\n }\n @if (el.state.maxlength !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> maxlength={{ el.state.maxlength }} </span>\n }\n @if (el.state.minlength !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> minlength={{ el.state.minlength }} </span>\n }\n @if (el.state.pattern !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> pattern={{ el.state.pattern }} </span>\n }\n @if (el.state.errorMessage !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-error\"> error: {{ el.state.errorMessage }} </span>\n }\n </td>\n <td class=\"sd-autoid-inspector__value\" [matTooltip]=\"el.state.dataValue || el.text || ''\">\n {{ el.state.dataValue || el.text || '\u2014' }}\n </td>\n </tr>\n } @empty {\n <tr>\n <td colspan=\"6\" class=\"sd-autoid-inspector__empty\">Kh\u00F4ng c\u00F3 k\u1EBFt qu\u1EA3.</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <!-- One section per sd-table group -->\n @for (group of tableGroups(); track group.scope) {\n <h4 class=\"sd-autoid-inspector__section-title\">\n <mat-icon class=\"is-info\">table_chart</mat-icon>\n Inside <code>{{ group.scope }}</code>\n <span class=\"sd-autoid-inspector__badge\">{{ group.items.length }}</span>\n </h4>\n <div class=\"sd-autoid-inspector__table-wrap\">\n <table class=\"sd-autoid-inspector__table\">\n <thead>\n <tr>\n <th class=\"is-num\">STT</th>\n <th>Name</th>\n <th>Autoid</th>\n <th>Tag</th>\n <th>State</th>\n <th>Value</th>\n </tr>\n </thead>\n <tbody>\n @for (el of group.items; track el.autoid + $index) {\n <tr [class.is-error]=\"el.duplicate\">\n <td class=\"is-num\">{{ el.stt }}</td>\n <td>{{ el.name || '\u2014' }}</td>\n <td>\n <div class=\"sd-autoid-inspector__autoid-cell\">\n <code>{{ el.autoid }}</code>\n <button\n type=\"button\"\n class=\"sd-autoid-inspector__icon-btn is-primary\"\n matTooltip=\"Copy xpath\"\n (click)=\"copyXpath(el.autoid)\">\n <mat-icon>content_copy</mat-icon>\n </button>\n </div>\n </td>\n <td>\n <span class=\"sd-autoid-inspector__tag-chip\">{{ el.tag }}</span>\n </td>\n <td class=\"sd-autoid-inspector__state-cell\">\n @if (el.state.disabled !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-error]=\"el.state.disabled === 'true'\">\n disabled={{ el.state.disabled }}\n </span>\n }\n @if (el.state.loading !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-warning]=\"el.state.loading === 'true'\">\n loading={{ el.state.loading }}\n </span>\n }\n @if (el.state.empty !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-info]=\"el.state.empty === 'true'\">\n empty={{ el.state.empty }}\n </span>\n }\n @if (el.state.invalid !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-error]=\"el.state.invalid === 'true'\">\n invalid={{ el.state.invalid }}\n </span>\n }\n @if (el.state.opened !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-info]=\"el.state.opened === 'true'\">\n opened={{ el.state.opened }}\n </span>\n }\n @if (el.state.count !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> count={{ el.state.count }} </span>\n }\n @if (el.state.required !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-warning]=\"el.state.required === 'true'\">\n required={{ el.state.required }}\n </span>\n }\n @if (el.state.maxlength !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> maxlength={{ el.state.maxlength }} </span>\n }\n @if (el.state.minlength !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> minlength={{ el.state.minlength }} </span>\n }\n @if (el.state.pattern !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> pattern={{ el.state.pattern }} </span>\n }\n @if (el.state.errorMessage !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-error\"> error: {{ el.state.errorMessage }} </span>\n }\n </td>\n <td class=\"sd-autoid-inspector__value\" [matTooltip]=\"el.state.dataValue || el.text || ''\">\n {{ el.state.dataValue || el.text || '\u2014' }}\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </ng-template>\n\n <ng-template #exportTpl>\n <div class=\"sd-autoid-inspector__export-grid\">\n <button type=\"button\" matTooltip=\"Copy JSON v\u00E0o clipboard\" (click)=\"copyJson()\">\n <mat-icon>content_copy</mat-icon>\n <span>Copy JSON</span>\n <small>clipboard</small>\n </button>\n <button type=\"button\" matTooltip=\"T\u1EA3i file JSON\" (click)=\"exportJson()\">\n <mat-icon>download</mat-icon>\n <span>JSON</span>\n <small>.json</small>\n </button>\n <button type=\"button\" matTooltip=\"T\u1EA3i file CSV\" (click)=\"exportCsv()\">\n <mat-icon>table_view</mat-icon>\n <span>CSV</span>\n <small>.csv</small>\n </button>\n <button type=\"button\" matTooltip=\"T\u1EA3i MD POM skeleton \u2014 cho AI vi\u1EBFt auto-test\" (click)=\"exportMdPom()\">\n <mat-icon>code</mat-icon>\n <span>MD POM</span>\n <small>AI test</small>\n </button>\n <button type=\"button\" matTooltip=\"T\u1EA3i MD reference table \u2014 group theo tag sd-*\" (click)=\"exportMdTable()\">\n <mat-icon>view_list</mat-icon>\n <span>MD Table</span>\n <small>reference</small>\n </button>\n </div>\n\n @if (e2eGeneratorHost()) {\n @let _e2eExporting = e2eExporting();\n <div class=\"sd-autoid-inspector__export-section\">\n <h4 class=\"sd-autoid-inspector__section-title\">\n <mat-icon class=\"is-info\">auto_awesome</mat-icon>\n T\u1EA1o E2E test\n </h4>\n <div class=\"sd-autoid-inspector__export-grid is-e2e\">\n <button\n type=\"button\"\n matTooltip=\"T\u1EA1o b\u1ED9 test Playwright (.zip)\"\n [disabled]=\"_e2eExporting !== null\"\n (click)=\"exportE2eTest('playwright')\">\n <mat-icon>{{ _e2eExporting === 'playwright' ? 'hourglass_empty' : 'play_arrow' }}</mat-icon>\n <span>Playwright</span>\n <small>.zip</small>\n </button>\n <button\n type=\"button\"\n matTooltip=\"T\u1EA1o b\u1ED9 test Robot Framework (.zip)\"\n [disabled]=\"_e2eExporting !== null\"\n (click)=\"openRobotExportContext()\">\n <mat-icon>{{ _e2eExporting === 'robot' ? 'hourglass_empty' : 'smart_toy' }}</mat-icon>\n <span>Robot Framework</span>\n <small>.zip</small>\n </button>\n </div>\n\n @if (robotContextOpen()) {\n <div class=\"sd-autoid-inspector__context-backdrop\" role=\"presentation\" (click)=\"closeRobotExportContext()\"></div>\n <section\n class=\"sd-autoid-inspector__context-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"sd-autoid-robot-context-title\">\n <header class=\"sd-autoid-inspector__context-header\">\n <div>\n <h4 id=\"sd-autoid-robot-context-title\">Ng\u1EEF c\u1EA3nh QC cho Robot Framework</h4>\n <small>Requirement, test case, test data</small>\n </div>\n <button type=\"button\" class=\"sd-autoid-inspector__icon-btn\" matTooltip=\"\u0110\u00F3ng\" (click)=\"closeRobotExportContext()\">\n <mat-icon>close</mat-icon>\n </button>\n </header>\n\n <div class=\"sd-autoid-inspector__context-form\">\n <label>\n <span>Component</span>\n <input\n type=\"text\"\n placeholder=\"SP-Wallet\"\n [ngModel]=\"robotExportContext().component || ''\"\n (ngModelChange)=\"updateRobotExportContext('component', $event)\" />\n </label>\n <label>\n <span>Story / Trace</span>\n <input\n type=\"text\"\n placeholder=\"US-001, AC-01\"\n [ngModel]=\"robotExportContext().storyLinkages || ''\"\n (ngModelChange)=\"updateRobotExportContext('storyLinkages', $event)\" />\n </label>\n <label class=\"is-full\">\n <span>Requirement / AC / BR</span>\n <textarea\n rows=\"4\"\n placeholder=\"D\u00E1n requirement ho\u1EB7c acceptance criteria...\"\n [ngModel]=\"robotExportContext().requirement || ''\"\n (ngModelChange)=\"updateRobotExportContext('requirement', $event)\"></textarea>\n </label>\n <label class=\"is-full\">\n <span>Test case QC \u0111\u00E3 \u0111\u1ECBnh ngh\u0129a</span>\n <textarea\n rows=\"4\"\n placeholder=\"TC-001: ...\"\n [ngModel]=\"robotExportContext().testCases || ''\"\n (ngModelChange)=\"updateRobotExportContext('testCases', $event)\"></textarea>\n </label>\n <label class=\"is-full\">\n <span>Test data</span>\n <textarea\n rows=\"3\"\n placeholder=\"wallet=V\u00ED A, amount=100000...\"\n [ngModel]=\"robotExportContext().testData || ''\"\n (ngModelChange)=\"updateRobotExportContext('testData', $event)\"></textarea>\n </label>\n <label class=\"is-full\">\n <span>Precondition</span>\n <textarea\n rows=\"2\"\n placeholder=\"User \u0111\u00E3 \u0111\u0103ng nh\u1EADp, d\u1EEF li\u1EC7u v\u00ED \u0111\u00E3 t\u1ED3n t\u1EA1i...\"\n [ngModel]=\"robotExportContext().precondition || ''\"\n (ngModelChange)=\"updateRobotExportContext('precondition', $event)\"></textarea>\n </label>\n <label class=\"is-full\">\n <span>Ghi ch\u00FA format QC</span>\n <textarea\n rows=\"3\"\n placeholder=\"Smoke=Y, Auto=Y, \u01B0u ti\u00EAn happy path...\"\n [ngModel]=\"robotExportContext().qcNotes || ''\"\n (ngModelChange)=\"updateRobotExportContext('qcNotes', $event)\"></textarea>\n </label>\n </div>\n\n <footer class=\"sd-autoid-inspector__context-actions\">\n <button type=\"button\" class=\"is-secondary\" [disabled]=\"_e2eExporting !== null\" (click)=\"closeRobotExportContext()\">\n Hu\u1EF7\n </button>\n <button\n type=\"button\"\n class=\"is-primary\"\n [disabled]=\"_e2eExporting !== null || !hasRobotExportContext()\"\n (click)=\"confirmRobotExport()\">\n <mat-icon>{{ _e2eExporting === 'robot' ? 'hourglass_empty' : 'download' }}</mat-icon>\n <span>\u0110\u1ED3ng \u00FD xu\u1EA5t</span>\n </button>\n </footer>\n </section>\n }\n\n @if (e2eExportOk(); as targetLabel) {\n <div class=\"sd-autoid-inspector__export-message is-success\">\u0110\u00E3 t\u1EA3i b\u1ED9 test {{ targetLabel }}.</div>\n }\n @if (e2eExportError(); as error) {\n <div class=\"sd-autoid-inspector__export-message is-error\">\n {{ error }}\n </div>\n }\n </div>\n }\n </ng-template>\n}\n", styles: [":host{position:fixed;inset:auto 0 0 auto;z-index:100000;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;font-size:13px;color:var(--sd-black500, #212121)}.sd-autoid-inspector__fab-wrap{position:fixed;right:16px;bottom:16px;z-index:100000}.sd-autoid-inspector__fab-wrap:hover .sd-autoid-inspector__fab-dismiss{opacity:1;transform:scale(1);pointer-events:auto}.sd-autoid-inspector__fab{width:36px;height:36px;border-radius:50%;border:none;background:var(--sd-primary, #2a66f4);color:#fff;cursor:pointer;box-shadow:0 3px 10px #00000038;display:inline-flex;align-items:center;justify-content:center;transition:transform .15s ease,background .15s ease,box-shadow .15s ease}.sd-autoid-inspector__fab:hover{background:var(--sd-primary-dark, #1c4ad9);transform:scale(1.08);box-shadow:0 5px 14px #00000047}.sd-autoid-inspector__fab mat-icon{font-size:18px;width:18px;height:18px}.sd-autoid-inspector__fab-dismiss{position:absolute;top:-6px;right:-6px;width:18px;height:18px;border-radius:50%;border:1px solid var(--sd-black200, #e6e6e6);background:#fff;color:var(--sd-black400, #757575);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;padding:0;opacity:0;transform:scale(.7);pointer-events:none;transition:opacity .12s ease,transform .12s ease,color .12s ease,background .12s ease;box-shadow:0 1px 4px #00000026}.sd-autoid-inspector__fab-dismiss:hover{background:var(--sd-error-light, #fed5d0);color:var(--sd-error-dark, #ba200e);border-color:var(--sd-error, #f82c13)}.sd-autoid-inspector__fab-dismiss mat-icon{font-size:12px;width:12px;height:12px;line-height:12px}.sd-autoid-inspector__backdrop{position:fixed;inset:0;padding:0;border:none;background:#00000052;z-index:100001;backdrop-filter:blur(2px)}.sd-autoid-inspector__panel{position:fixed;right:20px;bottom:80px;width:min(860px,100vw - 40px);max-height:calc(100vh - 110px);background:#fff;border-radius:12px;box-shadow:0 16px 48px #00000038;display:flex;flex-direction:column;z-index:100002;overflow:hidden;border:1px solid var(--sd-black200, #e6e6e6)}.sd-autoid-inspector__header{display:flex;align-items:center;gap:8px;padding:8px 10px;border-bottom:1px solid var(--sd-black200, #e6e6e6);background:var(--sd-black100, #f2f2f2);flex-shrink:0}.sd-autoid-inspector__segments{display:inline-flex;border:1px solid var(--sd-black200, #e6e6e6);border-radius:8px;background:#fff;overflow:hidden}.sd-autoid-inspector__segments button{border:none;background:transparent;padding:5px 12px;cursor:pointer;font-size:12px;font-weight:500;color:var(--sd-black400, #757575);display:inline-flex;align-items:center;gap:6px;border-right:1px solid var(--sd-black200, #e6e6e6);transition:background .12s ease,color .12s ease}.sd-autoid-inspector__segments button:last-child{border-right:none}.sd-autoid-inspector__segments button:hover:not(.is-active){background:var(--sd-black100, #f2f2f2);color:var(--sd-black500, #212121)}.sd-autoid-inspector__segments button.is-active{background:var(--sd-primary, #2a66f4);color:#fff}.sd-autoid-inspector__badge{background:var(--sd-black200, #e6e6e6);color:var(--sd-black500, #212121);border-radius:10px;padding:0 7px;font-size:11px;font-weight:600;line-height:16px}.is-active .sd-autoid-inspector__badge{background:#ffffff40;color:#fff}.sd-autoid-inspector__badge.is-warn{background:var(--sd-error, #f82c13);color:#fff}.sd-autoid-inspector__actions{display:inline-flex;gap:4px;margin-left:auto}.sd-autoid-inspector__icon-btn{border:none;background:transparent;cursor:pointer;padding:4px;border-radius:6px;color:var(--sd-black400, #757575);display:inline-flex;align-items:center;justify-content:center;transition:background .12s ease,color .12s ease}.sd-autoid-inspector__icon-btn:hover{background:var(--sd-black100, #f2f2f2);color:var(--sd-black500, #212121)}.sd-autoid-inspector__icon-btn.is-primary{color:var(--sd-primary, #2a66f4)}.sd-autoid-inspector__icon-btn.is-primary:hover{background:var(--sd-primary-light, #eaf1ff);color:var(--sd-primary-dark, #1c4ad9)}.sd-autoid-inspector__icon-btn mat-icon{font-size:18px;width:18px;height:18px}.sd-autoid-inspector__close-btn{border:none;cursor:pointer;padding:4px;border-radius:50%;width:28px;height:28px;background:transparent;color:var(--sd-black400, #757575);display:inline-flex;align-items:center;justify-content:center;transition:background .12s ease,color .12s ease}.sd-autoid-inspector__close-btn:hover{background:var(--sd-error-light, #fed5d0);color:var(--sd-error-dark, #ba200e)}.sd-autoid-inspector__close-btn mat-icon{font-size:18px;width:18px;height:18px}.sd-autoid-inspector__body{padding:14px;overflow:auto;flex:1;background:#fff}.sd-autoid-inspector__toast{position:absolute;top:56px;right:14px;background:var(--sd-success, #4caf50);color:#fff;padding:6px 12px;border-radius:6px;font-size:12px;z-index:2;box-shadow:0 4px 12px #0000002e}.sd-autoid-inspector__audit-toolbar{display:flex;justify-content:flex-end;margin-bottom:12px}.sd-autoid-inspector__toggle{display:inline-flex;align-items:center;gap:8px;border:1px solid var(--sd-black200, #e6e6e6);background:#fff;border-radius:999px;padding:5px 12px 5px 10px;cursor:pointer;font-size:12px;font-weight:500;color:var(--sd-black400, #757575);transition:all .15s ease}.sd-autoid-inspector__toggle:hover{border-color:var(--sd-primary, #2a66f4);color:var(--sd-primary, #2a66f4)}.sd-autoid-inspector__toggle.is-on{border-color:var(--sd-primary, #2a66f4);color:var(--sd-primary, #2a66f4);background:var(--sd-primary-light, #eaf1ff)}.sd-autoid-inspector__toggle mat-icon{font-size:16px;width:16px;height:16px}.sd-autoid-inspector__toggle-state{font-size:10px;font-weight:700;padding:1px 7px;border-radius:999px;background:var(--sd-black200, #e6e6e6);color:var(--sd-black400, #757575)}.is-on .sd-autoid-inspector__toggle-state{background:var(--sd-primary, #2a66f4);color:#fff}.sd-autoid-inspector__summary{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:16px}.sd-autoid-inspector__stat{background:var(--sd-black100, #f2f2f2);border-radius:10px;padding:12px;text-align:center;border:1px solid transparent}.sd-autoid-inspector__stat.is-info{background:var(--sd-info-light, #e7e9ff);border-color:var(--sd-info, #2962ff)}.sd-autoid-inspector__stat.is-info .sd-autoid-inspector__stat-value{color:var(--sd-info, #2962ff)}.sd-autoid-inspector__stat.is-success{background:var(--sd-success-light, #dbefdc);border-color:var(--sd-success, #4caf50)}.sd-autoid-inspector__stat.is-success .sd-autoid-inspector__stat-value{color:var(--sd-success-dark, #39833c)}.sd-autoid-inspector__stat.is-warning{background:var(--sd-warning-light, #ffeacc);border-color:var(--sd-warning, #ff9600)}.sd-autoid-inspector__stat.is-warning .sd-autoid-inspector__stat-value{color:var(--sd-warning-dark, #bf7000)}.sd-autoid-inspector__stat.is-error{background:var(--sd-error-light, #fed5d0);border-color:var(--sd-error, #f82c13)}.sd-autoid-inspector__stat.is-error .sd-autoid-inspector__stat-value{color:var(--sd-error-dark, #ba200e)}.sd-autoid-inspector__stat-value{font-size:22px;font-weight:700;line-height:1;margin-bottom:4px}.sd-autoid-inspector__stat-label{font-size:11px;color:var(--sd-black400, #757575);text-transform:uppercase;letter-spacing:.4px;font-weight:600}.sd-autoid-inspector__section-title{display:flex;align-items:center;gap:6px;margin:14px 0 8px;font-size:13px;font-weight:600;color:var(--sd-black500, #212121)}.sd-autoid-inspector__section-title mat-icon{font-size:18px;width:18px;height:18px}.sd-autoid-inspector__section-title mat-icon.is-error{color:var(--sd-error, #f82c13)}.sd-autoid-inspector__section-title mat-icon.is-warning{color:var(--sd-warning, #ff9600)}.sd-autoid-inspector__section-title mat-icon.is-info{color:var(--sd-info, #2962ff)}.sd-autoid-inspector__success-card{display:flex;align-items:center;gap:12px;padding:14px 16px;background:var(--sd-success-light, #dbefdc);border:1px solid var(--sd-success, #4caf50);border-radius:10px;color:var(--sd-success-dark, #39833c)}.sd-autoid-inspector__success-card mat-icon{font-size:28px;width:28px;height:28px;color:var(--sd-success, #4caf50)}.sd-autoid-inspector__success-card strong{display:block;font-size:13px;margin-bottom:2px}.sd-autoid-inspector__success-card small{font-size:11px;opacity:.85}.sd-autoid-inspector__table-wrap{border:1px solid var(--sd-black200, #e6e6e6);border-radius:8px;overflow:auto;max-height:60vh;background:#fff}.sd-autoid-inspector__table{width:100%;border-collapse:separate;border-spacing:0;font-size:12px}.sd-autoid-inspector__table th,.sd-autoid-inspector__table td{padding:8px 10px;text-align:left;vertical-align:middle;border-bottom:1px solid var(--sd-black200, #e6e6e6)}.sd-autoid-inspector__table thead th{position:sticky;top:0;z-index:1;background:var(--sd-black100, #f2f2f2);font-weight:600;color:var(--sd-black500, #212121);text-transform:none;font-size:11px;letter-spacing:.3px;border-bottom:none;box-shadow:inset 0 -1px 0 var(--sd-black200, #e6e6e6),0 2px 4px #0000000a;white-space:nowrap}.sd-autoid-inspector__table thead th.is-num{text-align:right;width:50px}.sd-autoid-inspector__table tbody tr{transition:background .1s ease}.sd-autoid-inspector__table tbody tr:hover{background:var(--sd-primary-light, #eaf1ff)}.sd-autoid-inspector__table tbody tr:last-child td{border-bottom:none}.sd-autoid-inspector__table tbody tr.is-error{background:var(--sd-error-light, #fed5d0)}.sd-autoid-inspector__table tbody tr.is-error:hover{background:var(--sd-error-light, #fed5d0);filter:brightness(.98)}.sd-autoid-inspector__table tbody tr.is-warning{background:var(--sd-warning-light, #ffeacc)}.sd-autoid-inspector__table tbody tr.is-warning:hover{background:var(--sd-warning-light, #ffeacc);filter:brightness(.98)}.sd-autoid-inspector__table td.is-num{text-align:right;color:var(--sd-black400, #757575);font-variant-numeric:tabular-nums}.sd-autoid-inspector__table code{font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:11px;background:var(--sd-black100, #f2f2f2);padding:2px 6px;border-radius:4px;color:var(--sd-primary-dark, #1c4ad9);border:1px solid var(--sd-black200, #e6e6e6)}.sd-autoid-inspector__autoid-cell{display:inline-flex;align-items:center;gap:6px}.sd-autoid-inspector__tag-chip{display:inline-block;font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:11px;background:var(--sd-info-light, #e7e9ff);color:var(--sd-info, #2962ff);border:1px solid var(--sd-info, #2962ff);padding:1px 8px;border-radius:999px;font-weight:500}.sd-autoid-inspector__value{max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--sd-black400, #757575);font-style:italic}.sd-autoid-inspector__state-cell{display:flex;flex-wrap:wrap;gap:4px;align-items:center;min-width:0}.sd-autoid-inspector__state-chip{display:inline-block;font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:10px;background:var(--sd-black100, #f2f2f2);color:var(--sd-black400, #757575);border:1px solid var(--sd-black200, #e6e6e6);padding:1px 6px;border-radius:999px;font-weight:500;white-space:nowrap}.sd-autoid-inspector__state-chip.is-error{background:var(--sd-error-light, #fed5d0);color:var(--sd-error-dark, #ba200e);border-color:var(--sd-error, #f82c13)}.sd-autoid-inspector__state-chip.is-warning{background:var(--sd-warning-light, #ffeacc);color:var(--sd-warning-dark, #bf7000);border-color:var(--sd-warning, #ff9600)}.sd-autoid-inspector__state-chip.is-info{background:var(--sd-info-light, #e7e9ff);color:var(--sd-info, #2962ff);border-color:var(--sd-info, #2962ff)}.sd-autoid-inspector__empty{text-align:center;color:var(--sd-black400, #757575);padding:24px;font-style:italic}.sd-autoid-inspector__filter{display:flex;align-items:center;gap:6px;background:var(--sd-black100, #f2f2f2);border:1px solid var(--sd-black200, #e6e6e6);border-radius:8px;padding:6px 10px;margin-bottom:12px;transition:border-color .15s ease}.sd-autoid-inspector__filter:focus-within{border-color:var(--sd-primary, #2a66f4);background:#fff}.sd-autoid-inspector__filter input{flex:1;border:none;background:transparent;outline:none;font-size:13px;color:var(--sd-black500, #212121)}.sd-autoid-inspector__filter input::placeholder{color:var(--sd-black300, #bfbfbf)}.sd-autoid-inspector__filter>mat-icon{font-size:18px;width:18px;height:18px;color:var(--sd-black400, #757575)}.sd-autoid-inspector__export-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(112px,1fr));gap:8px}.sd-autoid-inspector__export-grid.is-e2e{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}.sd-autoid-inspector__export-grid button{border:1px solid var(--sd-black200, #e6e6e6);background:#fff;border-radius:10px;padding:12px 10px;cursor:pointer;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;min-width:0;transition:border-color .15s ease,background .15s ease,transform .12s ease;text-align:center}.sd-autoid-inspector__export-grid button:hover{border-color:var(--sd-primary, #2a66f4);background:var(--sd-primary-light, #eaf1ff);transform:translateY(-1px)}.sd-autoid-inspector__export-grid button:disabled{cursor:wait;opacity:.68;transform:none}.sd-autoid-inspector__export-grid button span{font-size:12px;font-weight:600;color:var(--sd-black500, #212121);line-height:1.25}.sd-autoid-inspector__export-grid button small{font-size:10px;color:var(--sd-black400, #757575);line-height:1.2}.sd-autoid-inspector__export-grid button mat-icon{font-size:22px;width:22px;height:22px;color:var(--sd-primary, #2a66f4)}.sd-autoid-inspector__export-section{margin-top:14px;padding-top:12px;border-top:1px solid var(--sd-black200, #e6e6e6)}.sd-autoid-inspector__context-backdrop{position:fixed;inset:0;z-index:10001;background:#141c2652}.sd-autoid-inspector__context-dialog{position:fixed;z-index:10002;top:50%;left:50%;width:min(720px,100vw - 28px);max-height:min(720px,100vh - 28px);transform:translate(-50%,-50%);display:flex;flex-direction:column;border:1px solid var(--sd-black200, #e6e6e6);border-radius:8px;background:#fff;box-shadow:0 18px 48px #0f172a38;overflow:hidden}.sd-autoid-inspector__context-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:14px 16px;border-bottom:1px solid var(--sd-black200, #e6e6e6)}.sd-autoid-inspector__context-header h4{margin:0;font-size:15px;font-weight:700;line-height:1.3;color:var(--sd-black500, #212121)}.sd-autoid-inspector__context-header small{display:block;margin-top:2px;font-size:11px;color:var(--sd-black400, #757575);line-height:1.3}.sd-autoid-inspector__context-form{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;padding:14px 16px;overflow:auto}.sd-autoid-inspector__context-form label{display:flex;flex-direction:column;gap:6px;min-width:0}.sd-autoid-inspector__context-form label.is-full{grid-column:1/-1}.sd-autoid-inspector__context-form span{font-size:12px;font-weight:600;color:var(--sd-black500, #212121);line-height:1.25}.sd-autoid-inspector__context-form input,.sd-autoid-inspector__context-form textarea{width:100%;min-width:0;border:1px solid var(--sd-black200, #e6e6e6);border-radius:6px;padding:8px 10px;color:var(--sd-black500, #212121);font:inherit;font-size:12px;line-height:1.4;outline:none;resize:vertical}.sd-autoid-inspector__context-form input:focus,.sd-autoid-inspector__context-form textarea:focus{border-color:var(--sd-primary, #2a66f4);box-shadow:0 0 0 2px #2a66f424}.sd-autoid-inspector__context-actions{display:flex;justify-content:flex-end;gap:8px;padding:12px 16px;border-top:1px solid var(--sd-black200, #e6e6e6)}.sd-autoid-inspector__context-actions button{min-height:34px;border-radius:6px;padding:0 12px;border:1px solid var(--sd-black200, #e6e6e6);background:#fff;color:var(--sd-black500, #212121);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:6px;font-size:12px;font-weight:600}.sd-autoid-inspector__context-actions button.is-primary{border-color:var(--sd-primary, #2a66f4);background:var(--sd-primary, #2a66f4);color:#fff}.sd-autoid-inspector__context-actions button:disabled{cursor:not-allowed;opacity:.62}.sd-autoid-inspector__context-actions button mat-icon{font-size:17px;width:17px;height:17px}.sd-autoid-inspector__export-message{margin-top:10px;border-radius:8px;padding:8px 10px;font-size:12px;line-height:1.35}.sd-autoid-inspector__export-message.is-success{background:var(--sd-success-light, #dbefdc);color:var(--sd-success-dark, #39833c);border:1px solid var(--sd-success, #4caf50)}.sd-autoid-inspector__export-message.is-error{background:var(--sd-error-light, #fed5d0);color:var(--sd-error-dark, #ba200e);border:1px solid var(--sd-error, #f82c13)}@media(max-width:560px){.sd-autoid-inspector__context-form{grid-template-columns:1fr}.sd-autoid-inspector__context-actions{flex-direction:column-reverse}.sd-autoid-inspector__context-actions button{width:100%}}\n"] }]
968
+ args: [{ selector: 'sd-autoid-inspector', changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [SdIcon, CommonModule, FormsModule, MatTooltipModule], template: "@let _enabled = enabled();\n@let _open = open();\n@if (_enabled && !dismissed()) {\n <div class=\"sd-autoid-inspector__fab-wrap\">\n <button\n type=\"button\"\n class=\"sd-autoid-inspector__fab\"\n data-autoid=\"sd-autoid-inspector-fab\"\n [attr.data-opened]=\"dataOpened()\"\n [matTooltip]=\"_open ? '\u0110\u00F3ng AutoId Inspector' : 'M\u1EDF AutoId Inspector'\"\n matTooltipPosition=\"left\"\n (click)=\"togglePanel()\">\n <sd-icon [name]=\"_open ? 'close' : 'tag'\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-autoid-inspector__fab-dismiss\"\n matTooltip=\"\u1EA8n t\u1EA1m \u2014 reload trang \u0111\u1EC3 hi\u1EC7n l\u1EA1i\"\n matTooltipPosition=\"left\"\n aria-label=\"\u1EA8n t\u1EA1m FAB\"\n (click)=\"dismissFab($event)\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n </div>\n}\n\n@if (_enabled && _open) {\n @let _segment = segment();\n <button type=\"button\" class=\"sd-autoid-inspector__backdrop\" aria-label=\"\u0110\u00F3ng AutoId Inspector\" (click)=\"closePanel()\"></button>\n <section\n class=\"sd-autoid-inspector__panel\"\n role=\"dialog\"\n aria-label=\"AutoId Inspector\"\n data-autoid=\"sd-autoid-inspector-panel\"\n [attr.data-segment]=\"dataSegment()\"\n [attr.data-highlight-on]=\"dataHighlightOn()\"\n [attr.data-element-count]=\"dataElementCount()\"\n [attr.data-missing-count]=\"dataMissingCount()\"\n [attr.data-duplicate-count]=\"dataDuplicateCount()\">\n <header class=\"sd-autoid-inspector__header\">\n <div class=\"sd-autoid-inspector__segments\">\n <button type=\"button\" [class.is-active]=\"_segment === 'audit'\" (click)=\"setSegment('audit')\">\n Audit\n @if (audit(); as a) {\n <span class=\"sd-autoid-inspector__badge\" [class.is-warn]=\"a.missingCount + a.duplicateCount > 0\">\n {{ a.missingCount + a.duplicateCount }}\n </span>\n }\n </button>\n <button type=\"button\" [class.is-active]=\"_segment === 'elements'\" (click)=\"setSegment('elements')\">\n Elements\n <span class=\"sd-autoid-inspector__badge\">{{ elements().length }}</span>\n </button>\n <button type=\"button\" [class.is-active]=\"_segment === 'export'\" (click)=\"setSegment('export')\">Export</button>\n </div>\n <div class=\"sd-autoid-inspector__actions\">\n <button type=\"button\" class=\"sd-autoid-inspector__icon-btn\" matTooltip=\"Qu\u00E9t l\u1EA1i\" (click)=\"refresh()\">\n <sd-icon name=\"refresh\"></sd-icon>\n </button>\n <button type=\"button\" class=\"sd-autoid-inspector__close-btn\" matTooltip=\"\u0110\u00F3ng\" aria-label=\"\u0110\u00F3ng panel\" (click)=\"closePanel()\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n </div>\n </header>\n\n @if (copyOk()) {\n <div class=\"sd-autoid-inspector__toast\">\u0110\u00E3 copy \u2713</div>\n }\n\n <div class=\"sd-autoid-inspector__body\">\n @switch (_segment) {\n @case ('audit') {\n <ng-container *ngTemplateOutlet=\"auditTpl\"></ng-container>\n }\n @case ('elements') {\n <ng-container *ngTemplateOutlet=\"elementsTpl\"></ng-container>\n }\n @case ('export') {\n <ng-container *ngTemplateOutlet=\"exportTpl\"></ng-container>\n }\n }\n </div>\n </section>\n\n <ng-template #auditTpl>\n @if (audit(); as a) {\n @let _highlightOn = highlightOn();\n <div class=\"sd-autoid-inspector__audit-toolbar\">\n <button type=\"button\" class=\"sd-autoid-inspector__toggle\" [class.is-on]=\"_highlightOn\" (click)=\"toggleHighlight()\">\n <sd-icon [name]=\"_highlightOn ? 'visibility' : 'visibility_off'\"></sd-icon>\n <span>Highlight DOM</span>\n <span class=\"sd-autoid-inspector__toggle-state\">{{ _highlightOn ? 'ON' : 'OFF' }}</span>\n </button>\n </div>\n <div class=\"sd-autoid-inspector__summary\">\n <div class=\"sd-autoid-inspector__stat is-info\">\n <div class=\"sd-autoid-inspector__stat-value\">{{ a.total }}</div>\n <div class=\"sd-autoid-inspector__stat-label\">T\u1ED5ng autoid</div>\n </div>\n <div class=\"sd-autoid-inspector__stat\" [class.is-error]=\"a.duplicateCount > 0\" [class.is-success]=\"a.duplicateCount === 0\">\n <div class=\"sd-autoid-inspector__stat-value\">{{ a.duplicateCount }}</div>\n <div class=\"sd-autoid-inspector__stat-label\">Tr\u00F9ng</div>\n </div>\n <div class=\"sd-autoid-inspector__stat\" [class.is-warning]=\"a.missingCount > 0\" [class.is-success]=\"a.missingCount === 0\">\n <div class=\"sd-autoid-inspector__stat-value\">{{ a.missingCount }}</div>\n <div class=\"sd-autoid-inspector__stat-label\">Thi\u1EBFu</div>\n </div>\n </div>\n\n @if (a.duplicateCount > 0) {\n <h4 class=\"sd-autoid-inspector__section-title\">\n <sd-icon class=\"is-error\" name=\"error\"></sd-icon>\n Autoid b\u1ECB tr\u00F9ng\n </h4>\n <div class=\"sd-autoid-inspector__table-wrap\">\n <table class=\"sd-autoid-inspector__table\">\n <thead>\n <tr>\n <th>Autoid</th>\n <th>S\u1ED1 l\u1EA7n</th>\n </tr>\n </thead>\n <tbody>\n @for (entry of a.duplicates | keyvalue; track entry.key) {\n <tr class=\"is-error\">\n <td>\n <code>{{ entry.key }}</code>\n </td>\n <td>{{ entry.value }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n\n @if (a.missingCount > 0) {\n <h4 class=\"sd-autoid-inspector__section-title\">\n <sd-icon class=\"is-warning\" name=\"warning_amber\"></sd-icon>\n Element thi\u1EBFu autoid\n </h4>\n <div class=\"sd-autoid-inspector__table-wrap\">\n <table class=\"sd-autoid-inspector__table\">\n <thead>\n <tr>\n <th>Tag</th>\n <th>Name hint</th>\n <th>Selector</th>\n </tr>\n </thead>\n <tbody>\n @for (m of a.missing; track $index) {\n <tr class=\"is-warning\">\n <td>\n <code>{{ m.tag }}</code>\n </td>\n <td>{{ m.nameHint }}</td>\n <td>\n <code>{{ m.selector }}</code>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n\n @if (a.duplicateCount === 0 && a.missingCount === 0) {\n <div class=\"sd-autoid-inspector__success-card\">\n <sd-icon name=\"check_circle\"></sd-icon>\n <div>\n <strong>Kh\u00F4ng c\u00F3 element thi\u1EBFu ho\u1EB7c tr\u00F9ng autoid.</strong>\n <small>Trang \u0111\u00E3 \u0111\u1EA1t chu\u1EA9n convention autoid.</small>\n </div>\n </div>\n }\n }\n </ng-template>\n\n <ng-template #elementsTpl>\n @let _filter = filter();\n <div class=\"sd-autoid-inspector__filter\">\n <sd-icon name=\"search\"></sd-icon>\n <input type=\"text\" placeholder=\"L\u1ECDc theo autoid / name / tag\" [ngModel]=\"_filter\" (ngModelChange)=\"filter.set($event)\" />\n @if (_filter) {\n <button type=\"button\" class=\"sd-autoid-inspector__icon-btn\" matTooltip=\"Xo\u00E1 filter\" (click)=\"filter.set('')\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n }\n </div>\n\n <!-- Top-level elements (not inside any sd-table) -->\n <h4 class=\"sd-autoid-inspector__section-title\">\n Top-level elements\n <span class=\"sd-autoid-inspector__badge\">{{ topLevelElements().length }}</span>\n </h4>\n <div class=\"sd-autoid-inspector__table-wrap\">\n <table class=\"sd-autoid-inspector__table\">\n <thead>\n <tr>\n <th class=\"is-num\">STT</th>\n <th>Name</th>\n <th>Autoid</th>\n <th>Tag</th>\n <th>State</th>\n <th>Value</th>\n </tr>\n </thead>\n <tbody>\n @for (el of topLevelElements(); track el.autoid + $index) {\n <tr [class.is-error]=\"el.duplicate\">\n <td class=\"is-num\">{{ el.stt }}</td>\n <td>{{ el.name || '\u2014' }}</td>\n <td>\n <div class=\"sd-autoid-inspector__autoid-cell\">\n <code>{{ el.autoid }}</code>\n <button\n type=\"button\"\n class=\"sd-autoid-inspector__icon-btn is-primary\"\n matTooltip=\"Copy xpath\"\n (click)=\"copyXpath(el.autoid)\">\n <sd-icon name=\"content_copy\"></sd-icon>\n </button>\n </div>\n </td>\n <td>\n <span class=\"sd-autoid-inspector__tag-chip\">{{ el.tag }}</span>\n </td>\n <td class=\"sd-autoid-inspector__state-cell\">\n @if (el.state.disabled !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-error]=\"el.state.disabled === 'true'\">\n disabled={{ el.state.disabled }}\n </span>\n }\n @if (el.state.loading !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-warning]=\"el.state.loading === 'true'\">\n loading={{ el.state.loading }}\n </span>\n }\n @if (el.state.empty !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-info]=\"el.state.empty === 'true'\">\n empty={{ el.state.empty }}\n </span>\n }\n @if (el.state.invalid !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-error]=\"el.state.invalid === 'true'\">\n invalid={{ el.state.invalid }}\n </span>\n }\n @if (el.state.opened !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-info]=\"el.state.opened === 'true'\">\n opened={{ el.state.opened }}\n </span>\n }\n @if (el.state.count !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> count={{ el.state.count }} </span>\n }\n @if (el.state.required !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-warning]=\"el.state.required === 'true'\">\n required={{ el.state.required }}\n </span>\n }\n @if (el.state.maxlength !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> maxlength={{ el.state.maxlength }} </span>\n }\n @if (el.state.minlength !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> minlength={{ el.state.minlength }} </span>\n }\n @if (el.state.pattern !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> pattern={{ el.state.pattern }} </span>\n }\n @if (el.state.errorMessage !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-error\"> error: {{ el.state.errorMessage }} </span>\n }\n </td>\n <td class=\"sd-autoid-inspector__value\" [matTooltip]=\"el.state.dataValue || el.text || ''\">\n {{ el.state.dataValue || el.text || '\u2014' }}\n </td>\n </tr>\n } @empty {\n <tr>\n <td colspan=\"6\" class=\"sd-autoid-inspector__empty\">Kh\u00F4ng c\u00F3 k\u1EBFt qu\u1EA3.</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <!-- One section per sd-table group -->\n @for (group of tableGroups(); track group.scope) {\n <h4 class=\"sd-autoid-inspector__section-title\">\n <sd-icon class=\"is-info\" name=\"table_chart\"></sd-icon>\n Inside <code>{{ group.scope }}</code>\n <span class=\"sd-autoid-inspector__badge\">{{ group.items.length }}</span>\n </h4>\n <div class=\"sd-autoid-inspector__table-wrap\">\n <table class=\"sd-autoid-inspector__table\">\n <thead>\n <tr>\n <th class=\"is-num\">STT</th>\n <th>Name</th>\n <th>Autoid</th>\n <th>Tag</th>\n <th>State</th>\n <th>Value</th>\n </tr>\n </thead>\n <tbody>\n @for (el of group.items; track el.autoid + $index) {\n <tr [class.is-error]=\"el.duplicate\">\n <td class=\"is-num\">{{ el.stt }}</td>\n <td>{{ el.name || '\u2014' }}</td>\n <td>\n <div class=\"sd-autoid-inspector__autoid-cell\">\n <code>{{ el.autoid }}</code>\n <button\n type=\"button\"\n class=\"sd-autoid-inspector__icon-btn is-primary\"\n matTooltip=\"Copy xpath\"\n (click)=\"copyXpath(el.autoid)\">\n <sd-icon name=\"content_copy\"></sd-icon>\n </button>\n </div>\n </td>\n <td>\n <span class=\"sd-autoid-inspector__tag-chip\">{{ el.tag }}</span>\n </td>\n <td class=\"sd-autoid-inspector__state-cell\">\n @if (el.state.disabled !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-error]=\"el.state.disabled === 'true'\">\n disabled={{ el.state.disabled }}\n </span>\n }\n @if (el.state.loading !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-warning]=\"el.state.loading === 'true'\">\n loading={{ el.state.loading }}\n </span>\n }\n @if (el.state.empty !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-info]=\"el.state.empty === 'true'\">\n empty={{ el.state.empty }}\n </span>\n }\n @if (el.state.invalid !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-error]=\"el.state.invalid === 'true'\">\n invalid={{ el.state.invalid }}\n </span>\n }\n @if (el.state.opened !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-info]=\"el.state.opened === 'true'\">\n opened={{ el.state.opened }}\n </span>\n }\n @if (el.state.count !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> count={{ el.state.count }} </span>\n }\n @if (el.state.required !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip\" [class.is-warning]=\"el.state.required === 'true'\">\n required={{ el.state.required }}\n </span>\n }\n @if (el.state.maxlength !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> maxlength={{ el.state.maxlength }} </span>\n }\n @if (el.state.minlength !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> minlength={{ el.state.minlength }} </span>\n }\n @if (el.state.pattern !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-info\"> pattern={{ el.state.pattern }} </span>\n }\n @if (el.state.errorMessage !== undefined) {\n <span class=\"sd-autoid-inspector__state-chip is-error\"> error: {{ el.state.errorMessage }} </span>\n }\n </td>\n <td class=\"sd-autoid-inspector__value\" [matTooltip]=\"el.state.dataValue || el.text || ''\">\n {{ el.state.dataValue || el.text || '\u2014' }}\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </ng-template>\n\n <ng-template #exportTpl>\n <div class=\"sd-autoid-inspector__export-grid\">\n <button type=\"button\" matTooltip=\"Copy JSON v\u00E0o clipboard\" (click)=\"copyJson()\">\n <sd-icon name=\"content_copy\"></sd-icon>\n <span>Copy JSON</span>\n <small>clipboard</small>\n </button>\n <button type=\"button\" matTooltip=\"T\u1EA3i file JSON\" (click)=\"exportJson()\">\n <sd-icon name=\"download\"></sd-icon>\n <span>JSON</span>\n <small>.json</small>\n </button>\n <button type=\"button\" matTooltip=\"T\u1EA3i file CSV\" (click)=\"exportCsv()\">\n <sd-icon name=\"table_view\"></sd-icon>\n <span>CSV</span>\n <small>.csv</small>\n </button>\n <button type=\"button\" matTooltip=\"T\u1EA3i MD POM skeleton \u2014 cho AI vi\u1EBFt auto-test\" (click)=\"exportMdPom()\">\n <sd-icon name=\"code\"></sd-icon>\n <span>MD POM</span>\n <small>AI test</small>\n </button>\n <button type=\"button\" matTooltip=\"T\u1EA3i MD reference table \u2014 group theo tag sd-*\" (click)=\"exportMdTable()\">\n <sd-icon name=\"view_list\"></sd-icon>\n <span>MD Table</span>\n <small>reference</small>\n </button>\n </div>\n\n @if (e2eGeneratorHost()) {\n @let _e2eExporting = e2eExporting();\n <div class=\"sd-autoid-inspector__export-section\">\n <h4 class=\"sd-autoid-inspector__section-title\">\n <sd-icon class=\"is-info\" name=\"auto_awesome\"></sd-icon>\n T\u1EA1o E2E test\n </h4>\n <div class=\"sd-autoid-inspector__export-grid is-e2e\">\n <button\n type=\"button\"\n matTooltip=\"T\u1EA1o b\u1ED9 test Playwright (.zip)\"\n [disabled]=\"_e2eExporting !== null\"\n (click)=\"exportE2eTest('playwright')\">\n <sd-icon [name]=\"_e2eExporting === 'playwright' ? 'hourglass_empty' : 'play_arrow'\"></sd-icon>\n <span>Playwright</span>\n <small>.zip</small>\n </button>\n <button\n type=\"button\"\n matTooltip=\"T\u1EA1o b\u1ED9 test Robot Framework (.zip)\"\n [disabled]=\"_e2eExporting !== null\"\n (click)=\"openRobotExportContext()\">\n <sd-icon [name]=\"_e2eExporting === 'robot' ? 'hourglass_empty' : 'smart_toy'\"></sd-icon>\n <span>Robot Framework</span>\n <small>.zip</small>\n </button>\n </div>\n\n @if (robotContextOpen()) {\n <div class=\"sd-autoid-inspector__context-backdrop\" role=\"presentation\" (click)=\"closeRobotExportContext()\"></div>\n <section\n class=\"sd-autoid-inspector__context-dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"sd-autoid-robot-context-title\">\n <header class=\"sd-autoid-inspector__context-header\">\n <div>\n <h4 id=\"sd-autoid-robot-context-title\">Ng\u1EEF c\u1EA3nh QC cho Robot Framework</h4>\n <small>Requirement, test case, test data</small>\n </div>\n <button type=\"button\" class=\"sd-autoid-inspector__icon-btn\" matTooltip=\"\u0110\u00F3ng\" (click)=\"closeRobotExportContext()\">\n <sd-icon name=\"close\"></sd-icon>\n </button>\n </header>\n\n <div class=\"sd-autoid-inspector__context-form\">\n <label>\n <span>Component</span>\n <input\n type=\"text\"\n placeholder=\"SP-Wallet\"\n [ngModel]=\"robotExportContext().component || ''\"\n (ngModelChange)=\"updateRobotExportContext('component', $event)\" />\n </label>\n <label>\n <span>Story / Trace</span>\n <input\n type=\"text\"\n placeholder=\"US-001, AC-01\"\n [ngModel]=\"robotExportContext().storyLinkages || ''\"\n (ngModelChange)=\"updateRobotExportContext('storyLinkages', $event)\" />\n </label>\n <label class=\"is-full\">\n <span>Requirement / AC / BR</span>\n <textarea\n rows=\"4\"\n placeholder=\"D\u00E1n requirement ho\u1EB7c acceptance criteria...\"\n [ngModel]=\"robotExportContext().requirement || ''\"\n (ngModelChange)=\"updateRobotExportContext('requirement', $event)\"></textarea>\n </label>\n <label class=\"is-full\">\n <span>Test case QC \u0111\u00E3 \u0111\u1ECBnh ngh\u0129a</span>\n <textarea\n rows=\"4\"\n placeholder=\"TC-001: ...\"\n [ngModel]=\"robotExportContext().testCases || ''\"\n (ngModelChange)=\"updateRobotExportContext('testCases', $event)\"></textarea>\n </label>\n <label class=\"is-full\">\n <span>Test data</span>\n <textarea\n rows=\"3\"\n placeholder=\"wallet=V\u00ED A, amount=100000...\"\n [ngModel]=\"robotExportContext().testData || ''\"\n (ngModelChange)=\"updateRobotExportContext('testData', $event)\"></textarea>\n </label>\n <label class=\"is-full\">\n <span>Precondition</span>\n <textarea\n rows=\"2\"\n placeholder=\"User \u0111\u00E3 \u0111\u0103ng nh\u1EADp, d\u1EEF li\u1EC7u v\u00ED \u0111\u00E3 t\u1ED3n t\u1EA1i...\"\n [ngModel]=\"robotExportContext().precondition || ''\"\n (ngModelChange)=\"updateRobotExportContext('precondition', $event)\"></textarea>\n </label>\n <label class=\"is-full\">\n <span>Ghi ch\u00FA format QC</span>\n <textarea\n rows=\"3\"\n placeholder=\"Smoke=Y, Auto=Y, \u01B0u ti\u00EAn happy path...\"\n [ngModel]=\"robotExportContext().qcNotes || ''\"\n (ngModelChange)=\"updateRobotExportContext('qcNotes', $event)\"></textarea>\n </label>\n </div>\n\n <footer class=\"sd-autoid-inspector__context-actions\">\n <button type=\"button\" class=\"is-secondary\" [disabled]=\"_e2eExporting !== null\" (click)=\"closeRobotExportContext()\">\n Hu\u1EF7\n </button>\n <button\n type=\"button\"\n class=\"is-primary\"\n [disabled]=\"_e2eExporting !== null || !hasRobotExportContext()\"\n (click)=\"confirmRobotExport()\">\n <sd-icon [name]=\"_e2eExporting === 'robot' ? 'hourglass_empty' : 'download'\"></sd-icon>\n <span>\u0110\u1ED3ng \u00FD xu\u1EA5t</span>\n </button>\n </footer>\n </section>\n }\n\n @if (e2eExportOk(); as targetLabel) {\n <div class=\"sd-autoid-inspector__export-message is-success\">\u0110\u00E3 t\u1EA3i b\u1ED9 test {{ targetLabel }}.</div>\n }\n @if (e2eExportError(); as error) {\n <div class=\"sd-autoid-inspector__export-message is-error\">\n {{ error }}\n </div>\n }\n </div>\n }\n </ng-template>\n}\n", styles: [":host{position:fixed;inset:auto 0 0 auto;z-index:100000;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;font-size:13px;color:var(--sd-text, #1a1b1f)}.sd-autoid-inspector__fab-wrap{position:fixed;right:16px;bottom:16px;z-index:100000}.sd-autoid-inspector__fab-wrap:hover .sd-autoid-inspector__fab-dismiss{opacity:1;transform:scale(1);pointer-events:auto}.sd-autoid-inspector__fab{width:36px;height:36px;border-radius:50%;border:none;background:var(--sd-primary, #005cbb);color:#fff;cursor:pointer;box-shadow:0 3px 10px #00000038;display:inline-flex;align-items:center;justify-content:center;transition:transform .15s ease,background .15s ease,box-shadow .15s ease}.sd-autoid-inspector__fab:hover{background:var(--sd-primary-dark, #00458f);transform:scale(1.08);box-shadow:0 5px 14px #00000047}.sd-autoid-inspector__fab sd-icon{font-size:18px;width:18px;height:18px}.sd-autoid-inspector__fab-dismiss{position:absolute;top:-6px;right:-6px;width:18px;height:18px;border-radius:50%;border:1px solid var(--sd-border, #c4c6d0);background:#fff;color:var(--sd-text-secondary, #44474f);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;padding:0;opacity:0;transform:scale(.7);pointer-events:none;transition:opacity .12s ease,transform .12s ease,color .12s ease,background .12s ease;box-shadow:0 1px 4px #00000026}.sd-autoid-inspector__fab-dismiss:hover{background:var(--sd-error-light, #ffdad6);color:var(--sd-error-dark, #93000a);border-color:var(--sd-error, #ba1a1a)}.sd-autoid-inspector__fab-dismiss sd-icon{font-size:12px;width:12px;height:12px;line-height:12px}.sd-autoid-inspector__backdrop{position:fixed;inset:0;padding:0;border:none;background:#00000052;z-index:100001;backdrop-filter:blur(2px)}.sd-autoid-inspector__panel{position:fixed;right:20px;bottom:80px;width:min(860px,100vw - 40px);max-height:calc(100vh - 110px);background:#fff;border-radius:12px;box-shadow:0 16px 48px #00000038;display:flex;flex-direction:column;z-index:100002;overflow:hidden;border:1px solid var(--sd-border, #c4c6d0)}.sd-autoid-inspector__header{display:flex;align-items:center;gap:8px;padding:8px 10px;border-bottom:1px solid var(--sd-border, #c4c6d0);background:var(--sd-surface-muted, #e7e8ed);flex-shrink:0}.sd-autoid-inspector__segments{display:inline-flex;border:1px solid var(--sd-border, #c4c6d0);border-radius:8px;background:#fff;overflow:hidden}.sd-autoid-inspector__segments button{border:none;background:transparent;padding:5px 12px;cursor:pointer;font-size:12px;font-weight:500;color:var(--sd-text-secondary, #44474f);display:inline-flex;align-items:center;gap:6px;border-right:1px solid var(--sd-border, #c4c6d0);transition:background .12s ease,color .12s ease}.sd-autoid-inspector__segments button:last-child{border-right:none}.sd-autoid-inspector__segments button:hover:not(.is-active){background:var(--sd-surface-muted, #e7e8ed);color:var(--sd-text, #1a1b1f)}.sd-autoid-inspector__segments button.is-active{background:var(--sd-primary, #005cbb);color:#fff}.sd-autoid-inspector__badge{background:var(--sd-border, #c4c6d0);color:var(--sd-text, #1a1b1f);border-radius:10px;padding:0 7px;font-size:11px;font-weight:600;line-height:16px}.is-active .sd-autoid-inspector__badge{background:#ffffff40;color:#fff}.sd-autoid-inspector__badge.is-warn{background:var(--sd-error, #ba1a1a);color:#fff}.sd-autoid-inspector__actions{display:inline-flex;gap:4px;margin-left:auto}.sd-autoid-inspector__icon-btn{border:none;background:transparent;cursor:pointer;padding:4px;border-radius:6px;color:var(--sd-text-secondary, #44474f);display:inline-flex;align-items:center;justify-content:center;transition:background .12s ease,color .12s ease}.sd-autoid-inspector__icon-btn:hover{background:var(--sd-surface-muted, #e7e8ed);color:var(--sd-text, #1a1b1f)}.sd-autoid-inspector__icon-btn.is-primary{color:var(--sd-primary, #005cbb)}.sd-autoid-inspector__icon-btn.is-primary:hover{background:var(--sd-primary-light, #d7e3ff);color:var(--sd-primary-dark, #00458f)}.sd-autoid-inspector__icon-btn sd-icon{font-size:18px;width:18px;height:18px}.sd-autoid-inspector__close-btn{border:none;cursor:pointer;padding:4px;border-radius:50%;width:28px;height:28px;background:transparent;color:var(--sd-text-secondary, #44474f);display:inline-flex;align-items:center;justify-content:center;transition:background .12s ease,color .12s ease}.sd-autoid-inspector__close-btn:hover{background:var(--sd-error-light, #ffdad6);color:var(--sd-error-dark, #93000a)}.sd-autoid-inspector__close-btn sd-icon{font-size:18px;width:18px;height:18px}.sd-autoid-inspector__body{padding:14px;overflow:auto;flex:1;background:#fff}.sd-autoid-inspector__toast{position:absolute;top:56px;right:14px;background:var(--sd-success, #2e7d32);color:#fff;padding:6px 12px;border-radius:6px;font-size:12px;z-index:2;box-shadow:0 4px 12px #0000002e}.sd-autoid-inspector__audit-toolbar{display:flex;justify-content:flex-end;margin-bottom:12px}.sd-autoid-inspector__toggle{display:inline-flex;align-items:center;gap:8px;border:1px solid var(--sd-border, #c4c6d0);background:#fff;border-radius:999px;padding:5px 12px 5px 10px;cursor:pointer;font-size:12px;font-weight:500;color:var(--sd-text-secondary, #44474f);transition:all .15s ease}.sd-autoid-inspector__toggle:hover{border-color:var(--sd-primary, #005cbb);color:var(--sd-primary, #005cbb)}.sd-autoid-inspector__toggle.is-on{border-color:var(--sd-primary, #005cbb);color:var(--sd-primary, #005cbb);background:var(--sd-primary-light, #d7e3ff)}.sd-autoid-inspector__toggle sd-icon{font-size:16px;width:16px;height:16px}.sd-autoid-inspector__toggle-state{font-size:10px;font-weight:700;padding:1px 7px;border-radius:999px;background:var(--sd-border, #c4c6d0);color:var(--sd-text-secondary, #44474f)}.is-on .sd-autoid-inspector__toggle-state{background:var(--sd-primary, #005cbb);color:#fff}.sd-autoid-inspector__summary{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:16px}.sd-autoid-inspector__stat{background:var(--sd-surface-muted, #e7e8ed);border-radius:10px;padding:12px;text-align:center;border:1px solid transparent}.sd-autoid-inspector__stat.is-info{background:var(--sd-info-light, #d4f7f6);border-color:var(--sd-info, #006a6a)}.sd-autoid-inspector__stat.is-info .sd-autoid-inspector__stat-value{color:var(--sd-info, #006a6a)}.sd-autoid-inspector__stat.is-success{background:var(--sd-success-light, #d8f0d3);border-color:var(--sd-success, #2e7d32)}.sd-autoid-inspector__stat.is-success .sd-autoid-inspector__stat-value{color:var(--sd-success-dark, #1b5e20)}.sd-autoid-inspector__stat.is-warning{background:var(--sd-warning-light, #ffddb0);border-color:var(--sd-warning, #a66300)}.sd-autoid-inspector__stat.is-warning .sd-autoid-inspector__stat-value{color:var(--sd-warning-dark, #7a4d00)}.sd-autoid-inspector__stat.is-error{background:var(--sd-error-light, #ffdad6);border-color:var(--sd-error, #ba1a1a)}.sd-autoid-inspector__stat.is-error .sd-autoid-inspector__stat-value{color:var(--sd-error-dark, #93000a)}.sd-autoid-inspector__stat-value{font-size:22px;font-weight:700;line-height:1;margin-bottom:4px}.sd-autoid-inspector__stat-label{font-size:11px;color:var(--sd-text-secondary, #44474f);text-transform:uppercase;letter-spacing:.4px;font-weight:600}.sd-autoid-inspector__section-title{display:flex;align-items:center;gap:6px;margin:14px 0 8px;font-size:13px;font-weight:600;color:var(--sd-text, #1a1b1f)}.sd-autoid-inspector__section-title sd-icon{font-size:18px;width:18px;height:18px}.sd-autoid-inspector__section-title sd-icon.is-error{color:var(--sd-error, #ba1a1a)}.sd-autoid-inspector__section-title sd-icon.is-warning{color:var(--sd-warning, #a66300)}.sd-autoid-inspector__section-title sd-icon.is-info{color:var(--sd-info, #006a6a)}.sd-autoid-inspector__success-card{display:flex;align-items:center;gap:12px;padding:14px 16px;background:var(--sd-success-light, #d8f0d3);border:1px solid var(--sd-success, #2e7d32);border-radius:10px;color:var(--sd-success-dark, #1b5e20)}.sd-autoid-inspector__success-card sd-icon{font-size:28px;width:28px;height:28px;color:var(--sd-success, #2e7d32)}.sd-autoid-inspector__success-card strong{display:block;font-size:13px;margin-bottom:2px}.sd-autoid-inspector__success-card small{font-size:11px;opacity:.85}.sd-autoid-inspector__table-wrap{border:1px solid var(--sd-border, #c4c6d0);border-radius:8px;overflow:auto;max-height:60vh;background:#fff}.sd-autoid-inspector__table{width:100%;border-collapse:separate;border-spacing:0;font-size:12px}.sd-autoid-inspector__table th,.sd-autoid-inspector__table td{padding:8px 10px;text-align:left;vertical-align:middle;border-bottom:1px solid var(--sd-border, #c4c6d0)}.sd-autoid-inspector__table thead th{position:sticky;top:0;z-index:1;background:var(--sd-surface-muted, #e7e8ed);font-weight:600;color:var(--sd-text, #1a1b1f);text-transform:none;font-size:11px;letter-spacing:.3px;border-bottom:none;box-shadow:inset 0 -1px 0 var(--sd-border, #c4c6d0),0 2px 4px #0000000a;white-space:nowrap}.sd-autoid-inspector__table thead th.is-num{text-align:right;width:50px}.sd-autoid-inspector__table tbody tr{transition:background .1s ease}.sd-autoid-inspector__table tbody tr:hover{background:var(--sd-primary-light, #d7e3ff)}.sd-autoid-inspector__table tbody tr:last-child td{border-bottom:none}.sd-autoid-inspector__table tbody tr.is-error{background:var(--sd-error-light, #ffdad6)}.sd-autoid-inspector__table tbody tr.is-error:hover{background:var(--sd-error-light, #ffdad6);filter:brightness(.98)}.sd-autoid-inspector__table tbody tr.is-warning{background:var(--sd-warning-light, #ffddb0)}.sd-autoid-inspector__table tbody tr.is-warning:hover{background:var(--sd-warning-light, #ffddb0);filter:brightness(.98)}.sd-autoid-inspector__table td.is-num{text-align:right;color:var(--sd-text-secondary, #44474f);font-variant-numeric:tabular-nums}.sd-autoid-inspector__table code{font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:11px;background:var(--sd-surface-muted, #e7e8ed);padding:2px 6px;border-radius:4px;color:var(--sd-primary-dark, #00458f);border:1px solid var(--sd-border, #c4c6d0)}.sd-autoid-inspector__autoid-cell{display:inline-flex;align-items:center;gap:6px}.sd-autoid-inspector__tag-chip{display:inline-block;font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:11px;background:var(--sd-info-light, #d4f7f6);color:var(--sd-info, #006a6a);border:1px solid var(--sd-info, #006a6a);padding:1px 8px;border-radius:999px;font-weight:500}.sd-autoid-inspector__value{max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--sd-text-secondary, #44474f);font-style:italic}.sd-autoid-inspector__state-cell{display:flex;flex-wrap:wrap;gap:4px;align-items:center;min-width:0}.sd-autoid-inspector__state-chip{display:inline-block;font-family:JetBrains Mono,Fira Code,Consolas,monospace;font-size:10px;background:var(--sd-surface-muted, #e7e8ed);color:var(--sd-text-secondary, #44474f);border:1px solid var(--sd-border, #c4c6d0);padding:1px 6px;border-radius:999px;font-weight:500;white-space:nowrap}.sd-autoid-inspector__state-chip.is-error{background:var(--sd-error-light, #ffdad6);color:var(--sd-error-dark, #93000a);border-color:var(--sd-error, #ba1a1a)}.sd-autoid-inspector__state-chip.is-warning{background:var(--sd-warning-light, #ffddb0);color:var(--sd-warning-dark, #7a4d00);border-color:var(--sd-warning, #a66300)}.sd-autoid-inspector__state-chip.is-info{background:var(--sd-info-light, #d4f7f6);color:var(--sd-info, #006a6a);border-color:var(--sd-info, #006a6a)}.sd-autoid-inspector__empty{text-align:center;color:var(--sd-text-secondary, #44474f);padding:24px;font-style:italic}.sd-autoid-inspector__filter{display:flex;align-items:center;gap:6px;background:var(--sd-surface-muted, #e7e8ed);border:1px solid var(--sd-border, #c4c6d0);border-radius:8px;padding:6px 10px;margin-bottom:12px;transition:border-color .15s ease}.sd-autoid-inspector__filter:focus-within{border-color:var(--sd-primary, #005cbb);background:#fff}.sd-autoid-inspector__filter input{flex:1;border:none;background:transparent;outline:none;font-size:13px;color:var(--sd-text, #1a1b1f)}.sd-autoid-inspector__filter input::placeholder{color:var(--sd-border-strong, #74777f)}.sd-autoid-inspector__filter>sd-icon{font-size:18px;width:18px;height:18px;color:var(--sd-text-secondary, #44474f)}.sd-autoid-inspector__export-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(112px,1fr));gap:8px}.sd-autoid-inspector__export-grid.is-e2e{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}.sd-autoid-inspector__export-grid button{border:1px solid var(--sd-border, #c4c6d0);background:#fff;border-radius:10px;padding:12px 10px;cursor:pointer;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;min-width:0;transition:border-color .15s ease,background .15s ease,transform .12s ease;text-align:center}.sd-autoid-inspector__export-grid button:hover{border-color:var(--sd-primary, #005cbb);background:var(--sd-primary-light, #d7e3ff);transform:translateY(-1px)}.sd-autoid-inspector__export-grid button:disabled{cursor:wait;opacity:.68;transform:none}.sd-autoid-inspector__export-grid button span{font-size:12px;font-weight:600;color:var(--sd-text, #1a1b1f);line-height:1.25}.sd-autoid-inspector__export-grid button small{font-size:10px;color:var(--sd-text-secondary, #44474f);line-height:1.2}.sd-autoid-inspector__export-grid button sd-icon{font-size:22px;width:22px;height:22px;color:var(--sd-primary, #005cbb)}.sd-autoid-inspector__export-section{margin-top:14px;padding-top:12px;border-top:1px solid var(--sd-border, #c4c6d0)}.sd-autoid-inspector__context-backdrop{position:fixed;inset:0;z-index:10001;background:#141c2652}.sd-autoid-inspector__context-dialog{position:fixed;z-index:10002;top:50%;left:50%;width:min(720px,100vw - 28px);max-height:min(720px,100vh - 28px);transform:translate(-50%,-50%);display:flex;flex-direction:column;border:1px solid var(--sd-border, #c4c6d0);border-radius:8px;background:#fff;box-shadow:0 18px 48px #0f172a38;overflow:hidden}.sd-autoid-inspector__context-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:14px 16px;border-bottom:1px solid var(--sd-border, #c4c6d0)}.sd-autoid-inspector__context-header h4{margin:0;font-size:15px;font-weight:700;line-height:1.3;color:var(--sd-text, #1a1b1f)}.sd-autoid-inspector__context-header small{display:block;margin-top:2px;font-size:11px;color:var(--sd-text-secondary, #44474f);line-height:1.3}.sd-autoid-inspector__context-form{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;padding:14px 16px;overflow:auto}.sd-autoid-inspector__context-form label{display:flex;flex-direction:column;gap:6px;min-width:0}.sd-autoid-inspector__context-form label.is-full{grid-column:1/-1}.sd-autoid-inspector__context-form span{font-size:12px;font-weight:600;color:var(--sd-text, #1a1b1f);line-height:1.25}.sd-autoid-inspector__context-form input,.sd-autoid-inspector__context-form textarea{width:100%;min-width:0;border:1px solid var(--sd-border, #c4c6d0);border-radius:6px;padding:8px 10px;color:var(--sd-text, #1a1b1f);font:inherit;font-size:12px;line-height:1.4;outline:none;resize:vertical}.sd-autoid-inspector__context-form input:focus,.sd-autoid-inspector__context-form textarea:focus{border-color:var(--sd-primary, #005cbb);box-shadow:0 0 0 2px color-mix(in srgb,var(--sd-primary, #005cbb) 18%,transparent)}.sd-autoid-inspector__context-actions{display:flex;justify-content:flex-end;gap:8px;padding:12px 16px;border-top:1px solid var(--sd-border, #c4c6d0)}.sd-autoid-inspector__context-actions button{min-height:34px;border-radius:6px;padding:0 12px;border:1px solid var(--sd-border, #c4c6d0);background:#fff;color:var(--sd-text, #1a1b1f);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:6px;font-size:12px;font-weight:600}.sd-autoid-inspector__context-actions button.is-primary{border-color:var(--sd-primary, #005cbb);background:var(--sd-primary, #005cbb);color:#fff}.sd-autoid-inspector__context-actions button:disabled{cursor:not-allowed;opacity:.62}.sd-autoid-inspector__context-actions button sd-icon{font-size:17px;width:17px;height:17px}.sd-autoid-inspector__export-message{margin-top:10px;border-radius:8px;padding:8px 10px;font-size:12px;line-height:1.35}.sd-autoid-inspector__export-message.is-success{background:var(--sd-success-light, #d8f0d3);color:var(--sd-success-dark, #1b5e20);border:1px solid var(--sd-success, #2e7d32)}.sd-autoid-inspector__export-message.is-error{background:var(--sd-error-light, #ffdad6);color:var(--sd-error-dark, #93000a);border:1px solid var(--sd-error, #ba1a1a)}@media(max-width:560px){.sd-autoid-inspector__context-form{grid-template-columns:1fr}.sd-autoid-inspector__context-actions{flex-direction:column-reverse}.sd-autoid-inspector__context-actions button{width:100%}}\n"] }]
970
969
  }], propDecorators: { onEscape: [{
971
970
  type: HostListener,
972
971
  args: ['document:keydown.escape']