create-appraisejs 0.1.10-alpha.8 → 0.2.0-alpha.1

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 (414) hide show
  1. package/README.md +52 -52
  2. package/dist/cli.e2e.test.d.ts +2 -0
  3. package/dist/cli.e2e.test.d.ts.map +1 -0
  4. package/dist/cli.e2e.test.js +73 -0
  5. package/dist/cli.e2e.test.js.map +1 -0
  6. package/dist/config.test.d.ts +2 -0
  7. package/dist/config.test.d.ts.map +1 -0
  8. package/dist/config.test.js +65 -0
  9. package/dist/config.test.js.map +1 -0
  10. package/dist/copy-template.test.d.ts +2 -0
  11. package/dist/copy-template.test.d.ts.map +1 -0
  12. package/dist/copy-template.test.js +71 -0
  13. package/dist/copy-template.test.js.map +1 -0
  14. package/dist/download-repo.test.d.ts +2 -0
  15. package/dist/download-repo.test.d.ts.map +1 -0
  16. package/dist/download-repo.test.js +14 -0
  17. package/dist/download-repo.test.js.map +1 -0
  18. package/dist/install.test.d.ts +2 -0
  19. package/dist/install.test.d.ts.map +1 -0
  20. package/dist/install.test.js +119 -0
  21. package/dist/install.test.js.map +1 -0
  22. package/dist/prepare-template-utils.d.ts.map +1 -1
  23. package/dist/prepare-template-utils.js +5 -0
  24. package/dist/prepare-template-utils.js.map +1 -1
  25. package/dist/prompts.test.d.ts +2 -0
  26. package/dist/prompts.test.d.ts.map +1 -0
  27. package/dist/prompts.test.js +58 -0
  28. package/dist/prompts.test.js.map +1 -0
  29. package/dist/scaffold-gitignore.d.ts +1 -0
  30. package/dist/scaffold-gitignore.d.ts.map +1 -1
  31. package/dist/scaffold-gitignore.js +3 -0
  32. package/dist/scaffold-gitignore.js.map +1 -1
  33. package/package.json +3 -2
  34. package/templates/default/.appraise-template-meta.json +2 -2
  35. package/templates/default/.env.example +2 -2
  36. package/templates/default/.vscode/settings.json +2 -1
  37. package/templates/default/README.md +53 -53
  38. package/templates/default/automation/mapping/locator-map.json +1 -14
  39. package/templates/default/automation/steps/actions/click.step.ts +58 -58
  40. package/templates/default/automation/steps/actions/hover.step.ts +27 -27
  41. package/templates/default/automation/steps/actions/input.step.ts +84 -84
  42. package/templates/default/automation/steps/actions/navigation.step.ts +70 -70
  43. package/templates/default/automation/steps/actions/random_data.step.ts +142 -142
  44. package/templates/default/automation/steps/actions/store.step.ts +86 -86
  45. package/templates/default/automation/steps/actions/wait.step.ts +90 -90
  46. package/templates/default/automation/steps/validations/active_state_assertion.step.ts +30 -30
  47. package/templates/default/automation/steps/validations/navigation_assertion.step.ts +22 -22
  48. package/templates/default/automation/steps/validations/text_assertion.step.ts +107 -107
  49. package/templates/default/automation/steps/validations/visibility_assertion.step.ts +26 -26
  50. package/templates/default/components.json +24 -24
  51. package/templates/default/cucumber.mjs +16 -16
  52. package/templates/default/eslint.config.mjs +16 -16
  53. package/templates/default/gitignore +3 -0
  54. package/templates/default/next-env.d.ts +6 -6
  55. package/templates/default/next.config.ts +11 -11
  56. package/templates/default/package-lock.json +146 -75
  57. package/templates/default/package.json +2 -1
  58. package/templates/default/packages/cucumber-runtime/package.json +13 -13
  59. package/templates/default/packages/cucumber-runtime/src/cache.util.ts +93 -93
  60. package/templates/default/packages/cucumber-runtime/src/cli.ts +68 -68
  61. package/templates/default/packages/cucumber-runtime/src/environment.util.ts +21 -21
  62. package/templates/default/packages/cucumber-runtime/src/executor.ts +32 -32
  63. package/templates/default/packages/cucumber-runtime/src/hooks.ts +25 -3
  64. package/templates/default/packages/cucumber-runtime/src/index.ts +17 -17
  65. package/templates/default/packages/cucumber-runtime/src/locator.util.ts +234 -234
  66. package/templates/default/packages/cucumber-runtime/src/parameter-types.ts +7 -7
  67. package/templates/default/packages/cucumber-runtime/src/paths.ts +66 -1
  68. package/templates/default/packages/cucumber-runtime/src/random-data.util.ts +35 -35
  69. package/templates/default/packages/cucumber-runtime/src/types.ts +13 -13
  70. package/templates/default/packages/cucumber-runtime/src/world.ts +44 -44
  71. package/templates/default/packages/cucumber-runtime/tsconfig.json +11 -11
  72. package/templates/default/packages/locator-picker-companion/dist/cli.d.ts +1 -0
  73. package/templates/default/packages/locator-picker-companion/dist/cli.js +302 -0
  74. package/templates/default/packages/locator-picker-companion/dist/index.d.ts +3 -0
  75. package/templates/default/packages/locator-picker-companion/dist/index.js +3 -0
  76. package/templates/default/packages/locator-picker-companion/dist/injected-picker-script.d.ts +1 -0
  77. package/templates/default/packages/locator-picker-companion/dist/injected-picker-script.js +615 -0
  78. package/templates/default/packages/locator-picker-companion/dist/launcher.d.ts +11 -0
  79. package/templates/default/packages/locator-picker-companion/dist/launcher.js +59 -0
  80. package/templates/default/packages/locator-picker-companion/dist/selector-generator.d.ts +3 -0
  81. package/templates/default/packages/locator-picker-companion/dist/selector-generator.js +257 -0
  82. package/templates/default/packages/locator-picker-companion/dist/session-file.d.ts +22 -0
  83. package/templates/default/packages/locator-picker-companion/dist/session-file.js +150 -0
  84. package/templates/default/packages/locator-picker-companion/dist/types.d.ts +31 -0
  85. package/templates/default/packages/locator-picker-companion/dist/types.js +1 -0
  86. package/templates/default/packages/locator-picker-companion/package.json +17 -0
  87. package/templates/default/packages/locator-picker-companion/src/cli.ts +428 -0
  88. package/templates/default/packages/locator-picker-companion/src/index.ts +3 -0
  89. package/templates/default/packages/locator-picker-companion/src/injected-picker-script.ts +771 -0
  90. package/templates/default/packages/locator-picker-companion/src/launcher.ts +77 -0
  91. package/templates/default/packages/locator-picker-companion/src/selector-generator.ts +339 -0
  92. package/templates/default/packages/locator-picker-companion/src/session-file.ts +226 -0
  93. package/templates/default/packages/locator-picker-companion/src/types.ts +42 -0
  94. package/templates/default/packages/locator-picker-companion/tsconfig.json +14 -0
  95. package/templates/default/postcss.config.mjs +8 -8
  96. package/templates/default/prisma/dev.db +0 -0
  97. package/templates/default/prisma/migrations/20251104113456_add_type_for_template_step_groups/migration.sql +16 -16
  98. package/templates/default/prisma/migrations/20251104170946_add_tags_to_test_suite_and_test_case/migration.sql +27 -27
  99. package/templates/default/prisma/migrations/20251112190024_add_cascade_delete_to_test_run_test_case/migration.sql +17 -17
  100. package/templates/default/prisma/migrations/20251113181100_add_test_run_log/migration.sql +12 -12
  101. package/templates/default/prisma/migrations/20251119191838_add_tag_type/migration.sql +28 -28
  102. package/templates/default/prisma/migrations/20251121164059_add_conflict_resolution/migration.sql +12 -12
  103. package/templates/default/prisma/migrations/20251223183400_add_report_model_to_db_schema/migration.sql +10 -10
  104. package/templates/default/prisma/migrations/20251223183637_add_report_test_case_entity_for_storing_test_results_for_individual_test_cases/migration.sql +10 -10
  105. package/templates/default/prisma/migrations/20251224083549_add_comprehensive_report_storage/migration.sql +108 -108
  106. package/templates/default/prisma/migrations/20251229194422_migrate_duration_to_string/migration.sql +55 -55
  107. package/templates/default/prisma/migrations/20251230124637_add_unique_constraint_to_test_run_name/migration.sql +27 -27
  108. package/templates/default/prisma/migrations/20260115094436_add_dashboard_metrics/migration.sql +59 -59
  109. package/templates/default/prisma/migrations/20260127172022_add_cascade_delete_to_step_parameters/migration.sql +34 -34
  110. package/templates/default/prisma/migrations/20260313093000_add_report_step_screenshot_path/migration.sql +1 -0
  111. package/templates/default/prisma/migrations/20260318120000_add_test_suite_context_to_test_run_test_case/migration.sql +20 -0
  112. package/templates/default/prisma/migrations/20260318173512_add_support_of_test_suite_level_runs/migration.sql +2 -0
  113. package/templates/default/prisma/schema.prisma +4 -0
  114. package/templates/default/scripts/install-playwright.ts +20 -0
  115. package/templates/default/scripts/protect-seeded-files.ts +38 -38
  116. package/templates/default/scripts/regenerate-features.ts +94 -94
  117. package/templates/default/scripts/setup-env.ts +19 -19
  118. package/templates/default/scripts/sync-all.ts +22 -66
  119. package/templates/default/scripts/sync-appraise-base-template.ts +57 -19
  120. package/templates/default/scripts/sync-environments.ts +40 -21
  121. package/templates/default/scripts/sync-locator-groups.ts +410 -410
  122. package/templates/default/scripts/sync-locators.ts +398 -398
  123. package/templates/default/scripts/sync-modules.ts +341 -341
  124. package/templates/default/scripts/sync-tags.ts +2 -3
  125. package/templates/default/scripts/sync-template-step-groups.ts +0 -3
  126. package/templates/default/scripts/sync-template-steps.ts +37 -11
  127. package/templates/default/scripts/sync-test-cases.ts +68 -16
  128. package/templates/default/scripts/sync-test-suites.ts +7 -5
  129. package/templates/default/src/actions/conflict/conflict.action.ts +33 -33
  130. package/templates/default/src/actions/dashboard/dashboard-actions.ts +240 -240
  131. package/templates/default/src/actions/environments/environment-actions.ts +188 -188
  132. package/templates/default/src/actions/locator/locator-actions.ts +490 -490
  133. package/templates/default/src/actions/locator-groups/locator-group-actions.ts +6 -3
  134. package/templates/default/src/actions/locator-picker/locator-picker-actions.ts +243 -0
  135. package/templates/default/src/actions/modules/module-actions.ts +135 -135
  136. package/templates/default/src/actions/reports/report-actions.ts +51 -49
  137. package/templates/default/src/actions/review/review-actions.ts +147 -147
  138. package/templates/default/src/actions/settings/sync-actions.ts +46 -0
  139. package/templates/default/src/actions/tags/tag-actions.ts +107 -107
  140. package/templates/default/src/actions/template-step/template-step-actions.ts +38 -4
  141. package/templates/default/src/actions/template-step-group/template-step-group-actions.ts +10 -3
  142. package/templates/default/src/actions/template-test-case/template-test-case-actions.ts +238 -238
  143. package/templates/default/src/actions/test-case/test-case-actions.ts +356 -356
  144. package/templates/default/src/actions/test-run/test-run-actions.ts +222 -129
  145. package/templates/default/src/actions/test-suite/test-suite-actions.ts +95 -17
  146. package/templates/default/src/actions/user/user-actions.ts +13 -13
  147. package/templates/default/src/app/(base)/environments/create/page.tsx +28 -28
  148. package/templates/default/src/app/(base)/environments/environment-form.tsx +219 -219
  149. package/templates/default/src/app/(base)/environments/environment-table-columns.tsx +96 -96
  150. package/templates/default/src/app/(base)/environments/environment-table.tsx +24 -24
  151. package/templates/default/src/app/(base)/environments/modify/[id]/page.tsx +46 -46
  152. package/templates/default/src/app/(base)/environments/page.tsx +59 -59
  153. package/templates/default/src/app/(base)/layout.tsx +10 -10
  154. package/templates/default/src/app/(base)/locator-groups/create/page.tsx +44 -44
  155. package/templates/default/src/app/(base)/locator-groups/locator-group-form.tsx +215 -215
  156. package/templates/default/src/app/(base)/locator-groups/locator-group-table-columns.tsx +77 -77
  157. package/templates/default/src/app/(base)/locator-groups/locator-group-table.tsx +28 -28
  158. package/templates/default/src/app/(base)/locator-groups/modify/[id]/page.tsx +46 -46
  159. package/templates/default/src/app/(base)/locator-groups/page.tsx +20 -8
  160. package/templates/default/src/app/(base)/locators/create/create-locator-workspace.tsx +542 -0
  161. package/templates/default/src/app/(base)/locators/create/page.tsx +32 -19
  162. package/templates/default/src/app/(base)/locators/locator-form.tsx +163 -163
  163. package/templates/default/src/app/(base)/locators/locator-table-columns.tsx +73 -73
  164. package/templates/default/src/app/(base)/locators/locator-table.tsx +28 -28
  165. package/templates/default/src/app/(base)/locators/modify/[id]/page.tsx +56 -27
  166. package/templates/default/src/app/(base)/locators/page.tsx +13 -13
  167. package/templates/default/src/app/(base)/locators/picker/page.tsx +7 -0
  168. package/templates/default/src/app/(base)/locators/sync-locators-button.tsx +66 -66
  169. package/templates/default/src/app/(base)/modules/create/page.tsx +34 -34
  170. package/templates/default/src/app/(base)/modules/modify/[id]/page.tsx +46 -46
  171. package/templates/default/src/app/(base)/modules/module-form.tsx +126 -126
  172. package/templates/default/src/app/(base)/modules/module-table-columns.tsx +85 -85
  173. package/templates/default/src/app/(base)/modules/module-table.tsx +24 -24
  174. package/templates/default/src/app/(base)/modules/page.tsx +59 -59
  175. package/templates/default/src/app/(base)/reports/[id]/page.tsx +40 -33
  176. package/templates/default/src/app/(base)/reports/duration-chart.tsx +33 -33
  177. package/templates/default/src/app/(base)/reports/feature-chart.tsx +78 -78
  178. package/templates/default/src/app/(base)/reports/overview-chart.tsx +49 -46
  179. package/templates/default/src/app/(base)/reports/page.tsx +98 -98
  180. package/templates/default/src/app/(base)/reports/report-metric-card.tsx +78 -16
  181. package/templates/default/src/app/(base)/reports/report-table-columns.tsx +189 -189
  182. package/templates/default/src/app/(base)/reports/report-table.tsx +72 -72
  183. package/templates/default/src/app/(base)/reports/report-view-table-columns.tsx +12 -1
  184. package/templates/default/src/app/(base)/reports/report-view-table.tsx +1 -0
  185. package/templates/default/src/app/(base)/reports/test-cases/page.tsx +40 -42
  186. package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table-columns.tsx +115 -115
  187. package/templates/default/src/app/(base)/reports/test-cases/test-cases-metric-table.tsx +27 -27
  188. package/templates/default/src/app/(base)/reports/test-suites/page.tsx +42 -42
  189. package/templates/default/src/app/(base)/reports/test-suites/test-suites-metric-table-columns.tsx +79 -79
  190. package/templates/default/src/app/(base)/reports/test-suites/test-suites-metric-table.tsx +27 -27
  191. package/templates/default/src/app/(base)/reports/view-logs-button.tsx +58 -60
  192. package/templates/default/src/app/(base)/reviews/create/page.tsx +26 -26
  193. package/templates/default/src/app/(base)/reviews/created-reviews-table.tsx +15 -15
  194. package/templates/default/src/app/(base)/reviews/modify/[id]/page.tsx +26 -26
  195. package/templates/default/src/app/(base)/reviews/page.tsx +26 -26
  196. package/templates/default/src/app/(base)/reviews/review/[id]/page.tsx +26 -26
  197. package/templates/default/src/app/(base)/reviews/review-form.tsx +11 -11
  198. package/templates/default/src/app/(base)/reviews/review-table-by-creator-columns.tsx +9 -9
  199. package/templates/default/src/app/(base)/reviews/review-table-by-reviewer-columns.tsx +9 -9
  200. package/templates/default/src/app/(base)/reviews/reviewer-reviews-table.tsx +15 -15
  201. package/templates/default/src/app/(base)/settings/page.tsx +30 -0
  202. package/templates/default/src/app/(base)/settings/settings-sync-panel.tsx +261 -0
  203. package/templates/default/src/app/(base)/tags/create/page.tsx +39 -39
  204. package/templates/default/src/app/(base)/tags/modify/[id]/page.tsx +50 -50
  205. package/templates/default/src/app/(base)/tags/page.tsx +58 -58
  206. package/templates/default/src/app/(base)/tags/tag-form.tsx +147 -147
  207. package/templates/default/src/app/(base)/tags/tag-table-columns.tsx +63 -63
  208. package/templates/default/src/app/(base)/tags/tag-table.tsx +29 -29
  209. package/templates/default/src/app/(base)/template-step-groups/create/page.tsx +28 -28
  210. package/templates/default/src/app/(base)/template-step-groups/modify/[id]/page.tsx +45 -45
  211. package/templates/default/src/app/(base)/template-step-groups/page.tsx +60 -60
  212. package/templates/default/src/app/(base)/template-step-groups/template-step-group-form.tsx +167 -167
  213. package/templates/default/src/app/(base)/template-step-groups/template-step-group-table-columns.tsx +89 -89
  214. package/templates/default/src/app/(base)/template-step-groups/template-step-group-table.tsx +32 -32
  215. package/templates/default/src/app/(base)/template-steps/create/page.tsx +37 -37
  216. package/templates/default/src/app/(base)/template-steps/modify/[id]/page.tsx +49 -49
  217. package/templates/default/src/app/(base)/template-steps/page.tsx +58 -58
  218. package/templates/default/src/app/(base)/template-steps/paramChip.tsx +213 -213
  219. package/templates/default/src/app/(base)/template-steps/template-step-form.tsx +384 -384
  220. package/templates/default/src/app/(base)/template-steps/template-step-table-columns.tsx +158 -158
  221. package/templates/default/src/app/(base)/template-steps/template-step-table.tsx +24 -24
  222. package/templates/default/src/app/(base)/template-test-cases/create/page.tsx +56 -56
  223. package/templates/default/src/app/(base)/template-test-cases/modify/[id]/page.tsx +89 -89
  224. package/templates/default/src/app/(base)/template-test-cases/page.tsx +58 -58
  225. package/templates/default/src/app/(base)/template-test-cases/template-test-case-flow.tsx +84 -84
  226. package/templates/default/src/app/(base)/template-test-cases/template-test-case-form.tsx +262 -262
  227. package/templates/default/src/app/(base)/template-test-cases/template-test-case-table-columns.tsx +76 -76
  228. package/templates/default/src/app/(base)/template-test-cases/template-test-case-table.tsx +32 -32
  229. package/templates/default/src/app/(base)/test-cases/create/page.tsx +76 -76
  230. package/templates/default/src/app/(base)/test-cases/create-from-template/generate/[id]/page.tsx +96 -96
  231. package/templates/default/src/app/(base)/test-cases/create-from-template/page.tsx +38 -38
  232. package/templates/default/src/app/(base)/test-cases/create-from-template/template-selection-form.tsx +73 -73
  233. package/templates/default/src/app/(base)/test-cases/modify/[id]/page.tsx +106 -106
  234. package/templates/default/src/app/(base)/test-cases/page.tsx +2 -2
  235. package/templates/default/src/app/(base)/test-cases/test-case-flow.tsx +82 -82
  236. package/templates/default/src/app/(base)/test-cases/test-case-form.tsx +395 -395
  237. package/templates/default/src/app/(base)/test-cases/test-case-table-columns.tsx +6 -71
  238. package/templates/default/src/app/(base)/test-cases/test-case-table.tsx +2 -2
  239. package/templates/default/src/app/(base)/test-runs/[id]/page.tsx +4 -1
  240. package/templates/default/src/app/(base)/test-runs/create/page.tsx +11 -9
  241. package/templates/default/src/app/(base)/test-runs/page.tsx +60 -60
  242. package/templates/default/src/app/(base)/test-runs/test-run-form.tsx +37 -44
  243. package/templates/default/src/app/(base)/test-runs/test-run-table-columns.tsx +229 -229
  244. package/templates/default/src/app/(base)/test-runs/test-run-table.tsx +127 -127
  245. package/templates/default/src/app/(base)/test-suites/create/page.tsx +3 -2
  246. package/templates/default/src/app/(base)/test-suites/modify/[id]/page.tsx +2 -1
  247. package/templates/default/src/app/(base)/test-suites/page.tsx +57 -21
  248. package/templates/default/src/app/(base)/test-suites/test-suite-form.tsx +16 -17
  249. package/templates/default/src/app/(base)/test-suites/test-suite-table-columns.tsx +2 -1
  250. package/templates/default/src/app/(base)/test-suites/test-suite-table.tsx +29 -29
  251. package/templates/default/src/app/(dashboard-components)/app-drawer.tsx +187 -187
  252. package/templates/default/src/app/(dashboard-components)/data-card-grid.tsx +12 -12
  253. package/templates/default/src/app/(dashboard-components)/data-card.tsx +26 -26
  254. package/templates/default/src/app/(dashboard-components)/execution-health-panel.tsx +56 -56
  255. package/templates/default/src/app/(dashboard-components)/ongoing-test-runs-card.tsx +87 -87
  256. package/templates/default/src/app/(dashboard-components)/quick-actions-drawer.tsx +44 -44
  257. package/templates/default/src/app/api/reports/steps/[stepId]/screenshot/route.ts +52 -0
  258. package/templates/default/src/app/api/test-runs/[runId]/download/route.ts +97 -34
  259. package/templates/default/src/app/api/test-runs/[runId]/logs/route.ts +4 -1
  260. package/templates/default/src/app/api/test-runs/[runId]/trace/[testCaseId]/route.ts +146 -147
  261. package/templates/default/src/app/globals.css +147 -147
  262. package/templates/default/src/app/layout.tsx +4 -0
  263. package/templates/default/src/app/page.tsx +2 -0
  264. package/templates/default/src/assets/icons/empty-tube.tsx +23 -23
  265. package/templates/default/src/assets/icons/tube-plus.tsx +29 -29
  266. package/templates/default/src/components/base-node.tsx +21 -21
  267. package/templates/default/src/components/chart/pie-chart.tsx +73 -73
  268. package/templates/default/src/components/data-extraction/locator-inspector.tsx +460 -460
  269. package/templates/default/src/components/data-state/empty-state.tsx +40 -40
  270. package/templates/default/src/components/data-visualization/info-card.tsx +70 -70
  271. package/templates/default/src/components/data-visualization/info-grid.tsx +22 -22
  272. package/templates/default/src/components/diagram/button-edge.tsx +54 -54
  273. package/templates/default/src/components/diagram/dynamic-parameters.tsx +474 -474
  274. package/templates/default/src/components/diagram/edit-header-option.tsx +36 -36
  275. package/templates/default/src/components/diagram/flow-diagram.tsx +470 -470
  276. package/templates/default/src/components/diagram/node-form.tsx +262 -262
  277. package/templates/default/src/components/diagram/options-header-node.tsx +57 -57
  278. package/templates/default/src/components/diagram/template-step-combobox.tsx +155 -155
  279. package/templates/default/src/components/form/error-message.tsx +7 -7
  280. package/templates/default/src/components/kokonutui/smooth-tab.tsx +453 -453
  281. package/templates/default/src/components/loading-skeleton/data-table/data-table-skeleton.tsx +30 -30
  282. package/templates/default/src/components/loading-skeleton/form/button-skeleton.tsx +8 -8
  283. package/templates/default/src/components/loading-skeleton/form/icon-button-skeleton.tsx +8 -8
  284. package/templates/default/src/components/loading-skeleton/form/text-input-skeleton.tsx +8 -8
  285. package/templates/default/src/components/loading-skeleton/visualization/table-skeleton.tsx +14 -14
  286. package/templates/default/src/components/logo.tsx +15 -15
  287. package/templates/default/src/components/navigation/command-badge.tsx +34 -34
  288. package/templates/default/src/components/navigation/command-chain-input.tsx +51 -51
  289. package/templates/default/src/components/navigation/entity-search-command.tsx +116 -116
  290. package/templates/default/src/components/navigation/nav-card.tsx +31 -31
  291. package/templates/default/src/components/navigation/nav-command.tsx +13 -0
  292. package/templates/default/src/components/navigation/nav-link.tsx +60 -60
  293. package/templates/default/src/components/navigation/nav-menu-card-deck.tsx +112 -112
  294. package/templates/default/src/components/node-header.tsx +159 -159
  295. package/templates/default/src/components/reports/test-case-logs-modal.tsx +310 -253
  296. package/templates/default/src/components/table/table-actions.tsx +172 -172
  297. package/templates/default/src/components/test-case/test-case-columns.tsx +76 -0
  298. package/templates/default/src/components/test-case/test-case-picker.tsx +244 -0
  299. package/templates/default/src/components/test-run/download-logs-button.tsx +92 -99
  300. package/templates/default/src/components/test-run/log-viewer.tsx +1 -14
  301. package/templates/default/src/components/test-run/test-run-details.tsx +15 -2
  302. package/templates/default/src/components/test-run/test-run-header.tsx +149 -149
  303. package/templates/default/src/components/test-run/view-report-button.tsx +102 -102
  304. package/templates/default/src/components/test-suite/test-suite-picker.tsx +444 -0
  305. package/templates/default/src/components/theme/mode-toggle.tsx +54 -54
  306. package/templates/default/src/components/theme/theme-provider.tsx +8 -8
  307. package/templates/default/src/components/typography/page-header-subtitle.tsx +7 -7
  308. package/templates/default/src/components/typography/page-header.tsx +7 -7
  309. package/templates/default/src/components/ui/alert-dialog.tsx +106 -106
  310. package/templates/default/src/components/ui/alert.tsx +43 -43
  311. package/templates/default/src/components/ui/avatar.tsx +40 -40
  312. package/templates/default/src/components/ui/badge.tsx +29 -29
  313. package/templates/default/src/components/ui/button.tsx +47 -47
  314. package/templates/default/src/components/ui/calendar.tsx +158 -158
  315. package/templates/default/src/components/ui/card.tsx +43 -43
  316. package/templates/default/src/components/ui/checkbox.tsx +28 -28
  317. package/templates/default/src/components/ui/command.tsx +135 -135
  318. package/templates/default/src/components/ui/data-table-column-header.tsx +61 -61
  319. package/templates/default/src/components/ui/data-table-pagination.tsx +87 -87
  320. package/templates/default/src/components/ui/data-table-view-options.tsx +50 -50
  321. package/templates/default/src/components/ui/data-table.tsx +261 -261
  322. package/templates/default/src/components/ui/dialog.tsx +97 -97
  323. package/templates/default/src/components/ui/dropdown-menu.tsx +182 -182
  324. package/templates/default/src/components/ui/input.tsx +22 -22
  325. package/templates/default/src/components/ui/kbd.tsx +28 -28
  326. package/templates/default/src/components/ui/label.tsx +19 -19
  327. package/templates/default/src/components/ui/loading.tsx +12 -12
  328. package/templates/default/src/components/ui/multi-select-with-preview.tsx +116 -116
  329. package/templates/default/src/components/ui/multi-select.tsx +142 -142
  330. package/templates/default/src/components/ui/navigation-menu.tsx +120 -120
  331. package/templates/default/src/components/ui/popover.tsx +33 -33
  332. package/templates/default/src/components/ui/progress.tsx +25 -25
  333. package/templates/default/src/components/ui/radio-group.tsx +44 -44
  334. package/templates/default/src/components/ui/scroll-area.tsx +40 -40
  335. package/templates/default/src/components/ui/select.tsx +151 -151
  336. package/templates/default/src/components/ui/separator.tsx +22 -22
  337. package/templates/default/src/components/ui/skeleton.tsx +7 -7
  338. package/templates/default/src/components/ui/table.tsx +76 -76
  339. package/templates/default/src/components/ui/tabs.tsx +55 -55
  340. package/templates/default/src/components/ui/textarea.tsx +21 -21
  341. package/templates/default/src/components/ui/toast.tsx +113 -113
  342. package/templates/default/src/components/ui/toaster.tsx +26 -26
  343. package/templates/default/src/components/user-prompt/delete-prompt.tsx +87 -87
  344. package/templates/default/src/config/db-config.ts +10 -10
  345. package/templates/default/src/constants/form-opts/diagram/node-form.ts +30 -30
  346. package/templates/default/src/constants/form-opts/environment-form-opts.ts +24 -24
  347. package/templates/default/src/constants/form-opts/locator-form-opts.ts +20 -20
  348. package/templates/default/src/constants/form-opts/locator-group-form-opts.ts +28 -28
  349. package/templates/default/src/constants/form-opts/module-form-opts.ts +21 -21
  350. package/templates/default/src/constants/form-opts/review-form-opts.ts +23 -23
  351. package/templates/default/src/constants/form-opts/tag-form-opts.ts +42 -42
  352. package/templates/default/src/constants/form-opts/template-selection-form-opts.ts +16 -16
  353. package/templates/default/src/constants/form-opts/template-step-group-form-opts.ts +24 -24
  354. package/templates/default/src/constants/form-opts/template-test-case-form-opts.ts +39 -39
  355. package/templates/default/src/constants/form-opts/template-test-step-form-opts.ts +36 -36
  356. package/templates/default/src/constants/form-opts/test-case-form-opts.ts +43 -43
  357. package/templates/default/src/constants/form-opts/test-run-form-opts.ts +5 -3
  358. package/templates/default/src/constants/form-opts/test-suite-form-opts.ts +24 -24
  359. package/templates/default/src/hooks/use-toast.ts +187 -187
  360. package/templates/default/src/lib/automation/paths.ts +42 -12
  361. package/templates/default/src/lib/automation/projection-service.ts +2 -1
  362. package/templates/default/src/lib/bidirectional-sync.ts +432 -432
  363. package/templates/default/src/lib/database-sync.ts +2 -7
  364. package/templates/default/src/lib/environment-file-utils.ts +184 -184
  365. package/templates/default/src/lib/executor/local-executor-adapter.ts +24 -25
  366. package/templates/default/src/lib/executor/types.ts +2 -2
  367. package/templates/default/src/lib/feature-file-generator.ts +5 -66
  368. package/templates/default/src/lib/gherkin-parser.ts +259 -259
  369. package/templates/default/src/lib/locator-group-file-utils.ts +307 -307
  370. package/templates/default/src/lib/locator-picker/session-manager.ts +452 -0
  371. package/templates/default/src/lib/locator-picker/suggestions.ts +124 -0
  372. package/templates/default/src/lib/metrics/metric-calculator.ts +5 -0
  373. package/templates/default/src/lib/module-hierarchy-builder.ts +205 -205
  374. package/templates/default/src/lib/path-helpers/module-path.ts +71 -71
  375. package/templates/default/src/lib/process/task-spawner.ts +39 -2
  376. package/templates/default/src/lib/sync/projected-feature-utils.ts +113 -0
  377. package/templates/default/src/lib/sync/sync-executor.ts +130 -0
  378. package/templates/default/src/lib/sync/sync-pending-counts.test.ts +239 -0
  379. package/templates/default/src/lib/sync/sync-pending-counts.ts +1358 -0
  380. package/templates/default/src/lib/sync/sync-registry.ts +149 -0
  381. package/templates/default/src/lib/tag-utils.ts +28 -0
  382. package/templates/default/src/lib/template-sync-utils.d.ts +6 -6
  383. package/templates/default/src/lib/template-sync-utils.js +46 -46
  384. package/templates/default/src/lib/template-sync-utils.ts +63 -63
  385. package/templates/default/src/lib/test-case-utils.ts +6 -6
  386. package/templates/default/src/lib/test-run/log-formatter.ts +83 -83
  387. package/templates/default/src/lib/test-run/matching.ts +117 -0
  388. package/templates/default/src/lib/test-run/process-manager.ts +2 -0
  389. package/templates/default/src/lib/test-run/report-parser.ts +352 -316
  390. package/templates/default/src/lib/test-run/test-run-executor.ts +13 -15
  391. package/templates/default/src/lib/test-run/winston-logger.ts +64 -66
  392. package/templates/default/src/lib/test-suite-identifier-service.ts +76 -0
  393. package/templates/default/src/lib/test-suite-utils.ts +6 -0
  394. package/templates/default/src/lib/transformers/gherkin-converter.ts +42 -42
  395. package/templates/default/src/lib/transformers/key-to-icon-transformer.tsx +95 -95
  396. package/templates/default/src/lib/transformers/template-test-case-converter.ts +160 -160
  397. package/templates/default/src/lib/utils/node-param-validation.ts +81 -81
  398. package/templates/default/src/lib/utils/template-step-file-generator.ts +104 -104
  399. package/templates/default/src/lib/utils/template-step-file-manager-intelligent.ts +0 -8
  400. package/templates/default/src/lib/utils/template-step-file-manager.ts +166 -166
  401. package/templates/default/src/lib/utils.ts +31 -31
  402. package/templates/default/src/types/diagram/diagram.ts +34 -34
  403. package/templates/default/src/types/diagram/template-step.ts +11 -11
  404. package/templates/default/src/types/executor/browser.type.ts +1 -1
  405. package/templates/default/src/types/form/actionHandler.ts +6 -6
  406. package/templates/default/src/types/locator/locator.type.ts +11 -11
  407. package/templates/default/src/types/locator-picker.ts +65 -0
  408. package/templates/default/src/types/step/step.type.ts +1 -1
  409. package/templates/default/src/types/table/data-table.ts +6 -6
  410. package/templates/default/src/types/test-case-picker.ts +6 -0
  411. package/templates/default/src/types/test-suite-picker.ts +18 -0
  412. package/templates/default/tailwind.config.ts +62 -62
  413. package/templates/default/tsconfig.json +4 -1
  414. package/templates/default/appraisejs.config.json +0 -1
@@ -0,0 +1,771 @@
1
+ import type { CompanionPickedLocatorPayload } from './types.js'
2
+
3
+ export function installLocatorPickerOverlay(): void {
4
+ type PickerState = {
5
+ picking: boolean
6
+ submitting: boolean
7
+ hoveredElement: Element | null
8
+ preview: CompanionPickedLocatorPayload | null
9
+ error: string
10
+ elements: {
11
+ root?: HTMLDivElement
12
+ startButton?: HTMLButtonElement
13
+ useButton?: HTMLButtonElement
14
+ pickAgainButton?: HTMLButtonElement
15
+ cancelButton?: HTMLButtonElement
16
+ helperText?: HTMLParagraphElement
17
+ statusText?: HTMLParagraphElement
18
+ selectorValue?: HTMLPreElement
19
+ metadataValue?: HTMLParagraphElement
20
+ errorValue?: HTMLParagraphElement
21
+ }
22
+ }
23
+
24
+ const globalState = window as Window & {
25
+ __appraiseLocatorPickerInstalled?: boolean
26
+ __appraiseLocatorPickerDomListenersBound?: boolean
27
+ __appraiseLocatorPickerState?: PickerState
28
+ __appraiseLocatorPickerConfirm?: (payload: CompanionPickedLocatorPayload) => Promise<void>
29
+ __appraiseLocatorPickerCancel?: () => Promise<void>
30
+ }
31
+
32
+ const ROOT_ID = 'appraise-locator-picker-root'
33
+ const STYLE_ID = 'appraise-locator-picker-style'
34
+ const HOVER_CLASS = 'appraise-locator-picker-hover'
35
+ const ACTIVE_CLASS = 'appraise-locator-picker-active'
36
+
37
+ const state: PickerState =
38
+ globalState.__appraiseLocatorPickerState ??
39
+ (globalState.__appraiseLocatorPickerState = {
40
+ picking: false,
41
+ submitting: false,
42
+ hoveredElement: null,
43
+ preview: null,
44
+ error: '',
45
+ elements: {},
46
+ })
47
+
48
+ function stopOverlayEvent(event: Event): void {
49
+ event.preventDefault()
50
+ event.stopPropagation()
51
+ }
52
+
53
+ function normalizeText(value: string | null | undefined): string {
54
+ return (value ?? '').replace(/\s+/g, ' ').trim()
55
+ }
56
+
57
+ function escapeForCss(value: string): string {
58
+ return value.replace(/[^a-zA-Z0-9_-]/g, match => `\\${match}`)
59
+ }
60
+
61
+ function escapeForSelectorText(value: string): string {
62
+ return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
63
+ }
64
+
65
+ function normalizeRoute(value: string): string {
66
+ if (!value) {
67
+ return '/'
68
+ }
69
+
70
+ try {
71
+ return new URL(value).pathname || '/'
72
+ } catch {
73
+ return value.startsWith('/') ? value : `/${value}`
74
+ }
75
+ }
76
+
77
+ function isLikelyStableIdentifier(value: string | null | undefined): value is string {
78
+ if (!value) {
79
+ return false
80
+ }
81
+
82
+ const normalized = value.trim()
83
+ if (!normalized || normalized.length > 120) {
84
+ return false
85
+ }
86
+
87
+ return !/\d{4,}/.test(normalized) && !/[A-Fa-f0-9]{8,}/.test(normalized)
88
+ }
89
+
90
+ function isFormControl(element: Element): boolean {
91
+ return ['input', 'textarea', 'select'].includes(element.tagName.toLowerCase())
92
+ }
93
+
94
+ function getLabelText(element: Element): string {
95
+ if (!isFormControl(element)) {
96
+ return ''
97
+ }
98
+
99
+ const input = element as HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
100
+ if (input.labels && input.labels.length > 0) {
101
+ return normalizeText(
102
+ Array.from(input.labels)
103
+ .map(label => label.textContent || '')
104
+ .join(' '),
105
+ )
106
+ }
107
+
108
+ return ''
109
+ }
110
+
111
+ function getAccessibleName(element: Element): string {
112
+ const ariaLabel = normalizeText(element.getAttribute('aria-label'))
113
+ if (ariaLabel) {
114
+ return ariaLabel
115
+ }
116
+
117
+ const labelledBy = normalizeText(element.getAttribute('aria-labelledby'))
118
+ if (labelledBy) {
119
+ const text = labelledBy
120
+ .split(/\s+/)
121
+ .map(id => document.getElementById(id)?.textContent || '')
122
+ .join(' ')
123
+ const normalized = normalizeText(text)
124
+ if (normalized) {
125
+ return normalized
126
+ }
127
+ }
128
+
129
+ const labelText = getLabelText(element)
130
+ if (labelText) {
131
+ return labelText
132
+ }
133
+
134
+ const alt = normalizeText(element.getAttribute('alt'))
135
+ if (alt) {
136
+ return alt
137
+ }
138
+
139
+ const title = normalizeText(element.getAttribute('title'))
140
+ if (title) {
141
+ return title
142
+ }
143
+
144
+ const placeholder = normalizeText(element.getAttribute('placeholder'))
145
+ if (placeholder) {
146
+ return placeholder
147
+ }
148
+
149
+ return normalizeText(element.textContent).slice(0, 240)
150
+ }
151
+
152
+ function getRole(element: Element): string {
153
+ const explicitRole = normalizeText(element.getAttribute('role'))
154
+ if (explicitRole) {
155
+ return explicitRole
156
+ }
157
+
158
+ const tagName = element.tagName.toLowerCase()
159
+ if (tagName === 'button') {
160
+ return 'button'
161
+ }
162
+
163
+ if (tagName === 'a' && element.hasAttribute('href')) {
164
+ return 'link'
165
+ }
166
+
167
+ if (tagName === 'textarea') {
168
+ return 'textbox'
169
+ }
170
+
171
+ if (tagName === 'select') {
172
+ return 'combobox'
173
+ }
174
+
175
+ if (tagName === 'img') {
176
+ return 'img'
177
+ }
178
+
179
+ if (tagName === 'input') {
180
+ const type = (element.getAttribute('type') || 'text').toLowerCase()
181
+ if (['button', 'submit', 'reset'].includes(type)) {
182
+ return 'button'
183
+ }
184
+
185
+ if (type === 'checkbox') {
186
+ return 'checkbox'
187
+ }
188
+
189
+ if (type === 'radio') {
190
+ return 'radio'
191
+ }
192
+
193
+ return 'textbox'
194
+ }
195
+
196
+ return ''
197
+ }
198
+
199
+ function countRoleNameMatches(role: string, accessibleName: string): number {
200
+ let count = 0
201
+
202
+ for (const candidate of Array.from(document.querySelectorAll('*'))) {
203
+ if (getRole(candidate) !== role) {
204
+ continue
205
+ }
206
+
207
+ if (getAccessibleName(candidate) !== accessibleName) {
208
+ continue
209
+ }
210
+
211
+ count += 1
212
+ if (count > 1) {
213
+ break
214
+ }
215
+ }
216
+
217
+ return count
218
+ }
219
+
220
+ function buildCssSelector(element: Element): string {
221
+ const tagName = element.tagName.toLowerCase()
222
+ const name = element.getAttribute('name')
223
+ const type = element.getAttribute('type')
224
+ const ariaLabel = element.getAttribute('aria-label')
225
+ const parts = [tagName]
226
+
227
+ if (isLikelyStableIdentifier(name)) {
228
+ parts.push(`[name="${escapeForSelectorText(name)}"]`)
229
+ }
230
+
231
+ if (isLikelyStableIdentifier(type)) {
232
+ parts.push(`[type="${escapeForSelectorText(type)}"]`)
233
+ }
234
+
235
+ if (isLikelyStableIdentifier(ariaLabel)) {
236
+ parts.push(`[aria-label="${escapeForSelectorText(ariaLabel)}"]`)
237
+ }
238
+
239
+ if (parts.length === 1) {
240
+ const stableClasses = Array.from(element.classList || []).filter(isLikelyStableIdentifier).slice(0, 2)
241
+ if (stableClasses.length > 0) {
242
+ parts.push(...stableClasses.map(className => `.${escapeForCss(className)}`))
243
+ }
244
+ }
245
+
246
+ return parts.length > 1 ? `css=${parts.join('')}` : ''
247
+ }
248
+
249
+ function buildXPathSelector(element: Element, text: string): string {
250
+ const tagName = element.tagName.toLowerCase()
251
+ const id = element.getAttribute('id')
252
+
253
+ if (id) {
254
+ return `xpath=//*[@id="${id.replace(/"/g, '\\"')}"]`
255
+ }
256
+
257
+ if (text) {
258
+ return `xpath=//${tagName}[normalize-space()="${text.replace(/"/g, '\\"')}"]`
259
+ }
260
+
261
+ return `xpath=//${tagName}`
262
+ }
263
+
264
+ function buildPrimarySelector(element: Element): {
265
+ selector: string
266
+ strategy: CompanionPickedLocatorPayload['strategy']
267
+ } {
268
+ for (const attributeName of ['data-testid', 'data-test', 'data-qa']) {
269
+ const value = element.getAttribute(attributeName)
270
+ if (isLikelyStableIdentifier(value)) {
271
+ return {
272
+ selector: `css=[${attributeName}="${escapeForSelectorText(value)}"]`,
273
+ strategy: 'test-id',
274
+ }
275
+ }
276
+ }
277
+
278
+ const accessibleName = getAccessibleName(element)
279
+ const role = getRole(element)
280
+ if (role && accessibleName && countRoleNameMatches(role, accessibleName) === 1) {
281
+ return {
282
+ selector: `role=${role}[name="${escapeForSelectorText(accessibleName)}"]`,
283
+ strategy: 'role',
284
+ }
285
+ }
286
+
287
+ const labelText = getLabelText(element)
288
+ if (labelText) {
289
+ return {
290
+ selector: `label="${escapeForSelectorText(labelText)}"`,
291
+ strategy: 'label',
292
+ }
293
+ }
294
+
295
+ const placeholder = normalizeText(element.getAttribute('placeholder'))
296
+ if (placeholder) {
297
+ return {
298
+ selector: `placeholder="${escapeForSelectorText(placeholder)}"`,
299
+ strategy: 'placeholder',
300
+ }
301
+ }
302
+
303
+ const id = element.getAttribute('id')
304
+ if (isLikelyStableIdentifier(id)) {
305
+ return {
306
+ selector: `css=#${escapeForCss(id)}`,
307
+ strategy: 'id',
308
+ }
309
+ }
310
+
311
+ const cssSelector = buildCssSelector(element)
312
+ if (cssSelector) {
313
+ return {
314
+ selector: cssSelector,
315
+ strategy: 'css',
316
+ }
317
+ }
318
+
319
+ return {
320
+ selector: buildXPathSelector(element, normalizeText(element.textContent).slice(0, 120)),
321
+ strategy: 'xpath',
322
+ }
323
+ }
324
+
325
+ function buildPreviewPayload(element: Element): CompanionPickedLocatorPayload {
326
+ const selection = buildPrimarySelector(element)
327
+ return {
328
+ selector: selection.selector,
329
+ strategy: selection.strategy,
330
+ currentUrl: window.location.href,
331
+ pathname: normalizeRoute(window.location.href),
332
+ pageTitle: document.title || '',
333
+ tagName: element.tagName.toLowerCase(),
334
+ text: normalizeText(element.textContent).slice(0, 240) || undefined,
335
+ accessibleName: getAccessibleName(element) || undefined,
336
+ }
337
+ }
338
+
339
+ function clearHover(): void {
340
+ if (state.hoveredElement instanceof Element) {
341
+ state.hoveredElement.classList.remove(HOVER_CLASS)
342
+ }
343
+
344
+ state.hoveredElement = null
345
+ }
346
+
347
+ function setPicking(enabled: boolean): void {
348
+ state.picking = enabled
349
+ document.documentElement.classList.toggle(ACTIVE_CLASS, enabled)
350
+ if (!enabled) {
351
+ clearHover()
352
+ }
353
+
354
+ render()
355
+ }
356
+
357
+ function resolveElement(target: EventTarget | null): Element | null {
358
+ if (target instanceof Element) {
359
+ return target
360
+ }
361
+
362
+ if (target instanceof Node) {
363
+ return target.parentElement
364
+ }
365
+
366
+ return null
367
+ }
368
+
369
+ function getMountTarget(): HTMLElement | null {
370
+ return document.body ?? document.documentElement ?? null
371
+ }
372
+
373
+ function ensureStyle(): void {
374
+ if (!document.documentElement) {
375
+ return
376
+ }
377
+
378
+ if (document.getElementById(STYLE_ID)) {
379
+ return
380
+ }
381
+
382
+ const style = document.createElement('style')
383
+ style.id = STYLE_ID
384
+ style.textContent = `
385
+ .${HOVER_CLASS} {
386
+ outline: 2px solid #0f766e !important;
387
+ outline-offset: 2px !important;
388
+ background-color: rgba(15, 118, 110, 0.12) !important;
389
+ cursor: crosshair !important;
390
+ }
391
+
392
+ html.${ACTIVE_CLASS},
393
+ html.${ACTIVE_CLASS} * {
394
+ cursor: crosshair !important;
395
+ }
396
+
397
+ #${ROOT_ID} {
398
+ position: fixed;
399
+ right: 20px;
400
+ bottom: 20px;
401
+ z-index: 2147483647;
402
+ width: min(360px, calc(100vw - 32px));
403
+ padding: 16px;
404
+ border-radius: 18px;
405
+ border: 1px solid rgba(15, 23, 42, 0.14);
406
+ background: rgba(255, 255, 255, 0.96);
407
+ box-shadow: 0 18px 40px rgba(15, 23, 42, 0.18);
408
+ backdrop-filter: blur(12px);
409
+ color: #0f172a;
410
+ font-family: "Segoe UI", "Helvetica Neue", sans-serif;
411
+ }
412
+
413
+ #${ROOT_ID} * {
414
+ box-sizing: border-box;
415
+ font-family: inherit;
416
+ }
417
+
418
+ #${ROOT_ID} .appraise-picker-heading {
419
+ margin: 0 0 6px;
420
+ font-size: 15px;
421
+ font-weight: 700;
422
+ }
423
+
424
+ #${ROOT_ID} .appraise-picker-copy {
425
+ margin: 0;
426
+ color: #475569;
427
+ font-size: 12px;
428
+ line-height: 1.45;
429
+ }
430
+
431
+ #${ROOT_ID} .appraise-picker-status {
432
+ margin: 10px 0 0;
433
+ color: #0f766e;
434
+ font-size: 12px;
435
+ font-weight: 600;
436
+ }
437
+
438
+ #${ROOT_ID} .appraise-picker-card {
439
+ margin-top: 14px;
440
+ padding: 12px;
441
+ border-radius: 14px;
442
+ background: #f8fafc;
443
+ border: 1px solid rgba(148, 163, 184, 0.25);
444
+ }
445
+
446
+ #${ROOT_ID} .appraise-picker-label {
447
+ display: block;
448
+ margin-bottom: 6px;
449
+ color: #334155;
450
+ font-size: 11px;
451
+ font-weight: 700;
452
+ letter-spacing: 0.04em;
453
+ text-transform: uppercase;
454
+ }
455
+
456
+ #${ROOT_ID} .appraise-picker-value {
457
+ margin: 0;
458
+ color: #0f172a;
459
+ font-size: 12px;
460
+ line-height: 1.5;
461
+ white-space: pre-wrap;
462
+ word-break: break-word;
463
+ }
464
+
465
+ #${ROOT_ID} .appraise-picker-actions {
466
+ display: flex;
467
+ flex-wrap: wrap;
468
+ gap: 8px;
469
+ margin-top: 14px;
470
+ }
471
+
472
+ #${ROOT_ID} button {
473
+ border: none;
474
+ border-radius: 999px;
475
+ padding: 10px 14px;
476
+ font-size: 12px;
477
+ font-weight: 700;
478
+ cursor: pointer;
479
+ }
480
+
481
+ #${ROOT_ID} button:disabled {
482
+ cursor: not-allowed;
483
+ opacity: 0.6;
484
+ }
485
+
486
+ #${ROOT_ID} .appraise-picker-primary {
487
+ background: #0f766e;
488
+ color: #ffffff;
489
+ }
490
+
491
+ #${ROOT_ID} .appraise-picker-secondary {
492
+ background: #e2e8f0;
493
+ color: #0f172a;
494
+ }
495
+
496
+ #${ROOT_ID} .appraise-picker-error {
497
+ margin: 10px 0 0;
498
+ color: #b91c1c;
499
+ font-size: 12px;
500
+ line-height: 1.4;
501
+ }
502
+ `
503
+
504
+ document.documentElement.appendChild(style)
505
+ }
506
+
507
+ function ensureRoot(): void {
508
+ const mountTarget = getMountTarget()
509
+ if (!mountTarget) {
510
+ return
511
+ }
512
+
513
+ if (state.elements.root?.isConnected) {
514
+ return
515
+ }
516
+
517
+ const root = document.createElement('div')
518
+ root.id = ROOT_ID
519
+ root.setAttribute('role', 'dialog')
520
+ root.setAttribute('aria-label', 'Appraise locator picker')
521
+
522
+ const heading = document.createElement('p')
523
+ heading.className = 'appraise-picker-heading'
524
+ heading.textContent = 'Appraise Locator Picker'
525
+
526
+ const helperText = document.createElement('p')
527
+ helperText.className = 'appraise-picker-copy'
528
+
529
+ const statusText = document.createElement('p')
530
+ statusText.className = 'appraise-picker-status'
531
+
532
+ const previewCard = document.createElement('div')
533
+ previewCard.className = 'appraise-picker-card'
534
+
535
+ const selectorLabel = document.createElement('span')
536
+ selectorLabel.className = 'appraise-picker-label'
537
+ selectorLabel.textContent = 'Selector'
538
+
539
+ const selectorValue = document.createElement('pre')
540
+ selectorValue.className = 'appraise-picker-value'
541
+
542
+ const metadataLabel = document.createElement('span')
543
+ metadataLabel.className = 'appraise-picker-label'
544
+ metadataLabel.textContent = 'Page'
545
+
546
+ const metadataValue = document.createElement('p')
547
+ metadataValue.className = 'appraise-picker-value'
548
+
549
+ previewCard.appendChild(selectorLabel)
550
+ previewCard.appendChild(selectorValue)
551
+ previewCard.appendChild(metadataLabel)
552
+ previewCard.appendChild(metadataValue)
553
+
554
+ const errorValue = document.createElement('p')
555
+ errorValue.className = 'appraise-picker-error'
556
+
557
+ const actions = document.createElement('div')
558
+ actions.className = 'appraise-picker-actions'
559
+
560
+ const startButton = document.createElement('button')
561
+ startButton.className = 'appraise-picker-primary'
562
+ startButton.type = 'button'
563
+ startButton.textContent = 'Start picking'
564
+ startButton.addEventListener('click', event => {
565
+ stopOverlayEvent(event)
566
+ state.submitting = false
567
+ state.preview = null
568
+ state.error = ''
569
+ setPicking(true)
570
+ })
571
+
572
+ const useButton = document.createElement('button')
573
+ useButton.className = 'appraise-picker-primary'
574
+ useButton.type = 'button'
575
+ useButton.textContent = 'Use selector'
576
+ useButton.addEventListener('click', event => {
577
+ stopOverlayEvent(event)
578
+ if (!state.preview || state.submitting || !globalState.__appraiseLocatorPickerConfirm) {
579
+ return
580
+ }
581
+
582
+ state.submitting = true
583
+ state.error = ''
584
+ render()
585
+
586
+ void globalState
587
+ .__appraiseLocatorPickerConfirm(state.preview)
588
+ .catch(error => {
589
+ state.error = error instanceof Error ? error.message : 'Unable to use selector.'
590
+ })
591
+ .finally(() => {
592
+ state.submitting = false
593
+ render()
594
+ })
595
+ })
596
+
597
+ const pickAgainButton = document.createElement('button')
598
+ pickAgainButton.className = 'appraise-picker-secondary'
599
+ pickAgainButton.type = 'button'
600
+ pickAgainButton.textContent = 'Pick again'
601
+ pickAgainButton.addEventListener('click', event => {
602
+ stopOverlayEvent(event)
603
+ state.submitting = false
604
+ state.preview = null
605
+ state.error = ''
606
+ setPicking(true)
607
+ })
608
+
609
+ const cancelButton = document.createElement('button')
610
+ cancelButton.className = 'appraise-picker-secondary'
611
+ cancelButton.type = 'button'
612
+ cancelButton.textContent = 'Cancel'
613
+ cancelButton.addEventListener('click', event => {
614
+ stopOverlayEvent(event)
615
+ if (state.submitting || !globalState.__appraiseLocatorPickerCancel) {
616
+ return
617
+ }
618
+
619
+ state.submitting = true
620
+ render()
621
+ void globalState.__appraiseLocatorPickerCancel().catch(() => undefined)
622
+ })
623
+
624
+ actions.append(startButton, useButton, pickAgainButton, cancelButton)
625
+ root.append(heading, helperText, statusText, previewCard, errorValue, actions)
626
+ root.addEventListener('pointerdown', event => event.stopPropagation())
627
+ root.addEventListener('click', event => event.stopPropagation())
628
+ mountTarget.appendChild(root)
629
+
630
+ state.elements = {
631
+ root,
632
+ startButton,
633
+ useButton,
634
+ pickAgainButton,
635
+ cancelButton,
636
+ helperText,
637
+ statusText,
638
+ selectorValue,
639
+ metadataValue,
640
+ errorValue,
641
+ }
642
+ }
643
+
644
+ function render(): void {
645
+ ensureStyle()
646
+ ensureRoot()
647
+ document.documentElement.classList.toggle(ACTIVE_CLASS, state.picking)
648
+
649
+ if (state.hoveredElement && !state.hoveredElement.isConnected) {
650
+ state.hoveredElement = null
651
+ }
652
+
653
+ const { helperText, statusText, selectorValue, metadataValue, errorValue, startButton, useButton, pickAgainButton, cancelButton } =
654
+ state.elements
655
+
656
+ if (!helperText || !statusText || !selectorValue || !metadataValue || !errorValue || !startButton || !useButton || !pickAgainButton || !cancelButton) {
657
+ return
658
+ }
659
+
660
+ helperText.textContent = state.preview
661
+ ? 'Review the generated selector, then confirm it or pick a different element.'
662
+ : 'Start picking, hover to highlight, then click one element to preview a single generated selector.'
663
+
664
+ if (state.picking) {
665
+ statusText.textContent = 'Picking mode is on'
666
+ } else if (state.preview) {
667
+ statusText.textContent = 'Selection ready'
668
+ } else {
669
+ statusText.textContent = 'Waiting to start'
670
+ }
671
+
672
+ selectorValue.textContent = state.preview?.selector ?? 'No selector picked yet.'
673
+ metadataValue.textContent = state.preview
674
+ ? [state.preview.pageTitle || '(untitled page)', state.preview.currentUrl].filter(Boolean).join('\n')
675
+ : 'The selected page metadata will appear here.'
676
+ errorValue.textContent = state.error
677
+
678
+ startButton.style.display = state.preview ? 'none' : 'inline-flex'
679
+ useButton.style.display = state.preview ? 'inline-flex' : 'none'
680
+ pickAgainButton.style.display = state.preview ? 'inline-flex' : 'none'
681
+
682
+ useButton.disabled = !state.preview || state.submitting
683
+ pickAgainButton.disabled = state.submitting
684
+ cancelButton.disabled = state.submitting
685
+ }
686
+
687
+ function installOrRender(): void {
688
+ render()
689
+ }
690
+
691
+ if (!globalState.__appraiseLocatorPickerDomListenersBound) {
692
+ globalState.__appraiseLocatorPickerDomListenersBound = true
693
+
694
+ document.addEventListener('DOMContentLoaded', installOrRender)
695
+ window.addEventListener('load', installOrRender)
696
+ }
697
+
698
+ if (globalState.__appraiseLocatorPickerInstalled) {
699
+ installOrRender()
700
+ return
701
+ }
702
+
703
+ globalState.__appraiseLocatorPickerInstalled = true
704
+
705
+ document.addEventListener(
706
+ 'mouseover',
707
+ event => {
708
+ if (!state.picking) {
709
+ return
710
+ }
711
+
712
+ const target = resolveElement(event.target)
713
+ if (!target || target.closest(`#${ROOT_ID}`)) {
714
+ return
715
+ }
716
+
717
+ clearHover()
718
+ target.classList.add(HOVER_CLASS)
719
+ state.hoveredElement = target
720
+ },
721
+ true,
722
+ )
723
+
724
+ document.addEventListener(
725
+ 'mouseout',
726
+ event => {
727
+ if (!state.picking) {
728
+ return
729
+ }
730
+
731
+ const target = resolveElement(event.target)
732
+ if (target instanceof Element) {
733
+ target.classList.remove(HOVER_CLASS)
734
+ }
735
+ },
736
+ true,
737
+ )
738
+
739
+ document.addEventListener(
740
+ 'click',
741
+ event => {
742
+ if (!state.picking) {
743
+ return
744
+ }
745
+
746
+ const target = resolveElement(event.target)
747
+ if (!target || target.closest(`#${ROOT_ID}`)) {
748
+ return
749
+ }
750
+
751
+ event.preventDefault()
752
+ event.stopPropagation()
753
+ event.stopImmediatePropagation()
754
+
755
+ void Promise.resolve()
756
+ .then(() => {
757
+ const preview = buildPreviewPayload(target)
758
+ state.preview = preview
759
+ state.error = ''
760
+ setPicking(false)
761
+ })
762
+ .catch(error => {
763
+ state.error = error instanceof Error ? error.message : 'Unable to inspect that element.'
764
+ setPicking(false)
765
+ })
766
+ },
767
+ true,
768
+ )
769
+
770
+ installOrRender()
771
+ }